Flutter MobX 响应式原理与实战详解

发布于:2025-06-26 ⋅ 阅读:(16) ⋅ 点赞:(0)
📚 Flutter 状态管理系列文章目录
  1. setState() 使用详解:原理及注意事项

  2. Flutter 状态管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux)

  3. InheritedWidget 组件使用及原理

  4. Flutter 中 Provider 的使用、注意事项与原理解析(含代码实战)

  5. GetX 用法详细解析以及注意事项

  6. Flutter BLoC 使用详细解析

  7. Flutter MobX 响应式原理与实战详解

✨ 起点:为什么要了解 MobX?

作为一个刚开始接触 Flutter 状态管理的开发者,我最初接触到的是 setStateProvider 等方式。当我看到有人说 MobX 是“最像魔法的状态管理库”时,我开始好奇:为什么它能不写 setState 就自动刷新 UI?

带着这样的疑问,我开启了对 MobX 的深入探索。这篇文章就是我把所有探索过程系统整理后的总结,希望对你也有帮助。


🛠 一、MobX 的安装与基本使用

MobX 的核心有三大类:

概念 说明
Observable 可观察状态,类似变量。变化时自动触发 UI 更新
Action 改变状态的行为,所有状态变更建议通过 action
Reaction 监听变化后做副作用,比如打印日志、导航等

1. 添加依赖

pubspec.yaml 中:

dependencies:
  flutter:
    sdk: flutter
  mobx: ^2.2.0
  flutter_mobx: ^2.0.6

dev_dependencies:
  build_runner: ^2.4.6
  mobx_codegen: ^2.4.0

2.创建一个 MobX Store(状态容器)

counter_store.dart

import 'package:mobx/mobx.dart';

part 'counter_store.g.dart'; // 自动生成的文件

class CounterStore = _CounterStore with _$CounterStore;

abstract class _CounterStore with Store {
  
  int count = 0;

  
  void increment() {
    count++;
  }
}

生成代码命令:

flutter pub run build_runner build
# 或者自动监听:
flutter pub run build_runner watch

3. 使用 Observer 监听状态

import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'counter_store.dart';

class CounterPage extends StatelessWidget {
  final CounterStore counter = CounterStore();

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("MobX Counter")),
      body: Center(
        child: Observer(
          builder: (_) => Text(
            '${counter.count}',
            style: TextStyle(fontSize: 40),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: counter.increment,
        child: Icon(Icons.add),
      ),
    );
  }
}

📚 二、MobX 核心注解说明

注解 作用 示例
@observable 可监听变量 @observable int count = 0;
@action 状态改变函数 @action void increment() => count++;
@computed 派生状态 String get fullName => "$firstName $lastName";

🧩 三、不同类型的 Observer 使用方式

类型 使用场景 是否支持 child 是否自动跟踪依赖
Observer 通用 UI 刷新
Observer.builder 自定义构建器
ObserverWidget(自定义) 可封装为组件
Observer + child: 子组件不依赖状态,可优化性能

1️⃣ 最常用的 Observer

适用于所有直接在 build() 方法中监听 observable 状态的场景。

✅ 示例:
Observer(
  builder: (_) => Text(
    '${counter.count}',
    style: TextStyle(fontSize: 32),
  ),
)

每当 counter.count 更新时,Text 会自动重建。


2️⃣ Observer + child 优化重建

用于优化性能:当 UI 中部分元素是静态的,可以通过 child 参数避免重建。

✅ 示例:
Observer(
  child: Icon(Icons.favorite), // 不会因为状态变化而重建
  builder: (_, child) => Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      child!,
      SizedBox(width: 8),
      Text('${counter.count}'),
    ],
  ),
)

Icon 是静态内容,MobX 不会重复创建,节省性能。


3️⃣ 封装成组件:自定义 ObserverWidget

当你想将 Observer 封装成复用组件时,可创建一个继承自 StatelessWidgetObserver 控件。

