Flutter Firestore массив с картами. Они работают? - PullRequest
0 голосов
/ 11 января 2020

Я не могу передавать (читать) документы, которые содержат свойства массива, такие как Карты в Firestore.

Использование Firestore с документом, содержащим массив с простым типом String, работает как положено. Легко написать (добавить с FieldValue.arrayUnion (['data1', 'data2', 'data3']) и отправить обратно с кодом вроде:

var test2 = List<String>();
    for (var item in data['items']) {
         print('The item $item');
         test2.add(item);
    }

test2 теперь можно использовать в качестве свойства моих элементов Когда я пытаюсь использовать Список, где тип элемента становится Картой в Firestore и представляет собой простой Класс, содержащий несколько Строк и свойство даты. Я могу записать их в FireStore, но не могу прочитать их обратно.

Сбой следующего кода: (нет ошибок в консоли отладки, но он не запускается)

var test2 = List<Item>();
    data['items'].forEach((item) {
      var description = item['description'];
       print('The description $description'); // <-- I don't get past this
       final x = Item.fromMap(item); // <-- so I can't even attempt this
       return test2.add(x);
     });

Мне никогда не удается вызвать мой конструктор Item.fromMap: вот еще одна попытка:

//  fails
final theItems = data['items'].map((x) {
      return Item.fromMap(x); // <-- never get here
    });

Хотя консоль отладки не говорит о наличии каких-либо проблем. Если я проверяю переменную theItems (отладчик «перебрасывает» пару строк до моего возврата) после неудачной итерации, это выглядит так:

MappedListIterable
_f:Closure
_source:List (1 item)
first:Unhandled exception:\ntype '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'\n#0      new Listing.fromMap.<anonymous closure> isEmpty:false
isNotEmpty:true
iterator:ListIterator
last:Unhandled exception:\ntype '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of 

Так случилось что-то плохое, но я понятия не имею, почему!

Кто-нибудь написал и получил свойства массива Firestore, содержащие Карты? оценили!

Подробнее: снимок экрана документа enter image description here

Вот код, который читает (передает) коллекцию

Stream<List<T>> collectionStream<T>({
    @required String path,
    @required T builder(Map<String, dynamic> data, String documentID),
    @required String userId,
    Query queryBuilder(Query query),
    int sort(T lhs, T rhs),
  }) {
    Query query = Firestore.instance.collection(path);
    if (queryBuilder != null) {
      query = queryBuilder(query);
    }
    final Stream<QuerySnapshot> snapshots = query.snapshots();
    return snapshots.map((snapshot)  {
      //print('document: path $path ${snapshot.documents[0]?.documentID}');
      final result = snapshot.documents
          .map((snapshot) => builder(snapshot.data, snapshot.documentID)) 
          .where((value) => value != null)
          .toList();

      if (sort != null) {
        result.sort(sort);
      }
      print('returning from CollectionStream');
      return result;
    });
  }

. На карте возникает проблема. Функция компоновщика разрешается следующим образом:

builder: (data, documentId) {
          return Listing.fromMap(data, documentId);
        },

Что заканчивается здесь

factory Listing.fromMap(
    Map<String, dynamic> data,
    String documentId,
  ) {
    if (data == null) {
      return null;
    }
    final theTime = (data['createdAt'] as Timestamp).toDate();
// see above code where I fail at getting at the items property/field

Вот класс элементов:

class Item {
  Item(this.description, this.imageURL, this.thumbnailURL,
      {this.status = ItemStatus.active,
      this.type = ListingType.free,
      this.price = 0,
      this.isUpdate = false,
      this.createdAt});


  final String description;
  final String imageURL;
  final String thumbnailURL;
  final ItemStatus status;
  final ListingType type;
  final int price;
  final bool isUpdate;
  DateTime createdAt;
  factory Item.fromMap(
    Map<String, dynamic> data,

  ) {
    if (data == null) {
      return null;
    }
    final theTime = (data['createdAt'] as Timestamp).toDate();
    return Item(

      data['description'],
      data['imageURL'],
      data['thumbnailURL'],
      status: _itemStatusFromString(data['status']),
      type: data['type'] == 'free' ? ListingType.free : ListingType.flatRate,
      createdAt: theTime,
    );
  }
  Map<String, dynamic> toMap() {
    // enums are for shit in Dart
    final statusString = status.toString().split('.')[1];
    final typeString = type.toString().split('.')[1];
    return {
      'description': description,
      'imageURL': imageURL,
      'thumbnailURL': thumbnailURL,
      'itemStatus': statusString,
      'price': price,
      'listingType': typeString,
      if (!isUpdate) 'createdAt': DateTime.now(),
      if (isUpdate) 'updatedAt': DateTime.now(),
    };
  }
}

Вышеуказанное никогда не вызывается для чтения (у нас sh) ... оно вызывается для записи данных.

1 Ответ

2 голосов
/ 13 января 2020

Это известная проблема с Firestore. Вот начальная тема Проблемы с флаттером Я обнаружил

Из прочтения проблем кажется, что многие используют пакет сериализатора, в котором проблема возникла. Он все еще активен ...

Вот мое решение НЕ использовать сериализатор, а просто делать это "вручную".

Мне удалось упростить проблему и выдать ошибку, которая была в состоянии Google , Ниже приводится одна страница, которая просто пишет и читает один документ. Класс A и B настолько малы, насколько это возможно.

Таким образом, код записывает один документ в Firestore, содержащий два свойства. Имя и предметы. Элементы, являющиеся списком класса B, который содержит класс A. Оформить заказ консоли Firebase. Чтение просто делает это.

Нет StreamController только консоль. Проблема в методе fromMap, где мы должны преобразовать массив объектов в Firesote в список экземпляров классов в Dart. Это не должно быть таким трудным, и как минимум это должно быть задокументировано ....

строка

var theRealItems = data['items'].map((i) => B.fromMap(i));

вызовет ошибку. И должен быть заменен на

var theItems = data['items'].map((i) {
      var z = Map<String, dynamic>.from(i);
      print(z['description']);
      return B.fromMap(z);
    }).toList();
    var theRealItems = List<B>.from(theItems);

Почему это так сложно до сих пор для меня загадка! Любой, кто совершенствует этот код: я весь в ушах.

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

class A {
  A(this.name, this.items);
  final name;
  final List<B> items;
  factory A.fromMap(
    Map<String, dynamic> data,
    String documentId,
  ) {
    if (data == null) {
      return null;
    }
    // we crash here !!!  
    var theRealItems = data['items'].map((i) => B.fromMap(i));

    // uncomment the 6 lines below
    // var theItems = data['items'].map((i) {
    //   var z = Map<String, dynamic>.from(i);
    //   print(z['description']);
    //   return B.fromMap(z);
    // }).toList();
    // var theRealItems = List<B>.from(theItems);

    return A(data['name'], theRealItems);
  }
  Map<String, dynamic> toMap() {
    var theItems = items.map((i) => i.toMap()).toList();
    return {'name': name, 'items': theItems};
  }
}

class B {
  B(this.description);
  final description;

  factory B.fromMap(
    Map<String, dynamic> data,
  ) {
    if (data == null) {
      return null;
    }
    return B(data['description']);
  }
  Map<String, dynamic> toMap() {
    return {
      'description': description,
    };
  }
}


class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              onPressed: () => _write(),
              child: Text('Write the Doc'),
            ),
            RaisedButton(
              onPressed: () => _read(),
              child: Text('Read the Doc ... check Debug Console!'),
            ),
          ],
        ),
      ),
    );
  }

  _write() async {
    try {
      var b = B('Inside B!');
      List<B> theList = List<B>();
      theList.add(b);
      var a = A('myName', theList);
      await Firestore.instance
          .collection('test')
          .document('testDoc')
          .setData(a.toMap());

      print('returning from write!');
    } catch (e) {
      print('Error ${e.toString()}');
    }
  }
   _read() async {
    try {
     var aFromFs = await Firestore.instance
          .collection('test')
          .document('testDoc')
          .get();
      var a = A.fromMap(aFromFs.data, aFromFs.documentID);
      print('the A from FireBase $a with name ${a.name} first item ${a.items.first.description}');

      print('returning from read!');
    } catch (e) {
      print('Oh no Error! ${e.toString()}');
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...