ente/lib/ui/image_editor_page.dart

307 lines
7.4 KiB
Dart
Raw Normal View History

2021-06-02 13:54:31 +00:00
import 'dart:convert';
import 'dart:typed_data';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:image_editor/image_editor.dart';
class ImageEditorPage extends StatefulWidget {
final ImageProvider imageProvider;
final String heroTag;
const ImageEditorPage(this.imageProvider, this.heroTag, {Key key}) : super(key: key);
@override
_ImageEditorPageState createState() => _ImageEditorPageState();
}
class _ImageEditorPageState extends State<ImageEditorPage> {
final GlobalKey<ExtendedImageEditorState> editorKey =
GlobalKey<ExtendedImageEditorState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0x00000000),
elevation: 0,
),
body: Container(
child: Column(
children: [
Expanded(child: _buildImage()),
_buildBottomBar(),
],
),
),
);
}
Widget _buildImage() {
return Hero(
tag: widget.heroTag,
child: ExtendedImage(
image: widget.imageProvider,
extendedImageEditorKey: editorKey,
mode: ExtendedImageMode.editor,
fit: BoxFit.contain,
initEditorConfigHandler: (_) => EditorConfig(
maxScale: 8.0,
cropRectPadding: const EdgeInsets.all(20.0),
hitTestSize: 20.0,
cornerColor: Color.fromRGBO(45, 194, 98, 1.0),
),
),
);
}
Widget _buildBottomBar() {
return Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
_buildFlipButton(),
_buildRotateLeftButton(),
_buildRotateRightButton(),
_buildSaveButton(),
]);
}
Widget _buildFlipButton() {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
flip();
},
child: Container(
width: 80,
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Icon(
Icons.flip,
color: Colors.white.withOpacity(0.8),
size: 20,
),
),
Padding(padding: EdgeInsets.all(2)),
Text(
"flip",
style: TextStyle(
color: Colors.white.withOpacity(0.6),
fontSize: 12,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
Widget _buildRotateLeftButton() {
return GestureDetector(
onTap: () {
rotate(false);
},
child: Container(
width: 80,
child: Column(
children: [
Icon(Icons.rotate_left, color: Colors.white.withOpacity(0.8)),
Padding(padding: EdgeInsets.all(2)),
Text(
"rotate left",
style: TextStyle(
color: Colors.white.withOpacity(0.6),
fontSize: 12,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
Widget _buildRotateRightButton() {
return GestureDetector(
onTap: () {
rotate(true);
},
child: Container(
width: 80,
child: Column(
children: [
Icon(Icons.rotate_right, color: Colors.white.withOpacity(0.8)),
Padding(padding: EdgeInsets.all(2)),
Text(
"rotate right",
style: TextStyle(
color: Colors.white.withOpacity(0.6),
fontSize: 12,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
Widget _buildSaveButton() {
return GestureDetector(
onTap: () {
// todo
},
child: Container(
width: 80,
child: Column(
children: [
Icon(Icons.save_alt_outlined, color: Colors.white.withOpacity(0.8)),
Padding(padding: EdgeInsets.all(2)),
Text(
"save copy",
style: TextStyle(
color: Colors.white.withOpacity(0.6),
fontSize: 12,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
Future<void> crop([bool test = false]) async {
final ExtendedImageEditorState state = editorKey.currentState;
if (state == null) {
return;
}
final Rect rect = state.getCropRect();
if (rect == null) {
// showToast('The crop rect is null.');
return;
}
final EditActionDetails action = state.editAction;
final double radian = action.rotateAngle;
final bool flipHorizontal = action.flipY;
final bool flipVertical = action.flipX;
// final img = await getImageFromEditorKey(editorKey);
final Uint8List img = state.rawImageData;
if (img == null) {
// showToast('The img is null.');
return;
}
final ImageEditorOption option = ImageEditorOption();
option.addOption(ClipOption.fromRect(rect));
option.addOption(
FlipOption(horizontal: flipHorizontal, vertical: flipVertical));
if (action.hasRotateAngle) {
option.addOption(RotateOption(radian.toInt()));
}
option.addOption(ColorOption.saturation(sat));
option.addOption(ColorOption.brightness(bright));
option.addOption(ColorOption.contrast(con));
option.outputFormat = const OutputFormat.png(88);
print(const JsonEncoder.withIndent(' ').convert(option.toJson()));
final DateTime start = DateTime.now();
final Uint8List result = await ImageEditor.editImage(
image: img,
imageEditorOption: option,
);
print('result.length = ${result?.length}');
final Duration diff = DateTime.now().difference(start);
print('image_editor time : $diff');
// showToast('handle duration: $diff',
// duration: const Duration(seconds: 5), dismissOtherToast: true);
if (result == null) return;
showPreviewDialog(result);
}
void flip() {
editorKey.currentState?.flip();
}
void rotate(bool right) {
editorKey.currentState?.rotate(right: right);
}
void showPreviewDialog(Uint8List image) {
showDialog<void>(
context: context,
builder: (BuildContext ctx) => GestureDetector(
onTap: () => Navigator.pop(context),
child: Container(
color: Colors.grey.withOpacity(0.5),
child: Center(
child: SizedBox.fromSize(
size: const Size.square(200),
child: Container(
child: Image.memory(image),
),
),
),
),
),
);
}
double sat = 1;
double bright = 1;
double con = 1;
Widget _buildSat() {
return Slider(
label: 'sat : ${sat.toStringAsFixed(2)}',
onChanged: (double value) {
setState(() {
sat = value;
});
},
value: sat,
min: 0,
max: 2,
);
}
Widget _buildBrightness() {
return Slider(
label: 'brightness : ${bright.toStringAsFixed(2)}',
onChanged: (double value) {
setState(() {
bright = value;
});
},
value: bright,
min: 0,
max: 2,
);
}
Widget _buildCon() {
return Slider(
label: 'con : ${con.toStringAsFixed(2)}',
onChanged: (double value) {
setState(() {
con = value;
});
},
value: con,
min: 0,
max: 4,
);
}
}