✅ 示例:
class CountText extends StatelessWidget {
  final CounterStore store;
  const CountText({required this.store});

  
  Widget build(BuildContext context) {
    return Observer(
      builder: (_) => Text('${store.count}', style: TextStyle(fontSize: 24)),
    );
  }
}

使用方式:

CountText(store: counterStore)

4️⃣ ListView/GridView 中的 Observer

当你需要对列表内容做响应式刷新时,每个 item 可以使用 Observer 包裹。

✅ 示例:
Observer(
  builder: (_) => ListView.builder(
    itemCount: todoStore.todos.length,
    itemBuilder: (_, index) {
      final todo = todoStore.todos[index];
      return Observer(
        builder: (_) => CheckboxListTile(
          value: todo.done,
          onChanged: (_) => todo.toggleDone(),
          title: Text(todo.title),
        ),
      );
    },
  ),
)

每个 CheckboxListTile 都会监听自己对应的 todo.done,提高效率。


🧠 补充:多层 Observer 嵌套性能说明

MobX 的响应式追踪是“精确依赖感知”的,每个 Observer 只监听自己用到的 @observable不会因为 store 中其他字段变化而重建,比传统 Provider 更细粒度。


✅ 小结对比
类型 优势 适用场景
Observer 最常用,自动追踪 通用响应式 UI
Observer + child 性能优化 静态元素嵌入 UI
自定义 StatelessWidget 包裹 Observer 组件复用 通用响应式组件
列表中嵌套 Observer 精细追踪每一项变化 todo、商品列表

✅ MobX 为什么可以“这么做”——原理解析

一、核心机制:可观察(Observable) + 自动追踪(Tracking)
  1. @observable 的变量不是普通变量,而是被 MobX 包装成了 可追踪对象(Atom)

  2. Observer 被构建时,MobX 开启一个 “追踪上下文”(Tracking Context):

    • Observer.builder() 中访问了哪些 @observable,就自动把这些变量注册为“依赖”。
  3. 一旦这些依赖发生变化,MobX 会自动通知 Observer 重新构建 UI。


二、过程举例说明

int count = 0;

Observer(
  builder: (_) => Text('$count'),
)

➡ MobX 做了什么?

  1. 初次 build 时读取了 count,MobX 会记录下这个“读取动作”:

    “这个 Observer 正在依赖 count!”

  2. 后续调用 count++(即修改 observable)时:

    • MobX 发现 count 改了,它就通知所有依赖 count 的观察者(也就是这个 Observer)重新执行 builder,从而刷新 UI。

三、MobX 响应式模型的类比

可以把 MobX 的响应式系统类比成:

  • @observable = 数据源(比如“温度传感器”)
  • Observer = 显示模块(比如“电子温度计”)
  • MobX = 自动布线系统,知道哪个显示器监听哪个传感器

只要传感器变化,显示模块就刷新。开发者什么都不用手动做。


四、底层依赖追踪的原理:Atom + Reaction

MobX 的核心类是:

说明
Atom 每个 observable 变量都是一个 Atom
Reaction 每个 Observer 内部注册了一个 reaction
Context 每次执行 builder() 时,MobX 开启上下文收集依赖
Derivation 指的是任何依赖 observable 的函数,比如 computedObserver

五、Flutter 中 UI 为什么可以重建?

因为 Observer 继承自 StatelessWidget,而其内部逻辑其实是这样的(伪代码):

class Observer extends StatelessWidget {
  final Reaction _reaction;

  Observer({required WidgetBuilder builder}) {
    _reaction = Reaction(() {
      // 当依赖的 observable 改变时,调用 setState 或 rebuild
    });
  }

  
  Widget build(BuildContext context) {
    // 启动依赖收集
    _reaction.track(() {
      builder(context); // 在这个过程中读取 observable,就会注册依赖
    });
  }
}

✅ 总结一句话

MobX 可以“这么做”,是因为它在 Observer.builder 执行时自动收集依赖变量,一旦这些变量变化,MobX 会主动通知刷新 UI。你不需要写 setState,它已经帮你自动“追踪 + 通知 + 重建”了。


