Browse Source

add onSave callback

lucky1213 4 years ago
parent
commit
acd0259f47

+ 42
- 44
packages/zefyr/example/lib/src/full_page.dart View File

33
 
33
 
34
 Delta getDelta() {
34
 Delta getDelta() {
35
   final doc =
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
   return Delta.fromJson(json.decode(doc) as List);
41
   return Delta.fromJson(json.decode(doc) as List);
42
 }
42
 }
43
 
43
 
88
       body: Column(
88
       body: Column(
89
         children: <Widget>[
89
         children: <Widget>[
90
           Container(
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
           Container(
111
           Container(
112
             child: Row(
112
             child: Row(
113
               children: <Widget>[
113
               children: <Widget>[
142
             ),
142
             ),
143
           ),
143
           ),
144
           Expanded(
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 View File

167
       return () => editor.redo();
167
       return () => editor.redo();
168
     } else if (action == ZefyrToolbarAction.undo && editor.controller.document.history.stack.undo.isNotEmpty) {
168
     } else if (action == ZefyrToolbarAction.undo && editor.controller.document.history.stack.undo.isNotEmpty) {
169
       return () => editor.undo();
169
       return () => editor.undo();
170
+    } else if (action == ZefyrToolbarAction.save) {
171
+      return () => editor.onSave();
170
     }
172
     }
171
 
173
 
172
     return null;
174
     return null;

+ 5
- 0
packages/zefyr/lib/src/widgets/editor.dart View File

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

+ 11
- 0
packages/zefyr/lib/src/widgets/scope.dart View File

45
     @required FocusScopeNode focusScope,
45
     @required FocusScopeNode focusScope,
46
     ZefyrImageDelegate imageDelegate,
46
     ZefyrImageDelegate imageDelegate,
47
     ZefyrLinkDelegate linkDelegate,
47
     ZefyrLinkDelegate linkDelegate,
48
+    void Function() onSave,
48
   })  : assert(mode != null),
49
   })  : assert(mode != null),
49
         assert(controller != null),
50
         assert(controller != null),
50
         assert(focusNode != null),
51
         assert(focusNode != null),
54
         _controller = controller,
55
         _controller = controller,
55
         _imageDelegate = imageDelegate,
56
         _imageDelegate = imageDelegate,
56
         _linkDelegate = linkDelegate,
57
         _linkDelegate = linkDelegate,
58
+        _onSave = onSave, 
57
         _focusNode = focusNode,
59
         _focusNode = focusNode,
58
         _focusScope = focusScope,
60
         _focusScope = focusScope,
59
         _cursorTimer = CursorTimer(),
61
         _cursorTimer = CursorTimer(),
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
   ZefyrMode _mode;
157
   ZefyrMode _mode;
147
   ZefyrMode get mode => _mode;
158
   ZefyrMode get mode => _mode;
148
   set mode(ZefyrMode value) {
159
   set mode(ZefyrMode value) {

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

330
 
330
 
331
     // Must set unique key for the toolbar to prevent it from reconstructing
331
     // Must set unique key for the toolbar to prevent it from reconstructing
332
     // new state each time we toggle overlay.
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
     final toolbar = ZefyrToolbarScaffold(
353
     final toolbar = ZefyrToolbarScaffold(
335
       key: _toolbarKey,
354
       key: _toolbarKey,
336
       body: Row(
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
       trailing: hasOverlay ? buildButton(context, ZefyrToolbarAction.showKeyboard) : buildButton(context, ZefyrToolbarAction.hideKeyboard),
358
       trailing: hasOverlay ? buildButton(context, ZefyrToolbarAction.showKeyboard) : buildButton(context, ZefyrToolbarAction.hideKeyboard),
358
     );
359
     );