一、Mixin 的本质
-
定义 :Mixin 是一种特殊的类,通过
mixin
关键字声明,不能直接实例化,但可以被其他类"混入"(使用with
关键字)。 -
核心思想:将可复用的代码片段注入到多个不相关的类中,实现代码的横向组合(而非纵向继承)。
二、Mixin 的工作原理
-
线性化(Linearization) :Dart 使用线性化算法处理混入顺序。当多个 Mixin 存在时,它们的属性和方法会按
with
的声明顺序从右到左叠加(最后混入的优先级最高)。Dartclass 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
对象(如 ValueNotifier
、AnimationController
**等)的监听管理,自动处理监听器的注册和清理,避免内存泄漏。同时也可以减少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,希望能够给读者带来一些思考。实际上个人认为封装的中心思想都是一致,仅是利用不同语言的特性。