From c493ba79e021759e24699bedbdab0a688ad7264f Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 18 Sep 2022 16:09:28 +0800 Subject: [PATCH] chore: unfocus the textField after switching to another popover --- .../widgets/header/field_editor.dart | 138 ++++++++++++++---- .../widgets/header/field_name_input.dart | 56 ------- .../header/field_type_option_editor.dart | 3 +- .../widgets/header/type_option/builder.dart | 4 +- .../widgets/header/type_option/number.dart | 2 - .../presentation/widgets/dialogs.dart | 3 - .../appflowy_popover/lib/popover.dart | 45 +++++- 7 files changed, 151 insertions(+), 100 deletions(-) delete mode 100644 frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_name_input.dart diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart index cc836809b8..1d7fad4e83 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart @@ -7,12 +7,12 @@ import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/button.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flowy_infra_ui/widget/rounded_input_field.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; -import 'field_name_input.dart'; import 'field_type_option_editor.dart'; class FieldEditor extends StatefulWidget { @@ -44,6 +44,12 @@ class _FieldEditorState extends State { super.initState(); } + @override + void dispose() { + popoverMutex.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocProvider( @@ -58,21 +64,13 @@ class _FieldEditorState extends State { return ListView( shrinkWrap: true, children: [ - FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), - fontSize: 12), - const VSpace(10), - const _FieldNameCell(), - const VSpace(10), - _DeleteFieldButton( - popoverMutex: popoverMutex, - onDeleted: () { - state.field.fold( - () => Log.error('Can not delete the field'), - (field) => widget.onDeleted?.call(field.id), - ); - }, + FlowyText.medium( + LocaleKeys.grid_field_editProperty.tr(), + fontSize: 12, ), const VSpace(10), + _FieldNameTextField(popoverMutex: popoverMutex), + ..._addDeleteFieldButton(state), _FieldTypeOptionCell(popoverMutex: popoverMutex), ], ); @@ -80,6 +78,24 @@ class _FieldEditorState extends State { ), ); } + + List _addDeleteFieldButton(FieldEditorState state) { + if (widget.onDeleted == null) { + return []; + } + return [ + const VSpace(10), + _DeleteFieldButton( + popoverMutex: popoverMutex, + onDeleted: () { + state.field.fold( + () => Log.error('Can not delete the field'), + (field) => widget.onDeleted?.call(field.id), + ); + }, + ), + ]; + } } class _FieldTypeOptionCell extends StatelessWidget { @@ -111,25 +127,89 @@ class _FieldTypeOptionCell extends StatelessWidget { } } -class _FieldNameCell extends StatelessWidget { - const _FieldNameCell({Key? key}) : super(key: key); +class _FieldNameTextField extends StatefulWidget { + final PopoverMutex popoverMutex; + const _FieldNameTextField({ + required this.popoverMutex, + Key? key, + }) : super(key: key); + + @override + State<_FieldNameTextField> createState() => _FieldNameTextFieldState(); +} + +class _FieldNameTextFieldState extends State<_FieldNameTextField> { + late String name; + FocusNode focusNode = FocusNode(); + VoidCallback? _popoverCallback; + TextEditingController controller = TextEditingController(); + + @override + void initState() { + focusNode.addListener(() { + if (focusNode.hasFocus) { + widget.popoverMutex.close(); + } + }); + + super.initState(); + } @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return FieldNameTextField( - name: state.name, - errorText: context.read().state.errorText, - onNameChanged: (newName) { - context - .read() - .add(FieldEditorEvent.updateName(newName)); - }, - ); + final theme = context.watch(); + + controller.text = context.read().state.name; + return BlocListener( + listenWhen: (previous, current) => previous.name != current.name, + listener: (context, state) { + controller.text = state.name; }, + child: BlocBuilder( + builder: (context, state) { + listenOnPopoverChhanged(context); + + return RoundedInputField( + height: 36, + autoFocus: true, + focusNode: focusNode, + style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500), + controller: controller, + normalBorderColor: theme.shader4, + errorBorderColor: theme.red, + focusBorderColor: theme.main1, + cursorColor: theme.main1, + errorText: context.read().state.errorText, + onChanged: (newName) { + context + .read() + .add(FieldEditorEvent.updateName(newName)); + }, + ); + }, + ), ); } + + void listenOnPopoverChhanged(BuildContext context) { + if (_popoverCallback != null) { + widget.popoverMutex.removePopoverStateListener(_popoverCallback!); + } + _popoverCallback = widget.popoverMutex.listenOnPopoverStateChanged(() { + if (focusNode.hasFocus) { + final node = FocusScope.of(context); + node.unfocus(); + } + }); + } + + @override + void didUpdateWidget(covariant _FieldNameTextField oldWidget) { + controller.selection = TextSelection.fromPosition( + TextPosition(offset: controller.text.length)); + + super.didUpdateWidget(oldWidget); + } } class _DeleteFieldButton extends StatelessWidget { @@ -171,12 +251,10 @@ class _DeleteFieldButton extends StatelessWidget { popupBuilder: (popupContext) { return PopoverAlertView( title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(), - cancel: () => popoverMutex.state?.close(), + cancel: () {}, confirm: () { onDeleted?.call(); - popoverMutex.state?.close(); }, - popoverMutex: popoverMutex, ); }, child: widget, diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_name_input.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_name_input.dart deleted file mode 100644 index 365064e33f..0000000000 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_name_input.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flowy_infra/theme.dart'; -import 'package:flowy_infra_ui/widget/rounded_input_field.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class FieldNameTextField extends StatefulWidget { - final void Function(String) onNameChanged; - final String name; - final String errorText; - const FieldNameTextField({ - required this.name, - required this.errorText, - required this.onNameChanged, - Key? key, - }) : super(key: key); - - @override - State createState() => _FieldNameTextFieldState(); -} - -class _FieldNameTextFieldState extends State { - late String name; - TextEditingController controller = TextEditingController(); - - @override - void initState() { - controller.text = widget.name; - super.initState(); - } - - @override - Widget build(BuildContext context) { - final theme = context.watch(); - return RoundedInputField( - height: 36, - autoFocus: true, - style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500), - controller: controller, - normalBorderColor: theme.shader4, - errorBorderColor: theme.red, - focusBorderColor: theme.main1, - cursorColor: theme.main1, - errorText: widget.errorText, - onChanged: widget.onNameChanged, - ); - } - - @override - void didUpdateWidget(covariant FieldNameTextField oldWidget) { - controller.text = widget.name; - controller.selection = TextSelection.fromPosition( - TextPosition(offset: controller.text.length)); - - super.didUpdateWidget(oldWidget); - } -} diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart index f541335844..fcbb211fc1 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart @@ -66,7 +66,8 @@ class FieldTypeOptionEditor extends StatelessWidget { height: GridSize.typeOptionItemHeight, child: AppFlowyStylePopover( constraints: BoxConstraints.loose(const Size(460, 440)), - triggerActions: PopoverTriggerActionFlags.click, + triggerActions: + PopoverTriggerActionFlags.click | PopoverTriggerActionFlags.hover, mutex: popoverMutex, offset: const Offset(20, 0), popupBuilder: (context) { diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart index f01efda675..e00eda5ec0 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart @@ -50,7 +50,9 @@ Widget? makeTypeOptionWidget({ required PopoverMutex popoverMutex, }) { final builder = makeTypeOptionWidgetBuilder( - dataController: dataController, popoverMutex: popoverMutex); + dataController: dataController, + popoverMutex: popoverMutex, + ); return builder.build(context); } diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart index 80627d062d..f79913ca58 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart @@ -123,8 +123,6 @@ class NumberFormatList extends StatelessWidget { format: format, onSelected: (format) { onSelected(format); - FlowyOverlay.of(context) - .remove(NumberFormatList.identifier()); }); }).toList(); diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart index 4cb763e50d..296f64d86d 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart @@ -1,4 +1,3 @@ -import 'package:appflowy_popover/popover.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/text_style.dart'; import 'package:flowy_infra/theme.dart'; @@ -88,13 +87,11 @@ class _CreateTextFieldDialog extends State { } class PopoverAlertView extends StatelessWidget { - final PopoverMutex popoverMutex; final String title; final void Function()? cancel; final void Function()? confirm; const PopoverAlertView({ - required this.popoverMutex, required this.title, this.confirm, this.cancel, diff --git a/frontend/app_flowy/packages/appflowy_popover/lib/popover.dart b/frontend/app_flowy/packages/appflowy_popover/lib/popover.dart index 6f9f4e9f33..ff090e347e 100644 --- a/frontend/app_flowy/packages/appflowy_popover/lib/popover.dart +++ b/frontend/app_flowy/packages/appflowy_popover/lib/popover.dart @@ -6,7 +6,42 @@ import 'package:flutter/services.dart'; /// If multiple popovers are exclusive, /// pass the same mutex to them. class PopoverMutex { - PopoverState? state; + final ValueNotifier _stateNofitier = ValueNotifier(null); + PopoverMutex(); + + void removePopoverStateListener(VoidCallback listener) { + _stateNofitier.removeListener(listener); + } + + VoidCallback listenOnPopoverStateChanged(VoidCallback callback) { + listenerCallback() { + callback(); + } + + _stateNofitier.addListener(listenerCallback); + return listenerCallback; + } + + void close() { + _stateNofitier.value?.close(); + } + + PopoverState? get state => _stateNofitier.value; + + set state(PopoverState? newState) { + if (_stateNofitier.value != null && _stateNofitier.value != newState) { + _stateNofitier.value?.close(); + } + _stateNofitier.value = newState; + } + + void _removeState() { + _stateNofitier.value = null; + } + + void dispose() { + _stateNofitier.dispose(); + } } class PopoverController { @@ -109,11 +144,7 @@ class PopoverState extends State { close(); if (widget.mutex != null) { - if (widget.mutex!.state != null && widget.mutex!.state != this) { - widget.mutex!.state!.close(); - } - - widget.mutex!.state = this; + widget.mutex?.state = this; } if (_popoverWithMask == null) { @@ -163,7 +194,7 @@ class PopoverState extends State { } if (widget.mutex?.state == this) { - widget.mutex!.state = null; + widget.mutex?._removeState(); } }