欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Flutter頁面?zhèn)髦档膸追N方式

 更新時間:2021年04月26日 10:30:19   作者:wangfeng6075lv-1  
這篇文章主要介紹了Flutter頁面?zhèn)髦档膸追N方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

今天來聊聊Flutter頁面?zhèn)髦档膸追N方式:

  • InheritWidget
  • Notification
  • Eventbus

(當(dāng)前Flutter版本:2.0.4)

InheritWidget

如果看過Provider的源碼的同學(xué)都知道,Provider跨組件傳值的原理就是根據(jù)系統(tǒng)提供的InheritWidget實現(xiàn)的,讓我們來看一下這個組件。
InheritWidget是一個抽象類,我們寫一個保存用戶信息的類UserInfoInheritWidget繼承于InheritWidget:

class UserInfoInheritWidget extends InheritedWidget {

  UserInfoBean userInfoBean;
  UserInfoInheritWidget({Key key, this.userInfoBean, Widget child}) : super (child: child);

  static UserInfoWidget of(BuildContext context){
    return context.dependOnInheritedWidgetOfExactType<UserInfoWidget>();
  }
  
  @override
  bool updateShouldNotify(UserInfoInheritWidget oldWidget) {
    return oldWidget.userInfoBean != userInfoBean;
  }
}

我們在這里面定義了一個靜態(tài)方法:of,并且傳入了一個context,根據(jù)context獲取當(dāng)前類,拿到當(dāng)前類中的UserInfoBean,其實獲取主題數(shù)據(jù)也是根據(jù)InheritWidget這種方式獲取Theme.of(context),關(guān)于of方法后面重點講一下,updateShouldNotify是刷新機(jī)制,什么時候刷新數(shù)據(jù)

還有一個用戶信息的實體:

class UserInfoBean {
  String name;
  String address;
  UserInfoBean({this.name, this.address});
}

我們做兩個頁面,第一個頁面顯示用戶信息,還有一個按鈕,點擊按鈕跳轉(zhuǎn)到第二個頁面,同樣也是顯示用戶信息:

class Page19PassByValue extends StatefulWidget {
  @override
  _Page19PassByValueState createState() => _Page19PassByValueState();
}

class _Page19PassByValueState extends State<Page19PassByValue> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('PassByValue'),
      ),
      body: DefaultTextStyle(
        style: TextStyle(fontSize: 30, color: Colors.black),
        child: Column(
          children: [
            Text(UserInfoWidget.of(context)!.userInfoBean.name),
            Text(UserInfoWidget.of(context)!.userInfoBean.address),
            SizedBox(height: 40),
            TextButton(
              child: Text('點擊跳轉(zhuǎn)'),
              onPressed: (){
                Navigator.of(context).push(CupertinoPageRoute(builder: (context){
                  return DetailPage();
                }));
              },
            )
          ],
        ),
      ),
    );
  }
}

class DetailPage extends StatefulWidget {
  @override
  _DetailPageState createState() => _DetailPageState();
}

class _DetailPageState extends State<DetailPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Detail'),
      ),
      body: DefaultTextStyle(
        style: TextStyle(fontSize: 30, color: Colors.black),
        child: Center(
          child: Column(
            children: [
              Text(UserInfoWidget.of(context).userInfoBean.name),
              Text(UserInfoWidget.of(context).userInfoBean.address),
              TextButton(
                    onPressed: () {
                      setState(() {
                        UserInfoWidget.of(context)!.updateBean('wf123','address123');
                      });
                    },
                    child: Text('點擊修改'))
            ],
          ),
        ),
      )
    );
  }
}

由于我們這里是跨組件傳值,需要把UserInfoWidget放在MaterialApp的上層,并給UserInfoBean一個初始值:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return UserInfoWidget(
      userInfoBean: UserInfoBean(name: 'wf', address: 'address'),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(title: 'Flutter Demo Home Page'),
      ),
    );
  }
}

這樣就實現(xiàn)了一個跨組件傳值,但是還有個問題,我們給UserInfoWidget賦值的時候是在最頂層,在真實業(yè)務(wù)場景中,如果我們把UserInfo的賦值放在MaterialApp上面,這時候我們還沒拿到用戶數(shù)據(jù)呢,所以就要有一個可以更新UserInfo的方法,并且修改后立即刷新,我們可以借助setState,把我們上面定義的UserInfoWidget改個名字然后封裝在StatefulWidget 中:

