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

Flutter调用Firebase Functions返回数据类型不匹配问题排查

Firebase Functions返回数据的类型转换问题

问题背景

处理Firebase Functions返回数据时遇到类型不匹配错误,相关代码如下:

@override
Future<RawCollection> list({required RawDocument data}) async {
  loggy.debug('Listing deck data...');

  final result = await _firebaseFunctions.httpsCallable('listDeck')(data);
  return result.data;
}

定义的类型别名:

typedef RawDocument = Map<String, dynamic>;
typedef RawCollection = Iterable<RawDocument>;

运行时抛出错误:

Unhandled Exception: type 'List<Object?>' is not a subtype of type 'FutureOr<Iterable<Map<String, dynamic>>>'

确认Firebase Functions返回的测试数据结构符合预期:

[
  {
    "createdAt": "2022-12-15T12:26:12.258Z",
    "targetLang": "ae",
    "sourceLang": "ak",
    "cardCount": 0,
    "id": "wiXvWfxjgMBn3nAC9uQg",
    "label": "bar",
    "userId": "fwOBxD0UoA6KBxCWauVB2cM7PL9n",
    "updatedAt": "2022-12-15T12:26:12.258Z"
  },
  {
    "createdAt": "2022-12-15T11:48:46.046Z",
    "targetLang": "an",
    "sourceLang": "sq",
    "cardCount": 0,
    "id": "EC21qguc1sXME82jysJQ",
    "label": "foo",
    "userId": "fwOBxD0UoA6KBxCWauVB2cM7PL9n",
    "updatedAt": "2022-12-15T11:48:46.046Z"
  },
  {
    "createdAt": "2022-12-15T15:39:28.297377",
    "targetLang": "ar",
    "sourceLang": "sq",
    "cardCount": 0,
    "id": "oWnkt9NcFStQxnU2x893",
    "label": "que",
    "userId": "fwOBxD0UoA6KBxCWauVB2cM7PL9n",
    "updatedAt": "2022-12-15T12:39:39.714Z"
  }
]

疑问:为什么响应类型是List<Object?>而非List<Map<String, dynamic>>?

排查尝试

使用.whereType过滤

尝试用.whereType筛选符合类型的元素,但结果为空:

@override
Future<RawCollection> list({required RawDocument data}) async {
  loggy.debug('Listing deck data...');

  final result = await _firebaseFunctions.httpsCallable('listDeck')(data);
  final resData = result.data.whereType<RawDocument>(); // 结果为空
  return resData;
}

即使调用toList(),返回结果仍然为空。

环境信息

[✓] Flutter (Channel stable, 3.3.10, on Fedora Linux 36 (KDE Plasma) 6.1.7-100.fc36.x86_64, locale en_US.UTF-8)
    • Flutter version 3.3.10 on channel stable at /home/erayerdin/.local/lib/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 135454af32 (6 weeks ago), 2022-12-15 07:36:55 -0800
    • Engine revision 3316dd8728
    • Dart version 2.18.6
    • DevTools version 2.15.0

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    • Android SDK at /home/erayerdin/.sdks/android/
    • Platform android-33, build-tools 33.0.0
    • ANDROID_SDK_ROOT = /home/erayerdin/.sdks/android
    • Java binary at: /var/lib/snapd/snap/android-studio/125/android-studio/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop
    • clang version 14.0.5 (Fedora 14.0.5-2.fc36)
    • cmake version 3.25.1
    • ninja version 1.10.2
    • pkg-config version 1.8.0

[✓] Android Studio (version 2021.3)
    • Android Studio at /var/lib/snapd/snap/android-studio/125/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
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] VS Code
    • VS Code at /snap/code/current
    • Flutter extension version 3.56.0

[✓] Connected device (3 available)
    • sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64    • Android 13 (API 33) (emulator)
    • Linux (desktop)              • linux         • linux-x64      • Fedora Linux 36 (KDE Plasma) 6.1.7-100.fc36.x86_64
    • Chrome (web)                 • chrome        • web-javascript • Google Chrome 109.0.5414.119

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!

问题原因

  1. 类型推断限制:Firebase Functions的httpsCallable返回的data默认是dynamic类型,Dart无法自动推断嵌套的Map结构,因此会将顶层列表解析为List<Object?>,而非期望的List<Map<String, dynamic>>。
  2. .whereType失效原因:直接调用result.data.whereType<RawDocument>()时,由于result.data是dynamic,Dart的类型系统无法正确识别Iterable的方法,且没有先将其转换为明确的List类型,导致类型过滤逻辑无法生效。

解决方案

方案1:显式类型转换

先将result.data转换为List,再通过cast或map转换为目标类型:

@override
Future<RawCollection> list({required RawDocument data}) async {
  loggy.debug('Listing deck data...');

  final result = await _firebaseFunctions.httpsCallable('listDeck')(data);
  // 先转为List,再转换元素类型
  return (result.data as List).cast<RawDocument>();
}

如果需要更安全的转换(避免类型转换异常),可以遍历每个元素并显式转换:

@override
Future<RawCollection> list({required RawDocument data}) async {
  loggy.debug('Listing deck data...');

  final result = await _firebaseFunctions.httpsCallable('listDeck')(data);
  final list = result.data as List;
  return list.map((item) => item as RawDocument).toList();
}

方案2:使用泛型版本的httpsCallable

直接指定httpsCallable的返回类型,让Dart类型系统提前识别返回结构:

@override
Future<RawCollection> list({required RawDocument data}) async {
  loggy.debug('Listing deck data...');

  // 指定泛型类型为List<RawDocument>
  final result = await _firebaseFunctions.httpsCallable<List<RawDocument>>('listDeck')(data);
  return result.data;
}

注意:泛型版本仅做静态类型断言,运行时仍需确保Firebase返回的数据结构与指定类型一致,否则仍会抛出类型转换异常。

修复.whereType的用法

如果坚持使用.whereType,需要先将result.data转换为明确的List<dynamic>类型:

@override
Future<RawCollection> list({required RawDocument data}) async {
  loggy.debug('Listing deck data...');

  final result = await _firebaseFunctions.httpsCallable('listDeck')(data);
  final list = result.data as List<dynamic>;
  return list.whereType<RawDocument>().toList();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 13:50:42