flutter狀態(tài)管理

盡可能減少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()],
        )
      )
    );
  }
}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市毁枯,隨后出現(xiàn)的幾起案子慈缔,更是在濱河造成了極大的恐慌,老刑警劉巖种玛,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藐鹤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡赂韵,警方通過(guò)查閱死者的電腦和手機(jī)娱节,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)右锨,“玉大人括堤,你說(shuō)我怎么就攤上這事∩芤疲” “怎么了悄窃?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蹂窖。 經(jīng)常有香客問(wèn)我轧抗,道長(zhǎng),這世上最難降的妖魔是什么瞬测? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任横媚,我火速辦了婚禮纠炮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灯蝴。我一直安慰自己恢口,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布穷躁。 她就那樣靜靜地躺著耕肩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪问潭。 梳的紋絲不亂的頭發(fā)上猿诸,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音狡忙,去河邊找鬼梳虽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灾茁,可吹牛的內(nèi)容都是我干的窜觉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼删顶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼竖螃!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起逗余,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤特咆,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后录粱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腻格,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年啥繁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菜职。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旗闽,死狀恐怖酬核,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情适室,我是刑警寧澤嫡意,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站捣辆,受9級(jí)特大地震影響蔬螟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜汽畴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一旧巾、第九天 我趴在偏房一處隱蔽的房頂上張望耸序。 院中可真熱鬧,春花似錦鲁猩、人聲如沸坎怪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芋忿。三九已至,卻和暖如春疾棵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痹仙。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工是尔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人开仰。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓拟枚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親众弓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恩溅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容