Flutterfire: [cloud_firestore_web] doc.set does not work with nested Timestamp

Created on 8 Nov 2020  Β·  10Comments  Β·  Source: FirebaseExtended/flutterfire

Bug report

Describe the bug
When I tried to doc.set an object that has a list with a Timestamp inside of it

{
    "dailyGoals": [
        {
            "goal": 20,
            "timeChanged": Timestamp.now()
        }
    ]
}

I got the following error:

Invalid argument (dartObject): Could not convert: Instance of 'DateTime'

Running through it with the debugger and at first I thought it was the codec utility trying to convert the same field twice but it looks like the codec correctly handles the data. I think the issue is here in jsify

Future<Null> set(Map<String, dynamic> data,
      [firestore_interop.SetOptions options]) {
    var jsObjectSet = (options != null)
        ? jsObject.set(jsify(data), options)
        : jsObject.set(jsify(data));
    return handleThenable(jsObjectSet);
  }

Steps to reproduce

Steps to reproduce the behavior:

  1. Create an object with an array that contains another object that contains a Timestamp
  2. Try to set that document to a document reference

Additional context


Flutter doctor

Run flutter doctor and paste the output below:

Click To Expand

[βœ“] Flutter (Channel dev, 1.24.0-3.0.pre, on Mac OS X 10.15.7 19H2 darwin-x64, locale en-US)
    β€’ Flutter version 1.24.0-3.0.pre at /Users/katzen/flutter
    β€’ Framework revision 2783f8e2e1 (2 weeks ago), 2020-10-22 09:36:06 -0700
    β€’ Engine revision defa8be2b1
    β€’ Dart version 2.11.0 (build 2.11.0-242.0.dev)

[βœ“] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    β€’ Android SDK at /Users/katzen/Library/Android/sdk
    β€’ Platform android-30, build-tools 29.0.3
    β€’ Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
    β€’ All Android licenses accepted.

[βœ“] Xcode - develop for iOS and macOS (Xcode 11.7)
    β€’ Xcode at /Applications/Xcode.app/Contents/Developer
    β€’ Xcode 11.7, Build version 11E801a
    β€’ CocoaPods version 1.10.0

[βœ“] Chrome - develop for the web
    β€’ Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[βœ“] Android Studio (version 4.0)
    β€’ Android Studio at /Applications/Android Studio.app/Contents
    β€’ Flutter plugin version 48.1.2
    β€’ Dart plugin version 193.7361
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)

[βœ“] Connected device (2 available)
    β€’ Web Server (web) β€’ web-server β€’ web-javascript β€’ Flutter Tools
    β€’ Chrome (web)     β€’ chrome     β€’ web-javascript β€’ Google Chrome 86.0.4240.183

β€’ No issues found!


Flutter dependencies

Run flutter pub deps -- --style=compact and paste the output below:

Click To Expand

- cloud_firestore 0.14.3 [flutter meta quiver firebase_core firebase_core_platform_interface cloud_firestore_platform_interface cloud_firestore_web]
- cloud_functions 0.7.0+1 [meta flutter firebase_core firebase_core_platform_interface cloud_functions_platform_interface cloud_functions_web]
- firebase_auth 0.18.3 [meta firebase_core firebase_core_platform_interface firebase_auth_platform_interface firebase_auth_web flutter]
- firebase_core 0.5.2 [firebase_core_platform_interface flutter quiver meta firebase_core_web]
- firebase_crashlytics 0.2.3 [flutter stack_trace firebase_core firebase_core_platform_interface firebase_crashlytics_platform_interface]
- firebase_image 0.2.0 [flutter firebase_core firebase_storage sqflite path path_provider]
- firebase_messaging 7.0.3 [meta flutter firebase_core]
- firebase_storage 4.0.1 [flutter firebase_core]

