hm21 / pro_image_editor

The pro_image_editor is a Flutter widget designed for image editing within your application. It provides a flexible and convenient way to integrate image editing capabilities into your Flutter project.
https://hm21.github.io/pro_image_editor/
BSD 3-Clause "New" or "Revised" License
89 stars 58 forks source link

Issue with Not Getting Edited Image Back from ProImageEditor #22

Closed MarcioPereira155 closed 5 months ago

MarcioPereira155 commented 5 months ago

I'm working on a FlutterFlow project that utilizes ProImageEditor to allow users to edit images within the app. I've encountered an issue where the edited image is not being returned correctly after the editing process.

Here is an overview of what I'm attempting to do:

The user selects an image to edit, which is then passed to ProImageEditor for editing. After editing, I expect the edited image bytes to be returned so I can save them on the user's device and display them in the app. The relevant code for this process is:

// Start of image editing
var editedBytes = await Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) => ProImageEditor.memory(
      widget.uploadedFile.bytes!,
      onImageEditingComplete: (Uint8List bytes) async {
        Navigator.pop(context, bytes); // Expected to return edited bytes
      },
      configs: ProImageEditorConfigs(
        // Editor configurations
      ),
    ),
  ),
);

// Save and display the edited image
if (editedBytes != null) {
  String? imagePath = await saveImageToDevice(editedBytes);
  if (imagePath != null) {
    setState(() {
      FFAppState().imagemEditadaPath = imagePath;
    });
  }
}

The issue is that the bytes of the edited image do not seem to be returned after editing, resulting in an inability to save and display the edited image. The onImageEditingComplete method does not seem to be functioning as expected, or perhaps I am missing a step in the process.

Full code:

// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

