如何解决Flutter Web:不带飞镖的裁切图像:io
我在dart:io无法用于Flutter Web的时候写这篇文章。 dart:io具有大多数Flutter成像软件包所需的常用“文件”类型。
尝试裁剪UInt8List格式的未知编码的图像。我花了几天时间构建了一个没有dart的简单裁剪工具:io
在下面检查解决方案。
解决方法
我会把它变成一个包装,但是我正在匆忙完成一个项目,没有时间。
使用以下代码块初始化裁剪路径:
Future<Uint8List> cropResult = await Navigator.push(
context,MaterialPageRoute(
builder: (ctx) => Cropper(
image: _image,),);
_image = await cropResult;
这是管理作物的路线页面。非常基础。
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as Im;
import 'dart:math';
class Cropper extends StatefulWidget {
final Uint8List image;
const Cropper({Key key,this.image}) : super(key: key);
@override
_CropperState createState() => _CropperState(image: image);
}
class _CropperState extends State<Cropper> {
Uint8List image;
Uint8List resultImg;
double scale = 1.0;
double zeroScale; //Initial scale to fit image in bounding crop box.
Offset offset = Offset(0.0,0.0); //Used in translation of image.
double cropRatio = 6 / 10; //aspect ratio of desired crop.
Im.Image decoded; //decoded image to get pixel dimensions
double imgWidth; //img pixel width
double imgHeight; //img pixel height
Size cropArea; //Size of crop bonding box
double cropPad; //Aesthetic crop box padding.
double pXa; //Positive X available in translation
double pYa; //Positive Y available in translation
double totalX; //Total X of scaled image
double totalY; //Total Y of scaled image
Completer _decoded = Completer<bool>();
Completer _encoded = Completer<Uint8List>();
_CropperState({this.image});
@override
initState() {
_decodeImg();
super.initState();
}
_decodeImg() {
if (_decoded.isCompleted) return;
decoded = Im.decodeImage(image);
imgWidth = decoded.width.toDouble();
imgHeight = decoded.height.toDouble();
_decoded?.complete(true);
}
_encodeImage(Im.Image cropped) async {
resultImg = Im.encodePng(cropped);
_encoded?.complete(resultImg);
}
void _cropImage() async {
double xPercent = pXa != 0.0 ? 1.0 - (offset.dx + pXa) / (2 * pXa) : 0.0;
double yPercent = pYa != 0.0 ? 1.0 - (offset.dy + pYa) / (2 * pYa) : 0.0;
double cropXpx = imgWidth * cropArea.width / totalX;
double cropYpx = imgHeight * cropArea.height / totalY;
double x0 = (imgWidth - cropXpx) * xPercent;
double y0 = (imgHeight - cropYpx) * yPercent;
Im.Image cropped = Im.copyCrop(
decoded,x0.toInt(),y0.toInt(),cropXpx.toInt(),cropYpx.toInt());
_encodeImage(cropped);
Navigator.pop(context,_encoded.future);
}
computeRelativeDim(double newScale) {
totalX = newScale * cropArea.height * imgWidth / imgHeight;
totalY = newScale * cropArea.height;
pXa = 0.5 * (totalX - cropArea.width);
pYa = 0.5 * (totalY - cropArea.height);
}
bool init = true;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: Text('Crop Photo'),centerTitle: true,leading: IconButton(
onPressed: _cropImage,tooltip: 'Crop',icon: Icon(Icons.crop),actions: [
RaisedButton(
onPressed: () => Navigator.pop(context,null),child: Text('Cancel'),)
],body: Column(
children: <Widget>[
Expanded(
child: FutureBuilder(
future: _decoded.future,builder: (ctx,snap) {
if (!snap.hasData)
return Center(
child: Text('Loading...'),);
return LayoutBuilder(
builder: (ctx,cstr) {
if (init) {
cropPad = cstr.maxHeight * 0.05;
double tmpWidth = cstr.maxWidth - 2 * cropPad;
double tmpHeight = cstr.maxHeight - 2 * cropPad;
cropArea = (tmpWidth / cropRatio > tmpHeight)
? Size(tmpHeight * cropRatio,tmpHeight)
: Size(tmpWidth,tmpWidth / cropRatio);
zeroScale = cropArea.height / imgHeight;
computeRelativeDim(scale);
init = false;
}
return GestureDetector(
onPanUpdate: (pan) {
double dy;
double dx;
if (pan.delta.dy > 0)
dy = min(pan.delta.dy,pYa - offset.dy);
else
dy = max(pan.delta.dy,-pYa - offset.dy);
if (pan.delta.dx > 0)
dx = min(pan.delta.dx,pXa - offset.dx);
else
dx = max(pan.delta.dx,-pXa - offset.dx);
setState(() => offset += Offset(dx,dy));
},child: Stack(
children: [
Container(
color: Colors.black.withOpacity(0.5),height: cstr.maxHeight,width: cstr.maxWidth,child: ClipRect(
child: Container(
alignment: Alignment.center,height: cropArea.height,width: cropArea.width,child: Transform.translate(
offset: offset,child: Transform.scale(
scale: scale * zeroScale,child: OverflowBox(
maxWidth: imgWidth,maxHeight: imgHeight,child: Image.memory(
image,IgnorePointer(
child: Center(
child: Container(
height: cropArea.height,decoration: BoxDecoration(
border:
Border.all(color: Colors.white,width: 2),],);
},);
},Row(
children: <Widget>[
Text('Scale:'),Expanded(
child: SliderTheme(
data: theme.sliderTheme,child: Slider(
divisions: 50,value: scale,min: 1,max: 2,label: '$scale',onChanged: (n) {
double dy;
double dx;
computeRelativeDim(n);
dy = (offset.dy > 0)
? min(offset.dy,pYa)
: max(offset.dy,-pYa);
dx = (offset.dx > 0)
? min(offset.dx,pXa)
: max(offset.dx,-pXa);
setState(() {
offset = Offset(dx,dy);
scale = n;
});
},);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。