class _UserInfoInheritWidget extends InheritedWidget {

  UserInfoBean userInfoBean;
  Function update;
  _UserInfoInheritWidget({Key key, this.userInfoBean, this.update, Widget child}) : super (child: child);

  updateBean(String name, String address){
    update(name, address);
  }

  @override
  bool updateShouldNotify(_UserInfoInheritWidget oldWidget) {
    return oldWidget.userInfoBean != userInfoBean;
  }
}

class UserInfoWidget extends StatefulWidget {
  UserInfoBean userInfoBean;
  Widget child;
  UserInfoWidget({Key key, this.userInfoBean, this.child}) : super (key: key);

  static _UserInfoInheritWidget of(BuildContext context){
    return context.dependOnInheritedWidgetOfExactType<_UserInfoInheritWidget>();
  }
  @override
  State<StatefulWidget> createState() => _UserInfoState();
}

class _UserInfoState extends State <UserInfoWidget> {

  _update(String name, String address){
    UserInfoBean bean = UserInfoBean(name: name, address: address);
    widget.userInfoBean = bean;
    setState(() {});
  }
  @override
  Widget build(BuildContext context) {
    return _UserInfoInheritWidget(
      child: widget.child,
      userInfoBean: widget.userInfoBean,
      update: _update,
    );
  }
}

上面把繼承自InheritWidget的類改了一個名字:_UserInfoInheritWidget,對外只暴露用StatefulWidget封裝過的UserInfoWidget,向_UserInfoInheritWidget傳入了包含setState的更新數(shù)據(jù)方法,更新數(shù)據(jù)的時候通過UserInfoWidget.of(context)獲取到繼承于InheritWidget的_UserInfoInheritWidget類,調(diào)用updateBean方法實際上就調(diào)用了包含setState的方法,所以做到了數(shù)據(jù)更新和頁面刷新

下面重點說一下UserInfoWidget.of(context)是如何獲取到繼承于InheritWidget類的對象的,通過查看類似的方法:Theme.of(context)發(fā)現(xiàn)是根據(jù)dependOnInheritedWidgetOfExactType,于是我們也照著它的樣子獲取到了_UserInfoInheritWidget,點到dependOnInheritedWidgetOfExactType源碼中看一下,發(fā)現(xiàn)跳轉(zhuǎn)到了BuildContext中定義了這個方法:

  T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });

了解Widget、Element、RenderObject三只之間關(guān)系的同學(xué)都知道,其實context是Element的一個實例,BuildContext的注釋也提到了這一點:

我們可以在Element中找到這個方法的實現(xiàn):

