Skip to content

Commit c493ba7

Browse files
committed
chore: unfocus the textField after switching to another popover
1 parent 6c27b54 commit c493ba7

File tree

7 files changed

+151
-100
lines changed

7 files changed

+151
-100
lines changed

frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart

Lines changed: 108 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import 'package:flowy_infra/theme.dart';
77
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
88
import 'package:flowy_infra_ui/style_widget/button.dart';
99
import 'package:flowy_infra_ui/style_widget/text.dart';
10+
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
1011
import 'package:flowy_infra_ui/widget/spacing.dart';
1112
import 'package:flowy_sdk/log.dart';
1213
import 'package:flutter/material.dart';
1314
import 'package:flutter_bloc/flutter_bloc.dart';
1415
import 'package:app_flowy/generated/locale_keys.g.dart';
15-
import 'field_name_input.dart';
1616
import 'field_type_option_editor.dart';
1717

1818
class FieldEditor extends StatefulWidget {
@@ -44,6 +44,12 @@ class _FieldEditorState extends State<FieldEditor> {
4444
super.initState();
4545
}
4646

47+
@override
48+
void dispose() {
49+
popoverMutex.dispose();
50+
super.dispose();
51+
}
52+
4753
@override
4854
Widget build(BuildContext context) {
4955
return BlocProvider(
@@ -58,28 +64,38 @@ class _FieldEditorState extends State<FieldEditor> {
5864
return ListView(
5965
shrinkWrap: true,
6066
children: [
61-
FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(),
62-
fontSize: 12),
63-
const VSpace(10),
64-
const _FieldNameCell(),
65-
const VSpace(10),
66-
_DeleteFieldButton(
67-
popoverMutex: popoverMutex,
68-
onDeleted: () {
69-
state.field.fold(
70-
() => Log.error('Can not delete the field'),
71-
(field) => widget.onDeleted?.call(field.id),
72-
);
73-
},
67+
FlowyText.medium(
68+
LocaleKeys.grid_field_editProperty.tr(),
69+
fontSize: 12,
7470
),
7571
const VSpace(10),
72+
_FieldNameTextField(popoverMutex: popoverMutex),
73+
..._addDeleteFieldButton(state),
7674
_FieldTypeOptionCell(popoverMutex: popoverMutex),
7775
],
7876
);
7977
},
8078
),
8179
);
8280
}
81+
82+
List<Widget> _addDeleteFieldButton(FieldEditorState state) {
83+
if (widget.onDeleted == null) {
84+
return [];
85+
}
86+
return [
87+
const VSpace(10),
88+
_DeleteFieldButton(
89+
popoverMutex: popoverMutex,
90+
onDeleted: () {
91+
state.field.fold(
92+
() => Log.error('Can not delete the field'),
93+
(field) => widget.onDeleted?.call(field.id),
94+
);
95+
},
96+
),
97+
];
98+
}
8399
}
84100

85101
class _FieldTypeOptionCell extends StatelessWidget {
@@ -111,25 +127,89 @@ class _FieldTypeOptionCell extends StatelessWidget {
111127
}
112128
}
113129

