概述
flutter基于Widget树渲染内容 代码形如
// 注意 Widget并不绑定state. widget总是会销毁重建 state则可能复用
// 因此属性是final 并且有一个创建state的函数
// 可以使用自身的属性创建state以设置其初始值
class A extends StatefulWidget {
// 样板代码 使用key控制state销毁重建
const A({super.key, required this.a});
final String a;
// 继承StatefulWidget并覆盖createState
@override
State<A> createState() => _AState();
}
// state包含了实际的Widget
class _AState extends State<A> {
int num = 0;
void increment() {
// 继承自State 调用后会重新渲染 即使这个函数什么都不做
setState(() {
num++;
});
}
@override
Widget build(BuildContext context) {
return Column(children: [
// widget来自State<A> 表明它从属的widget 它也具有属性a
Text('a: $widget.a'),
Text('$num'),
// 注意 这里的Text实例在是编译时确定不变的 使用const进行优化
OutlinedButton(onPressed: increment, child: const Text('+1'))
]);
}
}
flutter渲染分三级 Widget->Element->RenderObject.按react的概念类比理解,Widget=组件,Element=fiber,state则是允许自定义的fiber内容
BuildContext
build函数的参数/State类的context属性 它是Widget树中的位置标识 本质是Widget所对应的 Element 实例
它可以用于查找沿Widget树向上查找特定类型的Widget
Theme.of(context)Navigator.of(context)
和react的useContext作用相同
需要注意的是 需要等Element创建之后才能使用
Builder
Scaffold(
body: ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar('a');
},
child: Text('Show'),
));
以上代码会报错 是因为需要试图从context的父Widget中找到一个Scaffold 但它使用的context是当前的Element 它的子元素才包含了Scaffold
Builder就可以解决这个问题 它可以为子元素提供深层次的context
Scaffold(
body: Builder(
// 这个context是Builder的context 可以找到Scaffold
builder: (context) => ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar('a');
},
child: Text('Show'),
),
));
InheritedWidget
功能等同于context 为下方widget提供共享数据
class CounterInherited extends InheritedWidget {
final int counter;
// 被标记为const的构造函数 自身可以被创建为const值
const CounterInherited({
required this.counter,
required super.child,
super.key,
});
// CounterInherited.of(context) 让下方widget获取此处的数据
static CounterInherited? of(BuildContext context) {
// 让使用它的widget订阅当前的InheritedWidget
return context.dependOnInheritedWidgetOfExactType<CounterInherited>();
}
// 是否需要通知订阅者
@override
bool updateShouldNotify(CounterInherited oldWidget) {
return oldWidget.counter != counter;
}
}
注意 InheritedWidget只是数据的提供者 如果想要改变数据 需要配合StatefulWidget 将数据和修改能力同时共享
state的生命周期
在state构造后 常规的生命周期函数如下 他们都继承自State类 并且覆写时必须调用super里对应的函数
initStatestate创建后.此时context已经就绪,对应的Element已经创建并挂载.didChangeDependenciesinitState后调用.当前state依赖的InheritedWidget变化后调用(在任何位置使用InheritedWidget都会被自动记录).didUpdateWidgetWidget变化后调用(首次渲染不调用).接受oldWidget.在此函数调用后,build才会被调用.deactivate临时移除时(例如路由切换).暂停动画、释放资源.activate首次渲染后或者重新加入时.dispose彻底销毁时.不能调用setState.
StatelessWidget
没有state的widget
class A extends StatelessWidget {
const A({super.key});
@override
Widget build(BuildContext context) {
//return xxx
}
}
与react的对比
在react中 每个组件都是props+state+render的聚合
但flutter中 widget只负责props的部分 任何形式的"数据变化"都必须依赖State