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
134 stars 72 forks source link

Feature Request: Custom appbar with built in functionalities #1

Closed sergiuvintu closed 9 months ago

sergiuvintu commented 9 months ago

I want to thank you for this awesome package. We have the following scenario: We would like to put some custom buttons on the appbar and still have the built in functionalities.(undo,redo, apply) Is there a way we can achieve this?

hm21 commented 9 months ago

It is nice to hear that you like my package. I made some changes in the code. Now it's possible to add your buttons with existing functions. Because my package doesn't use a special state management package, we have to update the widgets that are dependent on the editor in a different way. Below is a complete example of how you can archive it with StreamBuilder. I made the example for every AppBar where I always add one custom IconButton.

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:pro_image_editor/pro_image_editor.dart';

class Demo extends StatefulWidget {
  const Demo({super.key});

  @override
  State<Demo> createState() => DemoState();
}

class DemoState extends State<Demo> {
  final _editorKey = GlobalKey<ProImageEditorState>();
  late StreamController _updateAppBarStream;

  @override
  void initState() {
    _updateAppBarStream = StreamController.broadcast();
    super.initState();
  }

  @override
  void dispose() {
    _updateAppBarStream.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ProImageEditor.network(
      'https://picsum.photos/id/237/2000',
      key: _editorKey,
      onImageEditingComplete: (byte) async {
        Navigator.pop(context);
      },
      onUpdateUI: () {
        _updateAppBarStream.add(null);
      },
      configs: ProImageEditorConfigs(
        customWidgets: ImageEditorCustomWidgets(
          appBar: AppBar(
            automaticallyImplyLeading: false,
            foregroundColor: Colors.white,
            backgroundColor: Colors.black,
            actions: [
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      tooltip: 'Cancel',
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.close),
                      onPressed: _editorKey.currentState?.closeEditor,
                    );
                  }),
              const Spacer(),
              IconButton(
                tooltip: 'Custom Icon',
                padding: const EdgeInsets.symmetric(horizontal: 8),
                icon: const Icon(
                  Icons.bug_report,
                  color: Colors.white,
                ),
                onPressed: () {},
              ),
              StreamBuilder(
                stream: _updateAppBarStream.stream,
                builder: (_, __) {
                  return IconButton(
                    tooltip: 'Undo',
                    padding: const EdgeInsets.symmetric(horizontal: 8),
                    icon: Icon(
                      Icons.undo,
                      color: _editorKey.currentState?.canUndo == true ? Colors.white : Colors.white.withAlpha(80),
                    ),
                    onPressed: _editorKey.currentState?.undoAction,
                  );
                },
              ),
              StreamBuilder(
                stream: _updateAppBarStream.stream,
                builder: (_, __) {
                  return IconButton(
                    tooltip: 'Redo',
                    padding: const EdgeInsets.symmetric(horizontal: 8),
                    icon: Icon(
                      Icons.redo,
                      color: _editorKey.currentState?.canRedo == true ? Colors.white : Colors.white.withAlpha(80),
                    ),
                    onPressed: _editorKey.currentState?.redoAction,
                  );
                },
              ),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      tooltip: 'Done',
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.done),
                      iconSize: 28,
                      onPressed: _editorKey.currentState?.doneEditing,
                    );
                  }),
            ],
          ),
          appBarPaintingEditor: AppBar(
            automaticallyImplyLeading: false,
            foregroundColor: Colors.white,
            backgroundColor: Colors.black,
            actions: [
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.arrow_back),
                      onPressed: _editorKey.currentState?.paintingEditor.currentState?.close,
                    );
                  }),
              const SizedBox(width: 80),
              const Spacer(),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(
                        Icons.line_weight_rounded,
                        color: Colors.white,
                      ),
                      onPressed: _editorKey.currentState?.paintingEditor.currentState?.openLineWeightBottomSheet,
                    );
                  }),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                        padding: const EdgeInsets.symmetric(horizontal: 8),
                        icon: Icon(
                          _editorKey.currentState?.paintingEditor.currentState?.fillBackground == true
                              ? Icons.format_color_reset
                              : Icons.format_color_fill,
                          color: Colors.white,
                        ),
                        onPressed: _editorKey.currentState?.paintingEditor.currentState?.toggleFill);
                  }),
              const Spacer(),
              IconButton(
                tooltip: 'Custom Icon',
                padding: const EdgeInsets.symmetric(horizontal: 8),
                icon: const Icon(
                  Icons.bug_report,
                  color: Colors.white,
                ),
                onPressed: () {},
              ),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      tooltip: 'Undo',
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: Icon(
                        Icons.undo,
                        color: _editorKey.currentState?.paintingEditor.currentState?.canUndo == true ? Colors.white : Colors.white.withAlpha(80),
                      ),
                      onPressed: _editorKey.currentState?.paintingEditor.currentState?.undoAction,
                    );
                  }),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      tooltip: 'Redo',
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: Icon(
                        Icons.redo,
                        color: _editorKey.currentState?.paintingEditor.currentState?.canRedo == true ? Colors.white : Colors.white.withAlpha(80),
                      ),
                      onPressed: _editorKey.currentState?.paintingEditor.currentState?.redoAction,
                    );
                  }),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      tooltip: 'Done',
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.done),
                      iconSize: 28,
                      onPressed: _editorKey.currentState?.paintingEditor.currentState?.done,
                    );
                  }),
            ],
          ),
          appBarTextEditor: AppBar(
            automaticallyImplyLeading: false,
            backgroundColor: Colors.black,
            foregroundColor: Colors.white,
            actions: [
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.arrow_back),
                      onPressed: _editorKey.currentState?.textEditor.currentState?.close,
                    );
                  }),
              const Spacer(),
              IconButton(
                tooltip: 'Custom Icon',
                padding: const EdgeInsets.symmetric(horizontal: 8),
                icon: const Icon(
                  Icons.bug_report,
                  color: Colors.white,
                ),
                onPressed: () {},
              ),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      onPressed: _editorKey.currentState?.textEditor.currentState?.toggleTextAlign,
                      icon: Icon(
                        _editorKey.currentState?.textEditor.currentState?.align == TextAlign.left
                            ? Icons.align_horizontal_left_rounded
                            : _editorKey.currentState?.textEditor.currentState?.align == TextAlign.right
                                ? Icons.align_horizontal_right_rounded
                                : Icons.align_horizontal_center_rounded,
                      ),
                    );
                  }),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      onPressed: _editorKey.currentState?.textEditor.currentState?.toggleBackgroundMode,
                      icon: const Icon(Icons.layers_rounded),
                    );
                  }),
              const Spacer(),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.done),
                      iconSize: 28,
                      onPressed: _editorKey.currentState?.textEditor.currentState?.done,
                    );
                  }),
            ],
          ),
          appBarCropRotateEditor: AppBar(
            automaticallyImplyLeading: false,
            backgroundColor: Colors.black,
            foregroundColor: Colors.white,
            actions: [
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.arrow_back),
                      onPressed: _editorKey.currentState?.cropRotateEditor.currentState?.close,
                    );
                  }),
              const Spacer(),
              IconButton(
                tooltip: 'Custom Icon',
                padding: const EdgeInsets.symmetric(horizontal: 8),
                icon: const Icon(
                  Icons.bug_report,
                  color: Colors.white,
                ),
                onPressed: () {},
              ),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      icon: const Icon(Icons.rotate_90_degrees_ccw_outlined),
                      onPressed: _editorKey.currentState?.cropRotateEditor.currentState?.rotate,
                    );
                  }),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      key: const ValueKey('pro-image-editor-aspect-ratio-btn'),
                      icon: const Icon(Icons.crop),
                      onPressed: _editorKey.currentState?.cropRotateEditor.currentState?.openAspectRatioOptions,
                    );
                  }),
              const Spacer(),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.done),
                      iconSize: 28,
                      onPressed: _editorKey.currentState?.cropRotateEditor.currentState?.done,
                    );
                  }),
            ],
          ),
          appBarFilterEditor: AppBar(
            automaticallyImplyLeading: false,
            backgroundColor: Colors.black,
            foregroundColor: Colors.white,
            actions: [
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.arrow_back),
                      onPressed: _editorKey.currentState?.filterEditor.currentState?.close,
                    );
                  }),
              const Spacer(),
              IconButton(
                tooltip: 'Custom Icon',
                padding: const EdgeInsets.symmetric(horizontal: 8),
                icon: const Icon(
                  Icons.bug_report,
                  color: Colors.white,
                ),
                onPressed: () {},
              ),
              StreamBuilder(
                  stream: _updateAppBarStream.stream,
                  builder: (_, __) {
                    return IconButton(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      icon: const Icon(Icons.done),
                      iconSize: 28,
                      onPressed: _editorKey.currentState?.filterEditor.currentState?.done,
                    );
                  }),
            ],
          ),
        ),
      ),
    );
  }
}
sergiuvintu commented 9 months ago

