理解InheritedWidget及应用
本篇文章主要介绍Flutter
中的InheritedWidget
小部件,以及其官方小部件库中的应用。
官方介绍
关于InheritedWidget
,在官方文档中是这样描述的:
- 1,可以沿着树向下传递信息。
- 2,可以使用
BuildContext.dependOnInheritedWidgetOfExactType
方法,获取最近的指定类型的inherited widget
。 - 3,当
inherited widget
的状态
发生改变时,所有依赖其状态的子部件都会进行rebuild
。
我们该如何理解InheritedWidget
呢,接下来,通过几个示例来阐述InheritedWidget
。
ThemeData
我们经常使用的ThemeData
就是一个inheritedWidget
,通常我们App
的root
为 MaterialApp
,这样我们的ThemeData
在Widget
树的最顶端。
我们可以使用
final themeData = Theme.of(context);
来获取当前的主题,当主题改变时,会从Widget
树的最顶端,沿着树,从上到下,依次刷新每个依赖themeData
的子部件。
static ThemeData of(BuildContext context) {
final _InheritedTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
final MaterialLocalizations? localizations = Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike;
final ThemeData theme = inheritedTheme?.theme.data ?? _kFallbackTheme;
return ThemeData.localize(theme, theme.typography.geometryThemeFor(category));
}
从static ThemeData of(BuildContext context)
的实现我们可以看出其是一个InheritedWidget
。
示例
无状态InheritedWidget
对于无状态的InheritedWidget
,我们以读取其共享的数据为例展开,接下来,我们新建Counter
类
class Counter extends InheritedWidget { // 1
const Counter( {Key? key, required this.child, required this.counter})
: super(key: key, child: child); // 2
final int counter;
final Widget child;
static Counter? of(BuildContext context) { // 3
return context.dependOnInheritedWidgetOfExactType<Counter>();
}
@override
bool updateShouldNotify(covariant Counter oldWidget) { // 4
return oldWidget.counter != counter;
}
}
- 1,
Counter
继承InheritedWidget
。 - 2,在构建时,需要提供
counter
值和子Widget
。 - 3,向子部件提供获取状态的接口。
- 4,是否通知子部件进行状态刷新。
我们可以这样使用该InheritedWidget
Widget buildInheritedWidget(BuildContext context) {
return Counter(
counter: 5,
child: Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
WidgetE(),
],
),
),
));
}
@override
Widget build(BuildContext context) {
return buildInheritedWidget(context);
}
运行结果如下
接下来,我们创建一个有状态的InheritedWidget
有状态的InheritedWidget
1,创建一个持有状态的InheritedWidget
。
class CounterWidget extends InheritedWidget {
const CounterWidget(
{Key? key,
required this.counter,
required this.child,
required this.data})
: super(child: child, key: key);
final int counter;
final Widget child;
final CounterWrapperState data; // 1
/// 获取 CounterWidget 实例
static CounterWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CounterWidget>();
}
@override
bool updateShouldNotify(covariant CounterWidget oldWidget) {
// TODO: implement updateShouldNotify
return oldWidget.counter != counter;
}
}
- 1,相比于无状态的,这里新增了一个
CounterWrapperState
。
解下来,创建一个StatefulWidget
,并将CounterWidget
作为该部件的子部件。
class CounterWrapper extends StatefulWidget {
final Widget child; // 1
const CounterWrapper({Key? key, required this.child}) : super(key: key);
static CounterWrapperState of(BuildContext context, {bool build = true}) { // 2
return build
? (context.dependOnInheritedWidgetOfExactType<CounterWidget>())!.data
: context.findAncestorWidgetOfExactType<CounterWidget>()!.data;
}
@override
CounterWrapperState createState() => CounterWrapperState();
}
class CounterWrapperState extends State<CounterWrapper> {
int counter = 0;
void incrementCounter() { // 3
setState(() {
counter++;
});
}
@override
Widget build(BuildContext context) { // 4
return CounterWidget(data: this, counter: counter, child: widget.child);
}
}
-
1,在构建时,需提供
child
,并传递给CounterWidget
。 -
2,提供获取状态的接口,有两种方式 : 当使用
dependOnInheritedWidgetOfExactType
时,当状态改变时,会重新rebuild
子部件。 使用findAncestorWidgetOfExactType
时,当状态改变时,子部件不会刷新。 -
3,计数器加1,刷新Widget。
-
4,将
CounterWidget
作为CounterWrapper
的子部件。
子Widget刷新、局部刷新、不刷新
接下来,创建5个不同的Widget
:
/// 使计数状态改变
class WidgetA extends StatefulWidget {
const WidgetA({Key? key}) : super(key: key);
@override
_WidgetAState createState() => _WidgetAState();
}
class _WidgetAState extends State<WidgetA> {
@override
Widget build(BuildContext context) {
print("A refresh");
return ElevatedButton(onPressed: onPressed, child: Text("Increment"));
}
onPressed() {
CounterWrapperState wrapper = CounterWrapper.of(context, build: false);
wrapper.incrementCounter();
}
}
/// 局部刷新
class WidgetB extends StatelessWidget {
const WidgetB({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print("widget B 整个 刷新");
return Builder(builder: (contextTwo) {
print("widget B Text 局部刷新");
final CounterWrapperState state =
CounterWrapper.of(contextTwo, build: true);
return Text('${state.counter}');
});
}
}
/// inheritedWidget刷新时,也刷新
class WidgetC extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterWrapperState state =
CounterWrapper.of(context, build: true);
print("widget C 刷新");
return new Text('I am Widget C ${state.counter}');
}
}
/// inheritedWidget刷新时,不刷新
class WidgetC1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterWrapperState state =
CounterWrapper.of(context, build: false);
print("widget C1 刷新");
return new Text('I am Widget C1 ${state.counter}');
}
}
/// 不依赖inheriteWidget状态的子部件
class WidgetD extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("widget D 刷新");
return new Text('I am Widget D');
}
}
- 1,WidgetA: 使 CounterWrapper 的状态改变。
- 2,WidgetB: 不刷新整个页面,对依赖部分进行
局部刷新
。 - 3,WidgetC: 依赖inheritedWidget的状态,状态改变时,也随之刷新。
- 4,WidgetC1: 只在初始化时,获取inheritedWidget的状态,不随之刷新。
- 5,WidgetD: inheritedWidget的子部件,但不依赖其状态
我们将这些Widget
加载到视图中
Widget buildStatefulInheritedWidget(BuildContext context) {
return CounterWrapper(
child: Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
WidgetB(),
WidgetA(),
WidgetC(),
WidgetC1(),
WidgetD(),
],
),
),
));
}
界面如下
点击increment
,只会有 WidgetC
和 WidgetB
进行刷新。
flutter: widget C 刷新
flutter: widget B Text 局部刷新
这样我们就可以定义我们自己的inheritedWidget
,并通知其子部件进行数据刷新。
总结
我们可以使用InheritedWidget
共享全局状态,子部件可以获取其状态,并控制小部件刷新
最后附上本文涉及的示例代码 inherited_widget_demo
如果觉得有收获请按如下方式给个
爱心三连
:👍:点个赞鼓励一下
。🌟:收藏文章,方便回看哦!
。💬:评论交流,互相进步!
。
转载自:https://juejin.cn/post/7097467791611002894