Bladeren bron

Re-arranged handling of focus scope so that it works with toolbar in an Overlay

Anatoly Pulyaevskiy 6 jaren geleden
bovenliggende
commit
c148574910

+ 31
- 11
packages/zefyr/lib/src/widgets/buttons.dart Bestand weergeven

@@ -300,11 +300,31 @@ class LinkButton extends StatefulWidget {
300 300
 }
301 301
 
302 302
 class _LinkButtonState extends State<LinkButton> {
303
-  final TextEditingController _inputController = new TextEditingController();
303
+  final FocusNode _focusNode = FocusNode();
304
+  final TextEditingController _inputController = TextEditingController();
304 305
   Key _inputKey;
305 306
   bool _formatError = false;
307
+  ZefyrEditorScope _editor;
308
+
306 309
   bool get isEditing => _inputKey != null;
307 310
 
311
+  @override
312
+  void dispose() {
313
+    _focusNode.dispose();
314
+    super.dispose();
315
+  }
316
+
317
+  @override
318
+  void didChangeDependencies() {
319
+    super.didChangeDependencies();
320
+    final toolbar = ZefyrToolbar.of(context);
321
+    if (_editor != toolbar.editor) {
322
+      _editor?.setToolbarFocusNode(null);
323
+      _editor = toolbar.editor;
324
+      _editor.setToolbarFocusNode(_focusNode);
325
+    }
326
+  }
327
+
308 328
   @override
309 329
   Widget build(BuildContext context) {
310 330
     final toolbar = ZefyrToolbar.of(context);
@@ -376,7 +396,7 @@ class _LinkButtonState extends State<LinkButton> {
376 396
         _inputController.text = '';
377 397
         _inputController.removeListener(_handleInputChange);
378 398
         toolbar.markNeedsRebuild();
379
-        toolbar.editor.focus(context);
399
+        toolbar.editor.focus();
380 400
       }
381 401
     });
382 402
   }
@@ -388,7 +408,7 @@ class _LinkButtonState extends State<LinkButton> {
388 408
         _inputKey = null;
389 409
         _inputController.text = '';
390 410
         _inputController.removeListener(_handleInputChange);
391
-        editor.focus(context);
411
+        editor.focus();
392 412
       });
393 413
     }
394 414
   }
@@ -424,7 +444,6 @@ class _LinkButtonState extends State<LinkButton> {
424 444
   }
425 445
 
426 446
   Widget buildOverlay(BuildContext context) {
427
-
428 447
     final toolbar = ZefyrToolbar.of(context);
429 448
     final style = toolbar.editor.selectionStyle;
430 449
 
@@ -438,7 +457,7 @@ class _LinkButtonState extends State<LinkButton> {
438 457
         : _LinkInput(
439 458
             key: _inputKey,
440 459
             controller: _inputController,
441
-            focusNode: toolbar.editor.toolbarFocusNode,
460
+            focusNode: _focusNode,
442 461
             formatError: _formatError,
443 462
           );
444 463
     final items = <Widget>[Expanded(child: body)];
@@ -517,13 +536,14 @@ class _LinkInputState extends State<_LinkInput> {
517 536
       keyboardType: TextInputType.url,
518 537
       focusNode: widget.focusNode,
519 538
       controller: widget.controller,
520
-      autofocus: true,
539
+//      autofocus: true,
521 540
       decoration: new InputDecoration(
522
-          hintText: 'https://',
523
-          filled: true,
524
-          fillColor: toolbarTheme.color,
525
-          border: InputBorder.none,
526
-          contentPadding: const EdgeInsets.all(10.0)),
541
+        hintText: 'https://',
542
+        filled: true,
543
+        fillColor: toolbarTheme.color,
544
+        border: InputBorder.none,
545
+        contentPadding: const EdgeInsets.all(10.0),
546
+      ),
527 547
     );
528 548
   }
529 549
 }

+ 39
- 20
packages/zefyr/lib/src/widgets/editor.dart Bestand weergeven

@@ -15,15 +15,14 @@ class ZefyrEditorScope extends ChangeNotifier {
15 15
     @required ZefyrImageDelegate imageDelegate,
16 16
     @required ZefyrController controller,
17 17
     @required FocusNode focusNode,
18
-    @required FocusNode toolbarFocusNode,
18
+    @required FocusScopeNode focusScope,
19 19
   })  : _controller = controller,
20 20
         _imageDelegate = imageDelegate,
