盡可能減少build方法調(diào)用的范圍 是狀態(tài)管理的目的,如果大范圍控件調(diào)用build 性能會(huì)很低.
用ValueListenableBuilder 局部刷新 整體的build方法不會(huì)執(zhí)行
class MSValueListenableBuilderDemo extends StatefulWidget {
const MSValueListenableBuilderDemo({Key? key}) : super(key: key);
@override
State<MSValueListenableBuilderDemo> createState() =>
_MSValueListenableBuilderDemoState();
}
class _MSValueListenableBuilderDemoState
extends State<MSValueListenableBuilderDemo> {
// 定義一個(gè)ValueNotifier米酬,當(dāng)數(shù)字變化時(shí)會(huì)通知 ValueListenableBuilder
final ValueNotifier<int> _counter = ValueNotifier<int>(0);
@override
Widget build(BuildContext context) {
// 點(diǎn)擊 + 按鈕不會(huì)觸發(fā)整個(gè) 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("點(diǎn)擊了", textScaleFactor: 1.5),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
// 點(diǎn)擊后值 +1奢赂,觸發(fā) ValueListenableBuilder 重新構(gòu)建
_counter.value++;
},
),
);
}
void test() {
ValueNotifier<double> notifier = ValueNotifier<double>(2.0);
//添加監(jiān)聽者
final VoidCallback listener = () {
print('---> ${notifier.value}');
};
notifier.addListener(listener);
//改編值后 監(jiān)聽者可以收到數(shù)據(jù)
notifier.value = 3.0;
}
}
Provider 狀態(tài)管理
yaml文件添加 provider: ^6.1.1
Provider.of<MyCounter>(context).userInfo.name; 這種使用widget build方法還會(huì)調(diào)用
Consumer 方法使用時(shí)候 只有Consumer 里面的builder方法才會(huì)調(diào)用
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("點(diǎn) 擊"),
onPressed: () {
counterVM.add();
subtractVM.sub();
},
),
],
);
},
),
);
}
}
StreamController
test() {
StreamSubscription<String> subscription;
//創(chuàng)建StreamController
var streamController = StreamController<String>();
// 獲取StreamSink用于發(fā)射事件
StreamSink<String> streamSink = streamController.sink;
// 獲取Stream用于監(jiān)聽
Stream<String> streamData = streamController.stream;
//監(jiān)聽事件
subscription = streamData.listen((value) {
// do something
});
//發(fā)射一個(gè)事件.
streamSink.add("111");
streamController.close();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
// 4浅乔、關(guān)流且警,避免內(nèi)存泄漏
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、聲明一個(gè)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、將需要局部刷新數(shù)據(jù)的組件嵌套在StreamBuilder組件內(nèi)展哭,并接收信息;
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`里添加數(shù)據(jù)匪傍,并通知`StreamBuilder`重新構(gòu)建;
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();
// 監(jiān)控暫停視頻事件
if (null == stopVideoSub) {
stopVideoSub =
playerEventBus.on<HibStopVideoPlayerEvent>().listen((event) {
}
});
}
void dispose() {
super.dispose();
if (null != stopVideoSub) {
stopVideoSub.cancel();
stopVideoSub = null;
}
}
setState() 方法內(nèi)部的工作原理如下:
首先觉痛,F(xiàn)lutter 框架會(huì)記錄需要重建的 Widget役衡。
然后,F(xiàn)lutter 框架會(huì)調(diào)用 build() 方法來(lái)重建 Widget薪棒。
在 build() 方法中手蝎,F(xiàn)lutter 框架會(huì)根據(jù) Widget 的新狀態(tài)來(lái)構(gòu)建 Widget 樹,并返回一個(gè)新的 Widget 樹俐芯。
最后棵介,F(xiàn)lutter 框架會(huì)比較新舊 Widget 樹的差異,并將差異應(yīng)用到渲染樹中吧史,以更新 Widget 的顯示邮辽。
需要注意的是,setState() 方法并不是立即執(zhí)行的贸营,而是將其標(biāo)記為“臟”狀態(tài)吨述,等到下一次構(gòu)建時(shí)再執(zhí)行。因此钞脂,如果在 setState() 方法調(diào)用后立即訪問(wèn) Widget 的狀態(tài)揣云,可能得到的還是舊的狀態(tài)。為了避免這種情況冰啃,可以使用 Future.microtask(() {}); 或 WidgetsBinding.instance.addPostFrameCallback() 方法來(lái)在下一次構(gòu)建之后獲取 Widget 的最新狀態(tài)灵再。
setState()的刷新區(qū)域如果控制不好的話肋层,會(huì)引起大范圍的重繪,慎用翎迁。
1.Flutter狀態(tài)管理和數(shù)據(jù)傳遞
數(shù)據(jù)同步關(guān)系有以下三種類型
由上往下,傳遞給子孫節(jié)點(diǎn)
由下往上净薛,傳遞給祖宗節(jié)點(diǎn)
兄弟節(jié)點(diǎn)傳遞
同步可能需要滿足以下場(chǎng)景: 組件A共享數(shù)據(jù)給組件B時(shí)汪榔,
組件B可以實(shí)時(shí)拿到組件A的變化值,
可以監(jiān)聽到數(shù)據(jù)變更肃拜,
組件B可以通知組件A進(jìn)行數(shù)據(jù)更改痴腌,
組件A可以決定是否需要重建。
Flutter提供了數(shù)據(jù)傳遞的幾種方案:
InheritedWidget: 適用于父組件傳遞給子組件的場(chǎng)景, 可跨層級(jí)
Notification:適用于子組件通知父組件數(shù)據(jù)改變的場(chǎng)景
Broadcast: 消息廣播機(jī)制
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適用于子組件通知父組件數(shù)據(jù)改變的場(chǎng)景燃领,是通過(guò)dispatch方法將消息由子到父派發(fā)出來(lái)的士聪,這種機(jī)制叫做通知冒泡,會(huì)通知到所有通過(guò)NotificationListener來(lái)監(jiān)聽的父節(jié)點(diǎn)猛蔽,也可以通過(guò)中間的某個(gè)節(jié)點(diǎn)來(lái)中止剥悟。
//自定義通知
class CustomNotification extends Notification {
CustomNotification(this.msg);
final String msg;
}
在子組件中通過(guò)dispatch派發(fā)消息
class CustomChild extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("Fire Notification"),
onPressed: () => CustomNotification("lala").dispatch(context),
);
}
}
在父組件中通過(guò)NotificationListener設(shè)置對(duì)自定義通知的監(jiān)聽
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函數(shù)中返回true即可曼库。默認(rèn)情況下onNotification返回false表示不阻止冒泡区岗。
return true;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text(_msg), CustomChild()],
)
)
);
}
}