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

Flutter Freezed包fromJson生成对象的runtimeType为何不符合预期?

Freezed 类反序列化后 runtimeType 不符问题

我在研究Flutter的freezed包,使用如下模型类:

import 'package:freezed_annotation/freezed_annotation.dart';
part 'ledger_summary_model.freezed.dart';
part 'ledger_summary_model.g.dart';

@freezed
class LedgerSummary with _$LedgerSummary {
  const factory LedgerSummary({
    required int ledgerId,
  }) = _LedgerSummary;
  factory LedgerSummary.fromJson(Map<String, dynamic> json) =>
      _$LedgerSummaryFromJson(json);
}

测试代码:

LedgerSummary ledgerSummary = const LedgerSummary(ledgerId: 1);
LedgerSummary ledgerSummaryAgain =
    LedgerSummary.fromJson(jsonDecode(jsonEncode(ledgerSummary)));
print(ledgerSummaryAgain.runtimeType.toString());

打印结果是_$_LedgerSummary,但我预期是LedgerSummary。

我的pubspec.yaml配置:

name: test_freeze
description: A new Flutter project.
publish_to: 'none' 
version: 1.0.0+1
environment:
  sdk: '>=3.1.0 <4.0.0'
dependencies:
  flutter:
    sdk: flutter
  freezed: ^2.4.0
  freezed_annotation: ^2.1.0
  flutter_localizations:
    sdk: flutter
  dart_mappable:
  dart_mappable_builder:
dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^2.1.0
  flutter_launcher_icons: ^0.13.1
  flutter_lints: ^2.0.1
flutter:
  uses-material-design: true

flutter doctor -v输出:

[√] Flutter (Channel stable, 3.13.1, on Microsoft Windows [Version 10.0.19044.3086], locale et-EE)
    • Flutter version 3.13.1 on channel stable at c:\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision e1e47221e8 (6 weeks ago), 2023-08-22 21:43:18 -0700
    • Engine revision b20183e040
    • Dart version 3.1.0
    • DevTools version 2.25.0

[√] Windows Version (Installed version of Windows is version 10 or higher)

[√] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at C:\Users\pavel\AppData\Local\Android\Sdk
    • Platform android-34, build-tools 34.0.0
    • ANDROID_HOME = C:\Users\pavel\AppData\Local\Android\Sdk
    • ANDROID_SDK_ROOT = C:\Users\pavel\AppData\Local\Android\Sdk
    • Java binary at: c:\Program Files\Android\Android Studio\jbr\bin\java
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-b2043.56-10027231)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.1.4)
    • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
    • Visual Studio Community 2022 version 17.1.32407.343
    • Windows 10 SDK version 10.0.19041.0

[√] Android Studio (version 2022.3)
    • Android Studio at c:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • android-studio-dir = c:\Program Files\Android\Android Studio
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-b2043.56-10027231)

[√] VS Code (version 1.82.2)
    • VS Code at C:\Users\pavel\AppData\Local\Programs\Microsoft VS Code
    • Flutter extension version 3.72.0

[√] Connected device (4 available)
    • sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64    • Android 14 (API 34) (emulator)
    • Windows (desktop)            • windows       • windows-x64    • Microsoft Windows [Version 10.0.19044.3086]
    • Chrome (web)                 • chrome        • web-javascript • Google Chrome 116.0.5845.141
    • Edge (web)                   • edge          • web-javascript • Microsoft Edge 117.0.2045.43

[√] Network resources
    • All expected network resources are available.

• No issues found!

我想实现一个包含多个freezed类的仓库,把这些类作为泛型对象交给单个滚动视图控制器。


问题原因与解决方案

这是Freezed的正常行为:LedgerSummary是抽象类,_$_LedgerSummary是Freezed自动生成的具体实现类。不管是通过构造函数还是fromJson创建实例,实际运行时类型都是生成的具体类,但实例在类型检查时会被视为LedgerSummary类型。

泛型场景处理方案

如果你的滚动视图控制器需要基于泛型区分不同Freezed类,可以通过以下方式处理:

  1. 统一基类标识
    定义一个抽象基类,让所有Freezed类实现它:

    abstract class BaseModel {}
    
    @freezed
    class LedgerSummary with _$LedgerSummary implements BaseModel {
      const factory LedgerSummary({required int ledgerId}) = _LedgerSummary;
      factory LedgerSummary.fromJson(Map<String, dynamic> json) => _$LedgerSummaryFromJson(json);
    }
    
    // 其他Freezed类同样实现BaseModel
    

    滚动视图控制器可以接受List<BaseModel>作为数据源,通过is关键字判断具体类型:

    if (item is LedgerSummary) {
      // 渲染LedgerSummary对应的UI组件
    } else if (item is OtherModel) {
      // 渲染OtherModel对应的UI组件
    }
    
  2. 密封类+when方法匹配
    如果所有模型属于同一业务体系,可以用Freezed的密封类特性,通过when方法做类型匹配:

    @freezed
    sealed class AppModel with _$AppModel {
      const factory AppModel.ledgerSummary({required int ledgerId}) = LedgerSummary;
      const factory AppModel.userInfo({required String name}) = UserInfo;
    
      factory AppModel.fromJson(Map<String, dynamic> json) => _$AppModelFromJson(json);
    }
    
    // 使用时直接通过when分发UI
    item.when(
      ledgerSummary: (ledgerId) => LedgerSummaryWidget(ledgerId: ledgerId),
      userInfo: (name) => UserInfoWidget(name: name),
    );
    
  3. 依赖类型检查而非runtimeType
    无需纠结runtimeType的字符串值,代码中用is关键字做类型判断足够可靠:

    print(ledgerSummaryAgain is LedgerSummary); // 输出true
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 18:14:59