Flutter中的异步(四)

Stream流实现信号灯

信号灯的流程

信号灯的跳转可以看做是在一个时间轴上发生的连续的事件,可以将信号灯的每次变化都看做是stream中的一个元素,每秒监听状态的通知,然后更新UI界面。

信号灯组件构建

信号灯状态

js 复制代码
const int _kAllowMaxCount = 10;
const int _kWaitMaxCount = 3;
const int _kDenialMaxCount = 10;

class SignalState {
  final int counter;
  final SignalType type;

  SignalState({
    required this.counter,
    required this.type,
  });
}

enum SignalType {
  allow, // 允许 - 绿灯
  denial, // 拒绝 - 红灯
  wait, // 等待 - 黄灯
}

SignalState表示秒数和当前状态。

其中SignalType是一个枚举类型分别为红绿灯的三种状态。

信号灯组件构建

如下所示,信号灯由三个 Lamp 组件和数字构成。三个灯分别表示 红、黄、绿 ,某一时刻只会量一盏,不亮的使用灰色示意。三个灯水平排列,有一个黑色背景装饰,和文字呈上下结构。

lamp组件构建如下所示:

js 复制代码
class Lamp extends StatelessWidget {
  final Color? color;

  const Lamp({Key? key, required this.color}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 40,
      height: 40,
      decoration: BoxDecoration(
        color: color ?? Colors.grey.withOpacity(0.8),
        shape: BoxShape.circle,
      ),
    );
  }
}

如下表示SinalLamp组件:

js 复制代码
class SignalLamp extends StatelessWidget {
  final SignalState state;

  const SignalLamp({Key? key, required this.state}) : super(key: key);

  Color get activeColor {
    switch (state.type) {
      case SignalType.allow:
        return Colors.green;
      case SignalType.denial:
        return Colors.red;
      case SignalType.wait:
        return Colors.amber;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
          padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
          decoration: BoxDecoration(
              color: Colors.black, borderRadius: BorderRadius.circular(30),),
          child: Wrap(
            alignment: WrapAlignment.center,
            crossAxisAlignment: WrapCrossAlignment.center,
            spacing: 15,
            children: [
              Lamp(color: state.type == SignalType.denial ? activeColor : null),
              Lamp(color: state.type == SignalType.wait ? activeColor : null),
              Lamp(color: state.type == SignalType.allow ? activeColor : null),
            ],
          ),
        ),
        Text(
          state.counter.toString(),
          style: TextStyle(
              fontWeight: FontWeight.bold, fontSize: 50, color: activeColor,
          ),
        )
      ],
    );
  }
}

stream事件添加与监听

使用Stream流来监听信号灯状态,并根据状态的改变来渲染界面。

js 复制代码
---->[SignalState]----
SignalState next() {
  if (counter > 1) {
    return SignalState(type: type, counter: counter - 1);
  } else {
    switch (type) {
      case SignalType.allow:
        return SignalState(
            type: SignalType.denial, counter: _kDenialMaxCount);
      case SignalType.denial:
        return SignalState(type: SignalType.wait, counter: _kWaitMaxCount);
      case SignalType.wait:
        return SignalState(type: SignalType.allow, counter: _kAllowMaxCount);
    }
  }
}

把每个事件通知看做元素,Stream 应用处理事件序列,只不过序列中的元素在此刻是未知的,何时触发也是不定的。Stream 基于 发布-订阅 的思想通过监听来处理这些事件。 其中两个非常重要的角色: 发布者 是元素的生产者,订阅者 是元素的消费者。

在引擎中的 async 包中封装了 StreamController 类用于控制元素的添加操作,同时提供 Stream 对象用于监听。代码处理如下,tag1 处,监听 streamControllerstream 对象。事件到来时触发 emit 方法 ( 方法名任意 ),在 emit 中会回调出 SignalState 对象,根据这个新状态更新界面即可。然后延迟 1s 继续添加下一状态。

js 复制代码
---->[_MyHomePageState]----
final StreamController<SignalState> streamController = StreamController();
SignalState _signalState = SignalState(counter: 10, type: SignalType.denial);

@override
void initState() {
  super.initState();
  streamController.stream.listen(emit); // tag1
  streamController.add(_signalState);
}

@override
void dispose() {
  super.dispose();
  streamController.close();
}

void emit(SignalState state) async {
  _signalState = state;
  setState(() {});
  await Future.delayed(const Duration(seconds: 1));
  streamController.add(state.next());
}

stream的控制与异常监听

Stream#listen 方法会返回一个 StreamSubscription 的订阅对象,通过该对象可以暂停、恢复、取消对流的监听。 StreamController 在构造时可以传入四个函数来监听流的状态:

  • onListen()
  • onPause()
  • onResume()
  • onCancel()

onListen 会在 stream 成员被监听时触发一次;onPauseonResumeonCancel 分别对应订阅者的 pauseresumecancel 方法。

相关推荐
Web极客码26 分钟前
深入了解WordPress网站访客意图
服务器·前端·wordpress
幺风1 小时前
Claude Code 源码分析 — Tool/MCP/Skill 可扩展工具系统
前端·javascript·ai编程
vjmap1 小时前
唯杰地图CAD图层加高性能特效扩展包发布
前端·gis
ZC跨境爬虫1 小时前
3D 地球卫星轨道可视化平台开发 Day7(AI异步加速+卫星系列精简+AI Agent自动评论)
前端·人工智能·3d·html·json
ID_180079054731 小时前
淘宝 API 上货 / 商品搬家 业务场景实现 + JSON 返回示例
前端·javascript·json
M ? A1 小时前
Vue 动态组件在 React 中,VuReact 会如何实现?
前端·javascript·vue.js·经验分享·react.js·面试·vureact
vipbic2 小时前
独立开发复盘:我用 Uni-app + Strapi v5 肝了一个“会上瘾”的打卡小程序
前端·微信小程序
IT_陈寒3 小时前
Vite的热更新突然失效,原来是因为这个配置
前端·人工智能·后端
ZC跨境爬虫3 小时前
3D 地球卫星轨道可视化平台开发 Day8(分步渲染200颗卫星+ 前端分页控制)
前端·python·3d·重构·html
竹林8183 小时前
RainbowKit快速集成多链钱包连接,我如何从“连不上”到“丝滑切换”
前端·javascript