Skip to content

Commit f1477f8

Browse files
blueneogeogitbook-bot
authored andcommitted
GitBook: [master] one page and 2 assets modified
1 parent 91a5279 commit f1477f8

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed
47.5 KB
Loading
47.5 KB
Loading

guide/writing-reactive-code.md

Lines changed: 136 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@
22

33
**Flutter leaves us with a lot of freedom in how we want to write reactive code. Flutter-view proposes a structure but does not impose it.**
44

5-
This guide will show you how we recommend you build a simple reactive app with an [MVVM approach](https://en.wikipedia.org/wiki/Model–view–viewmodel) using the ReactiveWidget and flutter-view. It will look like this:
5+
This guide will show you how we recommend you build a simple reactive app with an [MVVM approach](https://en.wikipedia.org/wiki/Model–view–viewmodel) using the ReactiveWidget and flutter-view. In essence building a reactive app in flutter-view always works the same:
6+
7+
1. **handle events from your views on the view-model**
8+
2. **have the view-model call your business logic for actions**
9+
3. **have those actions update your business model**
10+
4. **have your views use the reactive tag to listen for any updates**
11+
12+
Our app will look like this:
613

714
![](../.gitbook/assets/screen-shot-2018-12-15-at-10.36.22-pm.png)
815

16+
You can find the[ full app source code](https://github.com/flutter-view/examples/tree/master/todolist) with some extra features added such as deleting and persistence in [the examples](../get-started/examples.md#todolist).
17+
918
_Note: this approach requires you add the_ [_**flutter\_view\_tools**_](https://pub.dartlang.org/packages/flutter_view_tools) _dependency to your project's **pubspec.yaml** file._
1019

1120
## The basic structure
@@ -25,7 +34,7 @@ To make things clearer, we will write a simple todo app. For this app, we will n
2534
* **lib/pages/taskspage/taskspage.dart**: the view of the taskspage, that shows all our tasks
2635
* **lib/pages/taskspage/taskspage-model.dart**: the view-model of our taskspage
2736

28-
## Creating the model
37+
## Creating the business model
2938

3039
To create any model class, we extend the [**Model**](https://pub.dartlang.org/documentation/scoped_model/latest/scoped_model/Model-class.html) class from the [scoped model library](https://pub.dartlang.org/packages/scoped_model).
3140

@@ -71,7 +80,7 @@ Both **Task** and **AppModel** extend [**Model**](https://pub.dartlang.org/docum
7180

7281
In any reactive app you want to be able to inform views to react to the data changing. The views can listen to the models by calling [**model.addListener\(\)**](https://pub.dartlang.org/documentation/scoped_model/latest/scoped_model/Model/addListener.html). You can then inform that data in a model has changed by calling [**model.notifyListeners\(\)**](https://pub.dartlang.org/documentation/scoped_model/latest/scoped_model/Model/notifyListeners.html).
7382

74-
## Creating a view
83+
## Creating a view and view-model
7584

7685
To present the app, we need two basic things:
7786

@@ -178,7 +187,7 @@ At line **7** we start with a builder. This is a convenient way to get access to
178187

179188
Line **8** creates the Scaffold of our page. It has three parts: an AppBar at line **9-10**, a body with a placeholder text at line **12-13** and a FloatingActionButton at like **15-16**. For now, we are not yet showing the tasks.
180189

181-
## Using a view and view-model
190+
## Wiring up the app
182191

183192
To use the **AppModel**, **Task**, **TasksPage** and **TasksPageModel** we just created, we need to start the app from **main.dart.**
184193

@@ -187,12 +196,15 @@ The top-level widget we create in **main.dart** should do the following:
187196
* create our **AppModel** and keep it in its state
188197
* start as home with our **TasksPage** and pass a **TasksPageModel**
189198

199+
{% code-tabs %}
200+
{% code-tabs-item title="main.dart" %}
190201
```dart
191202
import 'package:flutter/material.dart';
192203
import 'package:todolist/model/app-model.dart';
193204
import 'package:todolist/pages/taskspage/taskspage-model.dart';
194205
import 'package:todolist/pages/taskspage/taskspage.dart';
195206
207+
196208
void main() {
197209
runApp(TodoListApp());
198210
}
@@ -220,6 +232,8 @@ class _TodoListAppState extends State<TodoListApp> {
220232
);
221233
}
222234
```
235+
{% endcode-tabs-item %}
236+
{% endcode-tabs %}
223237

224238
At line **15** we create the state for our **TodoListApp.** It keeps the **AppModel** at line **17**. In the **initState\(\)** method, we initialize our **app**.
225239

@@ -237,51 +251,75 @@ In the case of our app, we want to show the tasks from the **AppModel** in our *
237251

238252
First lets set some example starter tasks. In **AppModel**, add some starter tasks:
239253

254+
{% code-tabs %}
255+
{% code-tabs-item title="app-model.dart" %}
240256
```text
241257
// this.tasks = [];
258+
242259
this.tasks = [
243-
Task(name: 'Do the dishes'),
244-
Task(name: 'Cut the grass'),
260+
Task(name: 'Do the dishes', done: true),
261+
Task(name: 'Enjoy the day', done: false),
245262
];
246263
247264
```
265+
{% endcode-tabs-item %}
266+
{% endcode-tabs %}
248267

249268
To actually show the tasks on the **tasks-page**, we can iterate through them with the flutter-view [**for property**](flow-control.md#for)**:**
250269

270+
{% code-tabs %}
271+
{% code-tabs-item title="task-page.pug" %}
251272
```css
252273
// #body(as='body')
253274
// center Here be tasks!
275+
254276
#body(as='body')
255277
list-view
256278
.task(for='task in model.app.tasks') ${task.name}
257279
```
280+
{% endcode-tabs-item %}
281+
{% endcode-tabs %}
258282

259283
Now after hot reloading you should see the two tasks, as two lines of text.
260284

261285
In line 5, we are creating an array of .task Containers, one for each element in **TaskPageModel.app.tasks.** In each container we put a text with the task name.
262286

263287
To clean up the presentation a bit, we can create a view that creates a single task, and repeat that instread. Add the following flutter-view code in **tasks-page.pug**:
264288

289+
{% code-tabs %}
290+
{% code-tabs-item title="tasks-page.pug" %}
265291
```css
266292
task-entry(flutter-view :task[Task] :model[TasksPageModel])
267293
card
268294
row
269295
.title ${task.name}
270296
checkbox(:^value='task.done')
271297
```
298+
{% endcode-tabs-item %}
299+
{% endcode-tabs %}
300+
301+
This renders a single task entry.
272302

273-
This renders a single task entry. Now we can use it in the body:
303+
_Note the **value** property in the checkbox tag. It needs to be escaped because value is a reserved flutter-view keyword, used to pass an unnamed parameter._
274304

305+
Now we can use it in the body:
306+
307+
{% code-tabs %}
308+
{% code-tabs-item title="tasks-page.pug" %}
275309
```css
276310
#body(as='body')
277311
list-view
278312
task-entry(for='task in model.app.tasks' :task='task' :model='model')
279313
```
314+
{% endcode-tabs-item %}
315+
{% endcode-tabs %}
280316

281317
In line 3, we are again using for to repeat the tag for each task. A **task-entry** takes two parameters: the task and the model, so we pass both.
282318

283319
Finally, let's add some styling. Create a tasks-page.sass next to tasks-page.pug, and put in the following styling:
284320

321+
{% code-tabs %}
322+
{% code-tabs-item title="tasks-page.sass" %}
285323
```css
286324
task-entry
287325
card
@@ -291,12 +329,103 @@ task-entry
291329
margin-left: 20
292330
font-size: 20
293331
```
332+
{% endcode-tabs-item %}
333+
{% endcode-tabs %}
294334

295335
Please compare this sass styling with the pug task-entry we added. The Row in task-entry gets assigned a space between main-axis-alignment, which pushes the text to the left and the checkbox to the right. Besides that we set a font size and margin to the **.title** Container.
296336

297337
See the generated task-page.dart to see what actually is being generated in Dart, by taking the Pug and applying the styles with shortcuts. The result looks like this:
298338

299339
![](../.gitbook/assets/screen-shot-2018-12-18-at-4.20.01-pm.png)
300340

341+
## Making the view reactive
342+
343+
**To make a view react to changes in your model, use the** [**reactive tag**](../reference/tag-shortcuts.md#reactive)**.**
344+
345+
Now let us create a new task entry with the text "new task" whenever the user presses the + button. This requires the following steps:
346+
347+
1. listen to the event of the user pressing the button
348+
2. handle the event in the view-model, asking the AppModel for a new task
349+
3. have the AppModel create the new task
350+
4. make the page-view react to changes to the AppModel.tasks
351+
352+
### Listening to the event of the user pressing the button
353+
354+
The + button is defined in tasks-page.pug. Change the code for the floating-action-button like this:
355+
356+
{% code-tabs %}
357+
{% code-tabs-item title="tasks-page.pug" %}
358+
```css
359+
floating-action-button(
360+
as='floatingActionButton'
361+
@on-pressed='model.onAddButtonPressed(context)')
362+
icon(:value='Icons.add')
363+
```
364+
{% endcode-tabs-item %}
365+
{% endcode-tabs %}
366+
367+
The **@on-pressed** event handler is new. It calls **TasksPageModel.onAddButtonPressed\(\)** with the current BuildContext. We need to create this method on **TasksPageModel.**
368+
369+
### **Handle the event in the view-model**
370+
371+
We want the view-model to ask the app to create a new task. Add the following code to **TasksPageModel** in tasks-page-model.dart:
372+
373+
{% code-tabs %}
374+
{% code-tabs-item title="tasks-page-model.dart" %}
375+
```dart
376+
onAddButtonPressed(BuildContext context) {
377+
this.app.addTask(title: "new task");
378+
}
379+
```
380+
{% endcode-tabs-item %}
381+
{% endcode-tabs %}
382+
383+
### Have the AppModel create the new task
384+
385+
Now to create the new task in the AppModel, we need to create a task and add it to the list of tasks. Add the following code to **AppModel** in app-model.dart:
386+
387+
{% code-tabs %}
388+
{% code-tabs-item title="app-model.dart" %}
389+
```dart
390+
addTask({String title}) {
391+
final task = Task(name: title);
392+
this.tasks.add(task);
393+
this.notifyListeners();
394+
}
395+
```
396+
{% endcode-tabs-item %}
397+
{% endcode-tabs %}
398+
399+
In line **2** we create the new task. We then add it. Finally and importantly, we need to call **notifyListeners\(\)** on the AppModel. This will inform the interface to respond to the new task being added.
400+
401+
### Make the page-view react to changes to the AppModel.tasks
402+
403+
To make page-view react to changes of the **AppModel**, we need to watch the **AppModel** using the **reactive tag**. Since we pass the **AppModel** to the **TasksPageModel**, we can use model.app to get a reference to the **AppModel.** Update the tasks-page body again:
404+
405+
{% code-tabs %}
406+
{% code-tabs-item title="tasks-page.pug" %}
407+
```css
408+
// #body(as='body')
409+
410+
reactive(as='body' watch='model.app')
411+
list-view
412+
task-entry(for='task in model.app.tasks' :task='task' :model='model')
413+
```
414+
{% endcode-tabs-item %}
415+
{% endcode-tabs %}
416+
417+
The only real replacement is that we changed a simple \#body container into a reactive tag, that watches the app for changes. When the **notifyListeners\(\)** call is made on the **AppModel**, everything below the reactive tag is reevaluated. Thus when we add a new task, it should now show in the view:
418+
419+
![](../.gitbook/assets/screen-shot-2018-12-18-at-5.00.15-pm%20%281%29.png)
420+
421+
This covers the basics of how to make any app react to your model changes.
422+
423+
Of course our todo app is still incomplete,however with this you should be able to understand the full implementation. See [the full example](../get-started/examples.md#todolist) with [source code](https://github.com/flutter-view/examples/tree/master/todolist) for a full implementation of this todo app. Notable additions there are:
424+
425+
* an add-task-dialog view that lets you enter a new task
426+
* being able to check tasks as done
427+
* swipe to delete tasks
428+
* automatic saving and loading
429+
301430

302431

0 commit comments

Comments
 (0)