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!
问题原因
- 类型推断限制:Firebase Functions的
httpsCallable返回的data默认是dynamic类型,Dart无法自动推断嵌套的Map结构,因此会将顶层列表解析为List<Object?>,而非期望的List<Map<String, dynamic>>。 .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
相关产品推荐
相关产品推荐

