Flutter возвращает полные массивы из Firebase Cloud Firestore

#firebase #flutter #google-cloud-firestore

#firebase #flutter #google-облако-firestore

Вопрос:

ПОСЛЕДНЯЯ ПРАВКА 2 сентября:

У меня не получается с этим разобраться, даже с вознаграждением, поэтому я попытаюсь задать более простой и конкретный вопрос.

Поэтому я реорганизовал базу данных в соответствии с приведенными ниже предложениями Дуга, поскольку иным образом я не могу каким-либо образом ссылаться на массивы в firebase. Итак, теперь у меня есть карта массивов, а не просто массивы. Вот так:

 ObjectsList  >  CarsMap (Map)
                   - sh899873jsa (Array)
                     0  "Toyota"
                     1  "Supra"
                     2  "1996"
                     3  "$4990"

                   - hasd823j399 (Array)
                     0  "Toyota"
                     1  "Corolla"
                     2  "2014"
                     3  "$11990"

                   - nelaoiwi283 (Array)
                     0  "Ford"
                     1  "Territory"
                     2  "2018"
                     3  "$35000"
  

Но я не знаю, как на самом деле использовать эту структуру, так как я никогда не видел этого раньше. Сейчас я получаю первую ошибку с кодом, предоставленным мне Фрэнком в его ответе ниже, который я преобразовал в:

  final DocumentReference documents = await Firestore.instance.collection('ObjectsList');
  DocumentSnapshot snapshot = await documents.get();
  Map<String, dynamic> data = snapshot.data;
  var loadCarItems = [];

  data.forEach((k,v) => {
    values = List<String>.from(v as List<String>),
    print(values),
    if (values[0] == "Toyota") {
      loadCarItems.add(values[0]),
    },
  });

  setState(() {
    CarItemsArray = loadCarItems;
  });
  

Но поскольку я перешел на карту> структура массива, я получаю сообщение об ошибке в этой строке:

 data.forEach((k,v) => {
  values = List<String>.from(v as List<String>),
  

Ошибка заключается в:

 Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<String>' in type cast
  

Совершенно очевидно, что мне нужно изменить этот синтаксис теперь, когда структура изменилась, но я понятия не имею, как, и не могу ничего найти в Интернете.

Предыдущая информация:

Я пытаюсь разработать способ возврата целых массивов из Firebase, чтобы затем я мог обрабатывать данные внутри.

Например, у меня есть документ в базе данных, который содержит массивы, например, так:

 ObjectsList  >  sh899873jsa
                   0  "Toyota"
                   1  "Supra"
                   2  "1996"
                   3  "$4990"

                 hasd823j399
                   0  "Toyota"
                   1  "Corolla"
                   2  "2014"
                   3  "$11990"

                  nelaoiwi283
                   0  "Ford"
                   1  "Territory"
                   2  "2018"
                   3  "$35000"
  

So for each array, I have generated a random key on creation, which is not important. I basically just need to be able to return all the data as separate objects. Ideally, I would like to be able to return «All Toyotas», for example. That’s the end game here.

Вот код, который я сгенерировал на данный момент на основе предложений Фрэнка ниже, который вывел меня на правильный путь.

Из файла сборки:

           Container(
            child: StreamBuilder(
              stream: Firestore.instance.collection('cars').document('ObjectsList').snapshots(),
              builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
                if (!snapshot.hasData) {
                  return LoadingAnimationBasic();
                }
                if (snapshot.data == null) {
                  return LoadingAnimationBasic();
                } else {
                  return ListView(
                    shrinkWrap: true,
                    children: _buildListCards(snapshot),
                  );
                }
              },
            ),
          ),
  

Функция _buildListCards, упрощенная, чтобы вы могли видеть, как она работает:

 _buildStoresList(AsyncSnapshot<DocumentSnapshot> snapshot) {
return snapshot.data.data.values
    .map((doc) => doc[0] == "Toyota" ? GestureDetector(
  child: Container(
    width: MediaQuery.of(context).size.width,
    child: Card(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(0.0),
      ),
      color: Colors.white70,
      elevation: 10,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(2.0),
            child: ConstrainedBox(
              constraints: BoxConstraints(
                maxWidth: 120,
                minWidth: 120,
                maxHeight: 100,
                minHeight: 100,
              ),
              child: Image.network(
                'some toyota picture URL',
                fit: BoxFit.cover,
              ),
            ),
          ),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Container(
                width: MediaQuery.of(context).size.width * 0.5,
                child: Padding(
                  padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
                  child: Text(
                    doc[1],
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                      fontSize: 18,
                    ),
                  ),
                ),
              ),
              Container(
                width: MediaQuery.of(context).size.width * 0.5,
                child: Padding(
                  padding: const EdgeInsets.fromLTRB(5, 10, 0, 0),
                  child: Text(
                    doc[2],
                    style: TextStyle(
                      fontSize: 12,
                    ),
                  ),
                ),
              ),
            ],
          ),
          Column(
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.fromLTRB(5, 40, 0, 0),
                child: Text(
                  doc[3],
                  style: TextStyle(
                    fontSize: 14,
                  ),
                ),
              ),
            ],
          ),
        ],
      ),
    ),
  ),
  onTap: () {
    futureTapHandlerHere();
  },
) : SizedBox(), )
    .toList();
}
  

