我们封装了哪些好用的Flutter Mixin

一、Mixin 的本质

  • 定义 :Mixin 是一种特殊的类,通过 mixin 关键字声明,不能直接实例化,但可以被其他类"混入"(使用 with 关键字)。

  • 核心思想:将可复用的代码片段注入到多个不相关的类中,实现代码的横向组合(而非纵向继承)。

二、Mixin 的工作原理

  • 线性化(Linearization) :Dart 使用线性化算法处理混入顺序。当多个 Mixin 存在时,它们的属性和方法会按 with 的声明顺序从右到左叠加(最后混入的优先级最高)。

    Dart 复制代码
    class MyWidget with MixinA, MixinB, MixinC {}
    // 方法解析顺序:MixinC → MixinB → MixinA → MyWidget
  • 冲突解决:如果多个 Mixin 有同名方法,后混入的会覆盖前面的(类似"最后一个生效"规则)。

三、Mixin vs 继承 vs 接口

特性 Mixin 继承 (extends) 接口 (implements)
复用方式 横向组合 纵向继承 强制实现契约
多重支持 是(with A,B 否(单继承)
代码复用 直接复用逻辑 复用父类实现 无默认实现
典型场景 跨类共享工具方法 "is-a"关系 "can-do"能力

四、我们在项目中封装哪些Mixin

1、AddListenerMixin

Dart 复制代码
mixin AddListenerMixin<T extends StatefulWidget> on State<T> {
  final Map<Listenable, VoidCallback> _registrationInfo = {};

  void addListener(Listenable listenable, VoidCallback listener) {
    listenable.addListener(listener);
    _registrationInfo[listenable] = listener;
  }

  @override
  void dispose() {
    for (var entry in _registrationInfo.entries) {
      entry.key.removeListener(entry.value);
    }
    _registrationInfo.clear();
    super.dispose();
  }
}

项目中使用的** AddListenerMixin 主要是简化 Listenable 对象(如 ValueNotifierAnimationController **等)的监听管理,自动处理监听器的注册和清理,避免内存泄漏。同时也可以减少dispose时的样板代码。

不过目前AddListenerMixin 不支持手动移除VoidCallback,也不支持批量操作。

2、SafeSetStateMixin

Dart 复制代码
mixin SafeSetStateMixin<T extends StatefulWidget> on State<T> {
  
  void safeSetState(VoidCallback fn) {
    fn.call();
    if (!mounted) return;
    final animStatus = ModalRoute.of(context)?.animation?.status;
    if (animStatus == null || animStatus == AnimationStatus.completed) {
      scheduleMicrotask(() {
        if (mounted) {
          setState(() {});
        }
      });
    } else {
      _listenRouteAnim();
      _needSetState = true;
    }
  }

  Animation? _routeAnim;
  bool _needSetState = false;

  void _listenRouteAnim() {
    _routeAnim?.removeStatusListener(_animStatusListener);
    _routeAnim = ModalRoute.of(context)?.animation;
    _routeAnim?.addStatusListener(_animStatusListener);
  }

  @override
  void deactivate() {
    _routeAnim?.removeStatusListener(_animStatusListener);
    _routeAnim = null;
    super.deactivate();
  }

  void _animStatusListener(AnimationStatus status) {
    if (_needSetState && status == AnimationStatus.completed) {
      _needSetState = false;
      safeSetState(() {});
    }
  }
}

SafeSetStateMixin 的作用

  • 避免在disposed状态下调用setState

  • 避免在动画未完成的情况下触发setState,通过监听动画状态,在动画完成后及时触发setState更新

  • 异步安全,通过 scheduleMicrotask 延迟 setState,避免在 build 过程中同步触发更

3、StreamControlMixin

Dart 复制代码
mixin StreamControlMixin<T extends StatefulWidget> on State<T> {
  final Set<StreamSubscription> _subscriptions = {};

  void addSubscription(StreamSubscription subscription) {
    if (subscription != null) {
      _subscriptions.add(subscription);
    }
  }

  bool removeSubscription(StreamSubscription subscription) {
    if (subscription != null) {
      return _subscriptions.remove(subscription);
    }
    return false;
  }

  void cancelAllEvents() {
    for (var subscription in _subscriptions) {
      try {
        subscription.cancel();
      } catch (e) {
        debugPrint('Error canceling subscription: $e');
      }
    }
    _subscriptions.clear();
  }

  bool hasSubscription(StreamSubscription subscription) {
    return _subscriptions.contains(subscription);
  }

  @override
  void dispose() {
    if (_subscriptions.isNotEmpty) {
      cancelAllEvents();
    }
    super.dispose();
  }
}

StreamControlMixin的作用

  • 提供统一的方式来管理StreamSubscription,支持手动添加、移除

  • 自动清理资源,在组件销毁时自动取消所有订阅,防止内存泄漏

  • 简化添加、移除流程,减少模版代码

五、总结

本篇主要是简单介绍了一下Mixin,总结分享了一下在项目我们封装了哪些Mixin,希望能够给读者带来一些思考。实际上个人认为封装的中心思想都是一致,仅是利用不同语言的特性。

相关推荐
火柴就是我4 小时前
让我们实现一个更好看的内部阴影按钮
android·flutter
王晓枫5 小时前
flutter接入三方库运行报错:Error running pod install
前端·flutter
砖厂小工11 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心11 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心11 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
shankss12 小时前
Flutter 下拉刷新库 pull_to_refresh_plus 设计与实现分析
flutter
Kapaseker14 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴14 小时前
Android17 为什么重写 MessageQueue
android
忆江南1 天前
iOS 深度解析
flutter·ios
明君879971 天前
Flutter 实现 AI 聊天页面 —— 记一次 Markdown 数学公式显示的踩坑之旅
前端·flutter