@override
  T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
    if (ancestor != null) {
      assert(ancestor is InheritedElement);
      return dependOnInheritedElement(ancestor, aspect: aspect) as T;
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

_inheritedWidgets是從哪來的,我們搜索一下在Element中發(fā)現(xiàn)

void _updateInheritance() {
    assert(_lifecycleState == _ElementLifecycle.active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }

再看一下_updateInheritance方法是什么時候調(diào)用的

@mustCallSuper
  void mount(Element? parent, dynamic newSlot) {
    ...
    ...省略無關(guān)代碼
    _parent = parent;
    _slot = newSlot;
    _lifecycleState = _ElementLifecycle.active;
    _depth = _parent != null ? _parent!.depth + 1 : 1;
    if (parent != null) // Only assign ownership if the parent is non-null
      _owner = parent.owner;
    final Key? key = widget.key;
    if (key is GlobalKey) {
      key._register(this);
    }
    _updateInheritance();//這里調(diào)用了一次
  }

還有:

@mustCallSuper
  void activate() {
    ...
    ...已省略無關(guān)代碼
    final bool hadDependencies = (_dependencies != null && _dependencies!.isNotEmpty) || _hadUnsatisfiedDependencies;
    _lifecycleState = _ElementLifecycle.active;
    _dependencies?.clear();
    _hadUnsatisfiedDependencies = false;
    _updateInheritance();//這里又調(diào)用了一次
    if (_dirty)
      owner!.scheduleBuildFor(this);
    if (hadDependencies)
      didChangeDependencies();
  }

從上面代碼我們可以看到每個頁面的Element都會通過_parent向下級傳遞父級信息,而我們的UserInfoWidget就保存在_parent中的_inheritedWidgets集合中:Map<Type, InheritedElement>? _inheritedWidgets;,當(dāng)_inheritedWidgets在頁面樹中向下傳遞的時候,如果當(dāng)前Widget是InheritWidget,在當(dāng)前Widget對應(yīng)的Element中先看_parent傳過來的_inheritedWidgets是否為空,如果為空就新建一個集合,把自己存到這個集合中,以當(dāng)前的類型作為key(這也是為什么調(diào)用of方法中的context.dependOnInheritedWidgetOfExactType方法為什么要傳當(dāng)前類型的原因),從_inheritedWidgets集合中去取值;如果不為空直接把自己存進(jìn)去,這就是of的原理了。

Notification

上面講的InheritWidget一般是根部組建向子級組件傳值,Notification是從子級組件向父級組件傳值,下面我們來看一下它的用法

class Page19PassByValue extends StatefulWidget {
  @override
  _Page19PassByValueState createState() => _Page19PassByValueState();
}

class _Page19PassByValueState extends State<Page19PassByValue> {
  UserInfoBean userInfoBean = UserInfoBean(name: 'wf', address: 'address');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('PassByValue'),
      ),
      body: Center(
        child: NotificationListener<MyNotification>(
          onNotification: (MyNotification data) {
            userInfoBean = data.userInfoBean;
            setState(() {});
            ///這里需要返回一個bool值,true表示阻止事件繼續(xù)向上傳遞,false表示事件可以繼續(xù)向上傳遞到父級組件
            return true;
          },
          child: Builder(
          ///這里用了一個Builder包裝了一下,為的是能取到
          ///NotificationListener的context
            builder: (context) {
              return Column(
                children: [
                  Text(userInfoBean.name),
                  Text(userInfoBean.address),
                  Container(
                    child: FlatButton(
                      child: Text('點擊傳值'),
                      onPressed: () {
                        MyNotification(userInfoBean: UserInfoBean(name: 'wf123', address: 'address123')).dispatch(context);
                      },
                    ),
                  )
                ],
              );
            },
          ),
        ),
      ),
    );
  }
}
///Notification是一個抽象類,
///使用Notification需要自定義一個class繼承Notification
class MyNotification extends Notification {
  UserInfoBean userInfoBean;
  MyNotification({this.userInfoBean}) : super();
}

我們到源碼中看一下這個dispatch方法:

void dispatch(BuildContext target) {
    // The `target` may be null if the subtree the notification is supposed to be
    // dispatched in is in the process of being disposed.
    target?.visitAncestorElements(visitAncestor);
  }

target就是我們傳進(jìn)來的context,也就是調(diào)用了BuildContext的visitAncestorElements方法,并且把visitAncestor方法作為一個參數(shù)傳過去,visitAncestor方法返回一個bool值:

  @protected
  @mustCallSuper
  bool visitAncestor(Element element) {
    if (element is StatelessElement) {
      final StatelessWidget widget = element.widget;
      if (widget is NotificationListener<Notification>) {
        if (widget._dispatch(this, element)) // that function checks the type dynamically
          return false;
      }
    }
    return true;
  }

我們進(jìn)入Element內(nèi)部看一下visitAncestorElements方法的實現(xiàn):

@override
  void visitAncestorElements(bool visitor(Element element)) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    Element? ancestor = _parent;
    while (ancestor != null && visitor(ancestor))
      ancestor = ancestor._parent;
  }

當(dāng)有父級節(jié)點,并且visitor方法返回true的時候執(zhí)行while循環(huán),visitor是Notification類傳進(jìn)來的方法,回過頭再看visitor方法的實現(xiàn),當(dāng)Element向visitor方法傳遞的ancestor是NotificationListener類的情況下,再判斷widget._dispatch方法,而widget._dispatch方法:

final NotificationListenerCallback<T>? onNotification;

  bool _dispatch(Notification notification, Element element) {
    if (onNotification != null && notification is T) {
      final bool result = onNotification!(notification);
      return result == true; // so that null and false have the same effect
    }
    return false;
  }

就是我們在外面寫的onNotification方法的實現(xiàn),我們在外面實現(xiàn)的onNotification方法返回true(即阻止事件繼續(xù)向上傳遞),上面的while循環(huán)主要是為了執(zhí)行我們onNotification里面的方法.

