Flutter 状态组件 InheritedWidget
视频
前言
今天会讲下 inheritedWidget 组件,InheritedWidget 是 Flutter 中非常重要和强大的一种 Widget,它可以使 Widget 树中的祖先 Widget 共享数据给它们的后代 Widget,从而简化了状态管理和数据传递的复杂性,提高了代码的可读性、可维护性和性能。
Provider 就是对 inheritedWidget 的高度封装
Flutter_bloc 也是这样
Flutter_bloc 中确实有 provider 的引用
如果你只是想简单的状态管理几个全局数据,完全可以轻巧的使用 inheritedWidget 。
今天就来讲下如何使用和要注意的地方。
参考
https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
状态管理
在 Flutter 中,状态管理是指管理应用程序的数据和状态的方法。在应用程序中,有许多不同的组件和部件,它们可能需要在不同的时间点使用相同的数据。状态管理的目的是使这些数据易于访问和共享,并确保应用程序的不同部分保持同步。
在 Flutter 中,有不同的状态管理方法可供选择,包括:
- StatefulWidget 和 State:StatefulWidget 允许你创建有状态的部件,而 State 则允许你管理该部件的状态。这是 Flutter 中最基本和最常用的状态管理方法。
- InheritedWidget:InheritedWidget 允许你共享数据和状态,并且可以让子部件自动更新当共享的数据发生变化时。
- Provider:Provider 是一个第三方库,它基于 InheritedWidget,可以更方便地管理应用程序中的状态。
- Redux:Redux 是一个流行的状态管理库,它基于单一数据源和不可变状态的概念,可以使状态管理更加可预测和易于维护。
- BLoC:BLoC 是一个基于流的状态管理库,它将应用程序状态分为输入、输出和转换。它可以使应用程序更清晰和可测试。
- GetX: GetX 是一个流行的 Flutter 状态管理和路由导航工具包,它提供了许多功能,包括快速且易于使用的状态管理、依赖注入、路由导航、国际化、主题管理等。是由社区开发和维护的第三方工具包。
步骤
第一步:用户状态 InheritedWidget 类
lib/states/user_profile.dart
// 用户登录信息
class UserProfileState extends InheritedWidget {
...
}
参数
const UserProfileState({
super.key,
required this.userName,
required this.changeUserName,
required Widget child, // 包含的子节点
}) : super(child: child);
/// 用户名
final String userName;
/// 修改用户名
final Function changeUserName;
of 方法查询,依据上下文 context
static UserProfileState? of(BuildContext context) {
final userProfile =
context.dependOnInheritedWidgetOfExactType<UserProfileState>();
// 安全检查
assert(userProfile != null, 'No UserProfileState found in context');
return userProfile;
}
需要做一个 userProfile 空安全检查
重写 updateShouldNotify 通知更新规则
@override
bool updateShouldNotify(UserProfileState oldWidget) {
return userName != oldWidget.userName;
}
如果用户名发生改变进行通知
第二步:头部底部组件 StatelessWidget
lib/widgets/header.dart
class HeaderWidget extends StatelessWidget {
const HeaderWidget({super.key});
@override
Widget build(BuildContext context) {
String? userName = UserProfileState.of(context)?.userName;
return Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue),
),
child: Text('登录:$userName'),
);
}
}
通过 String? userName = UserProfileState.of(context)?.userName; 的方式
读取状态数据 userName
lib/widgets/bottom.dart
class BottomWidget extends StatelessWidget {
const BottomWidget({super.key});
@override
Widget build(BuildContext context) {
String? userName = UserProfileState.of(context)?.userName;
return Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue),
),
child: Text('登录:$userName'),
);
}
}
第三步:用户组件 StatefulWidget
lib/widgets/user_view.dart
class UserView extends StatefulWidget {
const UserView({super.key});
@override
State<UserView> createState() => _UserViewState();
}
class _UserViewState extends State<UserView> {
...
成员变量
class _UserViewState extends State<UserView> {
String? _userName;
重新 didChangeDependencies 依赖函数更新数据
@override
void didChangeDependencies() {
_userName = UserProfileState.of(context)?.userName;
super.didChangeDependencies();
}
通过 UserProfileState.of(context)?.userName; 的方式读取
build 函数
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.purple),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('用户名:$_userName'),
ElevatedButton(
onPressed: () {
// 随机 10 个字母
String randomString = String.fromCharCodes(
List.generate(
10,
(index) => 97 + Random().nextInt(26),
),
);
// 改变用户名
UserProfileState.of(context)?.changeUserName(randomString);
},
child: const Text('改变名称'),
),
],
),
);
}
randomString 是一个随机的 10 个字母
通过 UserProfileState.of(context)?.changeUserName(randomString); 的方式触发函数,进行状态更改。
最后:页面调用 AppPage
lib/page.dart
class AppPage extends StatefulWidget {
const AppPage({super.key});
@override
State<AppPage> createState() => _AppPageState();
}
class _AppPageState extends State<AppPage> {
...
成员变量
class _AppPageState extends State<AppPage> {
String _userName = '未登录';
给了一个 未登录 的默认值
修改用户名函数
// 修改用户名
void _changeUserName(String userName) {
setState(() {
_userName = userName;
});
}
build 函数
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('InheritedWidget'),
),
body: UserProfileState(
userName: _userName,
changeUserName: _changeUserName,
child: SafeArea(
child: Column(
children: const [
// 头部
HeaderWidget(),
// 正文
Expanded(child: UserView()),
// 底部
BottomWidget(),
],
),
),
),
);
}
可以发现 UserProfileState 被套在了最外层,当然还有 Scaffold 。
包裹的子组件有:HeaderWidget、BottomWidget、UserView
状态过程如下:
- UserView 触发 _changeUserName 修改用户名
- _userName 改变的数据压入 UserProfileState
- UserProfileState 触发 updateShouldNotify
- 组件 didChangeDependencies 被触发
- 最后子成员组件更新成功
代码
https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_inherited_widget
小结
在 Flutter 中,InheritedWidget 是一种特殊的 Widget,它允许 Widget 树中的祖先 Widget 共享数据给它们的后代 Widget,而无需通过回调或参数传递数据。下面是 InheritedWidget 的一些主要作用和好处:
- 共享数据:InheritedWidget 允许祖先 Widget 共享数据给它们的后代 Widget,这使得在 Widget 树中传递数据变得更加容易和高效。这种共享数据的方式避免了回调和参数传递的复杂性,使得代码更加简洁易懂。
- 自动更新:当共享的数据发生变化时,InheritedWidget 会自动通知它的后代 Widget 进行更新,这使得状态管理变得更加容易。这种自动更新的方式避免了手动管理状态的复杂性,使得代码更加健壮和易于维护。
- 跨 Widget 树:InheritedWidget 可以跨 Widget 树共享数据,这使得在应用程序中不同模块之间传递数据变得更加容易。这种跨 Widget 树的共享方式避免了在不同模块之间传递数据时的复杂性,使得代码更加模块化和易于扩展。
- 性能优化:InheritedWidget 可以避免不必要的 Widget 重建,从而提高应用程序的性能。当共享的数据没有发生变化时,InheritedWidget 不会通知后代 Widget 进行更新,这避免了不必要的 Widget 重建,提高了应用程序的性能。
感谢阅读本文
如果我有什么错?请在评论中让我知道。我很乐意改进。
© 猫哥 ducafecat.com
end