前言
果然了解的越多,越发现自己狗屁都不是。StatefulBuilder
和 Builder
之前真的不知道。还是在 对话框状态管理
中了解到了这两个东西。
简介
以下内容来自通义灵码
在Flutter中,StatefulBuilder
和 Builder
都是用来动态构建 widget 树的组件,但它们在处理状态管理和构建上下文方面有所不同:
StatefulBuilder
StatefulBuilder
是一个特殊的 widget,主要用于那些本身不是 StatefulWidget 但却需要在构建过程中更改和反应状态变化的场景。它接收一个 builder 函数作为参数,这个函数会在状态更改时被调用并重新构建其内部 widget 树。- 使用
StatefulBuilder
时,你可以直接在其 builder 方法内调用提供的_setState
参数来更新局部状态,并立即触发内部 widget 树的重建,而无需刷新整个页面或父级 StatefulWidget 的状态。
StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) {
bool isChecked = ...; // 可变状态
return Checkbox(
value: isChecked,
onChanged: (bool newValue) {
setState(() {
isChecked = newValue;
});
},
);
},
);
Builder
Builder
widget 不具备自身的状态管理能力,但它主要用于构建作用域内的BuildContext,特别是在涉及到InheritedWidget、Theme、Navigator等需要访问新构建环境的场景下。Builder
主要用于将现有的 build 方法内部的上下文传递给其内部的 builder 函数,这样就可以在新的上下文中构建子 widget。这有助于隔离环境影响,尤其是在处理多个嵌套的Consumer
,StreamBuilder
或者InheritedWidget
时。
Builder(
builder: (BuildContext context) {
// 这里可以使用新的BuildContext来构建子widget
return Container(...);
},
)
简而言之:
StatefulBuilder
允许你在没有独立 StatefulWidget 的情况下,在 widget 树的任何位置动态地更新状态。Builder
则是为了获取或创建一个新的构建上下文,以便于在不同的环境中正确构建和访问 widget。它本身不涉及状态管理,更多关注的是构建环境的控制与传递。
案例
上面那篇文章里有对应的例子。这里也只是对其记录和学习。
Future<bool?> showDeleteDialog() {
// 记录是否选中
bool isChecked = true;
return showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("提示"),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
// 高度设置为最小
mainAxisSize: MainAxisSize.min,
children: [
const Text("是否删除?"),
Row(
children: [
const Text("同时删除子目录?"),
Checkbox(
value: isChecked,
onChanged: (bool? value) {
isChecked = value!;
})
],
)
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context, false);
},
child: const Text("取消")),
TextButton(
onPressed: () {
// 返回选中状态
Navigator.pop(context, isChecked);
},
child: const Text("确定")),
],
);
});
}
可以看出,UI没有被更新,文章中也给出了解释:
对话框也是通过路由的方式来实现的,那么上面的代码实际上就等同于企图在父路由中调用setState来让子路由更新,这显然是不行的!简尔言之,根本原因就是context不对。
解决方法那就是使用 StatefulBuilder
和 Builder
获取到独立的context
Row(
children: [
const Text("同时删除子目录?"),
StatefulBuilder(builder: (context, setState) {
return Checkbox(
value: isChecked,
onChanged: (bool? value) {
setState(() {
isChecked = value!;
});
});
})
],
)
Row(
children: [
const Text("同时删除子目录?"),
Builder(builder: (context) {
return Checkbox(
value: isChecked,
onChanged: (bool? value) {
(context as Element).markNeedsBuild();
isChecked = value ?? false;
});
})
],
)
StatefulBuilder
中会提供一个setState
用来更新局部状态,触发UI更新。
Builder
则是为了获取或创建一个新的构建上下文,调用Element
的markNeedsBuild()
方法来进行局部状态更新。