transitive dependencies:
- cloud_firestore_platform_interface 2.2.0 [flutter meta collection firebase_core plugin_platform_interface]
- cloud_firestore_web 0.2.1 [flutter flutter_web_plugins http_parser meta firebase_core firebase_core_web cloud_firestore_platform_interface js]
- cloud_functions_platform_interface 3.0.1 [flutter meta firebase_core plugin_platform_interface]
- cloud_functions_web 3.0.1 [firebase_core cloud_functions_platform_interface flutter flutter_web_plugins firebase http_parser meta]
- firebase 7.3.2 [http http_parser js]
- firebase_auth_platform_interface 2.1.3 [flutter meta firebase_core plugin_platform_interface]
- firebase_auth_web 0.3.2 [flutter flutter_web_plugins meta http_parser intl firebase_core firebase_core_web firebase_auth_platform_interface js]
- firebase_core_platform_interface 2.0.0 [flutter meta plugin_platform_interface quiver]
- firebase_core_web 0.2.1 [firebase_core_platform_interface flutter flutter_web_plugins meta js]
- firebase_crashlytics_platform_interface 1.1.3 [flutter meta collection firebase_core plugin_platform_interface]


web cloud_firestore bug

Most helpful comment

So either the Cloud Firestore SDK needs to do the work of converting occurrences of DateTime into Timestamp, or we need the above to continue to work, regardless of where the Timestamp may exist in the Map passed to SDK functions (e.g. it should work in nested maps).

I think it should do both! Will be looked at soon.

All 10 comments

@Nolence Just to confirm, Timestamp is from this library?

Hi, yes, the Timestamp I used was from Firestore

I should mention that the exact code I used works on android and ios but only failed when I tried it on web

After upgrading to the latest cloud_firestore (0.14.3), I'm also experiencing a new error related to the Timestamp class (from cloud_firestore_platform_interface):

Expected a value of type 'Timestamp', but got one of type 'LinkedMap<String, dynamic>'.

Downgrading all the way back to cloud_firestore 0.14.0+2 is my current workaround.

It has been my understanding since I started using Cloud Firestore and Flutter a couple of years ago that we need to use the Timestamp class from the SDK in order for the date and time to be stored properly.

However, I've never wanted to couple my code to Timestamp, favoring Dart's DateTime instead.

Thus, throughout my app I have classes like the following:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:meta/meta.dart';

@immutable
class Comment {
  final String profileId;
  final DateTime date;
  final String text;

  Comment({@required this.profileId, DateTime date, @required this.text})
      : date = date ?? DateTime.now();

  factory Comment.fromFirestore(Map json) => Comment(
        profileId: json["profileId"] as String,
        date: (json["date"] as Timestamp).toDate(),
        text: json["text"] as String,
      );

  Map<String, dynamic> toFirestore() => <String, dynamic>{
       "profileId": profileId,
       "date": Timestamp.fromDate(date),
       "text": text,
      };
//...

Comment.fromFirestore and Comment.toFirestore would be called from a Repository class, which calls DocumentSnapshot.data() or DocumentReference.set() respectively.

So either the Cloud Firestore SDK needs to do the work of converting occurrences of DateTime into Timestamp, or we need the above to continue to work, regardless of where the Timestamp may exist in the Map passed to SDK functions (e.g. it should work in nested maps).


code sample

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  FirebaseApp app = await Firebase.initializeApp();
  assert(app != null);
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Material App',
      theme: ThemeData.dark(),
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  final map = {
    "dailyGoals": [
      {"goal": 20, "timeChanged": Timestamp.now()}
    ]
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Firestore Example'),
      ),
      body: Center(
        child: BookList(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          FirebaseFirestore.instance
              .collection('test_collection')
              .doc('doc_01')
              .set(map);
        },
      ),
    );
  }
}


logs

Running "flutter pub get" in cloud_firestore_example...
Launching lib/main.dart on Chrome in debug mode...
 lib/main.dart
