#flutter
#flutter
Вопрос:
У меня есть список изображений листа ответов на сервере.Мне нужно отобразить эти изображения, и мне нужно выбрать какую-то определенную область этого изображения и сохранить только эту область в качестве изображения. Я выполнил отображение и выделение части, но я не знаю, как сохранить только эту выделенную область в качестве изображения. пожалуйста, помогите мне как можно скорее.
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'dart:io';
import 'package:http/http.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
primaryColor: Colors.red,
scaffoldBackgroundColor: Colors.white,
canvasColor: Colors.transparent),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
List<AnswerSheet> answerSheets = [
AnswerSheet(
imageUrl:
'https://d18x2uyjeekruj.cloudfront.net/wp-content/uploads/2019/04/Scan12.jpg',
downloadedImagePath: '',
selectedOffsets: List()),
AnswerSheet(
imageUrl:
'https://d18x2uyjeekruj.cloudfront.net/wp-content/uploads/2019/04/Scan12.jpg',
downloadedImagePath: '',
selectedOffsets: List())
];
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
resizeToAvoidBottomInset: false,
body: GestureDetector(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) =>
EntryItem(answerSheets[index]),
itemCount: answerSheets.length,
),
));
}
}
class EntryItem extends StatefulWidget {
final AnswerSheet answerSheet;
EntryItem(this.answerSheet);
@override
_EntryItemState createState() => _EntryItemState();
}
class _EntryItemState extends State<EntryItem> {
final GlobalKey _myCanvasKey = new GlobalKey();
MyCustomPainter _customPainter;
Offset _startOffset;
bool isLoading = true;
ui.Image _image;
bool _isImageEditable = true;
BuildContext context;
@override
void initState() {
super.initState();
_downloadImage();
}
@override
Widget build(BuildContext context) {
this.context = context;
if (isLoading) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Center(child: CircularProgressIndicator()));
} else {
return GestureDetector(
onHorizontalDragStart: _onHorizontalDragStart,
onHorizontalDragUpdate: _onHorizontalDragUpdate,
onHorizontalDragEnd: _onHorizontalDragEnd,
child: Container(
margin: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Color(0xFFF4F4F4),
borderRadius: BorderRadius.circular(10.0)),
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: CustomPaint(
key: _myCanvasKey,
size: MediaQuery.of(context).size,
painter: _customPainter,
),
),
));
}
}
Future downloadAndSaveImage(AnswerSheet answerSheet) async {
print("downloading....");
var response = await get(answerSheet.imageUrl);
var documentDirectory = await getApplicationDocumentsDirectory();
File file = File(join(documentDirectory.path, '${DateTime.now()}.jpg'));
file.writeAsBytesSync(response.bodyBytes);
answerSheet.downloadedImagePath = file.path;
print(file.path);
return response.bodyBytes;
}
Future<Null> _downloadImage() async {
List<int> img = await downloadAndSaveImage(widget.answerSheet);
_image = await _loadImage(img);
setState(() {
_customPainter =
MyCustomPainter(answerSheet: widget.answerSheet, image: _image);
isLoading = false;
});
}
Future<ui.Image> _loadImage(List<int> img) async {
final Completer<ui.Image> completer = Completer();
ui.decodeImageFromList(img, (ui.Image img) {
return completer.complete(img);
});
return completer.future;
}
Rect getRect(Offset offset) {
return Rect.fromPoints(offset, Offset(offset.dx 30, offset.dy 30));
}
_onHorizontalDragStart(DragStartDetails detailData) {
if (_isImageEditable) {
_startOffset = detailData.globalPosition;
widget.answerSheet.selectedOffsets.add(_startOffset);
}
}
_onHorizontalDragUpdate(DragUpdateDetails detailData) {
if (_isImageEditable) {
widget.answerSheet.selectedOffsets
.add(Offset(detailData.localPosition.dx, _startOffset.dy));
_myCanvasKey.currentContext.findRenderObject().markNeedsPaint();
}
}
_onHorizontalDragEnd(DragEndDetails details) {
print('_onHorizontalDragEnd');
if (_isImageEditable) {
_myCanvasKey.currentContext.findRenderObject().markNeedsPaint();
_startOffset = null;
}
}
}
class MyCustomPainter extends CustomPainter {
final AnswerSheet answerSheet;
final ui.Image image;
MyCustomPainter({this.answerSheet, this.image});
Paint _paint = new Paint()
..color = Colors.deepOrange.withOpacity(0.3)
..style = PaintingStyle.stroke;
Offset _prvOffset;
@override
void paint(Canvas canvas, Size size) {
_paint.strokeWidth = 40;
Rect myRect = Offset(0.0, 0.0) amp; Size(size.width, size.height);
drawImage(myRect, size, canvas, new Paint(), BoxFit.fill);
if (answerSheet.selectedOffsets.length != 0) {
for (Offset currOffset in answerSheet.selectedOffsets) {
if (_prvOffset != null amp;amp; currOffset != null)
canvas.drawLine(_prvOffset, currOffset, _paint);
_prvOffset = currOffset;
}
_prvOffset = null;
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
Paint getPaint(Color color) {
return new Paint()
..isAntiAlias = true
..strokeWidth = 1.0
..color = color
..style = PaintingStyle.fill;
}
void drawImage(
Rect outputRect, Size size, Canvas canvas, Paint paint, BoxFit fit) {
final Size imageSize =
Size(image.width.toDouble(), image.height.toDouble());
final FittedSizes sizes = applyBoxFit(fit, imageSize, outputRect.size);
final Rect inputSubrect =
Alignment.center.inscribe(sizes.source, Offset.zero amp; imageSize);
final Rect outputSubrect =
Alignment.center.inscribe(sizes.destination, outputRect);
canvas.drawImageRect(image, inputSubrect, outputSubrect, paint);
}
}
class AnswerSheet {
String imageUrl;
String downloadedImagePath;
List<Offset> selectedOffsets;
AnswerSheet({this.imageUrl, this.downloadedImagePath, this.selectedOffsets});
}
Ответ №1:
Простой плагин для захвата виджетов в виде изображений.
Этот плагин переносит ваши виджеты внутрь RenderRepaintBoundary
Этот удобный плагин можно использовать для захвата любого виджета, включая полноэкранные скриншоты и отдельные виджеты, такие как Text ().
Комментарии:
1. но в моей проблеме это не текст, это изображение внутри холста, и мне нужна только эта выделенная часть. не нужен весь экран
2. это сохранит нужную вам часть
Ответ №2:
Хорошо, вам нужно использовать image_cropper или пакеты обрезки. Это полезно для вас. Посмотрите на приведенный ниже пример:
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ImageCropper',
theme: ThemeData.light().copyWith(primaryColor: Colors.deepOrange),
home: MyHomePage(
title: 'ImageCropper',
),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({this.title});
@override
_MyHomePageState createState() => _MyHomePageState();
}
enum AppState {
free,
picked,
cropped,
}
class _MyHomePageState extends State<MyHomePage> {
AppState state;
File imageFile;
@override
void initState() {
super.initState();
state = AppState.free;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: imageFile != null ? Image.file(imageFile) : Container(),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.deepOrange,
onPressed: () {
if (state == AppState.free)
_pickImage();
else if (state == AppState.picked)
_cropImage();
else if (state == AppState.cropped) _clearImage();
},
child: _buildButtonIcon(),
),
);
}
Widget _buildButtonIcon() {
if (state == AppState.free)
return Icon(Icons.add);
else if (state == AppState.picked)
return Icon(Icons.crop);
else if (state == AppState.cropped)
return Icon(Icons.clear);
else
return Container();
}
Future<Null> _pickImage() async {
imageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
if (imageFile != null) {
setState(() {
state = AppState.picked;
});
}
}
Future<Null> _cropImage() async {
File croppedFile = await ImageCropper.cropImage(
sourcePath: imageFile.path,
aspectRatioPresets: Platform.isAndroid
? [
CropAspectRatioPreset.square,
CropAspectRatioPreset.ratio3x2,
CropAspectRatioPreset.original,
CropAspectRatioPreset.ratio4x3,
CropAspectRatioPreset.ratio16x9
]
: [
CropAspectRatioPreset.original,
CropAspectRatioPreset.square,
CropAspectRatioPreset.ratio3x2,
CropAspectRatioPreset.ratio4x3,
CropAspectRatioPreset.ratio5x3,
CropAspectRatioPreset.ratio5x4,
CropAspectRatioPreset.ratio7x5,
CropAspectRatioPreset.ratio16x9
],
androidUiSettings: AndroidUiSettings(
toolbarTitle: 'Cropper',
toolbarColor: Colors.deepOrange,
toolbarWidgetColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false),
iosUiSettings: IOSUiSettings(
title: 'Cropper',
));
if (croppedFile != null) {
imageFile = croppedFile;
setState(() {
state = AppState.cropped;
});
}
}
void _clearImage() {
imageFile = null;
setState(() {
state = AppState.free;
});
}
}
Комментарии:
1. мне нужно обрезать программно