import 'package:extended_image/extended_image.dart';
import 'package:pro_image_editor/pro_image_editor.dart';
import 'package:image_editor/image_editor.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:colorfilter_generator/colorfilter_generator.dart';
import 'package:rounded_background_text/rounded_background_text.dart';
import 'package:screenshot/screenshot.dart';
import 'package:vibration/vibration.dart';
import 'dart:async';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart' hide Category;
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:pro_image_editor/widgets/loading_dialog.dart';
import 'package:image/image.dart' as img;
import 'package:http/http.dart';
import 'package:path_provider/path_provider.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:firebase_core/firebase_core.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({
    super.key,
    this.width,
    this.height,
    required this.uploadedFile,
  });

  final double? width; // Largura opcional para o widget
  final double? height; // Altura opcional para o widget
  final FFUploadedFile uploadedFile; // O arquivo enviado pelo usuário

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Uint8List? editedImageBytes; // Armazena os bytes da imagem editada
  // Define uma variável para armazenar os bytes da imagem a serem exibidos
  Uint8List? imageToShow;

  // Salva os bytes da imagem editada no dispositivo e retorna o caminho do arquivo
  Future<String?> saveImageToDevice(Uint8List imageBytes) async {
    try {
      // Obtém o diretório de documentos do aplicativo
      final directory = await getApplicationDocumentsDirectory();
      // Cria um caminho de arquivo único
      String fileName =
          "edited_image_${DateTime.now().millisecondsSinceEpoch}.png";
      File imageFile = File('${directory.path}/$fileName');
      // Escreve os bytes da imagem no arquivo
      await imageFile.writeAsBytes(imageBytes);
      // Retorna o caminho do arquivo onde a imagem foi salva
      return imageFile.path;
    } catch (e) {
      print("Erro ao salvar a imagem no dispositivo: $e");
      return null;
    }
  }

  // Local onde você deseja decidir qual imagem mostrar
  Future<Uint8List?> getImageToShow() async {
    if (FFAppState().imagemEditadaPath != null &&
        FFAppState().imagemEditadaPath.isNotEmpty) {
      // Tenta carregar a imagem editada do caminho do arquivo de forma assíncrona
      try {
        final file = File(FFAppState().imagemEditadaPath);
        return await file.readAsBytes();
      } catch (e) {
        print("Erro ao ler a imagem editada: $e");
        return null;
      }
    } else if (widget.uploadedFile.bytes != null) {
      // Retorna os bytes da imagem original enviada
      return widget.uploadedFile.bytes;
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            // Botão para iniciar a edição de imagem
            OutlinedButton.icon(
              onPressed: () async {
                if (widget.uploadedFile.bytes != null) {
                  // Edita a imagem usando ProImageEditor.memory com os bytes do arquivo enviado
                  var editedBytes = await Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => ProImageEditor.memory(
                        widget.uploadedFile.bytes!,
                        onImageEditingComplete: (Uint8List bytes) async {
                          Navigator.pop(context,
                              bytes); // Retorna os bytes editados para a tela anterior
                        },
                        configs: ProImageEditorConfigs(
                          // Configurações para o ProImageEditor
                          // Inclui localização, personalizações de UI e alternâncias de recursos
                          // Consulte a documentação do ProImageEditor para detalhes
                          i18n: const I18n(
                            various: I18nVarious(
                              loadingDialogMsg: 'Por favor, aguarde...',
                              closeEditorWarningTitle:
                                  'Fechar o Editor de Imagens?',
                              closeEditorWarningMessage:
                                  'Tem certeza de que deseja fechar o Editor de imagens? Suas alterações não serão salvas.',
                              closeEditorWarningConfirmBtn: 'Ok',
                              closeEditorWarningCancelBtn: 'Cancelar',
                            ),
                            paintEditor: I18nPaintingEditor(
                              bottomNavigationBarText: 'Desenhar',
                              freestyle: 'Livre',
                              arrow: 'Seta',
                              line: 'Linha',
                              rectangle: 'Retângulo',
                              circle: 'Círculo',
                              dashLine: 'Linha Tracejada',
                              lineWidth: 'Espessura da linha',
                              toggleFill: 'Alternar Preenchimento',
                              undo: 'Desfazer',
                              redo: 'Refazer',
                              done: 'Feito',
                              back: 'Voltar',
                              smallScreenMoreTooltip: 'Mais',
                            ),
                            textEditor: I18nTextEditor(
                              inputHintText: 'Insira o texto',
                              bottomNavigationBarText: 'Texto',
                              back: 'Voltar',
                              done: 'Feito',
                              textAlign: 'Alinhar texto',
                              backgroundMode: 'Modo de fundo',
                              smallScreenMoreTooltip: 'Mais',
                            ),
                            cropRotateEditor: I18nCropRotateEditor(
                              bottomNavigationBarText: 'Cortar/Girar',
                              rotate: 'Girar',
                              ratio: 'Recortes',
                              back: 'Voltar',
                              done: 'Feito',
                              prepareImageDialogMsg: 'Por favor, aguarde...',
                              applyChangesDialogMsg: 'Por favor, aguarde...',
                              smallScreenMoreTooltip: 'Mais',
                            ),
                            cancel: 'Cancelar',
                            undo: 'Desfazer',
                            redo: 'Refazer',
                            done: 'Feito',
                            remove: 'Remover',
                            doneLoadingMsg:
                                'As alterações estão sendo aplicadas',
                          ),
                          helperLines: const HelperLines(
                            showVerticalLine: true,
                            showHorizontalLine: true,
                            showRotateLine: true,
                            hitVibration: true,
                          ),
                          customWidgets: const ImageEditorCustomWidgets(),
                          imageEditorTheme: const ImageEditorTheme(
                            layerHoverCursor: SystemMouseCursors.move,
                            helperLine: HelperLineTheme(
                              horizontalColor: Color(0XFF4C68FC),
                              verticalColor: Color(0XFF4C68FC),
                              rotateColor: Color(0xFFE91E63),
                            ),
                            paintingEditor: PaintingEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              lineWidthBottomSheetColor: Color(0xFF252728),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(255, 22, 22, 22),
                              bottomBarColor: Color(0xFF000000),
                              bottomBarActiveItemColor: Color(0XFF4C68FC),
                              bottomBarInactiveItemColor: Color(0xFFEEEEEE),
                            ),
                            textEditor: TextEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(155, 0, 0, 0),
                              inputHintColor: Color(0xFFBDBDBD),
                              inputCursorColor: Color(0XFF4C68FC),
                            ),
                            cropRotateEditor: CropRotateEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(255, 22, 22, 22),
                              cropCornerColor: Color(0XFF4C68FC),
                            ),
                            filterEditor: FilterEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              previewTextColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(255, 22, 22, 22),
                            ),
                            blurEditor: BlurEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(255, 22, 22, 22),
                            ),
                            emojiEditor: EmojiEditorTheme(),
                            stickerEditor: StickerEditorTheme(),
                            background: Color.fromARGB(255, 22, 22, 22),
                            loadingDialogTheme: LoadingDialogTheme(
                              textColor: Color(0xFFE1E1E1),
                            ),
                            uiOverlayStyle: SystemUiOverlayStyle(
                              statusBarColor: Color(0x42000000),
                              statusBarIconBrightness: Brightness.light,
                              systemNavigationBarIconBrightness:
                                  Brightness.light,
                              statusBarBrightness: Brightness.dark,
                              systemNavigationBarColor: Color(0xFF000000),
                            ),
                          ),
                          icons: const ImageEditorIcons(
                            paintingEditor: IconsPaintingEditor(
                              bottomNavBar: Icons.edit_rounded,
                              lineWeight: Icons.line_weight_rounded,
                              freeStyle: Icons.edit,
                              arrow: Icons.arrow_right_alt_outlined,
                              line: Icons.horizontal_rule,
                              fill: Icons.format_color_fill,
                              noFill: Icons.format_color_reset,
                              rectangle: Icons.crop_free,
                              circle: Icons.lens_outlined,
                              dashLine: Icons.power_input,
                            ),
                            textEditor: IconsTextEditor(
                              bottomNavBar: Icons.text_fields,
                              alignLeft: Icons.align_horizontal_left_rounded,
                              alignCenter:
                                  Icons.align_horizontal_center_rounded,
                              alignRight: Icons.align_horizontal_right_rounded,
                              backgroundMode: Icons.layers_rounded,
                            ),
                            cropRotateEditor: IconsCropRotateEditor(
                              bottomNavBar: Icons.crop_rotate_rounded,
                              rotate: Icons.rotate_90_degrees_ccw_outlined,
                              aspectRatio: Icons.crop,
                            ),
                            filterEditor: IconsFilterEditor(
                              bottomNavBar: Icons.filter,
                            ),
                            emojiEditor: IconsEmojiEditor(
                              bottomNavBar:
                                  Icons.sentiment_satisfied_alt_rounded,
                            ),
                            stickerEditor: IconsStickerEditor(
                              bottomNavBar: Icons.layers_outlined,
                            ),
                            closeEditor: Icons.clear,
                            doneIcon: Icons.done,
                            applyChanges: Icons.done,
                            backButton: Icons.arrow_back,
                            undoAction: Icons.undo,
                            redoAction: Icons.redo,
                            removeElementZone: Icons.delete_outline_rounded,
                          ),
                          paintEditorConfigs: const PaintEditorConfigs(
                            enabled: true,
                            hasOptionFreeStyle: true,
                            hasOptionArrow: true,
                            hasOptionLine: true,
                            hasOptionRect: true,
                            hasOptionCircle: true,
                            hasOptionDashLine: true,
                            canToggleFill: true,
                            canChangeLineWidth: true,
                            initialFill: false,
                            showColorPicker: true,
                            freeStyleHighPerformanceScaling: true,
                            initialStrokeWidth: 10.0,
                            initialColor: Color(0xffff0000),
                            initialPaintMode: PaintModeE.freeStyle,
                          ),
                          textEditorConfigs: const TextEditorConfigs(
                            enabled: true,
                            canToggleTextAlign: true,
                            canToggleBackgroundMode: true,
                            initFontSize: 24.0,
                            initialTextAlign: TextAlign.center,
                            initialBackgroundColorMode:
                                LayerBackgroundColorModeE.backgroundAndColor,
                          ),
                          cropRotateEditorConfigs: CropRotateEditorConfigs(
                            initAspectRatio: 3.0 /
                                4.0, // Exemplo de como definir um valor inicial
                            aspectRatios: const [
                              AspectRatioItem(
                                  text: 'Personalizado', value: null),
                              AspectRatioItem(
                                  text: 'Original',
                                  value:
                                      0.0), // Supondo que 0.0 representa a proporção original
                              AspectRatioItem(text: '1x1', value: 1.0 / 1.0),
                              AspectRatioItem(text: '4x3', value: 4.0 / 3.0),
                              AspectRatioItem(text: '3x4', value: 3.0 / 4.0),
                              AspectRatioItem(text: '16x9', value: 16.0 / 9.0),
                              AspectRatioItem(text: '9x16', value: 9.0 / 16.0),
                            ],
                            enabled: true,
                            canRotate: true,
                            canChangeAspectRatio: true,
                          ),
                          filterEditorConfigs:
                              FilterEditorConfigs(enabled: false),
                          blurEditorConfigs: const BlurEditorConfigs(
                            enabled: false,
                          ),
                          emojiEditorConfigs: const EmojiEditorConfigs(
                            enabled: false,
                          ),
                          designMode: ImageEditorDesignModeE.material,
                          heroTag: 'hero',
                          theme: ThemeData(
                            useMaterial3: true,
                            colorScheme: ColorScheme.fromSeed(
                              seedColor: Colors.blue.shade800,
                              brightness: Brightness.dark,
                            ),
                          ),
                        ),
                      ),
                    ),
                  );
                  if (editedBytes != null) {
                    // Salva a imagem editada no dispositivo e obtém o caminho do arquivo
                    String? imagePath = await saveImageToDevice(editedBytes);
                    if (imagePath != null) {
                      setState(() {
                        FFAppState().imagemEditadaPath =
                            imagePath; // Atualiza o estado com o novo caminho da imagem
                      });
                    }
                  }
                }
              },
              icon: const Icon(Icons.edit), // Ícone para o botão de edição
              label: const Text('Editar imagem'), // Rótulo do botão
              // Personalizando a aparência do botão
              style: OutlinedButton.styleFrom(
                side: BorderSide(
                    color: Color(0XFF4C68FC),
                    width: 1), // Cor e espessura da borda
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12), // Raio da borda
                ),
                foregroundColor: Color(0XFF4C68FC), // Cor do texto e ícone
              ),
            ),
            const SizedBox(height: 15), // Espaçamento entre widgets
            // Usa um FutureBuilder para esperar pela determinação assíncrona da imagem a mostrar
            Flexible(
              child: FutureBuilder<Uint8List?>(
                future: getImageToShow(),
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.done &&
                      snapshot.hasData) {
                    // A imagem é exibida dentro de um SizedBox para definir suas dimensões máximas.
                    // O widget de imagem em si irá se adaptar para manter sua proporção dentro deste espaço.
                    return SizedBox(
                      width: double.infinity, // Usa toda a largura disponível
                      height: 500, // Altura máxima
                      child: Image.memory(
                        snapshot.data!,
                        fit: BoxFit
                            .contain, // Mantém a proporção da imagem sem cortá-la
                      ),
                    );
                  } else {
                    // Exibe um placeholder ou um widget de carregamento enquanto a imagem está sendo preparada
                    return SizedBox(
                      width: double.infinity,
                      height: 500,
                      child: Center(child: CircularProgressIndicator()),
                    );
                  }
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}
hm21 commented 5 months ago

