我有一个资产图像,在这个图像上有一个容器,当单击保存按钮时我必须裁剪图像并将裁剪图像显示到下一个屏幕。我尝试这段代码,但是当单击“保存”按钮时显示错误。在flutter中没有任何包的情况下如何解决这个问题?
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
GlobalKey globalKey = GlobalKey();
Offset framePosition = Offset(100, 100); // Initial position of the frame container
double frameSize = 150; // Initial size of the frame container
String backgroundImage = 'images/rose1.png'; // Background image path
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Screen"),
),
body: Stack(
children: [
// Background image
Image.asset(
backgroundImage,
fit: BoxFit.cover,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
),
// Frame container
Positioned(
left: framePosition.dx,
top: framePosition.dy,
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
framePosition += details.delta;
});
},
child: RepaintBoundary(
key: globalKey,
child: Container(
width: frameSize,
height: frameSize,
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2.0),
),
),
),
),
),
// Save button
Positioned(
bottom: 20,
right: 20,
child: ElevatedButton(
onPressed: () => _cropAndNavigate(context),
child: Text("Save and Next"),
),
),
],
),
);
}
Future<void> _cropAndNavigate(BuildContext context) async {
// Capture the image within the frame container
ui.Image image = await _captureImage(globalKey.currentContext!);
// Get frame container position and size relative to the background image
RenderBox containerBox = globalKey.currentContext!.findRenderObject() as RenderBox;
Offset containerPosition = containerBox.localToGlobal(Offset.zero);
double containerWidth = containerBox.size.width;
double containerHeight = containerBox.size.height;
// Calculate the crop coordinates relative to the background image
double left = (framePosition.dx - containerPosition.dx) * 2;
double top = (framePosition.dy - containerPosition.dy) * 2;
double width = frameSize * 2;
double height = frameSize * 2;
// Crop the image
Uint8List croppedImage = await _cropImage(image, left.toInt(), top.toInt(), width.toInt(), height.toInt());
// Navigate to the next screen and pass the cropped image
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NextScreen(croppedImage: croppedImage),
),
);
}
Future<ui.Image> _captureImage(BuildContext context) async {
RenderRepaintBoundary boundary = context.findRenderObject() as RenderRepaintBoundary;
ui.Image image = await boundary.toImage(pixelRatio: 2.0);
return image;
}
Future<Uint8List> _cropImage(ui.Image image, int left, int top, int width, int height) async {
final ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
final Uint8List pngBytes = byteData!.buffer.asUint8List();
// Crop the image
// Here, you should implement your cropping logic based on the given coordinates
// For simplicity, I'll just create a copy of the original image
Uint8List croppedImage = Uint8List.fromList(pngBytes.sublist(left, left + width));
return croppedImage;
}
}
class NextScreen extends StatelessWidget {
final Uint8List croppedImage;
NextScreen({required this.croppedImage});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Next Screen"),
),
body: Center(
child: Image.memory(
croppedImage,
fit: BoxFit.cover,
),
),
);
}
}
我有一个资产图像,在这个图像上有一个容器,当单击保存按钮时我必须裁剪图像并将裁剪图像显示到下一个屏幕。我尝试这段代码,但是当单击“保存”按钮时显示错误。在flutter中没有任何包的情况下如何解决这个问题?
要使用偏移量裁剪
Widget
,您可以使用 CustomClipper
和 SizedOverflowBox
,只需使用一些数学计算对齐即可。
要保存图像,小部件需要位于 RepaintBoundary
下面。如果您想显示其余图像,只需在堆栈中绘制两次即可。
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;
final paintKey = GlobalKey();
class Crop extends StatefulWidget {
const Crop({super.key});
@override
State<Crop> createState() => _CropState();
}
class _CropState extends State<Crop> {
int imageName = 0;
List<File> files = [];
double dx = 25;
double dy = 25;
double width = 100;
double height = 100;
@override
Widget build(BuildContext context) {
final imageWidget = FlutterLogo(size: 200);
return Scaffold(
appBar: AppBar(
title: const Text('Crop'),
),
body: ListView(
children: [
ImageView(
imageWidget: imageWidget,
dx: dx,
dy: dy,
width: width,
height: height,
originSize: Size(200, 200)),
Slider(
value: dx,
onChanged: (value) => setState(() => dx = value),
max: 100),
Slider(
value: dy,
onChanged: (value) => setState(() => dy = value),
max: 100),
Slider(
value: width,
onChanged: (value) => setState(() => width = value),
max: 100),
Slider(
value: height,
onChanged: (value) => setState(() => height = value),
max: 100),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final boundary = paintKey.currentContext!.findRenderObject()
as RenderRepaintBoundary;
final image = await boundary.toImage();
final byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
final pngBytes = byteData!.buffer.asUint8List();
final dir = await getApplicationDocumentsDirectory();
final file = File('${dir.path}/image$imageName.png');
await file.writeAsBytes(pngBytes);
files.add(file);
imageName += 1;
if (context.mounted) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(
title: const Text('Crop'),
),
body: Center(
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
),
itemCount: files.length,
itemBuilder: (context, index) {
return Image.file(files[index]);
},
),
),
),
));
}
// Save the image to the device.
},
child: const Icon(Icons.save),
),
);
}
}
class ImageView extends StatelessWidget {
const ImageView({
super.key,
required this.imageWidget,
required this.dx,
required this.dy,
required this.width,
required this.height,
required this.originSize,
});
final FlutterLogo imageWidget;
final double dx;
final double dy;
final double width;
final double height;
final Size originSize;
@override
Widget build(BuildContext context) {
return Stack(
children: [
imageWidget,
Positioned(
left: dx,
top: dy,
child: Container(
foregroundDecoration: BoxDecoration(
border: Border.all(
color: Colors.red,
width: 2,
),
),
child: ImageCliper(
rect: Rect.fromLTWH(dx, dy, width, height),
size: originSize,
child: imageWidget),
),
),
],
);
}
}
class ImageCliper extends StatelessWidget {
const ImageCliper({
super.key,
required this.child,
required this.rect,
required this.size,
});
final Widget child;
final Rect rect;
final Size size;
@override
Widget build(BuildContext context) {
assert(Rect.fromLTWH(0, 0, size.width, size.height)
.contains(rect.bottomRight));
assert(Rect.fromLTWH(0, 0, size.width, size.height).contains(rect.topLeft));
final widthLeft = size.width - rect.width;
final heightLeft = size.height - rect.height;
final widthAlignment = rect.left / widthLeft * 2 - 1;
final heightAlignment = rect.top / heightLeft * 2 - 1;
return RepaintBoundary(
key: paintKey,
child: SizedOverflowBox(
size: Size(rect.width, rect.height),
alignment: Alignment(widthAlignment, heightAlignment),
child: ClipRect(
clipper: MyCliper(rect),
child: child,
),
),
);
}
}
class MyCliper extends CustomClipper<Rect> {
MyCliper(this.rect);
final Rect rect;
@override
Rect getClip(Size size) {
return rect;
}
@override
bool shouldReclip(covariant MyCliper oldClipper) {
return oldClipper.rect != rect;
}
}