flutterdartflutter-freezed

How to pattern match the runtimeType of a class?


I am implementing an infinite scroll list for a home feed in a Flutter app. The list contains different items that are rendered with different widgets. I use the freezed package to create domain models that I initialize with the response from an API. Now I need some way to differentiate the models to render the correct widget. I decided to create a general HomeFeedItem widget, that does the rendering.

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),
      ],
    );
  }
}

The issue is that for example of EntryPreview the runtimeType is _$EntryPreviewImpl, which is private. So this approach fails and every item is Text("Unknown item type")

I could add a getter to each model that returns a type. But I was wondering if there isn't a better way to pattern match the type of a freezed class.


Solution

  • Doing:

    switch (item.runtimeType) {
      case TodayDivider:
        ...
    

    uses a constant pattern that compares the Type object returned by item.runtimeType for equality. Equality for Type objects is object identity; they compare equal only to themselves regardless of relationships between types.

    You instead should use a variable pattern that matches item and not item.runtimeType:

        switch (item) {
          case TodayDivider _:
            widget = const TodayDivider();
          case DateDivider item:
            widget = DateDivider(date: item.date);
          case CheckinCTA _:
            widget = const CheckinCTA();
          case EntryPreview item:
            widget = EntryPreview(
              id: item.id,
              title: item.title,
              summary: item.summary,
            );
          default:
            widget = const Text('Unknown item type');
        }