Переглянути джерело

feat: add mutex to property list

Vincent Chan 2 роки тому
батько
коміт
8229371f63

+ 66 - 59
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart

@@ -1,6 +1,7 @@
 import 'dart:collection';
 import 'dart:collection';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
 import 'package:app_flowy/plugins/grid/application/cell/select_option_editor_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/cell/select_option_editor_bloc.dart';
+import 'package:appflowy_popover/popover.dart';
 
 
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra/theme.dart';
@@ -228,76 +229,82 @@ class _CreateOptionCell extends StatelessWidget {
   }
   }
 }
 }
 
 
-class _SelectOptionCell extends StatelessWidget {
+class _SelectOptionCell extends StatefulWidget {
   final SelectOptionPB option;
   final SelectOptionPB option;
   final bool isSelected;
   final bool isSelected;
   const _SelectOptionCell(this.option, this.isSelected, {Key? key})
   const _SelectOptionCell(this.option, this.isSelected, {Key? key})
       : super(key: key);
       : super(key: key);
 
 
+  @override
+  State<_SelectOptionCell> createState() => _SelectOptionCellState();
+}
+
+class _SelectOptionCellState extends State<_SelectOptionCell> {
+  late PopoverController _popoverController;
+
+  @override
+  void initState() {
+    _popoverController = PopoverController();
+    super.initState();
+  }
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
     final theme = context.watch<AppTheme>();
-    return SizedBox(
-      height: GridSize.typeOptionItemHeight,
-      child: Row(
-        children: [
-          Flexible(
-            fit: FlexFit.loose,
-            child: SelectOptionTagCell(
-              option: option,
-              onSelected: (option) {
-                context
-                    .read<SelectOptionCellEditorBloc>()
-                    .add(SelectOptionEditorEvent.selectOption(option.id));
-              },
-              children: [
-                if (isSelected)
-                  Padding(
-                    padding: const EdgeInsets.only(right: 6),
-                    child: svgWidget("grid/checkmark"),
-                  ),
-              ],
+    return Popover(
+      controller: _popoverController,
+      offset: const Offset(20, 0),
+      child: SizedBox(
+        height: GridSize.typeOptionItemHeight,
+        child: Row(
+          children: [
+            Flexible(
+              fit: FlexFit.loose,
+              child: SelectOptionTagCell(
+                option: widget.option,
+                onSelected: (option) {
+                  context
+                      .read<SelectOptionCellEditorBloc>()
+                      .add(SelectOptionEditorEvent.selectOption(option.id));
+                },
+                children: [
+                  if (widget.isSelected)
+                    Padding(
+                      padding: const EdgeInsets.only(right: 6),
+                      child: svgWidget("grid/checkmark"),
+                    ),
+                ],
+              ),
             ),
             ),
-          ),
-          FlowyIconButton(
-            width: 30,
-            onPressed: () => _showEditPannel(context),
-            iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
-            icon: svgWidget("editor/details", color: theme.iconColor),
-          )
-        ],
+            FlowyIconButton(
+              width: 30,
+              onPressed: () => _popoverController.show(),
+              iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
+              icon: svgWidget("editor/details", color: theme.iconColor),
+            )
+          ],
+        ),
       ),
       ),
-    );
-  }
-
-  void _showEditPannel(BuildContext context) {
-    final pannel = SelectOptionTypeOptionEditor(
-      option: option,
-      onDeleted: () {
-        context
-            .read<SelectOptionCellEditorBloc>()
-            .add(SelectOptionEditorEvent.deleteOption(option));
-      },
-      onUpdated: (updatedOption) {
-        context
-            .read<SelectOptionCellEditorBloc>()
-            .add(SelectOptionEditorEvent.updateOption(updatedOption));
+      popupBuilder: (BuildContext popoverContext) {
+        return OverlayContainer(
+          constraints: BoxConstraints.loose(const Size(200, 300)),
+          child: SelectOptionTypeOptionEditor(
+            option: widget.option,
+            onDeleted: () {
+              context
+                  .read<SelectOptionCellEditorBloc>()
+                  .add(SelectOptionEditorEvent.deleteOption(widget.option));
+            },
+            onUpdated: (updatedOption) {
+              context
+                  .read<SelectOptionCellEditorBloc>()
+                  .add(SelectOptionEditorEvent.updateOption(updatedOption));
+            },
+            key: ValueKey(widget.option
+                .id), // Use ValueKey to refresh the UI, otherwise, it will remain the old value.
+          ),
+        );
       },
       },
-      key: ValueKey(option
-          .id), // Use ValueKey to refresh the UI, otherwise, it will remain the old value.
-    );
-    final overlayIdentifier = (SelectOptionTypeOptionEditor).toString();
-
-    FlowyOverlay.of(context).remove(overlayIdentifier);
-    FlowyOverlay.of(context).insertWithAnchor(
-      widget: OverlayContainer(
-        child: pannel,
-        constraints: BoxConstraints.loose(const Size(200, 300)),
-      ),
-      identifier: overlayIdentifier,
-      anchorContext: context,
-      anchorDirection: AnchorDirection.rightWithCenterAligned,
-      anchorOffset: Offset(2 * overlayContainerPadding.left, 0),
     );
     );
   }
   }
 }
 }

