Как сохранить пертикулярную часть экрана в виде изображения в flutter

#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. мне нужно обрезать программно