文章目录
- 一、什么是状态?
- 1、状态的种类
- 2、状态管理交给谁
- 2.1状态交给自己管理
- 2.2状态交给父Widget管理
- 2.3混合状态管理
- 2.4全局状态管理
- 二、Flutter 中常见的状态管理方式
- 1、setState
- 1.1 setState特点
- 1.2 setState构建
- 1.3 setState构建优化
- 2、InheritedWidget / InheritedModel
- 2.1 InheritedWidget
- 2.2 InheritedModel
- 3、Provider
- 3.1添加依赖
- 3.2创建登录管理状态模型
- 3.3创建登录页面
- 3.4运行应用
- 4、Riverpod
- 4.1什么是 Riverpod
- 4.2使用 Riverpod 实现登录页面
-
- 添加依赖
-
- 创建登录逻辑
-
- 构建登录页面UI
-
- 主入口
-
- 5、Bloc (Business Logic Component)
- 5.1 为什么使用 Bloc
- 5.2 Bloc 核心概念
- 5.3 在 Flutter 中使用 Bloc
-
- 添加依赖
-
- 创建 CounterCubit
-
- 使用 CounterCubit 构建 UI
-
- 6、Redux
- 6.1 Redux 核心概念
-
- Store
-
- State
-
- Action
-
- Reducer
-
- Middleware
-
- 6.2 Redux 的工作流程
- 6.3 在 Flutter 中使用 Redux
- 6.1 Redux 核心概念
- 7、GetX
- 7.1 GetX状态管理
- 7.2 GetX路由
- 7.3 GetX依赖管理
- 7.4 GetX国际化
- 1、setState
响应式的编程框架中都会有一个永恒的主题——"状态(State)管理",无论是在 React/Vue 还是 Flutter 中,他们讨论的问题和解决的思想都是一致的。状态管理是 Flutter 开发中的一个核心概念,因为 Flutter 的 UI 构建是基于状态的。
一、什么是状态?
在 Flutter 中,状态指的是影响 UI 展示的数据。例如,用户的输入、应用的配置、从服务器获取的数据等。Flutter 的 UI 是响应式的,意味着 UI 会随着状态的变化而自动更新。
1、状态的种类
局部状态:与某个特定 widget 相关的状态,只影响当前 widget 的渲染。
全局状态:跨多个 widget 的状态,通常需要跨多个页面或组件共享。
2、状态管理交给谁
- Widget 管理自己的状态。
- Widget 管理子 Widget 状态。
- 混合管理(父 Widget 和子 Widget 都管理状态)。
如何决定使用哪种管理方法?下面是官方给出的一些原则可以帮助你做决定:
- 如果状态是用户数据,如复选框的选中状态、滑块的位置,则该状态最好由父 Widget 管理。
- 如果状态是有关界面外观效果的,例如颜色、动画,那么状态最好由 Widget 本身来管理。
- 如果某一个状态是不同 Widget 共享的则最好由它们共同的父 Widget 管理。
2.1状态交给自己管理
// TapboxA 管理自身状态
class TapboxA extends StatefulWidget {
TapboxA({Key? key}) : super(key: key);
@override
_TapboxAState createState() => _TapboxAState();
}
class _TapboxAState extends State<TapboxA> {
bool _active = false;
void _handleTap() {
setState(() {
_active = !_active;
});
}
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
child: Text(
_active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: _active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}
}
2.2状态交给父Widget管理
// ParentWidget 为 TapboxB 管理状态
class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return Container(
child: TapboxB(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}
class TapboxB extends StatelessWidget {
TapboxB({Key? key, this.active: false, required this.onChanged})
: super(key: key);
final bool active;
final ValueChanged<bool> onChanged;
void _handleTap() {
onChanged(!active);
}
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
child: Text(
active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}
}
2.3混合状态管理
class ParentWidgetC extends StatefulWidget {
@override
_ParentWidgetCState createState() => _ParentWidgetCState();
}
class _ParentWidgetCState extends State<ParentWidgetC> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return Container(
child: TapboxC(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}
class TapboxC extends StatefulWidget {
TapboxC({Key? key, this.active: false, required this.onChanged})
: super(key: key);
final bool active;
final ValueChanged<bool> onChanged;
@override
_TapboxCState createState() => _TapboxCState();
}
class _TapboxCState extends State<TapboxC> {
bool _highlight = false;
void _handleTapDown(TapDownDetails details) {
setState(() {
_highlight = true;
});
}
void _handleTapUp(TapUpDetails details) {
setState(() {
_highlight = false;
});
}
void _handleTapCancel() {
setState(() {
_highlight = false;
});
}
void _handleTap() {
widget.onChanged(!widget.active);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: _handleTapDown,
onTapUp: _handleTapUp,
onTap: _handleTap,
onTapCancel: _handleTapCancel,
child: Container(
child: Center(
child: Text(
widget.active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],
border: _highlight
? Border.all(
color: Colors.teal[700],
width: 10.0,
)
: null,
),
),
);
}
}
2.4全局状态管理
当应用中需要一些跨组件(包括跨路由)的状态需要同步时,上面介绍的方法便很难胜任了。正确的做法是通过一个全局状态管理器来处理这种相距较远的组件之间的通信。目前主要有两种办法:
- 实现一个全局的事件总线,将语言状态改变对应为一个事件,然后在APP中依赖应用语言的组件的initState方法中订阅语言改变的事件。
- 使用一些专门用于状态管理的包,如 Provider、Redux,或者使用专业的开发工具如 AppUploader 来管理应用状态。
二、Flutter 中常见的状态管理方式
1、setState
2、InheritedWidget / InheritedModel
3、Provider
4、Riverpod
5、Bloc (Business Logic Component)
6、Redux
7、GetX
接下来我们依次讲解这些状态管理方式。
1、setState
class _HomePageState extends State<HomePage> {
int counter = 0;
void _incrementCounter() {
setState(() {
counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('setState Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Counter: $counter'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
),
),
);
}
}
1.1 setState特点
setState 的特点:
- 适用于简单的状态管理。
- 每次调用 setState 会导致 widget 的重建。
- 它是最基础的状态管理方式,但不适用于复杂的应用
1.2 setState构建
当setState被调用时,它会将_dirty标志位设置为true。这个标志位的改变会通知 Flutter 框架,当前State对象所对应的Widget需要重新构建。
1.3 setState构建优化
- Flutter 有一套优化机制来避免不必要的重建。它会根据Widget的类型和属性来判断是否真的需要重新构建。
- 对于StatefulWidget,Flutter 会比较State对象的runtimeType和key属性。
- 频繁调用setState可能会导致性能问题
2、InheritedWidget / InheritedModel
2.1 InheritedWidget
import 'package:flutter/material.dart';
class MySharedDataWidget extends InheritedWidget {
int sharedData;
MySharedDataWidget({
Key? key,
required this.sharedData,
required Widget child,
}) : super(key: key, child: child);
static MySharedDataWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MySharedDataWidget>();
}
@override
bool updateShouldNotify(MySharedDataWidget oldWidget) {
return oldWidget.sharedData != sharedData;
}
}
class DataUsingWidget extends StatelessWidget {
const DataUsingWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final int? sharedData = MySharedDataWidget.of(context)?.sharedData;
return Text('共享的数据是: $sharedData', style: TextStyle(fontSize: 18));
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _sharedData = 42;
void _updateSharedData() {
setState(() {
_sharedData++;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('InheritedWidget示例'),
),
body: Column(
children: [
MySharedDataWidget(
sharedData: _sharedData,
child: DataUsingWidget(),
),
ElevatedButton(
onPressed: _updateSharedData,
child: const Text('更新共享数据'),
),
],
),
),
);
}
}
2.2 InheritedModel
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyInheritedModelExample(),
);
}
}
class MyInheritedModelExample extends StatefulWidget {
@override
_MyInheritedModelExampleState createState() => _MyInheritedModelExampleState();
}
class _MyInheritedModelExampleState extends State<MyInheritedModelExample> {
String text1 = 'Text 1';
String text2 = 'Text 2';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('InheritedModel Example')),
body: MyModel(
text1: text1,
text2: text2,
child: Column(
children: [
ElevatedButton(
onPressed: () {
setState(() {
text1 = 'Updated Text 1';
});
},
child: Text('Update Text 1'),
),
ElevatedButton(
onPressed: () {
setState(() {
text2 = 'Updated Text 2';
});
},
child: Text('Update Text 2'),
),
TextWidget1(),
TextWidget2(),
],
),
),
);
}
}
class MyModel extends InheritedModel<String> {
final String text1;
final String text2;
MyModel({required this.text1, required this.text2, required Widget child}) : super(child: child);
@override
bool updateShouldNotify(covariant MyModel oldWidget) {
return text1 != oldWidget.text1 || text2 != oldWidget.text2;
}
@override
bool updateShouldNotifyDependent(covariant MyModel oldWidget, Set<String> dependencies) {
if (dependencies.contains('text1') && text1 != oldWidget.text1) {
return true;
}
if (dependencies.contains('text2') && text2 != oldWidget.text2) {
return true;
}
return false;
}
static MyModel? of(BuildContext context, String aspect) {
return InheritedModel.inheritFrom<MyModel>(context, aspect: aspect);
}
}
class TextWidget1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
final model = MyModel.of(context, 'text1');
return Text('Text 1: ${model?.text1}');
}
}
class TextWidget2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
final model = MyModel.of(context, 'text2');
return Text('Text 2: ${model?.text2}');
}
}
3、Provider
3.1添加依赖
dependencies:
flutter:
sdk: flutter
provider: 6.0.0
3.2创建登录管理状态模型
import 'package:flutter/material.dart';
class LoginModel with ChangeNotifier {
String _username = '';
String _password = '';
bool _isLoading = false;
String? _errorMessage;
String get username => _username;
String get password => _password;
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
void setUsername(String username) {
_username = username;
notifyListeners();
}
void setPassword(String password) {
_password = password;
notifyListeners();
}
Future<void> login() async {
if (_username.isEmpty || _password.isEmpty) {
_errorMessage = '用户名和密码不能为空';
notifyListeners();
return;
}
_isLoading = true;
notifyListeners();
await Future.delayed(Duration(seconds: 2));
if (_username == 'admin' && _password == '123456') {
_errorMessage = null;
} else {
_errorMessage = '用户名或密码错误';
}
_isLoading = false;
notifyListeners();
}
}
3.3创建登录页面
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'login_model.dart';
class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => LoginModel(),
child: Scaffold(
appBar: AppBar(title: Text('登录')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Consumer<LoginModel>(
builder: (context, loginModel, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
onChanged: loginModel.setUsername,
decoration: InputDecoration(
labelText: '用户名',
errorText: loginModel.errorMessage != null ? '错误: ${loginModel.errorMessage}' : null,
),
),
SizedBox(height: 16),
TextField(
onChanged: loginModel.setPassword,
obscureText: true,
decoration: InputDecoration(
labelText: '密码',
errorText: loginModel.errorMessage != null ? '错误: ${loginModel.errorMessage}' : null,
),
),
SizedBox(height: 16),
if (loginModel.isLoading)
Center(child: CircularProgressIndicator()),
if (!loginModel.isLoading)
ElevatedButton(
onPressed: () {
loginModel.login();
},
child: Text('登录'),
),
if (loginModel.errorMessage != null)
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: Text(
loginModel.errorMessage!,
style: TextStyle(color: Colors.red),
),
),
],
);
},
),
),