|
@@ -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.
|