You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: guide/writing-reactive-code.md
+136-7Lines changed: 136 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,10 +2,19 @@
2
2
3
3
**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.**
4
4
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**
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
+
9
18
_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._
10
19
11
20
## The basic structure
@@ -25,7 +34,7 @@ To make things clearer, we will write a simple todo app. For this app, we will n
25
34
***lib/pages/taskspage/taskspage.dart**: the view of the taskspage, that shows all our tasks
26
35
***lib/pages/taskspage/taskspage-model.dart**: the view-model of our taskspage
27
36
28
-
## Creating the model
37
+
## Creating the business model
29
38
30
39
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).
31
40
@@ -71,7 +80,7 @@ Both **Task** and **AppModel** extend [**Model**](https://pub.dartlang.org/docum
71
80
72
81
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).
73
82
74
-
## Creating a view
83
+
## Creating a view and view-model
75
84
76
85
To present the app, we need two basic things:
77
86
@@ -178,7 +187,7 @@ At line **7** we start with a builder. This is a convenient way to get access to
178
187
179
188
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.
180
189
181
-
## Using a view and view-model
190
+
## Wiring up the app
182
191
183
192
To use the **AppModel**, **Task**, **TasksPage** and **TasksPageModel** we just created, we need to start the app from **main.dart.**
184
193
@@ -187,12 +196,15 @@ The top-level widget we create in **main.dart** should do the following:
187
196
* create our **AppModel** and keep it in its state
188
197
* start as home with our **TasksPage** and pass a **TasksPageModel**
@@ -220,6 +232,8 @@ class _TodoListAppState extends State<TodoListApp> {
220
232
);
221
233
}
222
234
```
235
+
{% endcode-tabs-item %}
236
+
{% endcode-tabs %}
223
237
224
238
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**.
225
239
@@ -237,51 +251,75 @@ In the case of our app, we want to show the tasks from the **AppModel** in our *
237
251
238
252
First lets set some example starter tasks. In **AppModel**, add some starter tasks:
239
253
254
+
{% code-tabs %}
255
+
{% code-tabs-item title="app-model.dart" %}
240
256
```text
241
257
// this.tasks = [];
258
+
242
259
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),
245
262
];
246
263
247
264
```
265
+
{% endcode-tabs-item %}
266
+
{% endcode-tabs %}
248
267
249
268
To actually show the tasks on the **tasks-page**, we can iterate through them with the flutter-view [**for property**](flow-control.md#for)**:**
250
269
270
+
{% code-tabs %}
271
+
{% code-tabs-item title="task-page.pug" %}
251
272
```css
252
273
// #body(as='body')
253
274
// center Here be tasks!
275
+
254
276
#body(as='body')
255
277
list-view
256
278
.task(for='task in model.app.tasks') ${task.name}
257
279
```
280
+
{% endcode-tabs-item %}
281
+
{% endcode-tabs %}
258
282
259
283
Now after hot reloading you should see the two tasks, as two lines of text.
260
284
261
285
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.
262
286
263
287
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**:
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._
274
304
305
+
Now we can use it in the body:
306
+
307
+
{% code-tabs %}
308
+
{% code-tabs-item title="tasks-page.pug" %}
275
309
```css
276
310
#body(as='body')
277
311
list-view
278
312
task-entry(for='task in model.app.tasks' :task='task' :model='model')
279
313
```
314
+
{% endcode-tabs-item %}
315
+
{% endcode-tabs %}
280
316
281
317
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.
282
318
283
319
Finally, let's add some styling. Create a tasks-page.sass next to tasks-page.pug, and put in the following styling:
284
320
321
+
{% code-tabs %}
322
+
{% code-tabs-item title="tasks-page.sass" %}
285
323
```css
286
324
task-entry
287
325
card
@@ -291,12 +329,103 @@ task-entry
291
329
margin-left: 20
292
330
font-size: 20
293
331
```
332
+
{% endcode-tabs-item %}
333
+
{% endcode-tabs %}
294
334
295
335
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 setafont size and margin to the **.title** Container.
296
336
297
337
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:
**To make aview 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-itemtitle="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:
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:
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
0 commit comments