Everything working fine. Thank you very much for your prompt response. The only issue is that I get "The getter 'canRedo' isn't defined for the type 'ProImageEditorState'. Try importing the library that defines 'canRedo', correcting the name to the name of an existing getter, or defining a getter or field named 'canRedo'" and the same for undo, but otherwise it is perfect. Also, by using stream builder do you think it is possible to modify the bottombar too? I would love to give it a try

hm21 commented 9 months ago

Thank you for your feedback. I can't reproduce the issue with the canRedo and canUndo getters in my tests. Have you updated to the latest version? Did you encounter this error in my demo example or only in your code? Btw, it's important to ensure null safety like this:

_editorKey.currentState?.canRedo == true

Even though the value is a boolean, on the first frame, the state is null. So, to check if it's true, we need to use == true. I understand it might seem a bit unusual, especially if you're used to languages like TypeScript or JavaScript, but in Dart, it's necessary.

To your question if it's possible to modify the BottomAppBar: Yes, you can do that. However, in versions before 2.2.0, this changes will only work in the main editor, not in the painting editor. To make things easier, I've already updated the code for you. I recommend updating to version 2.2.0. Below is an example of how to customize the BottomAppBar. Just remember to keep the height of the BottomAppBar set to kToolbarHeight. Changing it can cause issues with the calculation of helper lines.

