模拟红绿灯来看GetX的定向刷新

前言

对于有些场景,我们可能有多个组件共享一份状态数据,但是状态数据改变后可能只需要更新其中的一个或多个组件,而不是依赖状态的全部组件。这个时候我们就可以用到 GetX 的定向更新。GetXupdate 方法中可以提供两个可选参数:

void update([List<Object>? ids, bool condition = true]) {
  if (!condition) {
    return;
  }
  if (ids == null) {
    refresh();
  } else {
    for (final id in ids) {
      refreshGroup(id);
    }
  }
}
  • ids:要更新的 id数组,id可以在 GetBuilder 构建的时候指定,若指定了ids,则之后更新与 ids 中的 id 匹配的组件:
GetBuilder<Controller>(
  id: 'text'
  init: Controller(), // use it only first time on each controller
  builder: (_) => Text(
    '${Get.find<Controller>().counter}', //here
  ),
),
  • condition:条件表达式,只有当这个条件为真的时候才会更新组件。

例如下面的代码只会在 counter 小于10的时候更新 idtext 的组件。

update(['text'], counter < 10);

接下来我们模拟带倒计时的红绿灯来演示GetX 的定向更新的使用。

业务逻辑

为了分别控制红绿灯,我们需要三个组件,分别是红灯、绿灯和黄灯。

image.png

然后是倒计时,我们设置规则如下:

  • 绿灯亮的时长为20秒,红灯为10秒,黄灯为3秒,计时通过定时器完成,每隔1秒减1。
  • 三个红灯共用一个计时器,但根据当前亮的灯的状态来定向更新哪个灯的倒计时时间,同时对于不亮的灯我们不显示倒计时时间(因为共享了倒计时时间,如果显示就会不对)。
  • 使用一个枚举来确定当前亮哪个灯,亮灯的次序为绿灯->黄灯->红灯->绿灯……

业务理顺了,开始撸代码!

红绿灯代码

首先我们构建一个通用的交通灯的组件 TrafficLed,需要四个参数:

  • 灯的颜色:ledColor,控制灯的倒计时数字颜色;
  • 倒计时时间:secondsLeft,倒计时时间;
  • 是否显示倒计时:showSeconds,使用 Offstate 控制是否显示倒计时时间。
  • 灯的大小:ledSize,默认尺寸为60,用于控制灯的尺寸。

对应代码很简单,这里我们为了好看做了点阴影,使得看起来有点立体感。

class TrafficLed extends StatelessWidget {
  final Color ledColor;
  final int secondsLeft;
  final bool showSeconds;
  final double ledSize;
  const TrafficLed({
    Key? key,
    required this.ledColor,
    required this.secondsLeft,
    required this.showSeconds,
    this.ledSize = 60.0,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        alignment: Alignment.center,
        width: ledSize,
        height: ledSize,
        decoration: BoxDecoration(
          color: Colors.black,
          borderRadius: BorderRadius.circular(ledSize / 2),
          boxShadow: [
            BoxShadow(
              color: Color(0xFF505050),
              offset: Offset(1, -1),
              blurRadius: 0.2,
            )
          ],
        ),
        child: Offstage(
          child: Text(
            '$secondsLeft',
            textAlign: TextAlign.center,
            style: TextStyle(
              color: this.ledColor,
              fontSize: 36,
              fontWeight: FontWeight.bold,
            ),
          ),
          offstage: !showSeconds,
        ),
      ),
    );
  }
}

接下来是整个灯的组合,这里我们使用横向的红绿灯,然后也用阴影做了一个有立体感的背景。关键代码在每个灯都使用了 GetBuilder 包裹,然后指定了每个灯的 id,这里以绿灯为例:

GetBuilder<TrafficLightController>(
  id: 'green',
  init: lightController,
  builder: (state) => TrafficLed(
    ledColor: (state.currentLight == TrafficLight.green
        ? Colors.green
        : Colors.black),
    secondsLeft: state.counter,
    showSeconds: state.currentLight == TrafficLight.green,
  ),
),