114-
class _FieldNameCell extends StatelessWidget {
115-
const _FieldNameCell({Key? key}) : super(key: key);
130+
class _FieldNameTextField extends StatefulWidget {
131+
final PopoverMutex popoverMutex;
132+
const _FieldNameTextField({
133+
required this.popoverMutex,
134+
Key? key,
135+
}) : super(key: key);
136+
137+
@override
138+
State<_FieldNameTextField> createState() => _FieldNameTextFieldState();
139+
}
140+
141+
class _FieldNameTextFieldState extends State<_FieldNameTextField> {
142+
late String name;
143+
FocusNode focusNode = FocusNode();
144+
VoidCallback? _popoverCallback;
145+
TextEditingController controller = TextEditingController();
146+
147+
@override
148+
void initState() {
149+
focusNode.addListener(() {
150+
if (focusNode.hasFocus) {
151+
widget.popoverMutex.close();
152+
}
153+
});
154+
155+
super.initState();
156+
}
116157

117158
@override
118159
Widget build(BuildContext context) {
119-
return BlocBuilder<FieldEditorBloc, FieldEditorState>(
120-
builder: (context, state) {
121-
return FieldNameTextField(
122-
name: state.name,
123-
errorText: context.read<FieldEditorBloc>().state.errorText,
124-
onNameChanged: (newName) {
125-
context
126-
.read<FieldEditorBloc>()
127-
.add(FieldEditorEvent.updateName(newName));
128-
},
129-
);
160+
final theme = context.watch<AppTheme>();
161+
162+
controller.text = context.read<FieldEditorBloc>().state.name;
163+
return BlocListener<FieldEditorBloc, FieldEditorState>(
164+
listenWhen: (previous, current) => previous.name != current.name,
165+
listener: (context, state) {
166+
controller.text = state.name;
130167
},
168+
child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
169+
builder: (context, state) {
170+
listenOnPopoverChhanged(context);
171+
172+
return RoundedInputField(
173+
height: 36,
174+
autoFocus: true,
175+
focusNode: focusNode,
176+
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
177+
controller: controller,
178+
normalBorderColor: theme.shader4,
179+
errorBorderColor: theme.red,
180+
focusBorderColor: theme.main1,
181+
cursorColor: theme.main1,
182+
errorText: context.read<FieldEditorBloc>().state.errorText,
183+
onChanged: (newName) {
184+
context
185+
.read<FieldEditorBloc>()
186+
.add(FieldEditorEvent.updateName(newName));
187+
},
188+
);
189+
},
190+
),
131191
);
132192
}
193+
194+
void listenOnPopoverChhanged(BuildContext context) {
195+
if (_popoverCallback != null) {
196+
widget.popoverMutex.removePopoverStateListener(_popoverCallback!);
197+
}
198+
_popoverCallback = widget.popoverMutex.listenOnPopoverStateChanged(() {
199+
if (focusNode.hasFocus) {
200+
final node = FocusScope.of(context);
201+
node.unfocus();
202+
}
203+
});
204+
}
205+
206+
@override
207+
void didUpdateWidget(covariant _FieldNameTextField oldWidget) {
208+
controller.selection = TextSelection.fromPosition(
209+
TextPosition(offset: controller.text.length));
210+
211+
super.didUpdateWidget(oldWidget);
212+
}
133213
}
134214

135215
class _DeleteFieldButton extends StatelessWidget {
@@ -171,12 +251,10 @@ class _DeleteFieldButton extends StatelessWidget {
171251
popupBuilder: (popupContext) {
172252
return PopoverAlertView(
173253
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
174-
cancel: () => popoverMutex.state?.close(),
254+
cancel: () {},
175255
confirm: () {
176256
onDeleted?.call();
177-
popoverMutex.state?.close();
178257
},
179-
popoverMutex: popoverMutex,
180258
);
181259
},
182260
child: widget,

frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_name_input.dart

Lines changed: 0 additions & 56 deletions
This file was deleted.

frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ class FieldTypeOptionEditor extends StatelessWidget {
6666
height: GridSize.typeOptionItemHeight,
6767
child: AppFlowyStylePopover(
6868
constraints: BoxConstraints.loose(const Size(460, 440)),
69-
triggerActions: PopoverTriggerActionFlags.click,
69+
triggerActions:
70+
PopoverTriggerActionFlags.click | PopoverTriggerActionFlags.hover,
7071
mutex: popoverMutex,
7172
offset: const Offset(20, 0),
7273
popupBuilder: (context) {

frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ Widget? makeTypeOptionWidget({
5050
required PopoverMutex popoverMutex,
5151
}) {
5252
final builder = makeTypeOptionWidgetBuilder(
53-
dataController: dataController, popoverMutex: popoverMutex);
53+
dataController: dataController,
54+
popoverMutex: popoverMutex,
55+
);
5456
return builder.build(context);
5557
}
5658

frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,6 @@ class NumberFormatList extends StatelessWidget {
123123
format: format,
124124
onSelected: (format) {
125125
onSelected(format);
126-
FlowyOverlay.of(context)
127-
.remove(NumberFormatList.identifier());
128126
});
129127
}).toList();
130128

frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import 'package:appflowy_popover/popover.dart';
21
import 'package:easy_localization/easy_localization.dart';
32
import 'package:flowy_infra/text_style.dart';
43
import 'package:flowy_infra/theme.dart';
@@ -88,13 +87,11 @@ class _CreateTextFieldDialog extends State<NavigatorTextFieldDialog> {
8887
}
8988

9089
class PopoverAlertView extends StatelessWidget {
91-
final PopoverMutex popoverMutex;
9290
final String title;
9391
final void Function()? cancel;
9492
final void Function()? confirm;
9593

9694
const PopoverAlertView({
97-
required this.popoverMutex,
9895
required this.title,
9996
this.confirm,
10097
this.cancel,

frontend/app_flowy/packages/appflowy_popover/lib/popover.dart

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,42 @@ import 'package:flutter/services.dart';
66
/// If multiple popovers are exclusive,
77
/// pass the same mutex to them.
88
class PopoverMutex {
9-
PopoverState? state;
9+
final ValueNotifier<PopoverState?> _stateNofitier = ValueNotifier(null);
10+
PopoverMutex();
11+
12+
void removePopoverStateListener(VoidCallback listener) {
13+
_stateNofitier.removeListener(listener);
14+
}
15+
16+
VoidCallback listenOnPopoverStateChanged(VoidCallback callback) {
17+
listenerCallback() {
18+
callback();
19+
}
20+
21+
_stateNofitier.addListener(listenerCallback);
22+
return listenerCallback;
23+
}
24+
25+
void close() {
26+
_stateNofitier.value?.close();
27+
}
28+
29+
PopoverState? get state => _stateNofitier.value;
30+
31+
set state(PopoverState? newState) {
32+
if (_stateNofitier.value != null && _stateNofitier.value != newState) {
33+
_stateNofitier.value?.close();
34+
}
35+
_stateNofitier.value = newState;
36+
}
37+
38+
void _removeState() {
39+
_stateNofitier.value = null;
40+
}
41+
42+
void dispose() {
43+
_stateNofitier.dispose();
44+
}
1045
}
1146

1247
class PopoverController {
@@ -109,11 +144,7 @@ class PopoverState extends State<Popover> {
109144
close();
110145

111146
if (widget.mutex != null) {
112-
if (widget.mutex!.state != null && widget.mutex!.state != this) {
113-
widget.mutex!.state!.close();
114-
}
115-
116-
widget.mutex!.state = this;
147+
widget.mutex?.state = this;
117148
}
118149

119150
if (_popoverWithMask == null) {
@@ -163,7 +194,7 @@ class PopoverState extends State<Popover> {
163194
}
164195

165196
if (widget.mutex?.state == this) {
166-
widget.mutex!.state = null;
197+
widget.mutex?._removeState();
167198
}
168199
}
169200

0 commit comments

Comments
 (0)