昨天稍微提到了狀態管理及 MobX 的基本介紹那今天就要來說明 MobX 中的核心概念。
MobX 最重要的就是這三個東西: Observables、Actions、Reactions
就是我們昨天提到的被觀察者,也就是在這個程式中的 reactive-data ,當 Observables 被改變時會通知監聽這個Observable的觀察者。
則是我們實作「要如何更改 Observable 」 的方法,我們如果要變更 Observable 只能透過 Actions。
reaction 會針對observables
的任何變動做出回應,像是我們可以使用 when
讓我們某個 observables
變成特定的值時就做出額外的動作。
而其實MobX提供的Observer Widget
也算是 Reaction 的一種,因為他追蹤了observables
的變動當他有更新時就會重新build
在程式碼中他們長得像這樣:
import 'package:mobx/mobx.dart';
part 'counter.g.dart';
class Counter = CounterBase with _$Counter;
abstract class CounterBase with Store {
@observable
int value = 0;
@action
void increment() {
value++;
}
}
我們在一個 abstract class
中宣告了一個值及function 我們只要分別加上 @observable
、@action
這兩個decorator 就能完成這件事情。
當然你會想為什麼會有 with _$Counter
、 part 'counter.g.dart'
等等奇怪的東西,其實這些decorator 是為了讓 MobX_codegen 讓我們產生 observable 以及 action 的內部實作:
class Counter {
Counter() {
increment = Action(_increment);
}
final _value = Observable(0);
int get value => _value.value;
set value(int newValue) => _value.value = newValue;
Action increment;
void _increment() {
_value.value++;
}
}
如果不套用 MobX_codegen 的就要手動寫這些code。
但需要搭配codegen 算是MobX的缺點之一,代表你每次存檔後要等一段時間讓 codegen 將MobX的code產生出來,雖然時間很短但終究會有被打斷的感覺。
那我們就開始將MobX套入我們的專案吧
就在 pubspec.yaml 新增套件:
dependencies:
# ... 省略其他
mobx: ^2.0.4
flutter_mobx: ^2.0.2
# ... 省略其他
dev_dependencies:
# ... 省略其他
build_runner: ^2.1.4
mobx_codegen: ^2.0.3
# ... 省略其他
然後新增一個檔案todo_view_model.dart
把我們在 setState
的實作搬到這裡,然後將 List
改為 ObservableList
,這兩者差距其實在這個專案是看出不來的,最主要是差在說如果單純是 List
的話裡面的元素變化時並不會被監聽,除非是整個 List
有變化之類的。
import 'package:mobx/mobx.dart';
import 'package:todolist/model/todo_model.dart';
part 'todo_view_model.g.dart';
class TodoViewModel = _TodoViewModel with _$TodoViewModel;
abstract class _TodoViewModel with Store {
@observable
ObservableList<TodoModel> todoList =
ObservableList.of([TodoModel(content: '123')]);
@action
void removeTodo(int hashCode) {
todoList =
ObservableList.of(todoList.where((todo) => todo.hashCode != hashCode));
}
@action
void addTodo(String input) {
todoList = ObservableList.of([...todoList, TodoModel(content: input)]);
}
@action
void toggleStatus(int hashCode) {
todoList = ObservableList.of(todoList.map((todo) {
if (todo.hashCode == hashCode) {
todo.isDone = !todo.isDone;
return todo;
} else {
return todo;
}
}));
}
}
然後在terminal 輸入:
flutter pub run build_runner watch
讓 MobX codegen 可以產生我們要的程式碼。
這樣我們就不需要使用 setState
了在main.dart裡面就會變成:
final todoViewModel = TodoViewModel();
void _handleAddNewTodo(String input) {
todoViewModel.addTodo(input);
_textEditingController.text = '';
}
void _handleRemoveTodo(int hashCode) {
todoViewModel.removeTodo(hashCode);
}
void _handleToggleStatus(int hashCode) {
todoViewModel.toggleStatus(hashCode);
}
然後使用 Observer
包住我們要動態更新的部分
Observer(
builder: (_) {
return Column(
children: [
// ... 省略
],
);
},
);
}
@computed
會對一個 observables
監聽後產生一個衍生的值,像是當我們想取得不同狀態的 todoList
,就可寫成這樣:
@computed
ObservableList<TodoModel> get completedTodos =>
ObservableList.of(todoList.where((todo) => todo.isDone == true));
@computed
ObservableList<TodoModel> get pendingTodos =>
ObservableList.of(todoList.where((todo) => todo.isDone != true));
程式碼:
https://github.com/zxc469469/flutter_todo_list/tree/Day21
今天大概說明了 MobX 基本用法,那這個小專案也差不多到這裡結束。
明天開始來串接API的部分