Thank you for your question.

Can you try the example below and give me feedback if it works?

Uint8List? editedBytes;
await Navigator.of(context).push(
 MaterialPageRoute(
    builder: (context) => ProImageEditor.memory(
      widget.uploadedFile.bytes!,
      onImageEditingComplete: (Uint8List bytes) async {
        editedBytes = bytes;
        // I recommend you that you save your image data here
        Navigator.pop(context);
      },
      configs: ProImageEditorConfigs(
          // configs...
          ),
    ),
  ),
);  

// Save and display the edited image
if (editedBytes != null) {
  String? imagePath = await saveImageToDevice(editedBytes);
  if (imagePath != null) {
    setState(() {
      FFAppState().imagemEditadaPath = imagePath;
    });
  }
}  
MarcioPereira155 commented 5 months ago

Thank you for your previous assistance, however, after following the instructions, I continued to experience challenges with the image editing functionality. Allow me to break down the scenario more precisely:

Problem Flow:

When editing the image using the editing widget, I can complete the editing process without apparent errors. Once complete, I select the "Done" option, which causes the image editor to close as expected, indicating the end of editing. I am redirected to the previous screen, which is the expected behavior. However, I encounter a critical problem here: the edited image is not updated in AppState nor is it displayed in the image widget intended for this purpose.