+ 1 - 0
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart

@@ -35,6 +35,7 @@ class _GridFieldCellState extends State<GridFieldCell> {
         builder: (context, state) {
         builder: (context, state) {
           final button = Popover(
           final button = Popover(
             controller: popover,
             controller: popover,
+            direction: PopoverDirection.bottomWithLeftAligned,
             child: FieldCellButton(
             child: FieldCellButton(
               field: state.field,
               field: state.field,
               onTap: () => popover.show(),
               onTap: () => popover.show(),

+ 0 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart

@@ -2,7 +2,6 @@ import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart'
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:appflowy_popover/popover.dart';
 import 'package:appflowy_popover/popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:easy_localization/easy_localization.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';

+ 20 - 12
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart

@@ -2,8 +2,10 @@ import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/grid/application/prelude.dart';
 import 'package:app_flowy/plugins/grid/application/prelude.dart';
+import 'package:appflowy_popover/popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
@@ -154,19 +156,25 @@ class CreateFieldButton extends StatelessWidget {
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
     final theme = context.watch<AppTheme>();
 
 
-    return FlowyButton(
-      text: const FlowyText.medium('New column', fontSize: 12),
-      hoverColor: theme.shader6,
-      onTap: () {
-        // FieldEditorPopOver.show(
-        //   context,
-        //   anchorContext: context,
-        //   gridId: gridId,
-        //   fieldName: "",
-        //   typeOptionLoader: NewFieldTypeOptionLoader(gridId: gridId),
-        // )
+    return Popover(
+      triggerActions: PopoverTriggerActionFlags.click,
+      direction: PopoverDirection.bottomWithRightAligned,
+      child: FlowyButton(
+        text: const FlowyText.medium('New column', fontSize: 12),
+        hoverColor: theme.shader6,
+        onTap: () {},
+        leftIcon: svgWidget("home/add"),
+      ),
+      popupBuilder: (BuildContext popover) {
+        return OverlayContainer(
+          constraints: BoxConstraints.loose(const Size(240, 200)),
+          child: FieldEditor(
+            gridId: gridId,
+            fieldName: "",
+            typeOptionLoader: NewFieldTypeOptionLoader(gridId: gridId),
+          ),
+        );
       },
       },
-      leftIcon: svgWidget("home/add"),
     );
     );
   }
   }
 }
 }

+ 2 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart

@@ -77,6 +77,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
               context
               context
                   .read<DateTypeOptionBloc>()
                   .read<DateTypeOptionBloc>()
                   .add(DateTypeOptionEvent.didSelectDateFormat(format));
                   .add(DateTypeOptionEvent.didSelectDateFormat(format));
+              PopoverContainerState.of(popoverContext).closeAll();
             },
             },
           ),
           ),
         );
         );
@@ -100,7 +101,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
                 context
                 context
                     .read<DateTypeOptionBloc>()
                     .read<DateTypeOptionBloc>()
                     .add(DateTypeOptionEvent.didSelectTimeFormat(format));
                     .add(DateTypeOptionEvent.didSelectTimeFormat(format));
-                PopoverContainerState.of(popoverContext).close();
+                PopoverContainerState.of(popoverContext).closeAll();
               }),
               }),
         );
         );
       },
       },

+ 5 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/multi_select.dart

