Flutter|常用数据通信组件

在做需求时经常会遇到组件间通信,本篇汇总了几种常用的通信方式。

父子组件之间的通信

最简单的方式就是通过构造器传递数据,但当嵌套层级比较深的时,传参就比较难看了,或许这时还考虑用单例,但对于简单数据来说未免大材小用了。
例如,还是这个二级页场景,由多个子孙组件组成(同色框表示同级),在顶层页面可以知道从上个页面携带的数据,但最下层的输入框组件(箭头位置)也需要知道部分数据才能发送评论,这里说下如何用Provider快速实现。

Provider

  • 介绍:基于InheritedWidget,一种自上向下的共享数据机制,方便子孙节点得到祖先节点共享的数据。
  • 举例:
// 1、添加依赖
dependencies:
  provider: ^5.0.0
 
// 2、通过Provider包裹的child都共享数据 feedShareData
...
return Scaffold(
  ...
  body: Provider(
    create: (_) => feedShareData,
    child: Builder(builder: (context) {
      return FeedDetailPage();
    }),
  ),
);
// 3、在子孙组件都可以读取并修改共享数据
feedShareData = context.read<NormalFeedsDetailShareData>();
...

  • 适用场景:当子组件需要共享祖先/父组件的数据时,尤其在跨层传递场景。

除了数据共享外,还有就是数据传递,也就是组件想知道另个组件的某些变化,并做出反应(callback)。在Dart中函数是可以当做对象传递的,但同样存在层级复杂时可能要传递大量的函数,接下来介绍常用的数据传递组件。

ChangeNotifier

  • 介绍:可用于扩展Model,并在Model发生变化时调用notifyListeners使得监听该Model的组件重新构建。
  • 举例:详见官方提供的案例,实现了加入购物车的效果。
  • 适用场景: 常和ProviderConsumer)配套解决数据共享&状态传递问题。

NotificationListener

  • 介绍:通知机制,即每个节点都可以向上发送Notification(自下向上的),除非中途被拦截否则会一直向上传。
  • 举例:在上篇Flutter小白|初探事件处理介绍了如何通过NotificationListener解决多个滑动组件下、监听最外层组件滑动到边界事件,可见使用NotificationListener可以很方便获取到可滑动子组件的滑动行为。当然还可以在子组件上通过Notification#dispatch发送自定义通知,然后在上层接收并处理。
// 1、自定义通知类
class CustomNotification extends Notification {
  String msg;
  CustomNotification(this.msg);
}
// 2、子组件发送通知
...
CustomNotification("Test").dispatch(context);
...
// 3、父组件接收和处理通知
...
NotificationListener<CustomNotification>(
      onNotification: (notification) {
       // 处理逻辑
         // 同样的返回值表示是否拦截
       return true;
      },
      child:...
)
  • 适用场景:当祖先/父组件要随着子组件的某些情况变化而变化时,常用于滑动情况。

兄弟组件之间的通信

ValueListenableBuilder

  • 介绍:顾名思义,对某个 value进行监听,只要value发生变化就会直接通过setState重新构建子组件树。同样的,因为会rebuild,使用时尽可能缩小包裹的组件范围,以减少不必要的组件被重建、提高性能。
  • 举例:例如想要实现这样的需求,底部输入框有内容时按钮亮起,无内容按钮置灰,代码如下:


// 1、创建:定义一个文本内容的ValueNotifier,
ValueNotifier<String> emojiInfoNotifier = ValueNotifier("");

// 2、使用:输入框组件和按钮组件都监听这个string信息
...
ValueListenableBuilder(
  valueListenable: emojiInfoNotifier,
  builder: (BuildContext context, String value, Widget? child) {
    // 输入框组件,根据输入内容展示,没有内容展示默认文案
    return Text(
      value.isEmpty
          ? Strings.share_thoughts
          : value,
      maxLines: 1,
      overflow: TextOverflow.ellipsis,
      style: TextStyle(fontSize: Res.t12, color: Res.c1),
    );
  },
),
...
ValueListenableBuilder(
  valueListenable: emojiInfoNotifier,
  builder: (BuildContext context, String value, Widget? child) {
    // 按钮组件,根据有无内容改变icon颜色
    return SvgPicture.asset(
      iconUrl,
      color: value.isEmpty ? Res.c4 : Res.c2,
      width: 16,
      height: 16,
    );
  },
),
...
// 3、随数据变化而变化:当点击表情按钮导致文本内容发生变化时,输入框组件和按钮组件都会发生变化,
...
GestureDetector(
  behavior: HitTestBehavior.opaque,
  onTap: () {
    // 超过最大表情展示个数会弹toast提示
    if (emojiInfoNotifier.value.characters.length >= maxInputEmojiCount) {
      Toasts.show(Strings.input_more);
    } else {
      emojiInfoNotifier.value += emoji?.text ?? "";
    }
  },
  child:... // 表情按钮
)...
// 4、销毁:在依赖组件dispose的同时销毁
@override
void dispose() {
  super.dispose();
  emojiInfoNotifier.dispose();
  ...
}
  • 适用场景:当组件A要随着其他组件B上操作的数据变化而变化时,可以在同个组件树,也可以不是。常用于组件关系不复杂且监听数据简单的情况。

EventBus

  • 介绍:对于要跨组件甚至跨页面组件通信的,这时用EventBus屡试不爽。关于 EventBus 开发一定非常熟悉了,核心就是观察者模式,一方发布消息,一方订阅感兴趣的消息并做处理。
  • 举例:
// 1、添加依赖
dependencies:
 event_bus: ^2.0.0

// 2、新增事件类
class CustomEvent {
  String msg;
  CustomEvent(this.msg);
}

// 3、发送事件(用单例维护,这里不展示了)
 `EventBusExt.getDefault().fire(CustomEvent("test"));` 

// 4、订阅事件并处理
EventBusExt.getDefault().on<CustomEvent>().listen((event) {
      // 处理逻辑
});
  • 适用场景:可以在任何位置传递事件,可以解决大多数通信需求。

总结

本篇从数据通信方式(共享数据、传递数据)和组件关系(父子关系、兄弟关系)两方面分别介绍了Flutter中常用的组件,整体看ProviderEventBus总是能很好的处理各类场景,功能强大,但也有维护成本的问题(比如要新增很多事件类等),一些简单场景可以考虑其他小组件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容