⚙️ 五、自己手写一个简化版 MobX(低配实现)

太棒了!下面我就来写一个低配版的 MobX 响应式系统实现,让你直观理解它的底层机制。这个版本包括:

  1. Observable<T>:可观察变量
  2. Reaction:监听依赖并自动响应
  3. autorun():一个简化版的 Observer 行为
  4. 自动依赖追踪:MobX 的“神操作”

🧠 目标:当你读取 observable 的时候自动注册依赖,当你修改 observable 的时候,能触发依赖函数。


🔧 第一步:实现全局上下文收集机制

typedef VoidCallback = void Function();

class ReactiveContext {
  static final ReactiveContext _instance = ReactiveContext._internal();
  VoidCallback? _currentReaction;

  factory ReactiveContext() => _instance;
  ReactiveContext._internal();

  void setCurrentReaction(VoidCallback? reaction) {
    _currentReaction = reaction;
  }

  VoidCallback? get currentReaction => _currentReaction;
}

🔮 第二步:实现 Observable

class Observable<T> {
  T _value;
  final Set<VoidCallback> _listeners = {};

  Observable(this._value);

  T get value {
    // 自动依赖收集
    final reaction = ReactiveContext()._currentReaction;
    if (reaction != null) {
      _listeners.add(reaction);
    }
    return _value;
  }

  set value(T newValue) {
    if (newValue != _value) {
      _value = newValue;
      // 通知所有监听者
      for (var listener in _listeners) {
        listener();
      }
    }
  }
}

🔁 第三步:实现 autorun(模拟 Observer)

void autorun(VoidCallback runner) {
  void wrapped() {
    ReactiveContext().setCurrentReaction(wrapped); // 注册当前 reaction
    runner();
    ReactiveContext().setCurrentReaction(null); // 清理
  }

  wrapped(); // 初始执行一次,收集依赖
}

🎯 示例:模拟 Counter 自动追踪 + 响应

void main() {
  final count = Observable(0);

  autorun(() {
    print("当前 count 是:${count.value}");
  });

  // 模拟用户操作
  count.value = 1; // 自动触发 autorun 输出
  count.value = 2;
}
✅ 输出:
当前 count 是:0
当前 count 是:1
当前 count 是:2

🧠 总结:

MobX 组件 我们实现的类
@observable Observable<T>
Observer autorun()
依赖追踪 ReactiveContext
reaction VoidCallback 注册

💡 提升建议:

你可以进一步加上:

  • computed(派生值)
  • action(封装状态更新)
  • dispose()(清除监听)
  • 与 Flutter Widget 结合(如用 ValueListenableBuilder 或自写 ObserverWidget

🗺 六、MobX 响应流程图

Observable
   ↓ (读取)
Reaction ← 自动注册依赖
   ↓
值变化?
   ↓ 是
刷新 UI(Observer)

(你也可以参考文中附图,完整展现了 MobX 的响应链条)


✅ 七、总结:MobX 值得用吗?

MobX 让状态管理变得优雅和现代化:

  • 不用写 setState
  • 不需要 notifyListeners
  • 自动依赖收集
  • 最细粒度响应更新

适合追求响应式编程和简洁架构的开发者。如果你喜欢 Vue、Svelte 那种“写了就动”的感觉,MobX 会让你爱不释手。


🧰 附加建议

  • 推荐将每个 Store 模块化、组件化;
  • 可结合依赖注入工具如 GetIt 使用;
  • 想要更复杂管理?可引入 reactionwhen 进行副作用管理。
📚 Flutter 状态管理系列文章目录
  1. setState() 使用详解:原理及注意事项

  2. Flutter 状态管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux)

  3. InheritedWidget 组件使用及原理

  4. Flutter 中 Provider 的使用、注意事项与原理解析(含代码实战)

  5. GetX 用法详细解析以及注意事项

  6. Flutter BLoC 使用详细解析

  7. Flutter MobX 响应式原理与实战详解


网站公告

今日签到

点亮在社区的每一天
去签到