场景
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(),
),
);
}
}
class MyAppState extends ChangeNotifier {
var current = WordPair.random();
void getNext() {
current = WordPair.random();
notifyListeners();
}
var favorites = <WordPair>[];
void toggleFavorite() {
if (favorites.contains(current)) {
favorites.remove(current);
} else {
favorites.add(current);
}
notifyListeners();
}
}
Analysis
在 Flutter 里,“一切皆 Widget”。
build
方法的声明:
Widget build(BuildContext context)
只要求“返回 某种 Widget”。并没有规定必须是 Container
、Text
或者别的具体类型——只要最终返回值 实现 了 Widget
抽象类即可。
ChangeNotifierProvider
本质上也是 Widget
ChangeNotifierProvider
(来自 provider 包)继承自 InheritedNotifier<ChangeNotifier>
,而 InheritedNotifier
又直接或间接继承自 StatelessWidget
,最终实现了 Widget
。
所以从类型系统的角度看,它完全符合 Widget build(...)
的返回要求:
Widget <-- 抽象基类
└─ StatelessWidget
└─ InheritedNotifier
└─ ChangeNotifierProvider ✓
类比到 iOS / UIKit
- 在 UIKit 里,
UIViewController.view
需要返回一个UIView
。
你既可以返回UILabel
、UIButton
,也可以返回自定义继承自UIView
的子类;本质上它们都是UIView
。 - Flutter 里
build
需要返回Widget
。ChangeNotifierProvider
、Container
、Text
……都继承自Widget
,所以都合法。
为什么要把 Provider 当根节点返回?
注入状态
ChangeNotifierProvider
的职责是把MyAppState
(一个ChangeNotifier
)放进 widget tree 的上层,以便子树里的任何 widget 都能通过context.watch<MyAppState>()
访问它。组合而非继承
Flutter 树形 UI 的推荐写法是“用 widget 组合功能”。把 Provider 放在最外层,可以在不修改下层页面的前提下,随时替换 / 拆分状态管理方案。仍然可以嵌套真实 UI
Provider 自己也有一个child
。在你的代码里,child
是MaterialApp(...)
——这是实际渲染 UI 的根。Provider 只是一个透明的“功能包装器”。
return ChangeNotifierProvider(
create: (_) => MyAppState(),
child: MaterialApp(
// ...UI 层次从这里开始
),
);
换句话说,ChangeNotifierProvider
≈ “带状态注入功能的空壳 View”,它把真正的 UI (MaterialApp
) 装进去,再把整个结果返回给 Flutter 框架。对 build
而言,这依旧只是一个合法的 Widget
。
总结
build
只要求返回任何实现了Widget
的对象。ChangeNotifierProvider
继承自Widget
,当然可以直接返回。- 这样写的好处是:在最外层注入全局状态,子树里随时
context.watch
、context.read
即可使用;UI 结构保持纯粹,逻辑与表现分离。