瀏覽代碼

add onSave callback

lucky1213 4 年之前
父節點
當前提交
acd0259f47

+ 42
- 44
packages/zefyr/example/lib/src/full_page.dart 查看文件

@@ -33,11 +33,11 @@ class FullPageEditorScreen extends StatefulWidget {
33 33
 
34 34
 Delta getDelta() {
35 35
   final doc =
36
-    r'[{"insert":"Zefyr", "attributes": {"color": "#595959"}},{"insert":"\n","attributes":{"heading":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"i":true}},{"insert":"\n"},{"insert":"​","attributes":{"embed":{"type":"image","source":"asset://images/breeze.jpg"}}},{"insert":"\n"},{"insert":"Photo by Hiroyuki Takeda.","attributes":{"i":true}},{"insert":"\nZefyr is currently in "},{"insert":"early preview","attributes":{"b":true}},{"insert":". If you have a feature request or found a bug, please file it at the "},{"insert":"issue tracker","attributes":{"a":"https://github.com/memspace/zefyr/issues"}},{"insert":'
37
-    r'".\nDocumentation"},{"insert":"\n","attributes":{"heading":3}},{"insert":"Quick Start","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/quick_start.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Data Format and Document Model","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/data_and_document.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Style Attributes","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/attr'
38
-    r'ibutes.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Heuristic Rules","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/heuristics.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"FAQ","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/faq.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Zefyr’s rich text editor is built with simplicity and fle'
39
-    r'xibility in mind. It provides clean interface for distraction-free editing. Think Medium.com-like experience.\nMarkdown inspired semantics"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Ever needed to have a heading line inside of a quote block, like this:\nI’m a Markdown heading"},{"insert":"\n","attributes":{"block":"quote","heading":3}},{"insert":"And I’m a regular paragraph"},{"insert":"\n","attributes":{"block":"quote"}},{"insert":"Code blocks"},{"insert":"\n","attributes":{"headin'
40
-    r'g":2}},{"insert":"Of course:\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"import ‘package:zefyr/zefyr.dart’;"},{"insert":"\n\n","attributes":{"block":"code"}},{"insert":"void main() {"},{"insert":"\n","attributes":{"block":"code"}},{"insert":" runApp(MyZefyrApp());"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"}"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"\n\n\n"}]';
36
+      r'[{"insert":"Zefyr", "attributes": {"color": "#595959"}},{"insert":"\n","attributes":{"heading":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"i":true}},{"insert":"\n"},{"insert":"​","attributes":{"embed":{"type":"image","source":"asset://images/breeze.jpg"}}},{"insert":"\n"},{"insert":"Photo by Hiroyuki Takeda.","attributes":{"i":true}},{"insert":"\nZefyr is currently in "},{"insert":"early preview","attributes":{"b":true}},{"insert":". If you have a feature request or found a bug, please file it at the "},{"insert":"issue tracker","attributes":{"a":"https://github.com/memspace/zefyr/issues"}},{"insert":'
37
+      r'".\nDocumentation"},{"insert":"\n","attributes":{"heading":3}},{"insert":"Quick Start","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/quick_start.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Data Format and Document Model","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/data_and_document.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Style Attributes","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/attr'
38
+      r'ibutes.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Heuristic Rules","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/heuristics.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"FAQ","attributes":{"a":"https://github.com/memspace/zefyr/blob/master/doc/faq.md"}},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Zefyr’s rich text editor is built with simplicity and fle'
39
+      r'xibility in mind. It provides clean interface for distraction-free editing. Think Medium.com-like experience.\nMarkdown inspired semantics"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Ever needed to have a heading line inside of a quote block, like this:\nI’m a Markdown heading"},{"insert":"\n","attributes":{"block":"quote","heading":3}},{"insert":"And I’m a regular paragraph"},{"insert":"\n","attributes":{"block":"quote"}},{"insert":"Code blocks"},{"insert":"\n","attributes":{"headin'
40
+      r'g":2}},{"insert":"Of course:\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"import ‘package:zefyr/zefyr.dart’;"},{"insert":"\n\n","attributes":{"block":"code"}},{"insert":"void main() {"},{"insert":"\n","attributes":{"block":"code"}},{"insert":" runApp(MyZefyrApp());"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"}"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"\n\n\n"}]';
41 41
   return Delta.fromJson(json.decode(doc) as List);
42 42
 }
43 43
 
@@ -88,26 +88,26 @@ class _FullPageEditorScreenState extends State<FullPageEditorScreen> {
88 88
       body: Column(
89 89
         children: <Widget>[
90 90
           Container(
91
-            height: 50,
92
-            child: Row(
93
-              children: <Widget>[
94
-                Expanded(
95
-                                  child: TextField(
96
-                    onChanged: (text) {
97
-                      SearchResultEntity a = _controller.search(text);
98
-                      setState(() {
99
-                        _searchResultEntity = a;
100
-                      });
101
-                    },
91
+              height: 50,
92
+              child: Row(
93
+                children: <Widget>[
94
+                  Expanded(
95
+                    child: TextField(
96
+                      onChanged: (text) {
97
+                        SearchResultEntity a = _controller.search(text);
98
+                        setState(() {
99
+                          _searchResultEntity = a;
100
+                        });
101
+                      },
102
+                    ),
102 103
                   ),
103
-                ),
104
-                Container(
105
-                  padding: EdgeInsets.symmetric(horizontal: 10),
106
-                  child: Text('${_searchResultEntity.current} / ${_searchResultEntity.total}'),
107
-                )
108
-              ],
109
-            )
110
-          ),
104
+                  Container(
105
+                    padding: EdgeInsets.symmetric(horizontal: 10),
106
+                    child: Text(
107
+                        '${_searchResultEntity.current} / ${_searchResultEntity.total}'),
108
+                  )
109
+                ],
110
+              )),
111 111
           Container(
112 112
             child: Row(
113 113
               children: <Widget>[
@@ -142,26 +142,24 @@ class _FullPageEditorScreenState extends State<FullPageEditorScreen> {
142 142
             ),
143 143
           ),
144 144
           Expanded(
145
-                      child: ZefyrScaffold(
146
-                    child: ZefyrTheme(
147
-                      data: ZefyrThemeData(
148
-                        // attributeTheme: AttributeTheme(
149
-                        //   link: TextStyle(
150
-                        //     color: Colors.red,
151
-                        //   ),
152
-                        // ),
153
-                      ),
154
-                      child: ZefyrEditor(
155
-                        autofocus: false,
156
-                        controller: _controller,
157
-                        focusNode: _focusNode,
158
-                        mode: ZefyrMode.edit,
159
-                        imageDelegate: CustomImageDelegate(),
160
-                        linkDelegate: CustomLinkDelegate(),
161
-                        keyboardAppearance: _darkTheme ? Brightness.dark : Brightness.light,
162
-                      ),
163
-                    ),
164
-                  ),
145
+            child: ZefyrScaffold(
146
+              child: ZefyrTheme(
147
+                data: ZefyrThemeData(),
148
+                child: ZefyrEditor(
149
+                  autofocus: false,
150
+                  controller: _controller,
151
+                  focusNode: _focusNode,
152
+                  mode: ZefyrMode.edit,
153
+                  imageDelegate: CustomImageDelegate(),
154
+                  linkDelegate: CustomLinkDelegate(),
155
+                  keyboardAppearance:
156
+                      _darkTheme ? Brightness.dark : Brightness.light,
157
+                  onSave: () {
158
+                    print(_controller.document.toJson());
159
+                  },
160
+                ),
161
+              ),
162
+            ),
165 163
           ),
166 164
         ],
167 165
       ),

+ 2
- 0
packages/zefyr/lib/src/widgets/buttons.dart 查看文件

@@ -167,6 +167,8 @@ class ZefyrButton extends StatelessWidget {
167 167
       return () => editor.redo();
168 168
     } else if (action == ZefyrToolbarAction.undo && editor.controller.document.history.stack.undo.isNotEmpty) {
169 169
       return () => editor.undo();
170
+    } else if (action == ZefyrToolbarAction.save) {
171
+      return () => editor.onSave();
170 172
     }
171 173
 
172 174
     return null;

+ 5
- 0
packages/zefyr/lib/src/widgets/editor.dart 查看文件

@@ -27,6 +27,7 @@ class ZefyrEditor extends StatefulWidget {
27 27
     this.toolbarDelegate,
28 28
     this.imageDelegate,
29 29
     this.linkDelegate,
30
+    this.onSave,
30 31
     this.selectionControls,
31 32
     this.physics,
32 33
     this.keyboardAppearance,
@@ -63,6 +64,8 @@ class ZefyrEditor extends StatefulWidget {
63 64
 
64 65
   final ZefyrLinkDelegate linkDelegate;
65 66
 
67
+  final void Function() onSave;
68
+
66 69
   /// Optional delegate for building the text selection handles and toolbar.
67 70
   ///
68 71
   /// If not provided then platform-specific implementation is used by default.
@@ -147,6 +150,7 @@ class _ZefyrEditorState extends State<ZefyrEditor> {
147 150
     _scope.mode = widget.mode;
148 151
     _scope.controller = widget.controller;
149 152
     _scope.focusNode = widget.focusNode;
153
+    _scope.onSave = widget.onSave;
150 154
     if (widget.imageDelegate != oldWidget.imageDelegate) {
151 155
       _imageDelegate = widget.imageDelegate;
152 156
       _scope.imageDelegate = _imageDelegate;
@@ -171,6 +175,7 @@ class _ZefyrEditorState extends State<ZefyrEditor> {
171 175
         mode: widget.mode,
172 176
         imageDelegate: _imageDelegate,
173 177
         linkDelegate: _linkDelegate,
178
+        onSave: widget.onSave,
174 179
         controller: widget.controller,
175 180
         focusNode: widget.focusNode,
176 181
         focusScope: FocusScope.of(context),

+ 11
- 0
packages/zefyr/lib/src/widgets/scope.dart 查看文件

@@ -45,6 +45,7 @@ class ZefyrScope extends ChangeNotifier {
45 45
     @required FocusScopeNode focusScope,
46 46
     ZefyrImageDelegate imageDelegate,
47 47
     ZefyrLinkDelegate linkDelegate,
48
+    void Function() onSave,
48 49
   })  : assert(mode != null),
49 50
         assert(controller != null),
50 51
         assert(focusNode != null),
@@ -54,6 +55,7 @@ class ZefyrScope extends ChangeNotifier {
54 55
         _controller = controller,
55 56
         _imageDelegate = imageDelegate,
56 57
         _linkDelegate = linkDelegate,
58
+        _onSave = onSave, 
57 59
         _focusNode = focusNode,
58 60
         _focusScope = focusScope,
59 61
         _cursorTimer = CursorTimer(),
@@ -143,6 +145,15 @@ class ZefyrScope extends ChangeNotifier {
143 145
     }
144 146
   }
145 147
 
148
+  void Function() _onSave;
149
+  void Function()  get onSave => _onSave;
150
+  set onSave(void Function() value) {
151
+    if (_onSave != value) {
152
+      _onSave = value;
153
+      notifyListeners();
154
+    }
155
+  }
156
+
146 157
   ZefyrMode _mode;
147 158
   ZefyrMode get mode => _mode;
148 159
   set mode(ZefyrMode value) {

+ 21
- 20
packages/zefyr/lib/src/widgets/toolbar.dart 查看文件

@@ -330,29 +330,30 @@ class ZefyrToolbarState extends State<ZefyrToolbar>
330 330
 
331 331
     // Must set unique key for the toolbar to prevent it from reconstructing
332 332
     // new state each time we toggle overlay.
333
-
333
+    final bottons = <Widget>[
334
+      kToolbarDivider,
335
+      Expanded(child: ListView(
336
+        scrollDirection: Axis.horizontal,
337
+        children: [
338
+          HeadingButton(),
339
+          buildButton(context, ZefyrToolbarAction.emoji),
340
+          if (editor.imageDelegate != null) ImageButton(),
341
+          // ImageButton(),
342
+          LinkButton(),
343
+        ],
344
+        physics: ClampingScrollPhysics(),
345
+      ),),
346
+      kToolbarDivider,
347
+      buildButton(context, ZefyrToolbarAction.undo),
348
+      buildButton(context, ZefyrToolbarAction.redo),
349
+    ];
350
+    if (editor.onSave != null) {
351
+      bottons.addAll([kToolbarDivider, buildButton(context, ZefyrToolbarAction.save)]);
352
+    }
334 353
     final toolbar = ZefyrToolbarScaffold(
335 354
       key: _toolbarKey,
336 355
       body: Row(
337
-        children: [
338
-          kToolbarDivider,
339
-          Expanded(child: ListView(
340
-            scrollDirection: Axis.horizontal,
341
-            children: [
342
-              HeadingButton(),
343
-              buildButton(context, ZefyrToolbarAction.emoji),
344
-              if (editor.imageDelegate != null) ImageButton(),
345
-              // ImageButton(),
346
-              LinkButton(),
347
-            ],
348
-            physics: ClampingScrollPhysics(),
349
-          ),),
350
-          kToolbarDivider,
351
-          buildButton(context, ZefyrToolbarAction.undo),
352
-          buildButton(context, ZefyrToolbarAction.redo),
353
-          kToolbarDivider,
354
-          buildButton(context, ZefyrToolbarAction.save),
355
-        ],
356
+        children: bottons,
356 357
       ),
357 358
       trailing: hasOverlay ? buildButton(context, ZefyrToolbarAction.showKeyboard) : buildButton(context, ZefyrToolbarAction.hideKeyboard),
358 359
     );