Research and Tests Performed:

I performed a direct test, trying to send the edited image bytes directly to the image widget, with the aim of displaying the edited image. Unfortunately, the edited image bytes do not appear to be transmitted correctly, indicating that the problem may lie in the stage of capturing or transmitting these bytes after editing.

Modified part of the code:

onPressed: () async {
                if (widget.uploadedFile.bytes != null) {
                  // Edita a imagem usando ProImageEditor.memory com os bytes do arquivo enviado
                  Uint8List? editedBytes;
                  await Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => ProImageEditor.memory(
                        widget.uploadedFile.bytes!,
                        onImageEditingComplete: (Uint8List bytes) async {
                          editedBytes = bytes;
                          // Save your image data here as recommended
                          String? imagePath = await saveImageToDevice(
                              editedBytes!); // Use the ! operator to assert that editedBytes is not null.
                          if (imagePath != null) {
                            setState(() {
                              FFAppState().imagemEditadaPath =
                                  imagePath; // Update the state with the new image path
                            });
                          }
                          Navigator.pop(
                              context); // Close the image editor and return to the previous screen
                        },

Below is the complete code with the changes:

// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

import 'package:extended_image/extended_image.dart';
import 'package:pro_image_editor/pro_image_editor.dart';
import 'package:image_editor/image_editor.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:colorfilter_generator/colorfilter_generator.dart';
import 'package:rounded_background_text/rounded_background_text.dart';
import 'package:screenshot/screenshot.dart';
import 'package:vibration/vibration.dart';
import 'dart:async';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart' hide Category;
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:pro_image_editor/widgets/loading_dialog.dart';
import 'package:image/image.dart' as img;
import 'package:http/http.dart';
import 'package:path_provider/path_provider.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:firebase_core/firebase_core.dart';
import 'dart:typed_data';

class MyHomePage extends StatefulWidget {
  const MyHomePage({
    super.key,
    this.width,
    this.height,
    required this.uploadedFile,
  });

  final double? width; // Largura opcional para o widget
  final double? height; // Altura opcional para o widget
  final FFUploadedFile uploadedFile; // O arquivo enviado pelo usuário

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // Define uma variável para armazenar os bytes da imagem a serem exibidos
  Uint8List? imageToShow;

  // Salva os bytes da imagem editada no dispositivo e retorna o caminho do arquivo
  Future<String?> saveImageToDevice(Uint8List imageBytes) async {
    try {
      // Obtém o diretório de documentos do aplicativo
      final directory = await getApplicationDocumentsDirectory();
      // Cria um caminho de arquivo único
      String fileName =
          "edited_image_${DateTime.now().millisecondsSinceEpoch}.png";
      File imageFile = File('${directory.path}/$fileName');
      // Escreve os bytes da imagem no arquivo
      await imageFile.writeAsBytes(imageBytes);
      // Retorna o caminho do arquivo onde a imagem foi salva
      return imageFile.path;
    } catch (e) {
      print("Erro ao salvar a imagem no dispositivo: $e");
      return null;
    }
  }

  // Local onde você deseja decidir qual imagem mostrar
  Future<Uint8List?> getImageToShow() async {
    if (FFAppState().imagemEditadaPath.isNotEmpty) {
      // Tenta carregar a imagem editada do caminho do arquivo de forma assíncrona
      try {
        final file = File(FFAppState().imagemEditadaPath);
        return await file.readAsBytes();
      } catch (e) {
        print("Erro ao ler a imagem editada: $e");
        return null;
      }
    } else if (widget.uploadedFile.bytes != null) {
      // Retorna os bytes da imagem original enviada
      return widget.uploadedFile.bytes;
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            // Botão para iniciar a edição de imagem
            OutlinedButton.icon(
              onPressed: () async {
                if (widget.uploadedFile.bytes != null) {
                  // Edita a imagem usando ProImageEditor.memory com os bytes do arquivo enviado
                  Uint8List? editedBytes;
                  await Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => ProImageEditor.memory(
                        widget.uploadedFile.bytes!,
                        onImageEditingComplete: (Uint8List bytes) async {
                          editedBytes = bytes;
                          // Save your image data here as recommended
                          String? imagePath = await saveImageToDevice(
                              editedBytes!); // Use the ! operator to assert that editedBytes is not null.
                          if (imagePath != null) {
                            setState(() {
                              FFAppState().imagemEditadaPath =
                                  imagePath; // Update the state with the new image path
                            });
                          }
                          Navigator.pop(
                              context); // Close the image editor and return to the previous screen
                        },
                        configs: ProImageEditorConfigs(
                          // Configurações para o ProImageEditor
                          // Inclui localização, personalizações de UI e alternâncias de recursos
                          // Consulte a documentação do ProImageEditor para detalhes
                          i18n: const I18n(
                            various: I18nVarious(
                              loadingDialogMsg: 'Por favor, aguarde...',
                              closeEditorWarningTitle:
                                  'Fechar o Editor de Imagens?',
                              closeEditorWarningMessage:
                                  'Tem certeza de que deseja fechar o Editor de imagens? Suas alterações não serão salvas.',
                              closeEditorWarningConfirmBtn: 'Ok',
                              closeEditorWarningCancelBtn: 'Cancelar',
                            ),
                            paintEditor: I18nPaintingEditor(
                              bottomNavigationBarText: 'Desenhar',
                              freestyle: 'Livre',
                              arrow: 'Seta',
                              line: 'Linha',
                              rectangle: 'Retângulo',
                              circle: 'Círculo',
                              dashLine: 'Linha Tracejada',
                              lineWidth: 'Espessura da linha',
                              toggleFill: 'Alternar Preenchimento',
                              undo: 'Desfazer',
                              redo: 'Refazer',
                              done: 'Feito',
                              back: 'Voltar',
                              smallScreenMoreTooltip: 'Mais',
                            ),
                            textEditor: I18nTextEditor(
                              inputHintText: 'Insira o texto',
                              bottomNavigationBarText: 'Texto',
                              back: 'Voltar',
                              done: 'Feito',
                              textAlign: 'Alinhar texto',
                              backgroundMode: 'Modo de fundo',
                              smallScreenMoreTooltip: 'Mais',
                            ),
                            cropRotateEditor: I18nCropRotateEditor(
                              bottomNavigationBarText: 'Cortar/Girar',
                              rotate: 'Girar',
                              ratio: 'Recortes',
                              back: 'Voltar',
                              done: 'Feito',
                              prepareImageDialogMsg: 'Por favor, aguarde...',
                              applyChangesDialogMsg: 'Por favor, aguarde...',
                              smallScreenMoreTooltip: 'Mais',
                            ),
                            cancel: 'Cancelar',
                            undo: 'Desfazer',
                            redo: 'Refazer',
                            done: 'Feito',
                            remove: 'Remover',
                            doneLoadingMsg:
                                'As alterações estão sendo aplicadas',
                          ),
                          helperLines: const HelperLines(
                            showVerticalLine: true,
                            showHorizontalLine: true,
                            showRotateLine: true,
                            hitVibration: true,
                          ),
                          customWidgets: const ImageEditorCustomWidgets(),
                          imageEditorTheme: const ImageEditorTheme(
                            layerHoverCursor: SystemMouseCursors.move,
                            helperLine: HelperLineTheme(
                              horizontalColor: Color(0XFF4C68FC),
                              verticalColor: Color(0XFF4C68FC),
                              rotateColor: Color(0xFFE91E63),
                            ),
                            paintingEditor: PaintingEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              lineWidthBottomSheetColor: Color(0xFF252728),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(255, 22, 22, 22),
                              bottomBarColor: Color(0xFF000000),
                              bottomBarActiveItemColor: Color(0XFF4C68FC),
                              bottomBarInactiveItemColor: Color(0xFFEEEEEE),
                            ),
                            textEditor: TextEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(155, 0, 0, 0),
                              inputHintColor: Color(0xFFBDBDBD),
                              inputCursorColor: Color(0XFF4C68FC),
                            ),
                            cropRotateEditor: CropRotateEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(255, 22, 22, 22),
                              cropCornerColor: Color(0XFF4C68FC),
                            ),
                            filterEditor: FilterEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              previewTextColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(255, 22, 22, 22),
                            ),
                            blurEditor: BlurEditorTheme(
                              appBarBackgroundColor: Color(0xFF000000),
                              appBarForegroundColor: Color(0xFFE1E1E1),
                              background: Color.fromARGB(255, 22, 22, 22),
                            ),
                            emojiEditor: EmojiEditorTheme(),
                            stickerEditor: StickerEditorTheme(),
                            background: Color.fromARGB(255, 22, 22, 22),
                            loadingDialogTheme: LoadingDialogTheme(
                              textColor: Color(0xFFE1E1E1),
                            ),
                            uiOverlayStyle: SystemUiOverlayStyle(
                              statusBarColor: Color(0x42000000),
                              statusBarIconBrightness: Brightness.light,
                              systemNavigationBarIconBrightness:
                                  Brightness.light,
                              statusBarBrightness: Brightness.dark,
                              systemNavigationBarColor: Color(0xFF000000),
                            ),
                          ),
                          icons: const ImageEditorIcons(
                            paintingEditor: IconsPaintingEditor(
                              bottomNavBar: Icons.edit_rounded,
                              lineWeight: Icons.line_weight_rounded,
                              freeStyle: Icons.edit,
                              arrow: Icons.arrow_right_alt_outlined,
                              line: Icons.horizontal_rule,
                              fill: Icons.format_color_fill,
                              noFill: Icons.format_color_reset,
                              rectangle: Icons.crop_free,
                              circle: Icons.lens_outlined,
                              dashLine: Icons.power_input,
                            ),
                            textEditor: IconsTextEditor(
                              bottomNavBar: Icons.text_fields,
                              alignLeft: Icons.align_horizontal_left_rounded,
                              alignCenter:
                                  Icons.align_horizontal_center_rounded,
                              alignRight: Icons.align_horizontal_right_rounded,
                              backgroundMode: Icons.layers_rounded,
                            ),
                            cropRotateEditor: IconsCropRotateEditor(
                              bottomNavBar: Icons.crop_rotate_rounded,
                              rotate: Icons.rotate_90_degrees_ccw_outlined,
                              aspectRatio: Icons.crop,
                            ),
                            filterEditor: IconsFilterEditor(
                              bottomNavBar: Icons.filter,
                            ),
                            emojiEditor: IconsEmojiEditor(
                              bottomNavBar:
                                  Icons.sentiment_satisfied_alt_rounded,
                            ),
                            stickerEditor: IconsStickerEditor(
                              bottomNavBar: Icons.layers_outlined,
                            ),
                            closeEditor: Icons.clear,
                            doneIcon: Icons.done,
                            applyChanges: Icons.done,
                            backButton: Icons.arrow_back,
                            undoAction: Icons.undo,
                            redoAction: Icons.redo,
                            removeElementZone: Icons.delete_outline_rounded,
                          ),
                          paintEditorConfigs: const PaintEditorConfigs(
                            enabled: true,
                            hasOptionFreeStyle: true,
                            hasOptionArrow: true,
                            hasOptionLine: true,
                            hasOptionRect: true,
                            hasOptionCircle: true,
                            hasOptionDashLine: true,
                            canToggleFill: true,
                            canChangeLineWidth: true,
                            initialFill: false,
                            showColorPicker: true,
                            freeStyleHighPerformanceScaling: true,
                            initialStrokeWidth: 10.0,
                            initialColor: Color(0xffff0000),
                            initialPaintMode: PaintModeE.freeStyle,
                          ),
                          textEditorConfigs: const TextEditorConfigs(
                            enabled: true,
                            canToggleTextAlign: true,
                            canToggleBackgroundMode: true,
                            initFontSize: 24.0,
                            initialTextAlign: TextAlign.center,
                            initialBackgroundColorMode:
                                LayerBackgroundColorModeE.backgroundAndColor,
                          ),
                          cropRotateEditorConfigs: CropRotateEditorConfigs(
                            initAspectRatio: 3.0 /
                                4.0, // Exemplo de como definir um valor inicial
                            aspectRatios: const [
                              AspectRatioItem(
                                  text: 'Personalizado', value: null),
                              AspectRatioItem(
                                  text: 'Original',
                                  value:
                                      0.0), // Supondo que 0.0 representa a proporção original
                              AspectRatioItem(text: '1x1', value: 1.0 / 1.0),
                              AspectRatioItem(text: '4x3', value: 4.0 / 3.0),
                              AspectRatioItem(text: '3x4', value: 3.0 / 4.0),
                              AspectRatioItem(text: '16x9', value: 16.0 / 9.0),
                              AspectRatioItem(text: '9x16', value: 9.0 / 16.0),
                            ],
                            enabled: true,
                            canRotate: true,
                            canChangeAspectRatio: true,
                          ),
                          filterEditorConfigs:
                              FilterEditorConfigs(enabled: false),
                          blurEditorConfigs: const BlurEditorConfigs(
                            enabled: false,
                          ),
                          emojiEditorConfigs: const EmojiEditorConfigs(
                            enabled: false,
                          ),
                          designMode: ImageEditorDesignModeE.material,
                          heroTag: 'hero',
                          theme: ThemeData(
                            useMaterial3: true,
                            colorScheme: ColorScheme.fromSeed(
                              seedColor: Colors.blue.shade800,
                              brightness: Brightness.dark,
                            ),
                          ),
                        ),
                      ),
                    ),
                  );
                }
              },
              icon: const Icon(Icons.edit), // Ícone para o botão de edição
              label: const Text('Editar imagem'), // Rótulo do botão
              // Personalizando a aparência do botão
              style: OutlinedButton.styleFrom(
                side: BorderSide(
                    color: Color(0XFF4C68FC),
                    width: 1), // Cor e espessura da borda
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12), // Raio da borda
                ),
                foregroundColor: Color(0XFF4C68FC), // Cor do texto e ícone
              ),
            ),
            const SizedBox(height: 15), // Espaçamento entre widgets
            // Usa um FutureBuilder para esperar pela determinação assíncrona da imagem a mostrar
            Flexible(
              child: FutureBuilder<Uint8List?>(
                future: getImageToShow(),
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.done &&
                      snapshot.hasData) {
                    // A imagem é exibida dentro de um SizedBox para definir suas dimensões máximas.
                    // O widget de imagem em si irá se adaptar para manter sua proporção dentro deste espaço.
                    return SizedBox(
                      width: double.infinity, // Usa toda a largura disponível
                      height: 500, // Altura máxima
                      child: Image.memory(
                        snapshot.data!,
                        fit: BoxFit
                            .contain, // Mantém a proporção da imagem sem cortá-la
                      ),
                    );
                  } else {
                    // Exibe um placeholder ou um widget de carregamento enquanto a imagem está sendo preparada
                    return SizedBox(
                      width: double.infinity,
                      height: 500,
                      child: Center(child: CircularProgressIndicator()),
                    );
                  }
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}
hm21 commented 5 months ago

