Closed gd08xxx closed 1 month ago
Hi,
The problem is that when you open the editor as a new page, it's required that you call Navigator.pop
inside the onImageEditingComplete
function. The new page should be opened inside onCloseEditor
so that the loading-dialog does not close it directly. I know it's sometimes a bit confusing why the loading-dialog closes after the onImageEditingComplete
function has been called. The idea here is that when you upload the final image to your server, which will definitely take some time, the same loading-dialog will still be active for the generation and upload part.
Anyway, I had a small issue with the loading-dialog that it was close to early, but in my tests it didn't affect anything. However, that everything will work fine, I still recommend you to update to version 4.3.4
.
Below is a demo code but without the package go_router
. Please let me know it in the case it didn't work for you.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pro_image_editor/pro_image_editor.dart';
void main() async {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Pro-Image-Editor',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue.shade800),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Uint8List? _image;
_openCropRotateEditor() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return CropRotateEditor.network(
'https://picsum.photos/id/230/2000',
initConfigs: CropRotateEditorInitConfigs(
theme: ThemeData.dark(),
convertToUint8List: true,
configs: const ProImageEditorConfigs(
cropRotateEditorConfigs: CropRotateEditorConfigs(
initAspectRatio: 16 / 9,
canChangeAspectRatio: false,
canRotate: false,
),
),
onImageEditingComplete: (Uint8List image) async {
_image = image;
Navigator.pop(context);
},
onCloseEditor: () {
if (_image != null) _openMainEditor();
},
),
);
},
),
);
}
_openMainEditor() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ProImageEditor.memory(
_image!,
configs: const ProImageEditorConfigs(
cropRotateEditorConfigs: CropRotateEditorConfigs(enabled: false),
),
callbacks: ProImageEditorCallbacks(
onImageEditingComplete: (Uint8List image) async {
_image = image;
Navigator.pop(context);
/// TODO: Upload your image...
},
onCloseEditor: () {
if (_image != null) _openPreviewEditor();
},
),
);
},
),
);
}
_openPreviewEditor() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return Scaffold(
appBar: AppBar(
title: const Text('Preview'),
),
body: Center(
child: Image.memory(_image!),
),
);
},
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FilledButton(
onPressed: _openCropRotateEditor,
child: const Text('Test'),
),
),
);
}
}
Thanks a lot for your detailed explanation, I've spent lots of time trying out various scenarios and your example alone worked for the forward flow, however the back flow does not work. I upgraded to ^4.3.5 and edited my code to something similar to your's. It worked 1/10 times, which is very strange.
I suspect the issue is from here in the source code, where you actually hide the dialog if it is mounted, however I have already navigated to a new screen and the mount state has changed:
WidgetsBinding.instance.addPostFrameCallback((_) async {
LoadingDialog loading = LoadingDialog();
await loading.show(
context,
theme: _theme,
configs: configs,
message: i18n.doneLoadingMsg,
);
if (callbacks.onThumbnailGenerated != null) {
if (_imageInfos == null) await decodeImage();
final List<dynamic> results = await Future.wait([
captureEditorImage(),
_controllers.screenshot.getOriginalImage(imageInfos: _imageInfos!),
]);
await callbacks.onThumbnailGenerated!(results[0], results[1]);
} else {
Uint8List? bytes = await captureEditorImage();
await onImageEditingComplete?.call(bytes);
}
**if (mounted) loading.hide(context);**
onCloseEditor?.call();
/// Allow users to continue editing if they didn't close the editor.
setState(() => _processFinalImage = false);
});
Or maybe the issue is due to async and await, where I noticed that you do not use "await" before Navigator.push() in the example that you have given me. In my scenario, using or not using "await" Navigator.push() with GoRoute.push both does not work for me all the time.
As a workaround, I used SizedBox.shrink() as my ImageEditorCustomWidgets.loadingDialog, is there a way to totally disable the LoadingDialog? (Setting ImageEditorCustomWidgets.loadingDialog = null uses the default dialog) My workaround now requires the user to tap on the screen twice to interact with it, because he/she has to disable the invisible custom SizedBox.shrink().
Thanks a lot for your detailed explanation, I've spent lots of time trying out various scenarios and your example alone worked for the forward flow, however the back flow does not work.
To clarify, the issues is only when navigating back from the preview or main-editor page, correct? Navigating back is not straightforward because the editor is designed to be destroyed upon leaving the page. If you keep the editor active and do not call Navigator.pop
inside onImageEditingComplete
, you will have many background isolates or web workers running unnecessarily. Therefore, if the user wants to navigate back, I recommend having them restart the entire process or use import/export functionality to save the state history and transformConfigs to create a new editor instance.
I suspect the issue is from here in the source code, where you actually hide the dialog if it is mounted, however I have already navigated to a new screen and the mount state has changed:
The code will only run when generating the final image, but that should not be a problem. As you can see, I call onCloseEditor
after checking if(mounted) loading.hide(context)
, so when you open the new editor inside onCloseEditor
, if (mounted)
should still evaluate to true.
Or maybe the issue is due to async and await, where I noticed that you do not use "await" before Navigator.push() in the example that you have given me. In my scenario, using or not using "await" Navigator.push() with GoRoute.push both does not work for me all the time.
Yes, it should not be necessary to await
call Navigator.push
because no other code will be executed which needs to await that task.
As a workaround, I used SizedBox.shrink() as my ImageEditorCustomWidgets.loadingDialog, is there a way to totally disable the LoadingDialog?
No, that's not possible because it's necessary that the user can't interact with the editor until we're done creating the final image.
Could you create a single-file example similar to the one I provided above, using the code you are currently trying to implement? If the example requires additional packages, please include the versions you are using. I can then help guide you toward a working solution. Keep in mind to just post code, which is really necessary.
@hm21 I upgraded the package to ^5.0.2 and everything worked fine! Thanks a lot for maintaining this package!
Package Version
^4.3.3
Flutter Version
3.22.2
Platforms
Android, iOS
How to reproduce?
I want the user to capture/select an image, navigate to a crop screen (forcing it to crop 16:9), navigate to an edit screen to let the user edit the image and show a preview screen.
There is a problem with the loading dialog that comes with the package.
What I expect: 1) Capture/select image 2) Navigate to CropScreen(force 16:9) 3) Finished cropping and press check mark on top right [Loading shows and dismiss] 4) Navigate to EditScreen with onImageEditingComplete 5) Finished editing and press checkmark on top right [Loading shows and dismiss] 6) Navigate to PreviewScreen with onImageEditingComplete
What happened: 1) Capture/select image 2) Navigate to CropScreen(force 16:9) 3) Finished cropping and press check mark on top right [Loading keeps showing] 4) Navigate to EditScreen with onImageEditingComplete 5) Need to dismiss loading dialog before can interact with EditScreen 6) Finished editing and press checkmark on top right [Loading keeps showing] 7) Navigate to PreviewScreen with onImageEditingComplete 8) Need to dismiss loading dialog before can interact with PreviewScreen
I saw the readme file regarding async in onImageEditingComplete and the loading dialog, however my IDE forces me to use async because your return type is Future Function(Uint8List bytes)
Logs (optional)
No response
Example code (optional)
EditScreen buildBody()