如何优化使用不同Moor DAO的重复Flutter泛型页面代码?
现有代码的核心问题
- 硬编码类型判断违反开闭原则:新增DAO必须修改
getDao()方法的判断逻辑,维护成本高 - 类型判断可靠性差:
runtimeType是严格匹配,如果DAO有子类实现会直接匹配失败 - 泛型完全未生效:定义了泛型T但没有加任何约束,也没有利用泛型做类型推导,反而用运行时判断绕开了泛型能力
- 冗余代码:State中重复存储dao实例,完全可以直接读取
widget.dao使用
优化方案
第一步:抽象公共DAO接口
因为三个DAO都具备list()公共行为,首先定义统一的抽象接口约束行为:
// 抽象公共DAO接口,E是列表返回的元素类型 abstract class BaseDao<E> { // 你可以根据实际返回值修改为Stream<List<E>>(Moor常用)或者Future<List<E>> List<E> list(); } // 三个现有DAO分别实现该接口 class FirstDao implements BaseDao<FirstType> { @override List<FirstType> list() { // 原有实现 } } class SecondDao implements BaseDao<SecondType> { @override List<SecondType> list() { // 原有实现 } } class ThirdDao implements BaseDao<ThirdType> { @override List<ThirdType> list() { // 原有实现 } }
第二步:优化通用Widget的泛型约束
直接通过泛型约束DAO的类型,完全删掉类型判断逻辑:
// E是列表元素类型,T是实现了BaseDao<E>的DAO类型 class RollingListChoice<E, T extends BaseDao<E>> extends StatefulWidget { final T dao; const RollingListChoice({super.key, required this.dao}); @override State<RollingListChoice<E, T>> createState() => _RollingListChoiceState<E, T>(); } class _RollingListChoiceState<E, T extends BaseDao<E>> extends State<RollingListChoice<E, T>> { @override Widget build(BuildContext context) { // 直接调用dao的list方法,不需要任何类型转换 final list = widget.dao.list(); // 这里可以替换成你原来的列表UI逻辑,类型安全 return ListView.builder( itemCount: list.length, itemBuilder: (context, index) => Text(list[index].toString()), ); } }
第三步:使用方式
// 分别传入不同DAO即可生成对应页面 RollingListChoice(dao: FirstDao()) RollingListChoice(dao: SecondDao()) RollingListChoice(dao: ThirdDao())
这类场景的最佳实践
- 优先用抽象接口约束公共行为,所有同类实现都遵循接口规范,完全避免运行时类型判断和强转
- 充分利用Dart泛型的类型约束能力,把类型检查提前到编译期,避免运行时类型错误
- 遵循开闭原则,后续新增同类型DAO页面,只需要实现
BaseDao接口,不需要修改通用Widget的任何代码 - 如果是Moor场景,
list()方法通常返回的是Stream,直接在通用Widget里加StreamBuilder处理异步加载、 loading、空状态逻辑即可,所有页面可以复用这套逻辑。
内容的提问来源于stack exchange,提问作者stacktrace2234
相关产品推荐
相关产品推荐

