首先给出demo:
demo:
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyPage(),
);
}
}
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
// _MyPageState2 createState() => _MyPageState2();
// _MyPageState3 createState() => _MyPageState3();
}
模式1:
mixin DebounceMixin {
bool _debounce = false;
Future<void> debouncer(Future<void> Function() callback) async {
if (_debounce) return;
_debounce = true;
try {
await callback();
} finally {
_debounce = false;
}
}
}
= demo ====================================================================
class _MyPageState extends State<MyPage> with DebounceMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Mixin Example")),
body: Center(
child: ElevatedButton(
onPressed: () async {
// 执行异步操作
await debouncer(() async {
// 模拟一个耗时操作
await Future.delayed(const Duration(seconds: 2));
// 完成后操作
print("===mixin demo===> 操作完成");
});
},
child: const Text("开始加载"),
),
),
);
}
}
模式2(模式1的变式):
有时,可能需要对状态进行监听,可以使用 ValueNotifier ,同时配合 ValueListenableBuilder 实现。
mixin DebounceListenableMixin {
final ValueNotifier<bool> debounce = ValueNotifier(false);
Future<void> debouncer(Future<void> Function() callback) async {
if (debounce.value) return;
debounce.value = true;
try {
await callback();
} finally {
debounce.value = false;
}
}
}
= demo ====================================================================
class _MyPageState2 extends State<MyPage> with DebounceListenableMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Mixin Example")),
body: Center(
child: ValueListenableBuilder<bool>(
valueListenable: debounce,
builder: (context, debounce, child) {
print("===mixin demo===> DebounceListenableMixin $debounce");
return ElevatedButton(
onPressed: () async {
// 执行异步操作
await debouncer(() async {
// 模拟一个耗时操作
await Future.delayed(const Duration(seconds: 2));
// 完成后操作
print("===mixin demo===> 操作完成");
});
},
child: const Text("开始加载"),
);
},
),
),
);
}
@override
void dispose() {
// 注意避免 ValueNotifier 的内存泄漏
debounce.dispose();
super.dispose();
}
}
模式3(模式2的变式):
当然,对有状态的组件,可以使用on关键字,直接在 State 上进行混入,并且这样带来了好处,可以方便在mixin中对 ValueNotifier 进行管理。
mixin DebounceListenableStateMixin<T extends StatefulWidget> on State<T> {
final ValueNotifier<bool> debounce = ValueNotifier(false);
Future<void> debouncer(Future<void> Function() callback) async {
if (debounce.value) return;
debounce.value = true;
try {
await callback();
} finally {
debounce.value = false;
}
}
@override
void dispose() {
debounce.dispose();
super.dispose();
}
}
模式4:
如果是针对频率进行限制:
mixin ThrottleMixin {
var _lastTime = 0;
Future<void> throttler(
Future<void> Function() callback, {
int milliseconds = 1000,
}) async {
final now = DateTime.now().millisecondsSinceEpoch;
if (now - _lastTime > milliseconds) {
_lastTime = now;
try {
await callback();
} catch (_) {}
}
}
}
= demo ====================================================================
class _MyPageState3 extends State<MyPage> with ThrottleMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Mixin Example")),
body: Center(
child: ElevatedButton(
onPressed: () async {
// 执行异步操作
await throttler(
() async {
// 模拟一个耗时操作
await Future.delayed(const Duration(seconds: 2));
// 完成后操作
print("===mixin demo===> 操作完成");
},
);
},
child: const Text("开始加载"),
),
),
);
}
}
使用mixin解决方案的优点:
灵活、可复用:不同组件都能使用
无侵入性:不改变原组件的默认行为
模式选择?
适用于防止按钮连点:模式1-模式3
适用于限制点击频率:模式4
mixin原理:
在 Dart 中,mixin 是一种代码复用的机制,允许将一组方法和属性注入到多个类中,而不需要使用继承。 mixin 类似于多继承,但它避免了多继承的复杂性。
1、定义mixin:使用 mixin 关键字来定义一个 mixin,一个 mixin 可以包含方法、属性
mixin LoggingMixin {
void log(String message) {
print("===mixin demo===> $message");
}
}
2、使用mixin:使用 with 关键字,一个类可以使用多个 mixin
class User with LoggingMixin {
final String name;
User({
required this.name,
});
void hello() {
log('Hello,$name');
}
}
3、使用多个mixin:一个类可以使用多个 mixin,只需在 with 后面列出多个 mixin,用逗号分隔
mixin ValidationMixin {
bool isValid(String? input) {
return input?.isNotEmpty ?? false;
}
}
class Admin with LoggingMixin, ValidationMixin {
final String name;
Admin({
required this.name,
});
void check() {
if (isValid(name)) {
log('$name is valid');
} else {
log('$name is invalid');
}
}
}
4、使用on关键字限制mixin的范围:使用 on 关键字来限制 mixin 只能用于特定类的子类
//在这个例子中,SpecialLoggingMixin 只能用于 User 类或其子类。
mixin SpecialLoggingMixin on User {
void specialLog(String message) {
print("===mixin demo===> specialLog:$message");
}
}
class SuperUser extends User with SpecialLoggingMixin {
SuperUser({required super.name});
}
5、“多继承”中的同名方法:with 允许多个 mixin 组合,且 右侧的 mixin 会覆盖左侧的
class A {
void say() {
print("A");
}
}
mixin B on A {
@override
void say() {
print("B");
}
}
mixin C {
void say() {
print("C");
}
}
class D extends A with B, C {
// 如果重写了,依然调用当前类的方法
// 如果没有重写,调用 with 最右边的方法
// @override
// void say() {
// print("D");
// }
}
void main() {
final d = D();
d.say(); // console print: C
}
6、class mixin
mixin class E {
// 既能作为mixin,又能作为常规的类
// 但是,不能有 extends、with、on,因为前两者mixin没有,后者常规的类没有
}
7、mixin的限制
不能有构造函数
不能继承类
8、如何选择mixin、extends、implements?
mixin 适合多个类共享相同功能,但不影响继承体系
extends 适合建立类层级关系
implements 适合定义接口,但需手动实现所有方法
9、mixin的本质
mixin 本质上是一个没有构造函数的类,它不能被直接实例化,编译器将方法和属性“复制”到使用它的类中。
mixin LoggingMixin {
void log(String message) {
print("===mixin demo===> $message");
}
}
class User with LoggingMixin {
final String name;
User({
required this.name,
});
void hello() {
log('Hello,$name');
}
}
在编译时,LoggingMixin 中的 log 方法会被“复制”到 User 类中,相当于 User 类直接包含了 log 方法的实现。 当多个 mixin 被应用到同一个类时,Dart 会按照 with 关键字的顺序,将 mixin 中的方法和属性线性化。 这意味着后面的 mixin 会覆盖前面的 mixin 中同名的方法或属性,以此来解决冲突问题。