flutter状态管理
尽可能减少build方法调用的范围 是状态管理的目的,如果大范围控件调用build 性能会很低.
用ValueListenableBuilder 局部刷新 整体的build方法不会执行
class MSValueListenableBuilderDemo extends StatefulWidget {
const MSValueListenableBuilderDemo({Key? key}) : super(key: key);
@override
State<MSValueListenableBuilderDemo> createState() =>
_MSValueListenableBuilderDemoState();
}
class _MSValueListenableBuilderDemoState
extends State<MSValueListenableBuilderDemo> {
// 定义一个ValueNotifier,当数字变化时会通知 ValueListenableBuilder
final ValueNotifier<int> _counter = ValueNotifier<int>(0);
@override
Widget build(BuildContext context) {
// 点击 + 按钮不会触发整个 ValueListenableRoute 组件的 build
print('build');
return Scaffold(
appBar: AppBar(title: Text("ValueListenableBuilderDemo")),
body: Center(
child: ValueListenableBuilder<int>(
valueListenable: _counter,
builder: (ctx, value, child) {
print('build-----ValueListenableBuilder');
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
child!,
Text("$value 次", textScaleFactor: 1.5),
],
);
},
child: Text("点击了", textScaleFactor: 1.5),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
// 点击后值 +1,触发 ValueListenableBuilder 重新构建
_counter.value++;
},
),
);
}
void test() {
ValueNotifier<double> notifier = ValueNotifier<double>(2.0);
//添加监听者
final VoidCallback listener = () {
print('---> ${notifier.value}');
};
notifier.addListener(listener);
//改编值后 监听者可以收到数据
notifier.value = 3.0;
}
}
Provider 状态管理
yaml文件添加 provider: ^6.1.1
Provider.of<MyCounter>(context).userInfo.name; 这种使用widget build方法还会调用
Consumer 方法使用时候 只有Consumer 里面的builder方法才会调用
void main() => runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (ctx) => MyCounter()),
ChangeNotifierProvider(create: (ctx) => MySubtract()),
],
child: ProviderDemo(),
));
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class UserInfo {
String name = "leo";
int age = 10;
UserInfo(this.name, this.age);
}
/// with ChangeNotifier
class MyCounter with ChangeNotifier {
UserInfo _userInfo = UserInfo("leo", 10);
UserInfo get userInfo => _userInfo;
void add() {
_userInfo.age++;
notifyListeners();
}
}
/// extends ChangeNotifier
class MySubtract extends ChangeNotifier {
UserInfo _userInfo = UserInfo("jim", 100);
UserInfo get userInfo => _userInfo;
void sub() {
_userInfo.age--;
notifyListeners();
}
}
class ProviderDemo extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
print('ProviderDemo build-----');
return MaterialApp(
title: 'Flutter widget',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
print('MyHomePage build-----');
return Scaffold(
appBar: AppBar(
title: Text("Flutter Provider"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MyContainer1(),
SizedBox(height: 30),
MyContainer2()
],
),
),
);
}
}
class MyContainer1 extends StatefulWidget {
@override
_MyContainer1State createState() => _MyContainer1State();
}
class _MyContainer1State extends State<MyContainer1> {
@override
Widget build(BuildContext context) {
print('MyContainer1 build-----');
final String name1 = Provider.of<MyCounter>(context).userInfo.name;
final int age1 = Provider.of<MyCounter>(context).userInfo.age;
final String name2 = Provider.of<MySubtract>(context).userInfo.name;
final int age2 = Provider.of<MySubtract>(context).userInfo.age;
return Column(
children: <Widget>[
Text("Container1 name1=$name1 age1=$age1", style: myTextStyle1()),
Text("Container1 name2=$name2 age2=$age2", style: myTextStyle2()),
],
);
}
}
TextStyle myTextStyle1() {
return TextStyle(fontSize: 20, color: Colors.blue);
}
TextStyle myTextStyle2() {
return TextStyle(fontSize: 20, color: Colors.green);
}
class MyContainer2 extends StatefulWidget {
@override
_MyContainer2State createState() => _MyContainer2State();
}
class _MyContainer2State extends State<MyContainer2> {
@override
Widget build(BuildContext context) {
print('_MyContainer2State build-----');
return Container(
child: Consumer2<MyCounter, MySubtract>(
builder: (ctx, counterVM, subtractVM, child) {
print('_MyContainer2State Consumer2 build-----');
return Column(
children: <Widget>[
Text(
"Container2 name1=${counterVM.userInfo.name} age1=${counterVM.userInfo.age}",
style: myTextStyle1()),
Text(
"Container2 name2=${subtractVM.userInfo.name} age2=${subtractVM.userInfo.age}",
style: myTextStyle2()),
SizedBox(
height: 30,
),
RaisedButton(
child: Text("点 击"),
onPressed: () {
counterVM.add();
subtractVM.sub();
},
),
],
);
},
),
);
}
}
StreamController
test() {
StreamSubscription<String> subscription;
//创建StreamController
var streamController = StreamController<String>();
// 获取StreamSink用于发射事件
StreamSink<String> streamSink = streamController.sink;
// 获取Stream用于监听
Stream<String> streamData = streamController.stream;
//监听事件
subscription = streamData.listen((value) {
// do something
});
//发射一个事件.
streamSink.add("111");
streamController.close();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
// 4、关流,避免内存泄漏
streamController.close();
}
}
import 'dart:async';
import 'package:flutter/material.dart';
class StreamBuildDemo extends StatefulWidget {
// const StreamBuildDemo({super.key});
@override
State<StreamBuildDemo> createState() => _StreamBuildDemoState();
}
class _StreamBuildDemoState extends State<StreamBuildDemo> {
int a = 0;
// 1、声明一个StreamController类型的控制器,命名为streamController;
final StreamController<int> streamController = StreamController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 2、将需要局部刷新数据的组件嵌套在StreamBuilder组件内,并接收信息;
StreamBuilder<int>(
stream: streamController.stream,
initialData: a,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Text('a : $a');
},
),
ElevatedButton(
onPressed: () {
a++;
setState(() {});
},
child: Text('setState'),
),
ElevatedButton(
onPressed: () {
a++;
// 3、往`StreamBuilder`里添加数据,并通知`StreamBuilder`重新构建;
streamController.add(a);
},
child: Text('streamBuilder'),
),
],
),
),
);
}
如果只是传递消息可以用 event_bus
import 'dart:async';
class EventBus {
StreamController _streamController;
StreamController get streamController => _streamController;
EventBus({bool sync = false})
: _streamController = StreamController.broadcast(sync: sync);
EventBus.customController(StreamController controller)
: _streamController = controller;
Stream<T> on<T>() {
if (T == dynamic) {
return streamController.stream as Stream<T>;
} else {
return streamController.stream.where((event) => event is T).cast<T>();
}
}
void fire(event) {
streamController.add(event);
}
void destroy() {
_streamController.close();
}
}
//使用示例
final EventBus playerEventBus = EventBus();
playerEventBus.fire(HibStopVideoPlayerEvent());
StreamSubscription stopVideoSub;
@override
void initState() {
super.initState();
// 监控暂停视频事件
if (null == stopVideoSub) {
stopVideoSub =
playerEventBus.on<HibStopVideoPlayerEvent>().listen((event) {
}
});
}
void dispose() {
super.dispose();
if (null != stopVideoSub) {
stopVideoSub.cancel();
stopVideoSub = null;
}
}
setState() 方法内部的工作原理如下:
首先,Flutter 框架会记录需要重建的 Widget。
然后,Flutter 框架会调用 build() 方法来重建 Widget。
在 build() 方法中,Flutter 框架会根据 Widget 的新状态来构建 Widget 树,并返回一个新的 Widget 树。
最后,Flutter 框架会比较新旧 Widget 树的差异,并将差异应用到渲染树中,以更新 Widget 的显示。
需要注意的是,setState() 方法并不是立即执行的,而是将其标记为“脏”状态,等到下一次构建时再执行。因此,如果在 setState() 方法调用后立即访问 Widget 的状态,可能得到的还是旧的状态。为了避免这种情况,可以使用 Future.microtask(() {}); 或 WidgetsBinding.instance.addPostFrameCallback() 方法来在下一次构建之后获取 Widget 的最新状态。
setState()的刷新区域如果控制不好的话,会引起大范围的重绘,慎用。
1.Flutter状态管理和数据传递
数据同步关系有以下三种类型
由上往下,传递给子孙节点
由下往上,传递给祖宗节点
兄弟节点传递
同步可能需要满足以下场景: 组件A共享数据给组件B时,
组件B可以实时拿到组件A的变化值,
可以监听到数据变更,
组件B可以通知组件A进行数据更改,
组件A可以决定是否需要重建。
Flutter提供了数据传递的几种方案:
InheritedWidget: 适用于父组件传递给子组件的场景, 可跨层级
Notification:适用于子组件通知父组件数据改变的场景
Broadcast: 消息广播机制
class TestPage extends StatefulWidget {
const TestPage({Key? key}) : super(key: key);
@override
State<TestPage> createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: MyInheritedWidget(
count: count,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const TestWidget(),
IconButton(
onPressed: () {
setState(() {
count++;
});
},
icon: const Icon(Icons.add),
)
],
),
),
));
}
}
class TestWidget extends StatefulWidget {
const TestWidget({Key? key}) : super(key: key);
@override
State<TestWidget> createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
@override
Widget build(BuildContext context) {
return Text(
MyInheritedWidget.of(context)?.count.toString() ?? "",
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400,
),
);
}
}
class MyInheritedWidget extends InheritedWidget {
final int count;
const MyInheritedWidget(
{super.key, required this.count, required Widget child})
: super(child: child);
static MyInheritedWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}
@override
bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
return oldWidget.count != count;
}
}
Notification适用于子组件通知父组件数据改变的场景,是通过dispatch方法将消息由子到父派发出来的,这种机制叫做通知冒泡,会通知到所有通过NotificationListener来监听的父节点,也可以通过中间的某个节点来中止。
//自定义通知
class CustomNotification extends Notification {
CustomNotification(this.msg);
final String msg;
}
在子组件中通过dispatch派发消息
class CustomChild extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("Fire Notification"),
onPressed: () => CustomNotification("lala").dispatch(context),
);
}
}
在父组件中通过NotificationListener设置对自定义通知的监听
class CustomNotificationRoute extends StatefulWidget {
@override
_CustomNotificationRouteState createState() => new _CustomNotificationRouteState();
}
class _CustomNotificationRouteState extends State<CustomNotificationRoute> {
String _msg = "通知: ";
@override
Widget build(BuildContext context) {
return Scaffold(
body: NotificationListener<CustomNotification>(
onNotification: (notification) {
setState(() {
_msg += notification.msg + " ";
});
//如何停止通知冒泡?在onNotification函数中返回true即可。默认情况下onNotification返回false表示不阻止冒泡。
return true;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text(_msg), CustomChild()],
)
)
);
}
}