Итак, единственное, что остается сейчас, — это иметь возможность редактировать / удалять эти записи из базы данных, и я думаю, что это должно зависеть от уникального идентификатора, сгенерированного при их создании. Я не вижу, как еще можно выполнить эту функциональность без идентификатора, но я не знаю, как на самом деле его использовать или вернуть из базы данных.

Комментарии:

1. snapshot.data это будет объект Map, свойства и значения которого соответствуют полям документа. Поскольку вы произвели рандомизацию имен полей, вам придется повторить свойства карты, изучить их и извлечь те, которые вы хотите. Похоже, что использование случайных имен полей усложняет вашу задачу больше, чем необходимо.

2. Я не могу придумать альтернативы этому, Дуг. Пользователи могут заполнять данные в этой таблице неизвестным и неограниченным объемом данных. Поскольку база данных Firebase должна иметь имя поля для каждого массива, что еще я могу использовать в качестве имен полей?

3. Вместо этого вы могли бы использовать одно поле и вложить в него карты случайных идентификаторов. Затем вы можете получить доступ к этому единственному полю и снова выполнить итерацию вложенных полей карты, чтобы точно знать, какое из вложенных полей было введено пользователем (в отличие от других полей с разными значениями).

Ответ №1:

Вызов snapshot.data() возвращает вам Map<String, dynamic> . Вы можете перебирать записи в этой карте, а затем получать первое дочернее значение каждого (массива).

Итак, что-то вроде этого:

Список.из (событие.снимок.значение в виде списка)

 final DocumentReference documents = await Firestore.instance.collection('cars').document('ObjectsList');
DocumentSnapshot snapshot = await documents.get();
Map<String, dynamic> data = snapshot.data();
var cars = [];
data.forEach((k,v) => {
  var values = List<String>.from(v as List<dynamic>);
  cars.add(values[0]);
})

setState(() {
  arrayOfCars = cars
});
  

Комментарии:

1. Большое тебе спасибо, Фрэнк. В строке: Map<String, dynamic> data = snapshot.data(); Я получаю сообщение об ошибке: «Выражение не вычисляется для функции, поэтому его нельзя вызвать» в snapshot.data()

2. О, кажется, вам просто нужно убрать круглые скобки, чтобы строка стала: Map<String, dynamic> data = snapshot.data; это была удачная догадка!

3. Хорошо, также эта строка: data.forEach((k, v) => { должно быть data.forEach((k, v) => () { и затем закройте функцию: }) становится }); После всего этого теперь она компилируется, но, к сожалению, не возвращает вообще никаких данных. Когда я печатаю arrayOfCars, каждый раз он просто пустой. Так что не совсем уверен, где что-то идет не так.

4. Хорошо, я кое-чего добился в вашем коде, но мне приходится немного менять. Подозреваю, что вы, вероятно, написали это навскидку, что впечатляет. Просто пытаюсь разобраться в этом, после чего опубликую обновление.

5. Спасибо за проработку этого, Бисклаврет. Если вы обнаружите подобные проблемы, вы можете просто отредактировать ответ, чтобы исправить их. Единственный, который вызывает большой сюрприз, — это первый, поскольку я всегда использовал data() для доступа к данным из моментального снимка, и вся документация предполагает, что это все еще так: pub.dev/documentation/cloud_firestore/latest/cloud_firestore / …

Ответ №2:

На этот вопрос не было ответа. Я все еще борюсь и пока что потерял 11 дней производственного времени на это.

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

Ответ №3:

Это не было решено, выполнив это в формате, указанном в вопросе, и я пробовал это таким образом, поскольку другой пользователь сказал мне, что я не смогу удалить отдельные массивы, если они не находятся под картой.

Ответ здесь заключается в том, что вышесказанное неверно, и решение состояло в том, чтобы полностью отказаться от карты и просто иметь массивы. Что я сделал здесь, поскольку для меня это имеет смысл, так это сделать так, чтобы первым объектом в массиве был уникальный код, чтобы я мог легко ссылаться на него, вот так:

 ObjectsList  >  
(document)      - sh899873jsa (Array)
                 0  "sh899873jsa" 
                 1  "Toyota"
                 2  "Supra"
                 3  "1996"
                 4  "$4990"

               - hasd823j399 (Array)
                 0  "hasd823j399"
                 1  "Toyota"
                 2  "Corolla"
                 3  "2014"
                 4  "$11990"

               - nelaoiwi283 (Array)
                 0  "nelaoiwi283"
                 1  "Ford"
                 2  "Territory"
                 3  "2018"
                 4  "$35000"
  

Затем на них можно ссылаться напрямую и легко удалять:

 _deleteMenuItem() async {
  await deleteSelectedImages();
  DocumentReference documentReference = Firestore.instance.collection('data').document('ObjectsList');
  documentReference.updateData({
    CarsMap[selectedItem][0]: FieldValue.delete(),
  });
}
  

Где CarsMap — это загруженный массив объектов car во время выполнения, а SelectedItem — это элемент, выбранный в пользовательском интерфейсе из ListTiles. Итак, шаг 1 состоял в том, чтобы захватить все данные из Firebase и заполнить их в массив CarsMap. Шагом 2 было создание ListTiles из этого массива. Шаг 3 состоял в том, чтобы создать метод onTap, который выбирает текущий ListTile, а затем сохраняет индекс этого тайла в SelectedItem.

Это делает то, что мне нужно, и теперь пользователи могут заполнять и удалять массивы из моей базы данных по мере необходимости.