21
-        _focusNode = focusNode,
22
-        _toolbarFocusNode = toolbarFocusNode {
21
+        _focusScope = focusScope,
22
+        _focusNode = focusNode {
23 23
     _selectionStyle = _controller.getSelectionStyle();
24 24
     _selection = _controller.selection;
25 25
     _controller.addListener(_handleControllerChange);
26
-    toolbarFocusNode.addListener(_handleFocusChange);
27 26
     _focusNode.addListener(_handleFocusChange);
28 27
   }
29 28
 
@@ -32,9 +31,9 @@ class ZefyrEditorScope extends ChangeNotifier {
32 31
   ZefyrImageDelegate _imageDelegate;
33 32
   ZefyrImageDelegate get imageDelegate => _imageDelegate;
34 33
 
34
+  FocusScopeNode get focusScope => _focusScope;
35
+  FocusScopeNode _focusScope;
35 36
   FocusNode _focusNode;
36
-  FocusNode _toolbarFocusNode;
37
-  FocusNode get toolbarFocusNode => _toolbarFocusNode;
38 37
 
39 38
   ZefyrController _controller;
40 39
   NotusStyle get selectionStyle => _selectionStyle;
@@ -46,7 +45,6 @@ class ZefyrEditorScope extends ChangeNotifier {
46 45
   void dispose() {
47 46
     assert(!_disposed);
48 47
     _controller.removeListener(_handleControllerChange);
49
-    _toolbarFocusNode.removeListener(_handleFocusChange);
50 48
     _focusNode.removeListener(_handleFocusChange);
51 49
     _disposed = true;
52 50
     super.dispose();
@@ -102,11 +100,23 @@ class ZefyrEditorScope extends ChangeNotifier {
102 100
     notifyListeners();
103 101
   }
104 102
 
103
+  FocusNode _toolbarFocusNode;
104
+
105
+  void setToolbarFocusNode(FocusNode node) {
106
+    assert(!_disposed);
107
+    if (_toolbarFocusNode != node) {
108
+      _toolbarFocusNode?.removeListener(_handleFocusChange);
109
+      _toolbarFocusNode = node;
110
+      _toolbarFocusNode.addListener(_handleFocusChange);
111
+      notifyListeners();
112
+    }
113
+  }
114
+
105 115
   FocusOwner get focusOwner {
106 116
     assert(!_disposed);
107 117
     if (_focusNode.hasFocus) {
108 118
       return FocusOwner.editor;
109
-    } else if (toolbarFocusNode.hasFocus) {
119
+    } else if (_toolbarFocusNode?.hasFocus == true) {
110 120
       return FocusOwner.toolbar;
111 121
     } else {
112 122
       return FocusOwner.none;
@@ -124,9 +134,9 @@ class ZefyrEditorScope extends ChangeNotifier {
124 134
     _controller.formatSelection(value);
125 135
   }
126 136
 
127
-  void focus(BuildContext context) {
137
+  void focus() {
128 138
     assert(!_disposed);
129
-    FocusScope.of(context).requestFocus(_focusNode);
139
+    _focusScope.requestFocus(_focusNode);
130 140
   }
131 141
 
132 142
   void hideKeyboard() {
@@ -181,7 +191,6 @@ class ZefyrEditor extends StatefulWidget {
181 191
 }
182 192
 
183 193
 class _ZefyrEditorState extends State<ZefyrEditor> {
184
-  final FocusNode _toolbarFocusNode = new FocusNode();
185 194
   ZefyrImageDelegate _imageDelegate;
186 195
   ZefyrEditorScope _scope;
187 196
   ZefyrThemeData _themeData;
@@ -194,7 +203,6 @@ class _ZefyrEditorState extends State<ZefyrEditor> {
194 203
       builder: (context) => _ZefyrToolbarContainer(
195 204
             theme: _themeData,
196 205
             toolbar: ZefyrToolbar(
197
-              focusNode: _toolbarFocusNode,
198 206
               editor: _scope,
199 207
               delegate: widget.toolbarDelegate,
200 208
             ),
@@ -213,6 +221,10 @@ class _ZefyrEditorState extends State<ZefyrEditor> {
213 221
       hideToolbar();
214 222
     } else if (_toolbar == null) {
215 223
       showToolbar();
224
+    } else {
225
+      WidgetsBinding.instance.addPostFrameCallback((_) {
226
+        _toolbar?.markNeedsBuild();
227
+      });
216 228
     }
217 229
   }
218 230
 
@@ -220,13 +232,6 @@ class _ZefyrEditorState extends State<ZefyrEditor> {
220 232
   void initState() {
221 233
     super.initState();
222 234
     _imageDelegate = widget.imageDelegate ?? new ZefyrDefaultImageDelegate();
223
-    _scope = ZefyrEditorScope(
224
-      toolbarFocusNode: _toolbarFocusNode,
225
-      imageDelegate: _imageDelegate,
226
-      controller: widget.controller,
227
-      focusNode: widget.focusNode,
228
-    );
229
-    _scope.addListener(_handleChange);
230 235
   }
231 236
 
232 237
   @override
@@ -244,6 +249,21 @@ class _ZefyrEditorState extends State<ZefyrEditor> {
244 249
   void didChangeDependencies() {
245 250
     super.didChangeDependencies();
246 251
 
252
+    if (_scope == null) {
253
+      _scope = ZefyrEditorScope(
254
+        imageDelegate: _imageDelegate,
255
+        controller: widget.controller,
256
+        focusNode: widget.focusNode,
257
+        focusScope: FocusScope.of(context),
258
+      );
259
+      _scope.addListener(_handleChange);
260
+    } else {
261
+      final focusScope = FocusScope.of(context);
262
+      if (focusScope != _scope._focusScope) {
263
+        _scope._focusScope = focusScope;
264
+      }
265
+    }
266
+
247 267
     final parentTheme = ZefyrTheme.of(context, nullOk: true);
248 268
     final fallbackTheme = ZefyrThemeData.fallback(context);
249 269
     _themeData = (parentTheme != null)
@@ -263,7 +283,6 @@ class _ZefyrEditorState extends State<ZefyrEditor> {
263 283
     hideToolbar();
264 284
     _scope.removeListener(_handleChange);
265 285
     _scope.dispose();
266
-    _toolbarFocusNode.dispose();
267 286
     super.dispose();
268 287
   }
269 288
 

+ 11
- 20
packages/zefyr/lib/src/widgets/toolbar.dart Bestand weergeven

@@ -102,13 +102,11 @@ class ZefyrToolbar extends StatefulWidget implements PreferredSizeWidget {
102 102
 
103 103
   const ZefyrToolbar({
104 104
     Key key,
105
-    @required this.focusNode,
106 105
     @required this.editor,
107 106
     this.autoHide: true,
108 107
     this.delegate,
109 108
   }) : super(key: key);
110 109
 
111
-  final FocusNode focusNode;
112 110
   final ZefyrToolbarDelegate delegate;
113 111
   final ZefyrEditorScope editor;
114 112
 
@@ -187,21 +185,12 @@ class ZefyrToolbarState extends State<ZefyrToolbar>
187 185
 
188 186
   ZefyrEditorScope get editor => widget.editor;
189 187
 
190
-  void _handleChange() {
191
-    if (_selection != editor.selection) {
192
-      _selection = editor.selection;
193
-      closeOverlay();
194
-    }
195
-    setState(() {});
196
-  }
197
-
198 188
   @override
199 189
   void initState() {
200 190
     super.initState();
201 191
     _delegate = widget.delegate ?? new _DefaultZefyrToolbarDelegate();
202 192
     _overlayAnimation = new AnimationController(
203 193
         vsync: this, duration: Duration(milliseconds: 100));
204
-    widget.editor.addListener(_handleChange);
205 194
   }
206 195
 
207 196
   @override
@@ -210,15 +199,14 @@ class ZefyrToolbarState extends State<ZefyrToolbar>
210 199
     if (widget.delegate != oldWidget.delegate) {
211 200
       _delegate = widget.delegate ?? new _DefaultZefyrToolbarDelegate();
212 201
     }
213
-    if (widget.editor != oldWidget.editor) {
214
-      oldWidget.editor.removeListener(_handleChange);
215
-      widget.editor.addListener(_handleChange);
202
+    if (_selection != editor.selection) {
203
+      _selection = editor.selection;
204
+      closeOverlay();
216 205
     }
217 206
   }
218 207
 
219 208
   @override
220 209
   void dispose() {
221
-    widget.editor.removeListener(_handleChange);
222 210
     super.dispose();
223 211
   }
224 212
 
@@ -253,11 +241,14 @@ class ZefyrToolbarState extends State<ZefyrToolbar>
253 241
 
254 242
     final constraints =
255 243
         BoxConstraints.tightFor(height: ZefyrToolbar.kToolbarHeight);
256
-    return new _ZefyrToolbarScope(
257
-      toolbar: this,
258
-      child: Container(
259
-        constraints: constraints,
260
-        child: Stack(children: layers),
244
+    return FocusScope(
245
+      node: editor.focusScope,
246
+      child: _ZefyrToolbarScope(
247
+        toolbar: this,
248
+        child: Container(
249
+          constraints: constraints,
250
+          child: Stack(children: layers),
251
+        ),
261 252
       ),
262 253
     );
263 254
   }

+ 1
- 0
packages/zefyr/test/widgets/buttons_test.dart Bestand weergeven

@@ -104,6 +104,7 @@ void main() {
104 104
       await tester
105 105
           .tap(find.widgetWithText(GestureDetector, 'Tap to edit link'));
106 106
       await tester.pumpAndSettle();
107
+      expect(editor.focusNode.hasFocus, isFalse);
107 108
       await editor.updateSelection(base: 10, extent: 10);
108 109
       expect(find.byIcon(Icons.link_off), findsNothing);
109 110
     });

+ 1
- 1
packages/zefyr/test/widgets/selection_test.dart Bestand weergeven

@@ -65,7 +65,7 @@ void main() {
65 65
       RenderBox renderObject =
66 66
           tester.firstRenderObject(find.byType(ZefyrEditableText));
67 67
       var offset = renderObject.localToGlobal(Offset.zero);
68
-      offset += Offset(50.0, renderObject.size.height - 5.0);
68
+      offset += Offset(50.0, renderObject.size.height - 500.0);
69 69
       await tester.tapAt(offset);
70 70
       await tester.pumpAndSettle();
71 71
       expect(editor.controller.selection.isCollapsed, isTrue);