import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:pro_image_editor/models/paint_editor/paint_bottom_bar_item.dart';
import 'package:pro_image_editor/models/theme/theme_shared_values.dart';
import 'package:pro_image_editor/pro_image_editor.dart';
import 'package:pro_image_editor/widgets/flat_icon_text_button.dart';
import 'package:pro_image_editor/widgets/pro_image_editor_desktop_mode.dart';

class Demo extends StatefulWidget {
  const Demo({super.key});

  @override
  State<Demo> createState() => DemoState();
}

class DemoState extends State<Demo> {
  final _editorKey = GlobalKey<ProImageEditorState>();
  late StreamController _updateUIStream;
  late ScrollController _bottomBarScrollCtrl;

  @override
  void initState() {
    _updateUIStream = StreamController.broadcast();
    _bottomBarScrollCtrl = ScrollController();
    super.initState();
  }

  @override
  void dispose() {
    _updateUIStream.close();
    _bottomBarScrollCtrl.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var bottomTextStyle = const TextStyle(fontSize: 10.0, color: Colors.white);
    List<PaintModeBottomBarItem> paintModes = [
      const PaintModeBottomBarItem(
        mode: PaintModeE.freeStyle,
        icon: Icons.edit,
        label: 'Freestyle',
      ),
      const PaintModeBottomBarItem(
        mode: PaintModeE.arrow,
        icon: Icons.arrow_right_alt_outlined,
        label: 'Arrow',
      ),
      const PaintModeBottomBarItem(
        mode: PaintModeE.line,
        icon: Icons.horizontal_rule,
        label: 'Line',
      ),
      const PaintModeBottomBarItem(
        mode: PaintModeE.rect,
        icon: Icons.crop_free,
        label: 'Rectangle',
      ),
      const PaintModeBottomBarItem(
        mode: PaintModeE.circle,
        icon: Icons.lens_outlined,
        label: 'Circle',
      ),
      const PaintModeBottomBarItem(
        mode: PaintModeE.dashLine,
        icon: Icons.power_input,
        label: 'Dash line',
      ),
    ];

    return LayoutBuilder(builder: (context, constraints) {
      return ProImageEditor.network(
        'https://picsum.photos/id/237/2000',
        key: _editorKey,
        onImageEditingComplete: (byte) async {},
        onUpdateUI: () {
          _updateUIStream.add(null);
        },
        configs: ProImageEditorConfigs(
          customWidgets: ImageEditorCustomWidgets(
            bottomNavigationBar: StreamBuilder(
                stream: _updateUIStream.stream,
                builder: (_, __) {
                  return Scrollbar(
                    controller: _bottomBarScrollCtrl,
                    scrollbarOrientation: ScrollbarOrientation.top,
                    thickness: isDesktop ? null : 0,
                    child: BottomAppBar(
                      /// kToolbarHeight is important that helperlines will work
                      height: kToolbarHeight,
                      color: Colors.black,
                      padding: EdgeInsets.zero,
                      child: Center(
                        child: SingleChildScrollView(
                          controller: _bottomBarScrollCtrl,
                          scrollDirection: Axis.horizontal,
                          child: ConstrainedBox(
                            constraints: BoxConstraints(
                              minWidth: min(constraints.maxWidth, 500),
                              maxWidth: 500,
                            ),
                            child: Padding(
                              padding: const EdgeInsets.symmetric(horizontal: 12.0),
                              child: Row(
                                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                mainAxisSize: MainAxisSize.min,
                                children: <Widget>[
                                  FlatIconTextButton(
                                    label: Text('Paint', style: bottomTextStyle),
                                    icon: const Icon(
                                      Icons.edit_rounded,
                                      size: 22.0,
                                      color: Colors.white,
                                    ),
                                    onPressed: _editorKey.currentState?.openPaintingEditor,
                                  ),
                                  FlatIconTextButton(
                                    label: Text('Text', style: bottomTextStyle),
                                    icon: const Icon(
                                      Icons.text_fields,
                                      size: 22.0,
                                      color: Colors.white,
                                    ),
                                    onPressed: _editorKey.currentState?.openTextEditor,
                                  ),
                                  FlatIconTextButton(
                                    label: Text('My Button', style: bottomTextStyle.copyWith(color: Colors.amber)),
                                    icon: const Icon(
                                      Icons.new_releases_outlined,
                                      size: 22.0,
                                      color: Colors.amber,
                                    ),
                                    onPressed: () {},
                                  ),
                                  FlatIconTextButton(
                                    label: Text('Crop/ Rotate', style: bottomTextStyle),
                                    icon: const Icon(
                                      Icons.crop_rotate_rounded,
                                      size: 22.0,
                                      color: Colors.white,
                                    ),
                                    onPressed: _editorKey.currentState?.openCropEditor,
                                  ),
                                  FlatIconTextButton(
                                    label: Text('Filter', style: bottomTextStyle),
                                    icon: const Icon(
                                      Icons.filter,
                                      size: 22.0,
                                      color: Colors.white,
                                    ),
                                    onPressed: _editorKey.currentState?.openFilterEditor,
                                  ),
                                  FlatIconTextButton(
                                    label: Text('Emoji', style: bottomTextStyle),
                                    icon: const Icon(
                                      Icons.sentiment_satisfied_alt_rounded,
                                      size: 22.0,
                                      color: Colors.white,
                                    ),
                                    onPressed: _editorKey.currentState?.openEmojiEditor,
                                  ),
                                  /* Be careful with the sticker editor. It's important you add 
                                 your own logic how to load items in `stickerEditorConfigs`.
                                  FlatIconTextButton(
                                    key: const ValueKey('open-sticker-editor-btn'),
                                    label: Text('Sticker', style: bottomTextStyle),
                                    icon: const Icon(
                                      Icons.layers_outlined,
                                      size: 22.0,
                                      color: Colors.white,
                                    ),
                                    onPressed: _editorKey.currentState?.openStickerEditor,
                                  ), */
                                ],
                              ),
                            ),
                          ),
                        ),
                      ),
                    ),
                  );
                }),
            bottomBarPaintingEditor: StreamBuilder(
              stream: _updateUIStream.stream,
              builder: (_, __) {
                return Scrollbar(
                  controller: _bottomBarScrollCtrl,
                  scrollbarOrientation: ScrollbarOrientation.top,
                  thickness: isDesktop ? null : 0,
                  child: BottomAppBar(
                    height: kToolbarHeight,
                    color: Colors.black,
                    padding: EdgeInsets.zero,
                    child: Center(
                      child: SingleChildScrollView(
                        controller: _bottomBarScrollCtrl,
                        scrollDirection: Axis.horizontal,
                        child: ConstrainedBox(
                          constraints: BoxConstraints(
                            minWidth: min(MediaQuery.of(context).size.width, 500),
                            maxWidth: 500,
                          ),
                          child: Wrap(
                            direction: Axis.horizontal,
                            alignment: WrapAlignment.spaceAround,
                            children: <Widget>[
                              FlatIconTextButton(
                                label: Text('My Button', style: bottomTextStyle.copyWith(color: Colors.amber)),
                                icon: const Icon(
                                  Icons.new_releases_outlined,
                                  size: 22.0,
                                  color: Colors.amber,
                                ),
                                onPressed: () {},
                              ),
                              ...List.generate(
                                paintModes.length,
                                (index) => Builder(
                                  builder: (_) {
                                    var item = paintModes[index];
                                    var color = _editorKey.currentState?.paintingEditor.currentState?.paintMode == item.mode
                                        ? imageEditorPrimaryColor
                                        : const Color(0xFFEEEEEE);

                                    return FlatIconTextButton(
                                      label: Text(
                                        item.label,
                                        style: TextStyle(fontSize: 10.0, color: color),
                                      ),
                                      icon: Icon(item.icon, color: color),
                                      onPressed: () {
                                        _editorKey.currentState?.paintingEditor.currentState?.setMode(item.mode);
                                        setState(() {});
                                      },
                                    );
                                  },
                                ),
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
        ),
      );
    });
  }
}
sergiuvintu commented 9 months ago

Yeah you were right. The getters are there after upgrading to 2.2.0. the only issue is that now I get an error when applying changes from the navigator : E/flutter ( 4358): #5 showAdaptiveDialog (package:flutter/src/material/dialog.dart:1463:14) E/flutter ( 4358): #6 ProImageEditorState.closeWarning (package:pro_image_editor/pro_image_editor_main.dart:1523:11) E/flutter ( 4358): #7 ProImageEditorState.build. (package:pro_image_editor/pro_image_editor_main.dart:1648:11) E/flutter ( 4358): #8 ModalRoute.onPopInvoked (package:flutter/src/widgets/routes.dart:1574:30) E/flutter ( 4358): #9 _RouteEntry.pop (package:flutter/src/widgets/navigator.dart:3083:11) E/flutter ( 4358): #10 NavigatorState.pop (package:flutter/src/widgets/navigator.dart:5251:13) E/flutter ( 4358): #11 Navigator.pop (package:flutter/src/widgets/navigator.dart:2569:27) E/flutter ( 4358): #12 LoadingDialog.hide (package:pro_image_editor/widgets/loading_dialog.dart:110:17) E/flutter ( 4358): #13 ProImageEditorState.doneEditing (package:pro_image_editor/pro_image_editor_main.dart:1505:26) E/flutter ( 4358):

hm21 commented 9 months ago

Thank you for your feedback. I'm currently unable to reproduce the issue you mentioned. Could you please share your build method where you're using the ProImageEditor? Additionally, it would be helpful to know on which platform you encountered this issue, or if it's a problem across multiple platforms?

sergiuvintu commented 9 months ago

Sure no problem, I am currently working on Android Emulator Api version 33.

 final _editor = GlobalKey<ProImageEditorState>();
  late StreamController _updateAppBarStream;

  @override
  void initState() {
    super.initState();
    _updateAppBarStream = StreamController.broadcast();
    cameraController.getStickers();
  }

  @override
  void dispose() {
    _updateAppBarStream.close();
    super.dispose();
  }
ProImageEditor.file(
    File(cameraController.editAsset.value.path),
    key: _editor,
    onImageEditingComplete:
        (Uint8List bytes) async {
      final directory =
          await getApplicationDocumentsDirectory();
      if (!File(
              "${directory.path}/${cameraController.editAsset.value.name}")
          .existsSync()) {
        File(directory.path +
                cameraController
                    .editAsset.value.name)
            .createSync(recursive: true);
      }
      File("${directory.path}/${cameraController.editAsset.value.name}")
          .writeAsBytesSync(bytes);
      var index = cameraController.selectedFiles
          .indexWhere((element) =>
              element.path ==
              cameraController
                  .editAsset.value.path);
      cameraController.selectedFiles[index] = XFile(
          "${directory.path}/${cameraController.editAsset.value.name}");
      cameraController.selectedFiles.refresh();
      Get.back();
      return;
    },
    configs: ProImageEditorConfigs(
      activePreferredOrientations: [
        DeviceOrientation.portraitUp,
        DeviceOrientation.portraitDown,
        DeviceOrientation.landscapeLeft,
        DeviceOrientation.landscapeRight,
      ],
      i18n: const I18n(
        various: I18nVarious(),
        paintEditor: I18nPaintingEditor(),
        textEditor: I18nTextEditor(),
        cropRotateEditor: I18nCropRotateEditor(),
        filterEditor: I18nFilterEditor(
            filters: I18nFilters()),
        emojiEditor: I18nEmojiEditor(),
        stickerEditor: I18nStickerEditor(),
        // More translations...
      ),
      helperLines: const HelperLines(
        showVerticalLine: true,
        showHorizontalLine: true,
        showRotateLine: true,
        hitVibration: true,
      ),
      customWidgets: ImageEditorCustomWidgets(
          appBar: AppBar(
        automaticallyImplyLeading: false,
        leading: Row(children: [
          GestureDetector(
            child: const Icon(Icons.chevron_left,
                size: 25.0, color: Colors.white),
            onTap: () {
              Get.back();
            },
          ),
          isImage
              ? const SizedBox()
              : GestureDetector(
                  onTap: () {
                    var index = cameraController
                        .selectedFiles
                        .indexWhere((element) =>
                            element.path ==
                            cameraController
                                .editAsset
                                .value
                                .path);
                    cameraController.selectedFiles
                        .removeAt(index);
                    cameraController.selectedFiles
                        .refresh();
                    cameraController.fetchAssets();
                    Get.back();
                  },
                  child: Icon(
                    FluentIcons.delete_48_regular,
                    color: white,
                    size: 25,
                  ))
        ]),
        foregroundColor: Colors.white,
        backgroundColor: dark,
        actions: [
          StreamBuilder(
            stream: _updateAppBarStream.stream,
            builder: (_, __) {
              return IconButton(
                tooltip: 'Undo',
                padding: const EdgeInsets.symmetric(
                    horizontal: 8),
                icon: Icon(
                  FluentIcons.arrow_undo_48_regular,
                  color: _editor
                              .currentState
                              ?.paintingEditor
                              .currentState
                              ?.canUndo ==
                          true
                      ? Colors.white
                      : Colors.white.withAlpha(80),
                ),
                onPressed: _editor
                    .currentState?.undoAction,
              );
            },
          ),
          StreamBuilder(
            stream: _updateAppBarStream.stream,
            builder: (_, __) {
              return IconButton(
                tooltip: 'Redo',
                padding: const EdgeInsets.symmetric(
                    horizontal: 8),
                icon: Icon(
                  FluentIcons.arrow_redo_48_regular,
                  color: _editor
                              .currentState
                              ?.paintingEditor
                              .currentState
                              ?.canRedo ==
                          true
                      ? Colors.white
                      : Colors.white.withAlpha(80),
                ),
                onPressed: _editor
                    .currentState?.redoAction,
              );
            },
          ),
          StreamBuilder(
              stream: _updateAppBarStream.stream,
              builder: (_, __) {
                return IconButton(
                  tooltip: 'Done',
                  padding:
                      const EdgeInsets.symmetric(
                          horizontal: 8),
                  icon: const Icon(FluentIcons
                      .checkmark_48_regular),
                  iconSize: 28,
                  onPressed: _editor
                      .currentState?.doneEditing,
                );
              }),
        ],
      )),
      imageEditorTheme: ImageEditorTheme(
        layerHoverCursor: SystemMouseCursors.move,
        helperLine: HelperLineTheme(
          horizontalColor: green,
          verticalColor: green,
          rotateColor: red,
        ),
        paintingEditor: const PaintingEditorTheme(),
        textEditor: const TextEditorTheme(),
        cropRotateEditor:
            const CropRotateEditorTheme(),
        filterEditor: const FilterEditorTheme(),
        emojiEditor: const EmojiEditorTheme(),
        stickerEditor: const StickerEditorTheme(),
        background: dark,
        loadingDialogTextColor: light,
        uiOverlayStyle: SystemUiOverlayStyle(
          statusBarColor: dark,
          statusBarIconBrightness: Brightness.light,
          systemNavigationBarIconBrightness:
              Brightness.light,
          statusBarBrightness: Brightness.dark,
          systemNavigationBarColor: dark,
        ),
      ),
      icons: const ImageEditorIcons(
        paintingEditor: IconsPaintingEditor(
            bottomNavBar:
                FluentIcons.paint_bucket_24_regular,
            lineWeight: FluentIcons
                .line_thickness_24_regular,
            freeStyle: FluentIcons.pen_48_regular,
            arrow:
                FluentIcons.arrow_right_48_regular,
            line: FluentIcons
                .line_horizontal_1_28_regular,
            fill: FluentIcons.color_fill_28_filled,
            noFill:
                FluentIcons.color_fill_28_regular,
            rectangle: FluentIcons
                .rectangle_landscape_48_regular,
            circle: FluentIcons.circle_48_regular,
            dashLine:
                FluentIcons.line_dashes_48_regular),
        textEditor: IconsTextEditor(),
        cropRotateEditor: IconsCropRotateEditor(),
        filterEditor: IconsFilterEditor(),
        emojiEditor: IconsEmojiEditor(),
        stickerEditor: IconsStickerEditor(),
        closeEditor:
            FluentIcons.chevron_left_48_regular,
        doneIcon: FluentIcons.checkmark_48_regular,
        applyChanges:
            FluentIcons.checkmark_48_regular,
        backButton:
            FluentIcons.arrow_left_48_regular,
        undoAction:
            FluentIcons.arrow_undo_48_regular,
        redoAction:
            FluentIcons.arrow_redo_48_regular,
        removeElementZone:
            FluentIcons.delete_48_regular,
      ),
      paintEditorConfigs:
          const PaintEditorConfigs(),
      textEditorConfigs: const TextEditorConfigs(),
      cropRotateEditorConfigs:
          const CropRotateEditorConfigs(),
      filterEditorConfigs: const FilterEditorConfigs(
          //   filterList: [
          //   ColorFilterGenerator(
          //       name: "CustomFilter",
          //       filters: [
          //         ColorFilterAddons.brightness(.1),
          //         ColorFilterAddons.contrast(.1),
          //         ColorFilterAddons.saturation(.15),
          //       ])
          // ]
          ),
      emojiEditorConfigs:
          const EmojiEditorConfigs(),
      stickerEditorConfigs: StickerEditorConfigs(
        enabled: true,
        buildStickers: (setLayer) {
          return ClipRRect(
              borderRadius:
                  const BorderRadius.vertical(
                      top: Radius.circular(20)),
              child: Container(
                color: const Color.fromARGB(
                    255, 224, 239, 251),
                child: Obx(
                  () => GridView.builder(
                    padding:
                        const EdgeInsets.all(16),
                    gridDelegate:
                        const SliverGridDelegateWithMaxCrossAxisExtent(
                      maxCrossAxisExtent: 150,
                      mainAxisSpacing: 10,
                      crossAxisSpacing: 10,
                    ),
                    itemCount: cameraController
                        .stickers.length,
                    shrinkWrap: true,
                    itemBuilder: (context, index) {
                      Widget widget = ClipRRect(
                        borderRadius:
                            BorderRadius.circular(
                                7),
                        child: Image.network(
                          cameraController
                              .stickers[index]
                              .stickerImage!,
                          width: 120,
                          height: 120,
                          fit: BoxFit.cover,
                        ),
                      );
                      return GestureDetector(
                        onTap: () =>
                            setLayer(widget),
                        child: MouseRegion(
                          cursor: SystemMouseCursors
                              .click,
                          child: widget,
                        ),
                      );
                    },
                  ),
                ),
              ));
        },
      ),
      designMode: ImageEditorDesignModeE.material,
      heroTag: 'hero',
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
          seedColor: green,
          brightness: Brightness.dark,
        ),
      ),
    ),
  )

I did not put the whole build method as I have a lot of unrelated stuff in there. But this is the usage of the library

hm21 commented 9 months ago

Thanks for sharing. I uploaded a new version 2.2.1 which should resolve this issue. Please tell me if the issue by you still exists after you update the package.

sergiuvintu commented 9 months ago

Yes everything good now. Thank you very much for all the support and keep up the good work!