Debug service listening on ws://127.0.0.1:55397/ckDBZs_cAS8=/ws
Connecting to VM Service at ws://127.0.0.1:55397/ckDBZs_cAS8=/ws
{date: Timestamp(seconds=1600429568, nanoseconds=464000000), name: test book 001}
{author: John Wick, title: John Wick}
2
Error: [cloud_firestore/unknown] Invalid argument (dartObject): Could not convert: Instance of 'DateTime'
    at Object.throw_ [as throw] (http://localhost:55353/dart_sdk.js:4338:11)
    at document_reference_web.DocumentReferenceWeb.new.set (http://localhost:55353/packages/cloud_firestore_web/src/write_batch_web.dart.lib.js:615:21)
    at set.next (<anonymous>)
    at runBody (http://localhost:55353/dart_sdk.js:37922:34)
    at Object._async [as async] (http://localhost:55353/dart_sdk.js:37953:7)
    at document_reference_web.DocumentReferenceWeb.new.set (http://localhost:55353/packages/cloud_firestore_web/src/write_batch_web.dart.lib.js:610:20)
    at cloud_firestore.DocumentReference.__.set (http://localhost:55353/packages/cloud_firestore/cloud_firestore.dart.lib.js:617:31)
    at http://localhost:55353/packages/triage/main.dart.lib.js:523:100
    at ink_well._InkResponseState.new.[_handleTap] (http://localhost:55353/packages/flutter/src/material/icon_button.dart.lib.js:49870:42)
    at http://localhost:55353/packages/flutter/src/material/icon_button.dart.lib.js:49979:668
    at tap.TapGestureRecognizer.new.invokeCallback (http://localhost:55353/packages/flutter/src/gestures/recognizer.dart.lib.js:189:18)
    at tap.TapGestureRecognizer.new.handleTapUp (http://localhost:55353/packages/flutter/src/gestures/tap.dart.lib.js:395:40)
    at tap.TapGestureRecognizer.new.[_checkUp] (http://localhost:55353/packages/flutter/src/gestures/tap.dart.lib.js:201:12)
    at tap.TapGestureRecognizer.new.handlePrimaryPointer (http://localhost:55353/packages/flutter/src/gestures/tap.dart.lib.js:148:23)
    at tap.TapGestureRecognizer.new.handleEvent (http://localhost:55353/packages/flutter/src/gestures/recognizer.dart.lib.js:448:16)
    at pointer_router.PointerRouter.new.[_dispatch] (http://localhost:55353/packages/flutter/src/gestures/pointer_router.dart.lib.js:74:9)
    at http://localhost:55353/packages/flutter/src/gestures/pointer_router.dart.lib.js:109:26
    at LinkedMap.new.forEach (http://localhost:55353/dart_sdk.js:25019:11)
    at pointer_router.PointerRouter.new.[_dispatchEventToRoutes] (http://localhost:55353/packages/flutter/src/gestures/pointer_router.dart.lib.js:106:29)
    at pointer_router.PointerRouter.new.route (http://localhost:55353/packages/flutter/src/gestures/pointer_router.dart.lib.js:98:37)
    at binding$5.WidgetsFlutterBinding.new.handleEvent (http://localhost:55353/packages/flutter/src/gestures/binding.dart.lib.js:314:26)
    at binding$5.WidgetsFlutterBinding.new.dispatchEvent (http://localhost:55353/packages/flutter/src/gestures/binding.dart.lib.js:297:24)
    at binding$5.WidgetsFlutterBinding.new.dispatchEvent (http://localhost:55353/packages/flutter/src/rendering/layer.dart.lib.js:5984:13)
    at binding$5.WidgetsFlutterBinding.new.[_handlePointerEventImmediately] (http://localhost:55353/packages/flutter/src/gestures/binding.dart.lib.js:268:14)
    at binding$5.WidgetsFlutterBinding.new.handlePointerEvent (http://localhost:55353/packages/flutter/src/gestures/binding.dart.lib.js:241:43)
    at binding$5.WidgetsFlutterBinding.new.[_flushPointerEventQueue] (http://localhost:55353/packages/flutter/src/gestures/binding.dart.lib.js:230:14)
    at binding$5.WidgetsFlutterBinding.new.[_handlePointerDataPacket] (http://localhost:55353/packages/flutter/src/gestures/binding.dart.lib.js:220:65)
    at Object._invoke1 (http://localhost:55353/dart_sdk.js:176863:7)
    at _engine.EnginePlatformDispatcher.__.invokeOnPointerDataPacket (http://localhost:55353/dart_sdk.js:160582:15)
    at _engine.PointerBinding.__.[_onPointerData] (http://localhost:55353/dart_sdk.js:161207:49)
    at http://localhost:55353/dart_sdk.js:161595:26
    at http://localhost:55353/dart_sdk.js:161554:16
    at http://localhost:55353/dart_sdk.js:161307:11
{author: John Wick, title: John Wick}
1

dependencies:
  flutter:
    sdk: flutter
  cloud_firestore: ^0.14.3
  cupertino_icons: ^0.1.3
  firebase_core: ^0.5.2


flutter doctor -v

[βœ“] Flutter (Channel dev, 1.24.0-7.0.pre, on Mac OS X 10.15.7 19H15 darwin-x64,
    locale en-GB)
    β€’ Flutter version 1.24.0-7.0.pre at /Users/tahatesser/Code/flutter_dev
    β€’ Framework revision a0860f6e87 (11 days ago), 2020-10-29 20:07:34 -0700
    β€’ Engine revision 073263e39d
    β€’ Dart version 2.11.0 (build 2.11.0-260.0.dev)

[βœ“] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
    β€’ Android SDK at /Users/tahatesser/Code/sdk
    β€’ Platform android-30, build-tools 30.0.2
    β€’ ANDROID_HOME = /Users/tahatesser/Code/sdk
    β€’ Java binary at: /Applications/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    β€’ Java version OpenJDK Runtime Environment (build
      1.8.0_242-release-1644-b3-6222593)
    β€’ All Android licenses accepted.

[βœ“] Xcode - develop for iOS and macOS (Xcode 12.2)
    β€’ Xcode at /Volumes/SanDisk/Xcode-beta.app/Contents/Developer
    β€’ Xcode 12.2, Build version 12B5044c
    β€’ CocoaPods version 1.10.0

[βœ“] Chrome - develop for the web
    β€’ Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[βœ“] Android Studio (version 4.1)
    β€’ Android Studio at /Applications/Android Studio.app/Contents
    β€’ 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
      1.8.0_242-release-1644-b3-6222593)

[βœ“] VS Code (version 1.51.0)
    β€’ VS Code at /Applications/Visual Studio Code.app/Contents
    β€’ Flutter extension version 3.16.0

[βœ“] Connected device (4 available)
    β€’ Taha’s iPad (mobile) β€’ 00008020-000255113EE8402E β€’ ios            β€’ iOS
      14.2
    β€’ macOS (desktop)      β€’ macos                     β€’ darwin-x64     β€’ Mac OS
      X 10.15.7 19H15 darwin-x64
    β€’ Web Server (web)     β€’ web-server                β€’ web-javascript β€’ Flutter
      Tools
    β€’ Chrome (web)         β€’ chrome                    β€’ web-javascript β€’ Google
      Chrome 86.0.4240.183

β€’ No issues found!

So either the Cloud Firestore SDK needs to do the work of converting occurrences of DateTime into Timestamp, or we need the above to continue to work, regardless of where the Timestamp may exist in the Map passed to SDK functions (e.g. it should work in nested maps).

I think it should do both! Will be looked at soon.

@bgetsug I just ran into that similar error of

Expected a value of type 'Timestamp', but got one of type 'LinkedMap<String, dynamic>'

i'm hoping you can file that as a separate issue so it gets some attention and isn't considered fixed from this thread

@Nolence please make a new issue with replication :)

@Nolence I had to use firebase_core_web and cloud_firestore_web from git to fix all my timestamps issue (including the last one you mentioned) as i don't think the fix has been published yet.
Can you try with this?

  cloud_firestore_web:
    git:
      url: git://github.com/FirebaseExtended/flutterfire
      path: packages/cloud_firestore/cloud_firestore_web
  firebase_core_web:
    git:
      url: git://github.com/FirebaseExtended/flutterfire
      path: packages/firebase_core/firebase_core_web

Thank you @alextekartik. You're right, those overrides got rid of the issue for me.

Sorry for the trouple @Ehesp, this seems already fixed upstream

Was this page helpful?
0 / 5 - 0 ratings