场景

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

Analysis

ChangeNotifierProvider 里,create: 参数接收一个函数,用来“现场”构造要提供给子树的对象。写成

create: (context) => MyAppState(),

可以逐句拆解成下面几点:

片段 含义 iOS 类比
(context) => ... 匿名函数(或称“箭头函数”);框架在插入 Provider 时调用一次。context 是当前 Provider 所在节点的 BuildContext,很少用到,但可用于读取别的 Provider 或 Theme。 类似在 loadView() 里用闭包初始化子视图
MyAppState() 函数返回的新实例。因为 MyAppState 继承自 ChangeNotifier,它具备 addListener / notifyListeners 等能力,能在数据变动时通知 UI 重建。 自己写的 Observable / NSNotification 发布者
返回值 这就是 Provider 要“注入”进 widget tree 的状态对象。子树里可以 context.watch<MyAppState>() 拿到同一个实例。 单例 / 依赖注入容器里保存的共享实例

生命周期 & 线程

  1. 只创建一次

    • ChangeNotifierProvider 首次出现在 widget tree 时,框架调用 create
    • 后续因父组件 setState 导致的重建不会再执行 create——实例被缓存并复用。
  2. 自动释放

    • 当 Provider 被移出 widget tree(比如页面 pop),它会自动调用 dispose(),而 ChangeNotifierProvider 已经帮你把 MyAppState.dispose() 调好。
    • 你无需手动管理内存/订阅取消,和 SwiftUI 的 @StateObject 有点像。
  3. 主线程保证

    • Flutter 所有 UI 更新与通知回调默认都在主 isolate(≈ 主线程)执行,不用担心线程切换。

为什么不用 value:

ChangeNotifierProvider(
  value: someExistingInstance,
  child: ...
)
  • value: 只适合已经存在的对象(比如从外部传进来的单例)。
  • 如果自己 new 一个对象再塞给 value:,Provider 就不知道何时该自动 dispose(),容易造成内存泄漏。
  • 因此自己创建对象时,应优先用 create:

总结

  • create: 负责 实例化托管 状态对象;
  • Provider 把该对象挂在 widget tree 上,供后代通过 context.watch / read / select 访问;
  • 当 Provider 被移除时,自动 dispose(),让资源安全回收。

把这点想成“Provider = 带生命周期管理的依赖注入容器”,就不难理解了。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