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

如何结合Flutter与Riverpod实现策略模式及算法注册方案?

如何在Riverpod中优雅注册策略模式的算法实现

我在Flutter应用里有个页面负责加载指定主题的数据,主题里包含用户交互时要用的算法标识。随着算法数量增加,用策略模式来根据标识选择对应实现很合适。现在用Riverpod做状态管理,已经写了初步的Provider代码:

abstract interface class TestAlgorithm {
  String foo();
}

@riverpod
class AlgorithmMap extends _$AlgorithmMap {
  @override
  Map<String, TestAlgorithm> build() {
    return {}; // 这里不知道怎么填充
  }

  // 是不是可以这么做?
  void registerAlgorithm(String algorithmKey, TestAlgorithm algorithm) {
    final nextState = {
      ...state
    };

    nextState[algorithmKey] = algorithm;
    state = nextState;
  }
}

@riverpod
TestAlgorithm? algorithm(AlgorithmRef ref, String algorithmKey) {
  final algorithmMap = ref.watch(algorithmMapProvider);
  return algorithmMap[algorithmKey];
}

组件可以通过监听algorithmProvider调用算法的foo方法,但我纠结怎么注册这些算法,目前有两个思路:

  • 在AlgorithmMap的build方法里手动构建映射:简单直接,但加新算法就得改Provider,违反开闭原则;
  • 用自定义注解+dart:mirrors扫描注册:能动态生成列表,不用改Provider,但会带来性能损耗和包体积变大的问题。

我想知道有没有注解生成器自动生成注册代码的方案?新增算法时跑生成器就能更新列表,兼顾两种方案的优点又避开缺点。或者还有其他更优的实现思路?


最优方案:用注解生成器自动生成注册代码

这完全可行,而且是兼顾开闭原则和性能的最佳方案——核心是用build_runner+source_gen在编译期生成注册代码,完全不会有运行时反射的开销。

具体实现步骤

  1. 定义自定义注解
    先创建一个注解类,用来标记需要注册的算法实现:
class AlgorithmKey {
  final String key;
  const AlgorithmKey(this.key);
}
  1. 给算法实现添加注解
    每个算法实现类加上这个注解,指定对应的标识:
@AlgorithmKey("algorithm_a")
class AlgorithmA implements TestAlgorithm {
  @override
  String foo() => "Result from Algorithm A";
}

@AlgorithmKey("algorithm_b")
class AlgorithmB implements TestAlgorithm {
  @override
  String foo() => "Result from Algorithm B";
}
  1. 编写代码生成器
    创建一个生成器类,扫描所有带AlgorithmKey注解的类,生成对应的注册代码:
class AlgorithmGenerator extends GeneratorForAnnotation<AlgorithmKey> {
  @override
  FutureOr<String> generate(BuildStep buildStep) async {
    final entries = <String>[];
    await for (final library in buildStep.findLibraryAssets()) {
      final libraryElement = await buildStep.resolver.libraryFor(library);
      for (final element in libraryElement.topLevelElements) {
        if (element is ClassElement) {
          final annotation = element.metadata.firstWhere(
            (m) => m.isConstant && m.type.name == "AlgorithmKey",
            orElse: () => null,
          );
          if (annotation != null) {
            final reader = ConstantReader(annotation.constantValue);
            final key = reader.read("key").stringValue;
            entries.add("    '$key': ${element.name}(),");
          }
        }
      }
    }
    // 生成完整的映射表代码
    return """
// 自动生成的算法注册代码,请勿手动修改
import 'package:your_app/algorithm_interface.dart';
import 'package:your_app/algorithms.dart';

Map<String, TestAlgorithm> generateAlgorithmMap() {
  return {
${entries.join("\n")}
  };
}
""";
  }
}
  1. 配置build_runner
    在pubspec.yaml里添加依赖:
dependencies:
  source_gen: ^1.4.0
  riverpod: ^2.3.0
  # 其他业务依赖

dev_dependencies:
  build_runner: ^2.4.0
  # 其他开发依赖

再创建build.yaml配置生成器:

targets:
  $default:
    builders:
      your_app|algorithm_generator:
        enabled: true
  1. 生成代码并在Riverpod中使用
    运行flutter pub run build_runner build生成generate_algorithm_map.dart,然后修改AlgorithmMap的build方法:
@riverpod
class AlgorithmMap extends _$AlgorithmMap {
  @override
  Map<String, TestAlgorithm> build() {
    // 使用自动生成的映射表
    return generateAlgorithmMap();
  }

  // 保留这个方法用于特殊场景的动态注册(可选)
  void registerAlgorithm(String algorithmKey, TestAlgorithm algorithm) {
    state = {...state, algorithmKey: algorithm};
  }
}

其他替代思路

  • 模块式注册:如果不想用代码生成,可以在每个算法文件里写一个register函数,然后在应用启动时(比如main函数里)批量调用这些函数,给AlgorithmMap注册。这种方式比手动写映射表灵活,也没有反射开销,但需要手动维护调用列表。
  • Riverpod初始化Provider:可以创建一个单独的Provider来初始化所有算法,在build里实例化所有算法并构建映射表,本质是把注册逻辑移到了单独的Provider里,比直接在AlgorithmMap里写映射更清晰,但还是需要手动维护实例列表。

内容的提问来源于stack exchange,提问作者Dubl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 03:17:25