Browse Source

Merge pull request #40 from memspace/toolbar-refactor

Toolbar refactor to decouple from editor context
Anatoly Pulyaevskiy 6 years ago
parent
commit
2b32049f30
No account linked to committer's email address

+ 3
- 2
.travis.yml View File

1
 language: dart
1
 language: dart
2
 
2
 
3
 dart:
3
 dart:
4
+  - stable
4
   - dev
5
   - dev
5
 
6
 
6
 os:
7
 os:
21
     - $HOME/.pub-cache
22
     - $HOME/.pub-cache
22
 
23
 
23
 env:
24
 env:
24
-#  - FLUTTER_VERSION=beta
25
+  - FLUTTER_VERSION=beta
25
   - FLUTTER_VERSION=dev
26
   - FLUTTER_VERSION=dev
26
 
27
 
27
 matrix:
28
 matrix:
37
 - pwd
38
 - pwd
38
 - ./tool/travis.sh notus
39
 - ./tool/travis.sh notus
39
 - ./tool/travis.sh zefyr
40
 - ./tool/travis.sh zefyr
40
-- bash <(curl -s https://codecov.io/bash)
41
+#- bash <(curl -s https://codecov.io/bash)

+ 1
- 0
packages/zefyr/.gitignore View File

13
 example/ios/.symlinks
13
 example/ios/.symlinks
14
 example/ios/Flutter/Generated.xcconfig
14
 example/ios/Flutter/Generated.xcconfig
15
 doc/api/
15
 doc/api/
16
+build/

+ 13
- 15
packages/zefyr/lib/src/widgets/buttons.dart View File

65
 
65
 
66
   @override
66
   @override
67
   Widget build(BuildContext context) {
67
   Widget build(BuildContext context) {
68
-    final editor = ZefyrEditor.of(context);
69
     final toolbar = ZefyrToolbar.of(context);
68
     final toolbar = ZefyrToolbar.of(context);
69
+    final editor = toolbar.editor;
70
     final toolbarTheme = ZefyrTheme.of(context).toolbarTheme;
70
     final toolbarTheme = ZefyrTheme.of(context).toolbarTheme;
71
     final pressedHandler = _getPressedHandler(editor, toolbar);
71
     final pressedHandler = _getPressedHandler(editor, toolbar);
72
     final iconColor = (pressedHandler == null)
72
     final iconColor = (pressedHandler == null)
236
   }
236
   }
237
 }
237
 }
238
 
238
 
239
-/// Controls heading styles.
239
+/// Controls image attribute.
240
 ///
240
 ///
241
 /// When pressed, this button displays overlay toolbar with three
241
 /// When pressed, this button displays overlay toolbar with three
242
 /// buttons for each heading level.
242
 /// buttons for each heading level.
278
   }
278
   }
279
 
279
 
280
   void _pickFromCamera() async {
280
   void _pickFromCamera() async {
281
-    final editor = ZefyrEditor.of(context);
281
+    final editor = ZefyrToolbar.of(context).editor;
282
     final image = await editor.imageDelegate.pickImage(ImageSource.camera);
282
     final image = await editor.imageDelegate.pickImage(ImageSource.camera);
283
     if (image != null)
283
     if (image != null)
284
       editor.formatSelection(NotusAttribute.embed.image(image));
284
       editor.formatSelection(NotusAttribute.embed.image(image));
285
   }
285
   }
286
 
286
 
