Flutter 組件刷新【實(shí)戰(zhàn)】附demo

介紹一下數(shù)據(jù)與Widget之間傳遞,數(shù)據(jù)改變-->觸發(fā)Widget刷新


1.0 所需工具

  • Provider

2.0 Provider

Provider 會在WidgetModel之間建立監(jiān)聽,以確保數(shù)據(jù)發(fā)生變化時(shí)刷新Widget

3.0 必要步驟:

  • Model 要繼承ChangeNotifier
  • Widget要監(jiān)聽Model數(shù)據(jù)變化

Provider 是flutter推薦使用的狀態(tài)管理工具,具體原理這里不展開分析,下面介紹一下如何快速使用,達(dá)到一個(gè)真實(shí)業(yè)務(wù)的場景.

4.0 Model創(chuàng)建

// 重要: model必須繼承ChangeNotifier
class TestClass with ChangeNotifier {
  var count = 1;

  action() {
    ++count;
    //數(shù)據(jù)變化后腰通過 notifyListeners() 通知所有監(jiān)聽者
    notifyListeners();
  }
}

4.1 監(jiān)聽Model

Model監(jiān)聽有兩種方式

  1. 通過addListener方式
  2. 通過Provider.of<T>(context)

4.1.1 通過 addListener 監(jiān)聽數(shù)據(jù)改變

當(dāng)數(shù)據(jù)改變并且調(diào)用 notifyListeners() 這里會收到回調(diào)

model.addListener(() { 
      //Do something
});

4.1.2 通過 Provider.of<T>(context) 方式監(jiān)聽

這里看到在build方法中我們獲取Provider傳入的Model.并沒有調(diào)用addListener.這里解釋一下.
通過查看 Provider.of 源碼中知道, Provider.of通過 context 將本次構(gòu)建的build方法綁定,并監(jiān)聽數(shù)據(jù)改變. 所以在數(shù)據(jù)變化后將自動(dòng)調(diào)用 build 方法進(jìn)行刷新.

  @override
  Widget build(BuildContext context) {
    final model = Provider.of<TestClass>(context);
    return Container()
  }

5.1 向下傳遞完整示例

下面將一個(gè)Model向下傳遞的完整實(shí)例貼出來.

import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

class TestClass with ChangeNotifier {
  var count = 1;
  action() {
    ++count;
    notifyListeners();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<TestClass>(
      create: (context)=>TestClass(),
      child: MaterialApp(
        theme: ThemeData.dark(),
        home: FirstScreen(),
      ),
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final model = Provider.of<TestClass>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Provider'),
      ),
      body: Container(
        child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => model.action(),
        child: Icon(Icons.add),
      ),
    );
  }
}

5.2 組件內(nèi)部通過Model控制數(shù)據(jù)刷新 完整示例

import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

class TestClass with ChangeNotifier {
  var count = 1;
  action() {
    ++count;

    notifyListeners();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  final TestClass source=TestClass();
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text('Provider'),
      ),
      body:ChangeNotifierProvider<TestClass>(
        create: (context) => source,
        child: Consumer<TestClass>(
          child: Container(),
          builder: (context, model, child) {
            return Container(
              child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
            );
          },
        ),
      ),
      
      floatingActionButton: FloatingActionButton(
        onPressed: () => source.action(),
        child: Icon(Icons.add),
      ),
    );
  }
}

6.0 Provider封裝

5.2 數(shù)據(jù)內(nèi)部控制刷新示例看出,實(shí)際代碼寫起來比較麻煩.所以封裝了一下. 只需要傳入必要參數(shù)就可以.

6.1 封裝Widget

class RefreshWidget<T extends ChangeNotifier> extends StatelessWidget {
  RefreshWidget(
      this.builder, {
        @required this.source,
        this.child,
      });

  final T source;
  final Function(BuildContext context, T value, Widget child) builder;
  final Widget child;

