Flutter开发:如何对Freezed生成的领域模型进行类型匹配?
Flutter Freezed类模式匹配优化方案(解决无限滚动列表多类型渲染问题)
我在Flutter应用中实现首页信息流的无限滚动列表,列表包含多种需要用不同Widget渲染的条目。使用freezed包创建领域模型,并通过API响应初始化这些模型。为区分不同模型以渲染对应Widget,编写了通用的HomeFeedItem Widget负责渲染:
class HomeFeedItem extends StatelessWidget { final dynamic item; const HomeFeedItem({Key? key, required this.item}) : super(key: key); @override Widget build(BuildContext context) { final Widget widget; switch (item.runtimeType) { case TodayDivider: widget = const TodayDivider(); case DateDivider: widget = DateDivider(date: item.date); case CheckinCTA: widget = const CheckinCTA(); case EntryPreview: widget = EntryPreview( id: item.id, title: item.title, summary: item.summary, ); default: widget = const Text('Unknown item type'); } return Column( children: [ widget, const SizedBox(height: 24), ], ); } }
问题在于,像EntryPreview这类Freezed生成的类,其runtimeType是私有类_$EntryPreviewImpl,导致上述switch匹配完全失效,所有条目都显示Text("Unknown item type")。我可以给每个模型添加返回类型的getter,但想了解更优的Freezed类模式匹配方式。
方案1:利用Freezed自带的when/map方法(推荐)
Freezed生成的类自带when或map方法,这是官方推荐的模式匹配方式,同时具备类型安全性,编译期就能检查是否覆盖所有类型。
首先将所有Feed条目抽象为一个Freezed union基类:
import 'package:freezed_annotation/freezed_annotation.dart'; part 'home_feed_item.freezed.dart'; @freezed sealed class HomeFeedItemModel with _$HomeFeedItemModel { const factory HomeFeedItemModel.todayDivider() = TodayDivider; const factory HomeFeedItemModel.dateDivider(DateTime date) = DateDivider; const factory HomeFeedItemModel.checkinCTA() = CheckinCTA; const factory HomeFeedItemModel.entryPreview({ required String id, required String title, required String summary, }) = EntryPreview; }
修改HomeFeedItem Widget,使用when方法完成匹配:
class HomeFeedItem extends StatelessWidget { final HomeFeedItemModel item; const HomeFeedItem({Key? key, required this.item}) : super(key: key); @override Widget build(BuildContext context) { final Widget widget = item.when( todayDivider: () => const TodayDivider(), dateDivider: (date) => DateDivider(date: date), checkinCTA: () => const CheckinCTA(), entryPreview: (id, title, summary) => EntryPreview( id: id, title: title, summary: summary, ), ); return Column( children: [ widget, const SizedBox(height: 24), ], ); } }
方案2:使用is关键字做类型判断
如果不想调整模型结构为union类型,可以用is运算符替代直接匹配runtimeType,它能正确识别Freezed生成的私有实现类实例:
class HomeFeedItem extends StatelessWidget { final dynamic item; const HomeFeedItem({Key? key, required this.item}) : super(key: key); @override Widget build(BuildContext context) { final Widget widget; if (item is TodayDivider) { widget = const TodayDivider(); } else if (item is DateDivider) { widget = DateDivider(date: item.date); } else if (item is CheckinCTA) { widget = const CheckinCTA(); } else if (item is EntryPreview) { widget = EntryPreview( id: item.id, title: item.title, summary: item.summary, ); } else { widget = const Text('Unknown item type'); } return Column( children: [ widget, const SizedBox(height: 24), ], ); } }
方案3:添加类型标识Getter(兼容旧代码)
如果无法修改现有模型结构,可给每个Freezed类添加自定义类型标识Getter,但这种方式类型安全性较差,仅作为兼容方案:
@freezed class EntryPreview with _$EntryPreview { const EntryPreview._(); // 必须添加私有构造函数才能定义自定义方法 const factory EntryPreview({ required String id, required String title, required String summary, }) = _EntryPreview; String get type => 'entry_preview'; }
其他模型同理添加type字段,之后在Widget中通过item.type判断类型。
内容的提问来源于stack exchange,提问作者Ole Spaarmann
相关产品推荐
相关产品推荐