@@ -1,6 +1,7 @@
 import 'package:app_flowy/plugins/grid/application/field/type_option/multi_select_type_option.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/multi_select_type_option.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
+import 'package:appflowy_popover/popover.dart';
 
 
 import '../field_type_option_editor.dart';
 import '../field_type_option_editor.dart';
 import 'builder.dart';
 import 'builder.dart';
@@ -39,7 +40,10 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget {
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return SelectOptionTypeOptionWidget(
     return SelectOptionTypeOptionWidget(
       options: selectOptionAction.typeOption.options,
       options: selectOptionAction.typeOption.options,
-      beginEdit: () => overlayDelegate.hideOverlay(context),
+      beginEdit: () {
+        overlayDelegate.hideOverlay(context);
+        PopoverContainerState.of(context).closeAll();
+      },
       overlayDelegate: overlayDelegate,
       overlayDelegate: overlayDelegate,
       typeOptionAction: selectOptionAction,
       typeOptionAction: selectOptionAction,
       // key: ValueKey(state.typeOption.hashCode),
       // key: ValueKey(state.typeOption.hashCode),

+ 2 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart

@@ -79,7 +79,7 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
                   ],
                   ],
                 ),
                 ),
               ),
               ),
-              popupBuilder: (BuildContext context) {
+              popupBuilder: (BuildContext popoverContext) {
                 return OverlayContainer(
                 return OverlayContainer(
                   constraints: BoxConstraints.loose(const Size(460, 440)),
                   constraints: BoxConstraints.loose(const Size(460, 440)),
                   child: NumberFormatList(
                   child: NumberFormatList(
@@ -87,6 +87,7 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
                       context
                       context
                           .read<NumberTypeOptionBloc>()
                           .read<NumberTypeOptionBloc>()
                           .add(NumberTypeOptionEvent.didSelectFormat(format));
                           .add(NumberTypeOptionEvent.didSelectFormat(format));
+                      PopoverContainerState.of(popoverContext).closeAll();
                     },
                     },
                     selectedFormat: state.typeOption.format,
                     selectedFormat: state.typeOption.format,
                   ),
                   ),

+ 55 - 37
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart

@@ -1,6 +1,8 @@
 import 'package:app_flowy/plugins/grid/application/field/type_option/select_option_type_option_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/select_option_type_option_bloc.dart';
+import 'package:appflowy_popover/popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
@@ -143,54 +145,70 @@ class _OptionList extends StatelessWidget {
   _OptionCell _makeOptionCell(BuildContext context, SelectOptionPB option) {
   _OptionCell _makeOptionCell(BuildContext context, SelectOptionPB option) {
     return _OptionCell(
     return _OptionCell(
       option: option,
       option: option,
-      onSelected: (option) {
-        final pannel = SelectOptionTypeOptionEditor(
-          option: option,
-          onDeleted: () {
-            delegate.hideOverlay(context);
-            context
-                .read<SelectOptionTypeOptionBloc>()
-                .add(SelectOptionTypeOptionEvent.deleteOption(option));
-          },
-          onUpdated: (updatedOption) {
-            delegate.hideOverlay(context);
-            context
-                .read<SelectOptionTypeOptionBloc>()
-                .add(SelectOptionTypeOptionEvent.updateOption(updatedOption));
-          },
-          key: ValueKey(option.id),
-        );
-        delegate.showOverlay(context, pannel);
-      },
     );
     );
   }
   }
 }
 }
 
 
-class _OptionCell extends StatelessWidget {
+class _OptionCell extends StatefulWidget {
   final SelectOptionPB option;
   final SelectOptionPB option;
-  final Function(SelectOptionPB) onSelected;
-  const _OptionCell({
-    required this.option,
-    required this.onSelected,
-    Key? key,
-  }) : super(key: key);
+  const _OptionCell({required this.option, Key? key}) : super(key: key);
+
+  @override
+  State<_OptionCell> createState() => _OptionCellState();
+}
+
+class _OptionCellState extends State<_OptionCell> {
+  late PopoverController _popoverController;
+
+  @override
+  void initState() {
+    _popoverController = PopoverController();
+    super.initState();
+  }
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
     final theme = context.watch<AppTheme>();
 
 
-    return SizedBox(
-      height: GridSize.typeOptionItemHeight,
-      child: SelectOptionTagCell(
-        option: option,
-        onSelected: onSelected,
-        children: [
-          svgWidget(
-            "grid/details",
-            color: theme.iconColor,
-          ),
-        ],
+    return Popover(
+      controller: _popoverController,
+      offset: const Offset(20, 0),
+      child: SizedBox(
+        height: GridSize.typeOptionItemHeight,
+        child: SelectOptionTagCell(
+          option: widget.option,
+          onSelected: (SelectOptionPB pb) {
+            _popoverController.show();
+          },
+          children: [
+            svgWidget(
+              "grid/details",
+              color: theme.iconColor,
+            ),
+          ],
+        ),
       ),
       ),
+      popupBuilder: (BuildContext popoverContext) {
+        return OverlayContainer(
+          constraints: BoxConstraints.loose(const Size(460, 440)),
+          child: SelectOptionTypeOptionEditor(
+            option: widget.option,
+            onDeleted: () {
+              context
+                  .read<SelectOptionTypeOptionBloc>()
+                  .add(SelectOptionTypeOptionEvent.deleteOption(widget.option));
+              PopoverContainerState.of(popoverContext).closeAll();
+            },
+            onUpdated: (updatedOption) {
+              context
+                  .read<SelectOptionTypeOptionBloc>()
+                  .add(SelectOptionTypeOptionEvent.updateOption(updatedOption));
+              PopoverContainerState.of(popoverContext).closeAll();
+            },
+            key: ValueKey(widget.option.id),
+          ),
+        );
+      },
     );
     );
   }
   }
 }
 }