  static Builder<T>(
      Function(BuildContext context, T value, Widget child) builder) {
    return builder;
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>.value(
      value: source,
      child: Consumer<T>(
        child: child,
        builder: (context, model, child) {
          return builder(context, model, child);
        },
      ),
    );
  }
}

6.2 封裝后使用對比

封裝前

ChangeNotifierProvider<TestClass>(
        create: (context) => source,
        child: Consumer<TestClass>(
          child: Container(),
          builder: (context, model, child) {
            return Container(
              child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
            );
          },
        ),
      ),

封裝后

RefreshWidget<TestClass>(
          (context, value, child) => Container(
                child: Center(
                  child: Text(
                    "${value.count}",
                    style: TextStyle(fontSize: 50),
                  ),
                ),
              ),
          source: source),
封裝后我們只需要關(guān)心下面三點(diǎn),對與新手比較友好,不用糾結(jié)太多
  • builder構(gòu)建方法(自動(dòng)補(bǔ)全)
  • source數(shù)據(jù)model
  • <T> model 類型

直接使用builder方法沒有方法提示, 這點(diǎn)很不友好.
封裝后將builder放到首位, 會將builder方法直接補(bǔ)全, 這才是我最終的目的, 可以有效減少control+c/v的次數(shù)

代碼很少直接貼完整Demo出來

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class TestClass with ChangeNotifier {
  var count = 1;
  action() {
    ++count;
    notifyListeners();
  }
}

class MyWidget extends StatelessWidget {
  var _value = TestClass();

  @override
  Widget build(BuildContext context) {
    return Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          GestureDetector(
            child:
            Text('Hello, World!  Click here~~~', style: Theme.of(context).textTheme.headline4),
            onTap: _value.action,
          ),
          RefreshWidget<TestClass>(
              RefreshWidget.b((context, value, child) => Container(
                child: Center(
                  child: Text(
                    "${value.count}",
                    style: TextStyle(fontSize: 50),
                  ),
                ),
              )),
              source: _value)
        ]);
  }
}

class RefreshWidget<T extends ChangeNotifier> extends StatelessWidget {
  RefreshWidget(
      this.builder, {
        @required this.source,
        this.child,
      });

  final T source;
  final Function(BuildContext context, T value, Widget child) builder;
  final Widget child;

  static b<T>(
      Function(BuildContext context, T value, Widget child) builder) {
    return builder;
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>.value(
      value: source,
      child: Consumer<T>(
        child: child,
        builder: (context, model, child) {
          return builder(context, model, child);
        },
      ),
    );
  }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妒潭,一起剝皮案震驚了整個(gè)濱河市冰寻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猪杭,老刑警劉巖樊拓,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纠亚,死亡現(xiàn)場離奇詭異,居然都是意外死亡筋夏,警方通過查閱死者的電腦和手機(jī)蒂胞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來条篷,“玉大人骗随,你說我怎么就攤上這事「疤荆” “怎么了鸿染?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長稚瘾。 經(jīng)常有香客問我牡昆,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任丢烘,我火速辦了婚禮柱宦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘播瞳。我一直安慰自己掸刊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布赢乓。 她就那樣靜靜地躺著忧侧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牌芋。 梳的紋絲不亂的頭發(fā)上蚓炬,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音躺屁,去河邊找鬼肯夏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛犀暑,可吹牛的內(nèi)容都是我干的驯击。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼耐亏,長吁一口氣:“原來是場噩夢啊……” “哼徊都!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起广辰,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤暇矫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后轨域,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袱耽,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杀餐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年干发,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片史翘。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡枉长,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出琼讽,到底是詐尸還是另有隱情必峰,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布钻蹬,位于F島的核電站吼蚁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肝匆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一粒蜈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旗国,春花似錦枯怖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至寿冕,卻和暖如春蕊程,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背驼唱。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工存捺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人曙蒸。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓捌治,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纽窟。 傳聞我的和親對象是個(gè)殘疾皇子肖油,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354