如何为高级枚举创建Mixin并在通用Widget中使用?
解决枚举滚轮选择器的通用适配问题
问题1:让滚轮选择器与枚举配合工作
直接通过泛型T extends Enum无法访问具体枚举的values和fromValue,因为Dart泛型类型参数在运行时仅为Type对象,不包含枚举的静态成员。解决方式是将枚举的values列表、索引转枚举的逻辑作为参数传入Widget:
修改后的WheelPickerWidget代码:
class WheelPickerWidget<T extends Enum> extends StatelessWidget { final T? value; final void Function(T)? onChanged; // 新增枚举值列表、索引转枚举方法两个必填参数 final List<T> enumValues; final T Function(int) indexToEnum; const WheelPickerWidget({ super.key, required this.value, required this.onChanged, required this.enumValues, required this.indexToEnum, }); @override Widget build(BuildContext context) { // 获取初始选中项的索引 final initialIndex = value != null ? enumValues.indexOf(value!) : 0; return ListWheelScrollView( physics: const BouncingScrollPhysics(), itemExtent: 50, diameterRatio: 0.6, squeeze: 1.8, clipBehavior: Clip.antiAlias, // 设置初始滚动位置 controller: FixedExtentScrollController(initialItem: initialIndex), onSelectedItemChanged: (index) => onChanged?.call(indexToEnum(index)), children: enumValues.map((c) => Text("$c")).toList(), ); } }
以AppTheme为例的使用示例:
WheelPickerWidget<AppTheme>( value: AppTheme.green, onChanged: (selectedTheme) { // 处理选中的主题逻辑 }, enumValues: AppTheme.values, indexToEnum: (index) => AppTheme.fromValue(index), )
问题2:创建通用适配方式
通过定义抽象接口约束枚举,避免重复传递参数,实现更简洁的复用:
步骤1:定义枚举适配接口
abstract class EnumPickerAdapter<T extends Enum> { // 枚举值列表 static List<T> get values => throw UnimplementedError(); // 根据索引转换为枚举的方法 static T fromIndex(int index) => throw UnimplementedError(); }
步骤2:让枚举实现该接口
以AppTheme为例:
enum AppTheme implements EnumPickerAdapter<AppTheme> { green(0), yellow(1), nightBlue(2); const AppTheme(this.value); final int value; @override factory AppTheme.fromValue(int v) => values.firstWhere( (x) => x.value == v, orElse: () => throw Exception("Unknown value $v"), ); // 实现接口的values静态成员 static List<AppTheme> get values => [green, yellow, nightBlue]; // 实现接口的fromIndex静态方法(若枚举value与索引一致,直接复用fromValue) static AppTheme fromIndex(int index) => fromValue(index); @override String toString() { switch (this) { case green: return i18n_Green.i18n; case yellow: return i18n_Yellow.i18n; case nightBlue: return i18n_Night_blue.i18n; } } }
步骤3:修改Widget适配接口
class WheelPickerWidget<T extends Enum & EnumPickerAdapter<T>> extends StatelessWidget { final T? value; final void Function(T)? onChanged; const WheelPickerWidget({ super.key, required this.value, required this.onChanged, }); @override Widget build(BuildContext context) { final enumValues = EnumPickerAdapter.values as List<T>; final initialIndex = value != null ? enumValues.indexOf(value!) : 0; return ListWheelScrollView( physics: const BouncingScrollPhysics(), itemExtent: 50, diameterRatio: 0.6, squeeze: 1.8, clipBehavior: Clip.antiAlias, controller: FixedExtentScrollController(initialItem: initialIndex), onSelectedItemChanged: (index) => onChanged?.call(T.fromIndex(index)), children: enumValues.map((c) => Text("$c")).toList(), ); } }
简化后的使用方式
WheelPickerWidget<AppTheme>( value: AppTheme.green, onChanged: (selectedTheme) { // 处理选中逻辑 }, )
注意:若枚举的value与索引不对应,需在fromIndex方法中单独实现索引到枚举的转换逻辑,不能直接复用fromValue。
内容的提问来源于stack exchange,提问作者Stéphane de Luca
相关产品推荐
相关产品推荐