總結(jié)一下:MyNotification執(zhí)行dispatch方法,傳遞context,根據(jù)當(dāng)前context向父級查找對應(yīng)NotificationListener,并且執(zhí)行NotificationListener里面的onNotification方法,返回true,則事件不再向上級傳遞,如果返回false則事件繼續(xù)向上一個NotificationListener傳遞,并執(zhí)行里面對應(yīng)的方法。Notification主要用在同一個頁面中,子級向父級傳值,比較輕量級,不過如果我們用了Provider可能就就直接借助Provider傳值了。

Eventbus

Eventbus用于兩個不同的頁面,可以跨多級頁面?zhèn)髦担梅ㄒ脖容^簡單,我創(chuàng)建了一個EventBusUtil來創(chuàng)建一個單例
import 'package:event_bus/event_bus.dart';

class EventBusUtil {
  static  EventBus ? _instance;
  static EventBus getInstance(){
    if (_instance == null) {
      _instance = EventBus();
    }
    return _instance!;
  }
}

在第一個頁面監(jiān)聽:

class Page19PassByValue extends StatefulWidget {
  @override
  _Page19PassByValueState createState() => _Page19PassByValueState();
}

class _Page19PassByValueState extends State<Page19PassByValue> {
  UserInfoBean userInfoBean = UserInfoBean(name: 'wf', address: 'address');
  @override
  void initState() {
    super.initState();
    EventBusUtil.getInstance().on<UserInfoBean>().listen((event) {
      setState(() {
        userInfoBean = event;
      });
    });
  }
  
  @override
  void dispose() {
    super.dispose();
    //不用的時候記得關(guān)閉
    EventBusUtil.getInstance().destroy();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('PassByValue'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(userInfoBean.name),
            Text(userInfoBean.address),
            TextButton(onPressed: (){
              Navigator.of(context).push(CupertinoPageRoute(builder: (_){
                return EventBusDetailPage();
              }));
            }, child: Text('點擊跳轉(zhuǎn)'))

          ],
        ),
      ),
    );
  }
}

在第二個頁面發(fā)送事件:

class EventBusDetailPage extends StatefulWidget {
  @override
  _EventBusDetailPageState createState() => _EventBusDetailPageState();
}

class _EventBusDetailPageState extends State<EventBusDetailPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('EventBusDetail'),
      ),
      body: Center(
        child: TextButton(onPressed: (){
          EventBusUtil.getInstance().fire(UserInfoBean(name: 'name EventBus', address: 'address EventBus'));
        }, child: Text('點擊傳值')),
      ),
    );
  }
}

我們看一下EventBus的源碼,發(fā)現(xiàn)只有幾十行代碼,他的內(nèi)部是創(chuàng)建了一個StreamController,通過StreamController來實現(xiàn)跨組件傳值,我們也可以直接使用一下這個StreamController實現(xiàn)頁面?zhèn)髦担?br />

class Page19PassByValue extends StatefulWidget {
  @override
  _Page19PassByValueState createState() => _Page19PassByValueState();
}

StreamController controller = StreamController();

class _Page19PassByValueState extends State<Page19PassByValue> {
  
  //設(shè)置一個初始值
  UserInfoBean userInfoBean = UserInfoBean(name: 'wf', address: 'address');
  @override
  void initState() {
    super.initState();
    controller.stream.listen((event) {
      setState(() {
        userInfoBean = event;
      });
    });
  }

  @override
  void dispose() {
    super.dispose();
    //頁面銷毀的時候記得關(guān)閉
    controller.close();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('PassByValue'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(userInfoBean.name),
            Text(userInfoBean.address),
            TextButton(onPressed: (){
              Navigator.of(context).push(CupertinoPageRoute(builder: (_){
                return MyStreamControllerDetail();
              }));
            }, child: Text('點擊跳轉(zhuǎn)'))
          ],
        ),
      )
    );
  }
}
class MyStreamControllerDetail extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _MyStreamControllerDetailState();
  }
}
class _MyStreamControllerDetailState extends State <MyStreamControllerDetail> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StreamController'),
      ),
      body: Center(
        child: TextButton(onPressed: (){
        //返回上個頁面,會發(fā)現(xiàn)頁面的數(shù)據(jù)已經(jīng)變了
          controller.sink.add(UserInfoBean(name: 'StreamController pass name: 123', address: 'StreamController pass address 123'));
        }, child: Text('點擊傳值'),),
      ),
    );
  }
}

到此這篇關(guān)于Flutter頁面?zhèn)髦档膸追N方式的文章就介紹到這了,更多相關(guān)Flutter頁面?zhèn)髦祪?nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論