每个灯对应的逻辑如下:

  • 如果当前状态中显示的灯和自身一致,倒计时的文字颜色就使用灯对应的颜色,即红、黄、绿;否则显示黑色(和背景色一致);
  • 绑定状态对象的倒计时时间;
  • 如果当前状态中显示的灯和自身一致,则显示倒计时,否则不显示。

状态管理代码

状态管理控制器为TrafficLightController,代码如下:

enum TrafficLight { green, red, yellow }

class TrafficLightController extends GetxController {
  late TrafficLight _currentLight;
  get currentLight => _currentLight;

  int _counter = 0;
  get counter => _counter;

  late Timer _downcountTimer;

  @override
  void onInit() {
    _counter = 20;
    _currentLight = TrafficLight.green;
    super.onInit();
  }

  @override
  void onReady() {
    _downcountTimer = Timer.periodic(Duration(seconds: 1), decreament);
    super.onReady();
  }

  void decreament(Timer timer) {
    _counter--;
    if (_counter == 0) {
      switch (_currentLight) {
        case TrafficLight.green:
          _currentLight = TrafficLight.yellow;
          _counter = 3;
          update(['green', 'yellow']);
          break;
        case TrafficLight.yellow:
          _currentLight = TrafficLight.red;
          _counter = 10;
          update(['red', 'yellow']);
          break;
        case TrafficLight.red:
          _currentLight = TrafficLight.green;
          _counter = 20;
          update(['red', 'green']);
          break;
      }
    } else {
      switch (_currentLight) {
        case TrafficLight.green:
          update(['green']);
          break;
        case TrafficLight.yellow:
          update(['yellow']);
          break;
        case TrafficLight.red:
          update(['red']);
          break;
      }
    }
  }

  @override
  void onClose() {
    _downcountTimer.cancel();
    super.onClose();
  }
}

这里使用了三个声明周期函数:

  • onInit:设置倒计时时间为20秒,灯状态为绿灯;
  • onReady:启动定时器;
  • onClose:关闭定时器。

核心业务逻辑都在 decreament 这个定时器回调方法里,这里如果倒计时到0的时候我们切换灯状态,重置倒计时时间,而且只更新该情况下需要刷新的灯(每次2个灯需要更新)。如果倒计时没有到0,那么我们只需要更新当前亮的灯就可以了。通过这种方式,我们可以定向更新,在共享状态数据的同时还可以减少不必要的刷新。

运行效果

运行效果如下图,源码已提交至:GetX 相关代码

屏幕录制2021-09-07 下午10.13.18.gif

总结

本篇介绍了 GetXGetBuilder使用id参数实现定向刷新的特性。这种情况适用于多个组件共用一个状态对象,但更新条件不同的情况,比如本例的红绿灯。同时,GetxControllerupdate 方法还可以实现条件更新,通过id 加条件组合能够实现更加进货精准的定向刷新。

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

推荐阅读更多精彩内容

  • 一、需求分析 1. 需要实现的功能 (1)通过控制台输入绿灯、黄灯、红灯的时间(2)输入完成后,按回车,先绿灯倒计...
    小雨Coding阅读 3,192评论 0 4
  • 我记得,在我上小学的时候,青岛的大多数路口的红绿灯都已经安装上显示倒计时的电子屏幕。 当时的我,对它还很好奇。 小...
    生还者阅读 1,517评论 18 49
  • 前几天爸妈送我回公司的时候经过一个路口,当时红灯倒计时已经结束,然而绿灯迟迟没有跳出来,老妈就在一旁催促到:都3、...
    所以不爱金牛啊阅读 694评论 0 1
  • 下班路,车行至路口,红灯倒计时还有几秒。 旁边车道的一辆黑色丰田越野车已经缓缓启动,倒计时结束的瞬间,“嗖”的一声...
    活出年轻态阅读 206评论 1 2
  • 八月初骑车路过八中门口,下午两点,学生们到了上课的时候,红绿灯前热闹起来。 想起来还没毕业时,从学...
    陸海阅读 126评论 0 1