+ 5 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/single_select.dart

@@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/grid/application/field/type_option/single_sele
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import '../field_type_option_editor.dart';
 import '../field_type_option_editor.dart';
+import 'package:appflowy_popover/popover.dart';
 import 'builder.dart';
 import 'builder.dart';
 import 'select_option.dart';
 import 'select_option.dart';
 
 
@@ -38,7 +39,10 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget {
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return SelectOptionTypeOptionWidget(
     return SelectOptionTypeOptionWidget(
       options: selectOptionAction.typeOption.options,
       options: selectOptionAction.typeOption.options,
-      beginEdit: () => overlayDelegate.hideOverlay(context),
+      beginEdit: () {
+        overlayDelegate.hideOverlay(context);
+        PopoverContainerState.of(context).closeAll();
+      },
       overlayDelegate: overlayDelegate,
       overlayDelegate: overlayDelegate,
       typeOptionAction: selectOptionAction,
       typeOptionAction: selectOptionAction,
       // key: ValueKey(state.typeOption.hashCode),
       // key: ValueKey(state.typeOption.hashCode),

+ 30 - 7
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart

@@ -19,7 +19,7 @@ import 'package:styled_widget/styled_widget.dart';
 import '../../../application/field/field_cache.dart';
 import '../../../application/field/field_cache.dart';
 import '../../layout/sizes.dart';
 import '../../layout/sizes.dart';
 
 
-class GridPropertyList extends StatelessWidget {
+class GridPropertyList extends StatefulWidget {
   final String gridId;
   final String gridId;
   final GridFieldCache fieldCache;
   final GridFieldCache fieldCache;
   const GridPropertyList({
   const GridPropertyList({
@@ -28,17 +28,34 @@ class GridPropertyList extends StatelessWidget {
     Key? key,
     Key? key,
   }) : super(key: key);
   }) : super(key: key);
 
 
+  @override
+  State<StatefulWidget> createState() => _GridPropertyListState();
+}
+
+class _GridPropertyListState extends State<GridPropertyList> {
+  late PopoverMutex _popoverMutex;
+
+  @override
+  void initState() {
+    _popoverMutex = PopoverMutex();
+    super.initState();
+  }
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return BlocProvider(
     return BlocProvider(
-      create: (context) =>
-          getIt<GridPropertyBloc>(param1: gridId, param2: fieldCache)
-            ..add(const GridPropertyEvent.initial()),
+      create: (context) => getIt<GridPropertyBloc>(
+          param1: widget.gridId, param2: widget.fieldCache)
+        ..add(const GridPropertyEvent.initial()),
       child: BlocBuilder<GridPropertyBloc, GridPropertyState>(
       child: BlocBuilder<GridPropertyBloc, GridPropertyState>(
         builder: (context, state) {
         builder: (context, state) {
           final cells = state.fields.map((field) {
           final cells = state.fields.map((field) {
             return _GridPropertyCell(
             return _GridPropertyCell(
-                gridId: gridId, field: field, key: ValueKey(field.id));
+              popoverMutex: _popoverMutex,
+              gridId: widget.gridId,
+              field: field,
+              key: ValueKey(field.id),
+            );
           }).toList();
           }).toList();
 
 
           return ListView.separated(
           return ListView.separated(
@@ -60,8 +77,13 @@ class GridPropertyList extends StatelessWidget {
 class _GridPropertyCell extends StatelessWidget {
 class _GridPropertyCell extends StatelessWidget {
   final FieldPB field;
   final FieldPB field;
   final String gridId;
   final String gridId;
-  const _GridPropertyCell({required this.gridId, required this.field, Key? key})
-      : super(key: key);
+  final PopoverMutex popoverMutex;
+  const _GridPropertyCell({
+    required this.gridId,
+    required this.field,
+    required this.popoverMutex,
+    Key? key,
+  }) : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
@@ -95,6 +117,7 @@ class _GridPropertyCell extends StatelessWidget {
 
 
   Widget _editFieldButton(AppTheme theme, BuildContext context) {
   Widget _editFieldButton(AppTheme theme, BuildContext context) {
     return Popover(
     return Popover(
+      mutex: popoverMutex,
       triggerActions: PopoverTriggerActionFlags.click,
       triggerActions: PopoverTriggerActionFlags.click,
       offset: const Offset(20, 0),
       offset: const Offset(20, 0),
       child: FlowyButton(
       child: FlowyButton(

+ 10 - 6
frontend/app_flowy/packages/appflowy_popover/lib/popover.dart

@@ -1,10 +1,7 @@
-import 'dart:ui';
-
 import 'package:appflowy_popover/layout.dart';
 import 'package:appflowy_popover/layout.dart';
 import 'package:flutter/gestures.dart';
 import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/services.dart';
-import './follower.dart';
 
 
 class PopoverMutex {
 class PopoverMutex {
   PopoverState? state;
   PopoverState? state;
@@ -128,6 +125,7 @@ class PopoverState extends State<Popover> {
         offset: widget.offset ?? Offset.zero,
         offset: widget.offset ?? Offset.zero,
         popupBuilder: widget.popupBuilder,
         popupBuilder: widget.popupBuilder,
         onClose: () => close(),
         onClose: () => close(),
+        onCloseAll: () => closeAll(),
       ));
       ));
 
 
       return Stack(children: children);
       return Stack(children: children);
@@ -155,6 +153,10 @@ class PopoverState extends State<Popover> {
     }
     }
   }
   }
 
 
+  closeAll() {
+    _popoverWithMask?.close();
+  }
+
   @override
   @override
   void deactivate() {
   void deactivate() {
     debugPrint("deactivate");
     debugPrint("deactivate");
@@ -247,6 +249,7 @@ class PopoverContainer extends StatefulWidget {
   final PopoverLink popoverLink;
   final PopoverLink popoverLink;
   final Offset offset;
   final Offset offset;
   final void Function() onClose;
   final void Function() onClose;
+  final void Function() onCloseAll;
 
 
   const PopoverContainer({
   const PopoverContainer({
     Key? key,
     Key? key,
@@ -255,6 +258,7 @@ class PopoverContainer extends StatefulWidget {
     required this.popoverLink,
     required this.popoverLink,
     required this.offset,
     required this.offset,
     required this.onClose,
     required this.onClose,
+    required this.onCloseAll,
   }) : super(key: key);
   }) : super(key: key);
 
 
   @override
   @override
@@ -274,9 +278,9 @@ class PopoverContainerState extends State<PopoverContainer> {
     );
     );
   }
   }
 
 
-  close() {
-    widget.onClose();
-  }
+  close() => widget.onClose();
+
+  closeAll() => widget.onCloseAll();
 
 
   static PopoverContainerState of(BuildContext context) {
   static PopoverContainerState of(BuildContext context) {
     if (context is StatefulElement && context.state is PopoverContainerState) {
     if (context is StatefulElement && context.state is PopoverContainerState) {