Flutter Streambuilder поток несогласованных данных моментального снимка FirebaseFirestore

# #firebase #flutter #dart #google-cloud-firestore

Вопрос:

Получение элементов корзины покупок мои данные моментального снимка противоречивы. Когда один товар находится в корзине, я получаю этот правильно отформатированный результат:

 {1111111111111: 1, PriceSmart: 540.0}
 

Когда в корзине два товара, а второй товар также «PriceSmart», я получаю ошибку, потому что возвращает этот результат:

 {1111111111111: 1, PriceSmart: 300.0, 5555555555555: 1}
 

и должно быть:

 {1111111111111: 1, PriceSmart: 540.0, 5555555555555: 1, PriceSmart: 300.0}
 

Это моя структура данных firebase:

Первый товар в корзине:

введите описание изображения здесь

Второй товар в корзине:

введите описание изображения здесь

В основном это объединение «продавца» (PriceSmart), когда мне нужно вернуть полные данные из каждого товара корзины, в противном случае я получаю ошибку, как только у меня в корзине более одного товара, а продавец один и тот же.

Пожалуйста, проверьте поток в моем коде и посмотрите, что не так с этой реализацией:

 class PriceUpdaterWidget extends StatefulWidget {
  const PriceUpdaterWidget({
    Key? key,
    required this.loginService,
    required this.code,
    required this.itemSubCategory,
  }) : super(key: key);
  final LoginService loginService;
  final String? code;
  final SubCategory? itemSubCategory;

  _PriceUpdaterWidgetState createState() => _PriceUpdaterWidgetState();
}

class _PriceUpdaterWidgetState extends State<PriceUpdaterWidget> {
  @override
  Widget build(BuildContext context) {
    CategorySelectionService catSelection =
        Provider.of<CategorySelectionService>(context, listen: false);

    Stream<DocumentSnapshot> priceDocStream = FirebaseFirestore.instance
        .collection('shoppers')
        .doc(widget.loginService.loggedInUserModel!.uid)
        .collection("cartItems")
        .doc(widget.code)
        .snapshots();
    return StreamBuilder<DocumentSnapshot>(
        stream: priceDocStream,
        builder:
            (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
          
          SellerNameService isSellerName =
              Provider.of<SellerNameService>(context, listen: false);

          var sellerName = isSellerName.isSellerName;

          if (snapshot.data != null) {
            return Text(
              snapshot.data![sellerName].toStringAsFixed(2),
              textAlign: TextAlign.center,
            );
          } else {
            return Text('No Data');
          }
        });
  }
}
 

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

1. Не могли бы вы быть немного более конкретными? Что вы имеете в виду, когда говорите, что получаете ошибку, какого рода? Можете ли вы привести минимальный воспроизводимый пример-простейший фрагмент кода, который работает без импорта любого другого вашего кода? Прямо сейчас я не могу запустить или протестировать это, потому что есть такие переменные, как LoginService и SelerNameService, которые могут повлиять на результат. Пожалуйста, обновите свой вопрос, чтобы включить упрощенную версию вашего кода с соответствующими частями, необходимыми для отладки вашей проблемы.

2. Кроме того, вы действительно не должны выполнять какую-либо логику или работать в своей функции сборки. Прямо сейчас вы получаете новый поток документов Firestore при каждом запуске функции сборки, что может происходить до 60 раз в секунду. Переместите эту логику в другое место, например initState , didChangeDependencies чтобы вы могли повторно использовать тот же поток, что может помочь вашей проблеме.

3. Извините, я не знаю, как я мог бы включить упрощенную версию, так как, как вы упомянули, некоторые переменные необходимы. Ошибка: ` Плохое состояние: поле не существует в documentsnapshotплатформе, соответствующий виджет, вызывающий ошибку, был StreamBuilder<DocumentSnapshot<Объект?><Объект?>> ` Я согласен с «новым потоком документов Firestore при каждом запуске функции сборки» может быть проблемой, однако, как новичок, я буду признателен, если вы укажете мне в правильном направлении, насколько «перемещение логики в другое место». Спасибо!

4. Ошибка возникает из-за того, что для возврата результата (обновленной цены) мне нужен код карты (1111111111111) и сумма (1), затем «продавец» и цена в следующем формате: {1111111111111: 1, цена: 540.0} затем, когда добавляется вторая карта, данные моментального снимка усекаются, например: {1111111111111: 1, цена: 300.0, 5555555555555: 1}

5. В этом случае вторая карта с ценой 300 отображается правильно, но первая карта возвращает ошибку «Плохое состояние: поле не существует в форме documentsnapshotплатформа, соответствующий виджет, вызывающий ошибку, был StreamBuilder<DocumentSnapshot<Объект?><Объект?>>», потому что возвращает только 5555555555555:1, без продавца и цены.

Ответ №1:

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

  • переместите Stream свою build функцию за пределы и в initState
  • обрабатывайте каждый снимок по одному за раз
  • держитесь подальше от обработки данных в виджете сборки
 class PriceUpdaterWidget extends StatefulWidget {
  final String login, code;
  const PriceUpdaterWidget(this.login, this.code);

  @override
  _PriceUpdaterWidgetState createState() => _PriceUpdaterWidgetState ();
}

class _PriceUpdaterWidgetState extends State<PriceUpdaterWidget> {
  Stream<DocumentSnapshot> priceStream;  // only one stream per widget

  @override
  void initState() {
    super.initState();
    priceStream = FirebaseFirestore.instance  // set the stream once
      .collection("shoppers")
      .doc(widget.login)
      .collection("cartItems")
      .doc(widget.code)
      .snapshots();
  }

  @override
  Widget build(BuildContext context) => StreamBuilder<DocumentSnapshot>(
    stream: priceStream,
    builder: (context, snapshot) {
      const String sellerName = "PriceSmart";
      return snapshot.data == null 
        ? const Text("No data") 
        : Text(
          snapshot.data[sellerName].toStringAsFixed(2),
          textAlign: TextAlign.center,
        );
    }
  );
}
 

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

1. Леви Лешес, реализовавший ваш код сейчас, работает лучше, потому что логика отсутствует в сборке (урок усвоен!), Однако все равно появляется та же ошибка: «Плохое состояние: поле не существует в форме DocumentSnapshotPlatform» Теперь я уверен, что проблема в моментальном снимке, ключи сворачиваются в один. Идеи???

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

3. Функция добавления цены работает нормально, я проверил, также данные в firebase верны. Завтра я продолжу ваш совет. Спасибо за помощь!