我的目标是让用户选择图像(通过image_picker),然后使用橡皮擦类型的工具来修改图像,使其透明。
vdgimpew1#
你需要做的并不是很直接,但是你可以尝试一下image package,用它可以做很多事情。
ru9i0ody2#
简单示例:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image_edit/image_edit_screen.dart'; import 'package:image_edit/image_processor.dart'; import 'dart:ui' as ui; void main() => runApp( ProviderScope( child: MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, ), home: MyApp(), ), ), ); class MyApp extends StatefulWidget { @override State<StatefulWidget> createState() => MyAppState(); } class MyAppState extends State<MyApp> { @override void initState() { super.initState(); } _enterImageEditController() async { final imageBytes = await ImageProcessor.getImageBytesAsset('lib/basic_0_people.png'); final bgImageBytes = await ImageProcessor.getImageBytesAsset('lib/eraser_bg.jpg'); ui.decodeImageFromList(imageBytes, (result) async { ui.decodeImageFromList(bgImageBytes, (bgResult) async { Navigator.of(context).push( MaterialPageRoute<void>( builder: (BuildContext ctx) => ImageEditScreen( imageBytes: imageBytes, bgImageBytes: bgImageBytes, image: result, bgImage: bgResult, ), ), ); }); }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color.fromARGB(255, 56, 66, 66), floatingActionButtonLocation: FloatingActionButtonLocation.centerTop, floatingActionButton: Wrap( direction: Axis.horizontal, children: [ Center( child: ElevatedButton( onPressed: () { _enterImageEditController(); }, child: Text( 'ImageEdit Controller', style: TextStyle(fontSize: 25), ), ), ), ], ), ); } }
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class ImageProcessor { static Future<Uint8List> getImageBytesAsset(String path) async { WidgetsFlutterBinding.ensureInitialized(); final byteData = await rootBundle.load(path); final uint8List = Uint8List.view(byteData.buffer); return uint8List; } }
import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'dart:ui' as ui; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image_edit/image_edit_service.dart'; class ImageEditScreen extends StatelessWidget { final Uint8List imageBytes; final Uint8List bgImageBytes; final ui.Image image; final ui.Image bgImage; ImageEditScreen({ Key? key, required this.imageBytes, required this.bgImageBytes, required this.image, required this.bgImage, }) : super(key: key); GlobalKey bgImageKey = GlobalKey(); GlobalKey imageKey = GlobalKey(); GlobalKey bgImageEraserKey = GlobalKey(); GlobalKey imageEraserKey = GlobalKey(); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.cyanAccent, floatingActionButtonLocation: FloatingActionButtonLocation.centerTop, floatingActionButton: Wrap( direction: Axis.horizontal, children: [ Center( child: FittedBox( child: SizedBox( width: image.width.toDouble(), height: image.height.toDouble(), child: Consumer( builder: (context, ref, child) { return GestureDetector( onPanStart: (details) { final imageEditService = ref.read(imageEditProvider.notifier); imageEditService.startEdit(details.localPosition); }, onPanUpdate: (details) { final imageEditService = ref.read(imageEditProvider.notifier); imageEditService.updateEdit(details.localPosition); }, child: Container( color: Colors.transparent, child: Stack( children: [ Positioned.fill( child: RepaintBoundary( key: bgImageKey, child: ImageEditPaint( canvasPaths: [], image: bgImage, ), ), ), Positioned.fill( child: Consumer( builder: (context, ref, child) { final imageEditState = ref.watch(imageEditProvider); return RepaintBoundary( key: imageEraserKey, child: ImageEditPaint( canvasPaths: imageEditState.eraserPath, image: image, ), ); }, ), ), ], ), ), ); }, ), ), ), ), Center( child: Consumer( builder: (context, ref, child) { return ElevatedButton( child: Text( 'undo', style: TextStyle( fontSize: 25, ), ), onPressed: () { final imageEditService = ref.read(imageEditProvider.notifier); imageEditService.undo(); }, ); }, ), ), ], ), ); } Future<Uint8List> takeScreenShot(GlobalKey screenshotKey) async { RenderRepaintBoundary boundary = screenshotKey.currentContext! .findRenderObject() as RenderRepaintBoundary; ui.Image image = await boundary.toImage(); ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png); Uint8List pngBytes = byteData!.buffer.asUint8List(); return pngBytes; } } class ImageEditPaint extends StatelessWidget { final List<PlaygroundEraserCanvasPath> canvasPaths; final ui.Image image; const ImageEditPaint({ Key? key, required this.canvasPaths, required this.image, }) : super(key: key); @override Widget build(BuildContext context) { return CustomPaint( isComplex: true, willChange: true, foregroundPainter: EraserPainter( canvasPaths: canvasPaths, image: image, ), ); } } class EraserPainter extends CustomPainter { final List<PlaygroundEraserCanvasPath> canvasPaths; final ui.Image image; EraserPainter({ required this.canvasPaths, required this.image, }); @override void paint(Canvas canvas, Size size) { canvas.saveLayer(Rect.fromLTWH(0, 0, size.width, size.height), Paint()); canvas.drawImage( image, Offset.zero, Paint()..filterQuality = FilterQuality.high, ); if (canvasPaths.isNotEmpty) { for (var canvasPath in canvasPaths) { if (canvasPath.drawPoints.isNotEmpty) { var eraserPaint = Paint() ..strokeWidth = 50 ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round ..strokeJoin = StrokeJoin.round ..blendMode = BlendMode.clear; for (int i = 0; i < canvasPath.drawPoints.length; i++) { Offset drawPoint = canvasPath.drawPoints[i]; if (canvasPath.drawPoints.length > 1) { if (i == 0) { canvas.drawLine(drawPoint, drawPoint, eraserPaint); } else { canvas.drawLine( canvasPath.drawPoints[i - 1], drawPoint, eraserPaint); } } else { canvas.drawLine(drawPoint, drawPoint, eraserPaint); } } } } } canvas.restore(); } @override bool shouldRepaint(covariant EraserPainter oldDelegate) => true; }
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; final imageEditProvider = StateNotifierProvider.autoDispose<ImageEditService, ImageEditState>((ref) { return ImageEditService(); }); class ImageEditService extends StateNotifier<ImageEditState> { ImageEditService() : super(ImageEditState( editType: EditType.eraser, eraserPath: [], paintPath: [])); PlaygroundEraserCanvasPath _currentPath = PlaygroundEraserCanvasPath(drawPoints: []); void startEdit(Offset position) { _currentPath = PlaygroundEraserCanvasPath(drawPoints: [position]); if (state.editType == EditType.eraser) { _editingHistory.add(EditType.eraser); List<PlaygroundEraserCanvasPath> tempList = List.from(state.eraserPath); tempList.add(_currentPath); state = state.copyWith(eraserPath: tempList); } else { _editingHistory.add(EditType.paint); List<PlaygroundEraserCanvasPath> tempList = List.from(state.paintPath); tempList.add(_currentPath); state = state.copyWith(paintPath: tempList); } } void updateEdit(Offset position) { _currentPath.drawPoints.add(position); if (state.editType == EditType.eraser) { List<PlaygroundEraserCanvasPath> tempList = List.from(state.eraserPath); tempList.last = _currentPath; state = state.copyWith(eraserPath: tempList); } else { List<PlaygroundEraserCanvasPath> tempList = List.from(state.paintPath); tempList.last = _currentPath; state = state.copyWith(paintPath: tempList); } } List<EditType> _editingHistory = []; void undo() { if (_editingHistory.isEmpty) return; final historyLast = _editingHistory.last; if (historyLast == EditType.eraser) { List<PlaygroundEraserCanvasPath> tempList = List.from(state.eraserPath); tempList.removeLast(); state = state.copyWith(eraserPath: tempList); } else { List<PlaygroundEraserCanvasPath> tempList = List.from(state.paintPath); tempList.removeLast(); state = state.copyWith(paintPath: tempList); } _editingHistory.removeLast(); } void updateEditType() { state = state.copyWith( editType: state.editType == EditType.eraser ? EditType.paint : EditType.eraser, ); } } class PlaygroundEraserCanvasPath { final List<Offset> drawPoints; PlaygroundEraserCanvasPath({ required this.drawPoints, }); } @immutable class ImageEditState { EditType editType; List<PlaygroundEraserCanvasPath> eraserPath; List<PlaygroundEraserCanvasPath> paintPath; ImageEditState({ required this.editType, required this.eraserPath, required this.paintPath, }); ImageEditState copyWith({ EditType? editType, List<PlaygroundEraserCanvasPath>? eraserPath, List<PlaygroundEraserCanvasPath>? paintPath, }) { return ImageEditState( editType: editType ?? this.editType, eraserPath: eraserPath ?? this.eraserPath, paintPath: paintPath ?? this.paintPath, ); } } enum EditType { eraser, paint, }
image example
2条答案
按热度按时间vdgimpew1#
你需要做的并不是很直接,但是你可以尝试一下image package,用它可以做很多事情。
ru9i0ody2#
简单示例:
image example