287
   void _pickFromGallery() async {
287
   void _pickFromGallery() async {
288
-    final editor = ZefyrEditor.of(context);
288
+    final editor = ZefyrToolbar.of(context).editor;
289
     final image = await editor.imageDelegate.pickImage(ImageSource.gallery);
289
     final image = await editor.imageDelegate.pickImage(ImageSource.gallery);
290
     if (image != null)
290
     if (image != null)
291
       editor.formatSelection(NotusAttribute.embed.image(image));
291
       editor.formatSelection(NotusAttribute.embed.image(image));
307
 
307
 
308
   @override
308
   @override
309
   Widget build(BuildContext context) {
309
   Widget build(BuildContext context) {
310
-    final editor = ZefyrEditor.of(context);
311
     final toolbar = ZefyrToolbar.of(context);
310
     final toolbar = ZefyrToolbar.of(context);
311
+    final editor = toolbar.editor;
312
     final enabled =
312
     final enabled =
313
         hasLink(editor.selectionStyle) || !editor.selection.isCollapsed;
313
         hasLink(editor.selectionStyle) || !editor.selection.isCollapsed;
314
 
314
 
322
   bool hasLink(NotusStyle style) => style.contains(NotusAttribute.link);
322
   bool hasLink(NotusStyle style) => style.contains(NotusAttribute.link);
323
 
323
 
324
   String getLink([String defaultValue]) {
324
   String getLink([String defaultValue]) {
325
-    final editor = ZefyrEditor.of(context);
325
+    final editor = ZefyrToolbar.of(context).editor;
326
     final attrs = editor.selectionStyle;
326
     final attrs = editor.selectionStyle;
327
     if (hasLink(attrs)) {
327
     if (hasLink(attrs)) {
328
       return attrs.value(NotusAttribute.link);
328
       return attrs.value(NotusAttribute.link);
351
   }
351
   }
352
 
352
 
353
   void doneEdit() {
353
   void doneEdit() {
354
-    final editor = ZefyrEditor.of(context);
355
     final toolbar = ZefyrToolbar.of(context);
354
     final toolbar = ZefyrToolbar.of(context);
356
     setState(() {
355
     setState(() {
357
       var error = false;
356
       var error = false;
360
           var uri = Uri.parse(_inputController.text);
359
           var uri = Uri.parse(_inputController.text);
361
           if ((uri.isScheme('https') || uri.isScheme('http')) &&
360
           if ((uri.isScheme('https') || uri.isScheme('http')) &&
362
               uri.host.isNotEmpty) {
361
               uri.host.isNotEmpty) {
363
-            editor.formatSelection(
362
+            toolbar.editor.formatSelection(
364
                 NotusAttribute.link.fromString(_inputController.text));
363
                 NotusAttribute.link.fromString(_inputController.text));
365
           } else {
364
           } else {
366
             error = true;
365
             error = true;
377
         _inputController.text = '';
376
         _inputController.text = '';
378
         _inputController.removeListener(_handleInputChange);
377
         _inputController.removeListener(_handleInputChange);
379
         toolbar.markNeedsRebuild();
378
         toolbar.markNeedsRebuild();
380
-        editor.focus(context);
379
+        toolbar.editor.focus(context);
381
       }
380
       }
382
     });
381
     });
383
   }
382
   }
384
 
383
 
385
   void cancelEdit() {
384
   void cancelEdit() {
386
     if (mounted) {
385
     if (mounted) {
387
-      final editor = ZefyrEditor.of(context);
386
+      final editor = ZefyrToolbar.of(context).editor;
388
       setState(() {
387
       setState(() {
389
         _inputKey = null;
388
         _inputKey = null;
390
         _inputController.text = '';
389
         _inputController.text = '';
395
   }
394
   }
396
 
395
 
397
   void unlink() {
396
   void unlink() {
398
-    final editor = ZefyrEditor.of(context);
397
+    final editor = ZefyrToolbar.of(context).editor;
399
     editor.formatSelection(NotusAttribute.link.unset);
398
     editor.formatSelection(NotusAttribute.link.unset);
400
     closeOverlay();
399
     closeOverlay();
401
   }
400
   }
407
   }
406
   }
408
 
407
 
409
   void openInBrowser() async {
408
   void openInBrowser() async {
410
-    final editor = ZefyrEditor.of(context);
409
+    final editor = ZefyrToolbar.of(context).editor;
411
     var link = getLink();
410
     var link = getLink();
412
     assert(link != null);
411
     assert(link != null);
413
     if (await canLaunch(link)) {
412
     if (await canLaunch(link)) {
425
   }
424
   }
426
 
425
 
427
   Widget buildOverlay(BuildContext context) {
426
   Widget buildOverlay(BuildContext context) {
428
-    final editor = ZefyrEditor.of(context);
429
     final toolbar = ZefyrToolbar.of(context);
427
     final toolbar = ZefyrToolbar.of(context);
430
-    final style = editor.selectionStyle;
428
+    final style = toolbar.editor.selectionStyle;
431
 
429
 
432
     String value = 'Tap to edit link';
430
     String value = 'Tap to edit link';
433
     if (style.contains(NotusAttribute.link)) {
431
     if (style.contains(NotusAttribute.link)) {
439
         : _LinkInput(
437
         : _LinkInput(
440
             key: _inputKey,
438
             key: _inputKey,
441
             controller: _inputController,
439
             controller: _inputController,
442
-            focusNode: editor.toolbarFocusNode,
440
+            focusNode: toolbar.editor.toolbarFocusNode,
443
             formatError: _formatError,
441
             formatError: _formatError,
444
           );
442
           );
445
     final items = <Widget>[Expanded(child: body)];
443
     final items = <Widget>[Expanded(child: body)];

+ 154
- 120
packages/zefyr/lib/src/widgets/editor.dart View File

10
 import 'theme.dart';
10
 import 'theme.dart';
11
 import 'toolbar.dart';
11
 import 'toolbar.dart';
12
 
12
 
13
+class ZefyrEditorScope extends ChangeNotifier {
14
+  ZefyrEditorScope({
15
+    @required ZefyrImageDelegate imageDelegate,
16
+    @required ZefyrController controller,
17
+    @required FocusNode focusNode,
18
+    @required FocusNode toolbarFocusNode,
19
+  })  : _controller = controller,
20
+        _imageDelegate = imageDelegate,
21
+        _focusNode = focusNode,
22
+        _toolbarFocusNode = toolbarFocusNode {
23
+    _selectionStyle = _controller.getSelectionStyle();
24
+    _selection = _controller.selection;
25
+    _controller.addListener(_handleControllerChange);
26
+    toolbarFocusNode.addListener(_handleFocusChange);
27
+    _focusNode.addListener(_handleFocusChange);
28
+  }
29
+
30
+  bool _disposed = false;
31
+
32
+  ZefyrImageDelegate _imageDelegate;
33
+  ZefyrImageDelegate get imageDelegate => _imageDelegate;
34
+
35
+  FocusNode _focusNode;
36
+  FocusNode _toolbarFocusNode;
37
+  FocusNode get toolbarFocusNode => _toolbarFocusNode;
38
+
39
+  ZefyrController _controller;
40
+  NotusStyle get selectionStyle => _selectionStyle;
41
+  NotusStyle _selectionStyle;
42
+  TextSelection get selection => _selection;
43
+  TextSelection _selection;
44
+
45
+  @override
46
+  void dispose() {
47
+    assert(!_disposed);
48
+    _controller.removeListener(_handleControllerChange);
49
+    _toolbarFocusNode.removeListener(_handleFocusChange);
50
+    _focusNode.removeListener(_handleFocusChange);
51
+    _disposed = true;
52
+    super.dispose();
53
+  }
54
+
55
+  void _updateControllerIfNeeded(ZefyrController value) {
56
+    if (_controller != value) {
57
+      _controller.removeListener(_handleControllerChange);
58
+      _controller = value;
59
+      _selectionStyle = _controller.getSelectionStyle();
60
+      _selection = _controller.selection;
61
+      _controller.addListener(_handleControllerChange);
62
+      notifyListeners();
63
+    }
64
+  }
65
+
66
+  void _updateFocusNodeIfNeeded(FocusNode value) {
67
+    if (_focusNode != value) {
68
+      _focusNode.removeListener(_handleFocusChange);
69
+      _focusNode = value;
70
+      _focusNode.addListener(_handleFocusChange);
71
+      notifyListeners();
72
+    }
73
+  }
74
+
75
+  void _updateImageDelegateIfNeeded(ZefyrImageDelegate value) {
76
+    if (_imageDelegate != value) {
77
+      _imageDelegate = value;
78
+      notifyListeners();
79
+    }
80
+  }
81
+
82
+  void _handleControllerChange() {
83
+    assert(!_disposed);
84
+    final attrs = _controller.getSelectionStyle();
85
+    final selection = _controller.selection;
86
+    if (_selectionStyle != attrs || _selection != selection) {
87
+      _selectionStyle = attrs;
88
+      _selection = _controller.selection;
89
+      notifyListeners();
90
+    }
91
+  }
92
+
93
+  void _handleFocusChange() {
94
+    assert(!_disposed);
95
+    if (focusOwner == FocusOwner.none && !_selection.isCollapsed) {
96
+      // Collapse selection if there is nothing focused.
97
+      _controller.updateSelection(_selection.copyWith(
98
+        baseOffset: _selection.extentOffset,
99
+        extentOffset: _selection.extentOffset,
100
+      ));
101
+    }
102
+    notifyListeners();
103
+  }
104
+
105
+  FocusOwner get focusOwner {
106
+    assert(!_disposed);
107
+    if (_focusNode.hasFocus) {
108
+      return FocusOwner.editor;
109
+    } else if (toolbarFocusNode.hasFocus) {
110
+      return FocusOwner.toolbar;
111
+    } else {
112
+      return FocusOwner.none;
113
+    }
114
+  }
115
+
116
+  void updateSelection(TextSelection value,
117
+      {ChangeSource source: ChangeSource.remote}) {
118
+    assert(!_disposed);
119
+    _controller.updateSelection(value, source: source);
120
+  }
121
+
122
+  void formatSelection(NotusAttribute value) {
123
+    assert(!_disposed);
124
+    _controller.formatSelection(value);
125
+  }
126
+
127
+  void focus(BuildContext context) {
128
+    assert(!_disposed);
129
+    FocusScope.of(context).requestFocus(_focusNode);
130
+  }
131
+
132
+  void hideKeyboard() {
133
+    assert(!_disposed);
134
+    _focusNode.unfocus();
135
+  }
136
+}
137
+
138
+class _ZefyrEditorScope extends InheritedWidget {
139
+  final ZefyrEditorScope scope;
140
+
141
+  _ZefyrEditorScope({Key key, Widget child, @required this.scope})
142
+      : super(key: key, child: child);
143
+
144
+  @override
145
+  bool updateShouldNotify(_ZefyrEditorScope oldWidget) {
146
+    return oldWidget.scope != scope;
147
+  }
148
+}
149
+
13
 /// Widget for editing Zefyr documents.
150
 /// Widget for editing Zefyr documents.
14
 class ZefyrEditor extends StatefulWidget {
151
 class ZefyrEditor extends StatefulWidget {
15
   const ZefyrEditor({
152
   const ZefyrEditor({
34
   final EdgeInsets padding;
171
   final EdgeInsets padding;
35
 
172
 
36
   static ZefyrEditorScope of(BuildContext context) {
173
   static ZefyrEditorScope of(BuildContext context) {
37
-    ZefyrEditorScope scope =
38
-        context.inheritFromWidgetOfExactType(ZefyrEditorScope);
39
-    return scope;
174
+    _ZefyrEditorScope widget =
175
+        context.inheritFromWidgetOfExactType(_ZefyrEditorScope);
176
+    return widget.scope;
40
   }
177
   }
41
 
178
 
42
   @override
179
   @override
43
   _ZefyrEditorState createState() => new _ZefyrEditorState();
180
   _ZefyrEditorState createState() => new _ZefyrEditorState();
44
 }
181
 }
45
 
182
 
46
-/// Inherited widget which provides access to shared state of a Zefyr editor.
47
-class ZefyrEditorScope extends InheritedWidget {
48
-  /// Current selection style
49
-  final NotusStyle selectionStyle;
50
-  final TextSelection selection;
51
-  final FocusOwner focusOwner;
52
-  final FocusNode toolbarFocusNode;
53
-  final ZefyrImageDelegate imageDelegate;
54
-  final ZefyrController _controller;
55
-  final FocusNode _focusNode;
56
-
57
-  ZefyrEditorScope({
58
-    Key key,
59
-    @required Widget child,
60
-    @required this.selectionStyle,
61
-    @required this.selection,
62
-    @required this.focusOwner,
63
-    @required this.toolbarFocusNode,
64
-    @required this.imageDelegate,
65
-    @required ZefyrController controller,
66
-    @required FocusNode focusNode,
67
-  })  : _controller = controller,
68
-        _focusNode = focusNode,
69
-        super(key: key, child: child);
70
-
71
-  void updateSelection(TextSelection value,
72
-      {ChangeSource source: ChangeSource.remote}) {
73
-    _controller.updateSelection(value, source: source);
74
-  }
75
-
76
-  void formatSelection(NotusAttribute value) {
77
-    _controller.formatSelection(value);
78
-  }
79
-
80
-  void focus(BuildContext context) {
81
-    FocusScope.of(context).requestFocus(_focusNode);
82
-  }
83
-
84
-  void hideKeyboard() {
85
-    _focusNode.unfocus();
86
-  }
87
-
88
-  @override
89
-  bool updateShouldNotify(ZefyrEditorScope oldWidget) {
90
-    return (selectionStyle != oldWidget.selectionStyle ||
91
-        selection != oldWidget.selection ||
92
-        focusOwner != oldWidget.focusOwner ||
93
-        imageDelegate != oldWidget.imageDelegate);
94
-  }
95
-}
96
-
97
 class _ZefyrEditorState extends State<ZefyrEditor> {
183
 class _ZefyrEditorState extends State<ZefyrEditor> {
98
   final FocusNode _toolbarFocusNode = new FocusNode();
184
   final FocusNode _toolbarFocusNode = new FocusNode();
99
-
100
-  NotusStyle _selectionStyle;
101
-  TextSelection _selection;
102
-  FocusOwner _focusOwner;
103
   ZefyrImageDelegate _imageDelegate;
185
   ZefyrImageDelegate _imageDelegate;
104
-
105
-  FocusOwner getFocusOwner() {
106
-    if (widget.focusNode.hasFocus) {
107
-      return FocusOwner.editor;
108
-    } else if (_toolbarFocusNode.hasFocus) {
109
-      return FocusOwner.toolbar;
110
-    } else {
111
-      return FocusOwner.none;
112
-    }
113
-  }
186
+  ZefyrEditorScope _scope;
114
 
187
 
115
   @override
188
   @override
116
   void initState() {
189
   void initState() {
117
     super.initState();
190
     super.initState();
118
-    _selectionStyle = widget.controller.getSelectionStyle();
119
-    _selection = widget.controller.selection;
120
-    _focusOwner = getFocusOwner();
121
     _imageDelegate = widget.imageDelegate ?? new ZefyrDefaultImageDelegate();
191
     _imageDelegate = widget.imageDelegate ?? new ZefyrDefaultImageDelegate();
122
-    widget.controller.addListener(_handleControllerChange);
123
-    _toolbarFocusNode.addListener(_handleFocusChange);
124
-    widget.focusNode.addListener(_handleFocusChange);
192
+    _scope = ZefyrEditorScope(
193
+      toolbarFocusNode: _toolbarFocusNode,
194
+      imageDelegate: _imageDelegate,
195
+      controller: widget.controller,
196
+      focusNode: widget.focusNode,
197
+    );
125
   }
198
   }
126
 
199
 
127
   @override
200
   @override
128
   void didUpdateWidget(ZefyrEditor oldWidget) {
201
   void didUpdateWidget(ZefyrEditor oldWidget) {
129
     super.didUpdateWidget(oldWidget);
202
     super.didUpdateWidget(oldWidget);
130
-    if (widget.focusNode != oldWidget.focusNode) {
131
-      oldWidget.focusNode.removeListener(_handleFocusChange);
132
-      widget.focusNode.addListener(_handleFocusChange);
133
-    }
134
-    if (widget.controller != oldWidget.controller) {
135
-      oldWidget.controller.removeListener(_handleControllerChange);
136
-      widget.controller.addListener(_handleControllerChange);
137
-      _selectionStyle = widget.controller.getSelectionStyle();
138
-      _selection = widget.controller.selection;
139
-    }
203
+    _scope._updateControllerIfNeeded(widget.controller);
204
+    _scope._updateFocusNodeIfNeeded(widget.focusNode);
140
     if (widget.imageDelegate != oldWidget.imageDelegate) {
205
     if (widget.imageDelegate != oldWidget.imageDelegate) {
141
       _imageDelegate = widget.imageDelegate ?? new ZefyrDefaultImageDelegate();
206
       _imageDelegate = widget.imageDelegate ?? new ZefyrDefaultImageDelegate();
207
+      _scope._updateImageDelegateIfNeeded(_imageDelegate);
142
     }
208
     }
143
   }
209
   }
144
 
210
 
145
   @override
211
   @override
146
   void dispose() {
212
   void dispose() {
147
-    widget.controller.removeListener(_handleControllerChange);
148
-    widget.focusNode.removeListener(_handleFocusChange);
149
-    _toolbarFocusNode.removeListener(_handleFocusChange);
213
+    _scope.dispose();
150
     _toolbarFocusNode.dispose();
214
     _toolbarFocusNode.dispose();
151
     super.dispose();
215
     super.dispose();
152
   }
216
   }
165
     final children = <Widget>[];
229
     final children = <Widget>[];
166
     children.add(Expanded(child: editable));
230
     children.add(Expanded(child: editable));
167
     final toolbar = ZefyrToolbar(
231
     final toolbar = ZefyrToolbar(
232
+      editor: _scope,
168
       focusNode: _toolbarFocusNode,
233
       focusNode: _toolbarFocusNode,
169
-      controller: widget.controller,
170
       delegate: widget.toolbarDelegate,
234
       delegate: widget.toolbarDelegate,
171
     );
235
     );
172
     children.add(toolbar);
236
     children.add(toolbar);
179
 
243
 
180
     return ZefyrTheme(
244
     return ZefyrTheme(
181
       data: actualTheme,
245
       data: actualTheme,
182
-      child: ZefyrEditorScope(
183
-        selection: _selection,
184
-        selectionStyle: _selectionStyle,
185
-        focusOwner: _focusOwner,
186
-        toolbarFocusNode: _toolbarFocusNode,
187
-        imageDelegate: _imageDelegate,
188
-        controller: widget.controller,
189
-        focusNode: widget.focusNode,
246
+      child: _ZefyrEditorScope(
247
+        scope: _scope,
190
         child: Column(children: children),
248
         child: Column(children: children),
191
       ),
249
       ),
192
     );
250
     );
193
   }
251
   }
194
-
195
-  void _handleControllerChange() {
196
-    final attrs = widget.controller.getSelectionStyle();
197
-    final selection = widget.controller.selection;
198
-    if (_selectionStyle != attrs || _selection != selection) {
199
-      setState(() {
200
-        _selectionStyle = attrs;
201
-        _selection = widget.controller.selection;
202
-      });
203
-    }
204
-  }
205
-
206
-  void _handleFocusChange() {
207
-    setState(() {
208
-      _focusOwner = getFocusOwner();
209
-      if (_focusOwner == FocusOwner.none && !_selection.isCollapsed) {
210
-        // Collapse selection if there is nothing focused.
211
-        widget.controller.updateSelection(_selection.copyWith(
212
-          baseOffset: _selection.extentOffset,
213
-          extentOffset: _selection.extentOffset,
214
-        ));
215
-      }
216
-    });
217
-  }
218
 }
252
 }

+ 13
- 0
packages/zefyr/lib/src/widgets/selection.dart View File

99
     super.initState();
99
     super.initState();
100
     _toolbarController = new AnimationController(
100
     _toolbarController = new AnimationController(
101
         duration: _kFadeDuration, vsync: widget.overlay);
101
         duration: _kFadeDuration, vsync: widget.overlay);
102
+    _selection = widget.controller.selection;
103
+    widget.controller.addListener(_handleChange);
102
   }
104
   }
103
 
105
 
104
   static const Duration _kFadeDuration = const Duration(milliseconds: 150);
106
   static const Duration _kFadeDuration = const Duration(milliseconds: 150);
112
       _toolbarController = new AnimationController(
114
       _toolbarController = new AnimationController(
113
           duration: _kFadeDuration, vsync: widget.overlay);
115
           duration: _kFadeDuration, vsync: widget.overlay);
114
     }
116
     }
117
+    if (oldWidget.controller != widget.controller) {
118
+      oldWidget.controller.removeListener(_handleChange);
119
+      widget.controller.addListener(_handleChange);
120
+    }
115
   }
121
   }
116
 
122
 
117
   @override
123
   @override
124
 
130
 
125
   @override
131
   @override
126
   void dispose() {
132
   void dispose() {
133
+    widget.controller.removeListener(_handleChange);
127
     hideToolbar();
134
     hideToolbar();
128
     _toolbarController.dispose();
135
     _toolbarController.dispose();
129
     _toolbarController = null;
136
     _toolbarController = null;
172
 
179
 
173
   bool _didCaretTap = false;
180
   bool _didCaretTap = false;
174
 
181
 
182
+  void _handleChange() {
183
+    if (_selection != widget.controller.selection) {
184
+      _updateToolbar();
185
+    }
186
+  }
187
+
175
   void _updateToolbar() {
188
   void _updateToolbar() {
176
     if (!mounted) {
189
     if (!mounted) {
177
       return;
190
       return;

+ 20
- 11
packages/zefyr/lib/src/widgets/toolbar.dart View File

103
   const ZefyrToolbar({
103
   const ZefyrToolbar({
104
     Key key,
104
     Key key,
105
     @required this.focusNode,
105
     @required this.focusNode,
106
-    @required this.controller,
106
+    @required this.editor,
107
     this.autoHide: true,
107
     this.autoHide: true,
108
     this.delegate,
108
     this.delegate,
109
   }) : super(key: key);
109
   }) : super(key: key);
110
 
110
 
111
   final FocusNode focusNode;
111
   final FocusNode focusNode;
112
-  final ZefyrController controller;
113
   final ZefyrToolbarDelegate delegate;
112
   final ZefyrToolbarDelegate delegate;
113
+  final ZefyrEditorScope editor;
114
 
114
 
115
   /// Whether to automatically hide this toolbar when editor loses focus.
115
   /// Whether to automatically hide this toolbar when editor loses focus.
116
   final bool autoHide;
116
   final bool autoHide;
185
 
185
 
186
   bool get hasOverlay => _overlayBuilder != null;
186
   bool get hasOverlay => _overlayBuilder != null;
187
 
187
 
188
+  ZefyrEditorScope get editor => widget.editor;
189
+
190
+  void _handleChange() {
191
+    if (_selection != editor.selection) {
192
+      _selection = editor.selection;
193
+      closeOverlay();
194
+    }
195
+    setState(() {});
196
+  }
197
+
188
   @override
198
   @override
189
   void initState() {
199
   void initState() {
190
     super.initState();
200
     super.initState();
191
     _delegate = widget.delegate ?? new _DefaultZefyrToolbarDelegate();
201
     _delegate = widget.delegate ?? new _DefaultZefyrToolbarDelegate();
192
     _overlayAnimation = new AnimationController(
202
     _overlayAnimation = new AnimationController(
193
         vsync: this, duration: Duration(milliseconds: 100));
203
         vsync: this, duration: Duration(milliseconds: 100));
204
+    widget.editor.addListener(_handleChange);
194
   }
205
   }
195
 
206
 
196
   @override
207
   @override
199
     if (widget.delegate != oldWidget.delegate) {
210
     if (widget.delegate != oldWidget.delegate) {
200
       _delegate = widget.delegate ?? new _DefaultZefyrToolbarDelegate();
211
       _delegate = widget.delegate ?? new _DefaultZefyrToolbarDelegate();
201
     }
212
     }
213
+    if (widget.editor != oldWidget.editor) {
214
+      oldWidget.editor.removeListener(_handleChange);
215
+      widget.editor.addListener(_handleChange);
216
+    }
202
   }
217
   }
203
 
218
 
204
   @override
219
   @override
205
-  void didChangeDependencies() {
206
-    super.didChangeDependencies();
207
-    final editor = ZefyrEditor.of(context);
208
-    if (_selection != editor.selection) {
209
-      _selection = editor.selection;
210
-      closeOverlay();
211
-    }
220
+  void dispose() {
221
+    widget.editor.removeListener(_handleChange);
222
+    super.dispose();
212
   }
223
   }
213
 
224
 
214
   @override
225
   @override
215
   Widget build(BuildContext context) {
226
   Widget build(BuildContext context) {
216
-    final editor = ZefyrEditor.of(context);
217
-
218
     if (editor.focusOwner == FocusOwner.none) {
227
     if (editor.focusOwner == FocusOwner.none) {
219
       return new Container();
228
       return new Container();
220
     }
229
     }

+ 2
- 0
packages/zefyr/test/widgets/editor_test.dart View File

23
       var editor =
23
       var editor =
24
           new EditorSandBox(tester: tester, document: doc, theme: theme);
24
           new EditorSandBox(tester: tester, document: doc, theme: theme);
25
       await editor.tapEditor();
25
       await editor.tapEditor();
26
+      // TODO: figure out why this extra pump is needed here
27
+      await tester.pumpAndSettle();
26
       EditableRichText p = tester.widget(find.byType(EditableRichText).first);
28
       EditableRichText p = tester.widget(find.byType(EditableRichText).first);
27
       expect(p.text.children.first.style.color, Colors.red);
29
       expect(p.text.children.first.style.color, Colors.red);
28
     });
30
     });

+ 1
- 1
packages/zefyr/tool/travis.sh View File

2
 
2
 
3
 set -e
3
 set -e
4
 
4
 
5
-$TRAVIS_BUILD_DIR/flutter/bin/flutter test --coverage
5
+$TRAVIS_BUILD_DIR/flutter/bin/flutter test