I see that the problem is no longer from the ImageEditor. I think the problem is from the FutureBuilder that it has already finished so when you update the image it is not called again to read it. I recommend using a global variable to make sure it reads the new image as below:

   Uint8List? newImageData;

  // Salva os bytes da imagem editada no dispositivo e retorna o caminho do arquivo
  Future<String?> saveImageToDevice(Uint8List imageBytes) async {
    try {
      newImageData = imageBytes;
      // Obtém o diretório de documentos do aplicativo
      final directory = await getApplicationDocumentsDirectory();
      // Cria um caminho de arquivo único
      String fileName =
          "edited_image_${DateTime.now().millisecondsSinceEpoch}.png";
      File imageFile = File('${directory.path}/$fileName');
      // Escreve os bytes da imagem no arquivo
      await imageFile.writeAsBytes(imageBytes);
      // Retorna o caminho do arquivo onde a imagem foi salva
      return imageFile.path;
    } catch (e) {
      print("Erro ao salvar a imagem no dispositivo: $e");
      return null;
    }
  }

and

 return SizedBox(
    width: double.infinity, // Usa toda a largura disponível
    height: 500, // Altura máxima
    child: Image.memory(
     newImageData ?? snapshot.data!,
      fit: BoxFit
          .contain, // Mantém a proporção da imagem sem cortá-la
    ),
  );