Browse Source

updating quick start

Anatoly Pulyaevskiy 5 years ago
parent
commit
b9e03abbd0
3 changed files with 129 additions and 23 deletions
  1. BIN
      assets/quick-start-rec-03.gif
  2. 103
    2
      doc/quick-start.md
  3. 26
    21
      packages/zefyr/example/lib/src/editor_page.dart

BIN
assets/quick-start-rec-03.gif View File


+ 103
- 2
doc/quick-start.md View File

@@ -148,6 +148,8 @@ Here is how it might look when we run the app and navigate to editor page:
148 148
 
149 149
 <img src="https://github.com/memspace/zefyr/raw/gitbook/assets/quick-start-rec-01.gif" width="600">
150 150
 
151
+### 04. Save document to a JSON file
152
+
151 153
 At this point we can already edit the document and apply styles, however if
152 154
 we navigate back from this page our changes will be lost. Let's fix this and
153 155
 add a button which saves the document to the device's file system.
@@ -227,12 +229,111 @@ class EditorPageState extends State<EditorPage> {
227 229
 }
228 230
 ```
229 231
 
230
-We have to use `Builder` here for our icon button because we need access to
231
-build context which has access to `Scaffold` widget's state.
232
+We have to use `Builder` here for our icon button because we need `BuildContext`
233
+which has access to `Scaffold` widget's state.
232 234
 
233 235
 Now we can reload our app, hit "Save" button and see the snack bar.
234 236
 
235 237
 <img src="https://github.com/memspace/zefyr/raw/gitbook/assets/quick-start-rec-02.gif" width="600">
236 238
 
239
+### 05. Load document from a JSON file
240
+
237 241
 Since we now have this document saved to a file, let's update our
238 242
 `_loadDocument` method to load saved file if it exists.
243
+
244
+```dart
245
+class EditorPageState extends State<EditorPage> {
246
+
247
+  // change: replace _loadDocument() method with following
248
+
249
+  /// Loads the document asynchronously from a file if it exists, otherwise
250
+  /// returns default document.
251
+  Future<NotusDocument> _loadDocument() async {
252
+    final file = File(Directory.systemTemp.path + "/quick_start.json");
253
+    if (await file.exists()) {
254
+      final contents = file.readAsStringSync();
255
+      return NotusDocument.fromJson(jsonDecode(contents));
256
+    }
257
+    final Delta delta = Delta()..insert("Zefyr Quick Start\n");
258
+    return NotusDocument.fromDelta(delta);
259
+  }
260
+}
261
+```
262
+
263
+We had to convert this method to be __async__ because file system operations
264
+are asynchronous. This breaks our `initState` logic so we need to fix it next.
265
+However we can no longer initialize `ZefyrController` in `initState` and
266
+therefore can't display the editor until document is loaded.
267
+
268
+A common way to fix this is to show loader animation while we are reading our
269
+document from file. But first, we still need to update `initState` method:
270
+
271
+```dart
272
+class EditorPageState extends State<EditorPage> {
273
+
274
+  // change: replace initState() method with following
275
+
276
+  @override
277
+  void initState() {
278
+    super.initState();
279
+    _focusNode = new FocusNode();
280
+    _loadDocument().then((document) {
281
+      setState(() {
282
+        _controller = new ZefyrController(document);
283
+      });
284
+    });
285
+  }
286
+}
287
+```
288
+
289
+We initialize `_controller` only when our document is fully loaded from file
290
+system. An important part here is to update `_controller` field inside of
291
+`setState` call as required by Flutter's stateful widgets.
292
+
293
+The only thing left is to update `build()` method to show loader animation:
294
+
295
+```dart
296
+class EditorPageState extends State<EditorPage> {
297
+
298
+  // change: replace build() method with following
299
+
300
+  @override
301
+  Widget build(BuildContext context) {
302
+    // If _controller is null we show Material Design loader, otherwise
303
+    // display Zefyr editor.
304
+    final Widget body = (_controller == null)
305
+        ? Center(child: CircularProgressIndicator())
306
+        : ZefyrScaffold(
307
+            child: ZefyrEditor(
308
+              padding: EdgeInsets.all(16),
309
+              controller: _controller,
310
+              focusNode: _focusNode,
311
+            ),
312
+          );
313
+
314
+    return Scaffold(
315
+      appBar: AppBar(
316
+        title: Text("Editor page"),
317
+        actions: <Widget>[
318
+          Builder(
319
+            builder: (context) => IconButton(
320
+              icon: Icon(Icons.save),
321
+              onPressed: () => _saveDocument(context),
322
+            ),
323
+          )
324
+        ],
325
+      ),
326
+      body: body,
327
+    );
328
+  }
329
+}
330
+```
331
+
332
+If we save changes now and reload the app we should see something like this:
333
+
334
+<img src="https://github.com/memspace/zefyr/raw/gitbook/assets/quick-start-rec-03.gif" width="600">
335
+
336
+Note that in your test you'll likely not notice loading animation at all. This
337
+is because reading a tiny file from disk is too fast. For the above recording
338
+we added an artificial delay of 1 second in order to demonstrate loading. We'll
339
+leave this task to you as an exercise.

+ 26
- 21
packages/zefyr/example/lib/src/editor_page.dart View File

@@ -20,16 +20,26 @@ class EditorPageState extends State<EditorPage> {
20 20
   @override
21 21
   void initState() {
22 22
     super.initState();
23
-    // Here we must load the document and pass it to Zefyr controller.
24
-    final document = _loadDocument();
25
-    _controller = new ZefyrController(document);
26 23
     _focusNode = new FocusNode();
24
+    _loadDocument().then((document) {
25
+      setState(() {
26
+        _controller = new ZefyrController(document);
27
+      });
28
+    });
27 29
   }
28 30
 
29 31
   @override
30 32
   Widget build(BuildContext context) {
31
-    // Note that the editor requires special `ZefyrScaffold` widget to be
32
-    // present somewhere up the widget tree.
33
+    final Widget body = (_controller == null)
34
+        ? Center(child: CircularProgressIndicator())
35
+        : ZefyrScaffold(
36
+            child: ZefyrEditor(
37
+              padding: EdgeInsets.all(16),
38
+              controller: _controller,
39
+              focusNode: _focusNode,
40
+            ),
41
+          );
42
+
33 43
     return Scaffold(
34 44
       appBar: AppBar(
35 45
         title: Text("Editor page"),
@@ -42,27 +52,22 @@ class EditorPageState extends State<EditorPage> {
42 52
           )
43 53
         ],
44 54
       ),
45
-      body: ZefyrScaffold(
46
-        child: ZefyrEditor(
47
-          padding: EdgeInsets.all(16),
48
-          controller: _controller,
49
-          focusNode: _focusNode,
50
-        ),
51
-      ),
55
+      body: body,
52 56
     );
53 57
   }
54 58
 
55
-  /// Loads the document to be edited in Zefyr.
56
-  NotusDocument _loadDocument() {
57
-    // For simplicity we hardcode a simple document with one line of text
58
-    // saying "Zefyr Quick Start".
59
+  /// Loads the document asynchronously from a file if it exists, otherwise
60
+  /// returns default document.
61
+  Future<NotusDocument> _loadDocument() async {
62
+    final file = File(Directory.systemTemp.path + "/quick_start.json");
63
+    if (await file.exists()) {
64
+      final contents = await file
65
+          .readAsString()
66
+          .then((data) => Future.delayed(Duration(seconds: 1), () => data));
67
+      return NotusDocument.fromJson(jsonDecode(contents));
68
+    }
59 69
     final Delta delta = Delta()..insert("Zefyr Quick Start\n");
60
-//     Note that delta must always end with newline.
61 70
     return NotusDocument.fromDelta(delta);
62
-
63
-//    final file = File(Directory.systemTemp.path + "/quick_start.json");
64
-//    final contents = file.readAsStringSync();
65
-//    return NotusDocument.fromJson(jsonDecode(contents));
66 71
   }
67 72
 
68 73
   void _saveDocument(BuildContext context) {