You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为高级枚举创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 09:02:54