Ограничение StreamBuilder

StreamBuilder перестраивается всякий раз, когда он получает новое событие. Это вызывает проблемы, например, с навигацией (Navigator.push) потому что если новое событие получено во время навигации, то этот триггер перестроить. Попытка навигации, пока дерево виджетов еще строится, приведет к ошибке.

Невозможно предотвратить перестроение, чтобы избежать этой проблемы по мере необходимости.

Предложенный обходной путь - это, как правило, получение потока из кэша. Также: здесь и здесь

Но у этого средства не может быть списка сборки StreamBuilder, который постоянно обновляется, если также требуется обеспечить навигацию по картам в списке. Например в карточке onPressed(), Смотрите здесь.

Таким образом, чтобы обновить данные, необходимо использовать pull для обновления...

У кого-нибудь есть лучшее решение? Или команда Flutter работает над устранением этого ограничения, например, путем разрешения предотвратить восстановление, если карта была нажата пользователем?

ОБНОВИТЬ:

TL; DR Является ли pull to refresh единственным способом обновления данных, поскольку поток в StreamBuilder должен кэшироваться, чтобы предотвратить его перестройку при каждом получении нового события?

ОБНОВЛЕНИЕ 2:

Я пытаюсь реализовать данные кеша, но мой код не работает:

Stream<QuerySnapshot> infoSnapshot;

fetchSnapshot()  {
  Stream<QuerySnapshot> infoSnapshot = Firestore.instance.collection(‘info’).where(‘available’, isEqualTo: true).snapshots();
  return infoSnapshot;
}


  @override
  void initState() {
    super.initState();
  fetchSnapshot();
  }

...

child: StreamBuilder(
stream: infoSnapshot,
builder: (context, snapshot) {

if(snapshot.hasData) {
   return ListView.builder(
        itemBuilder: (context, index) =>
            build(context, snapshot.data.documents[index]),
        itemCount: snapshot.data.documents.length,
     );
  } else {
      return _emptyStateWidget();
  }

ОБНОВЛЕНИЕ 3:

У меня есть попытка использовать StreamController но не может реализовать правильно:

Stream<QuerySnapshot> infoStream;
StreamController<QuerySnapshot> infoStreamController = StreamController<QuerySnapshot>();

  @override
  void initState() {
    super.initState();

  infoStream = Firestore.instance.collection(‘info’).where(‘available’, isEqualTo: true).snapshots();
  infoStreamController.addStream(infoStream);
  }

...

child: StreamBuilder(
stream: infoStreamController.stream,
builder: (context, snapshot) {

ОБНОВЛЕНИЕ 4:

Предложение использовать _localStreamController дать ошибку:

StreamController<QuerySnapshot> _localStreamController = StreamController<QuerySnapshot>();

  @override
  void initState() {
    super.initState();

Firestore.instance.collection(‘info’).snapshots().listen((QuerySnapshot querySnapshot) {

//      if(userAdded == null) {
        _localStreamController.add(querySnapshot);
//      }

    });
...
child: StreamBuilder(
stream: _localStreamController.stream,
builder: (context, snapshot) {

Получатель "поток" был вызван на ноль.

Метод 'add' был вызван на нуль.

1 ответ

Кажется, что реальная проблема, основанная на ваших комментариях выше, состоит в том, что она вылетает после того, как вы ушли от представления, используя поток. Вы должны либо:

  • Отмените свой потоковый контроллер, когда вы уходите, чтобы он не слушал больше событий.
  • Или просто не испускайте никаких новых значений через поток после навигации. Добавьте паузу, пока не вернетесь к представлению

Обновление: добавление кода с псевдо-примером

class Widget {
  // Your local stream 
  Stream<String> _localStream;
  // Value to indicate if you have navigated away
  bool hasNavigated = false;
  ...
  void init() {
    // subscribe to the firebase stream
    firebaseStream...listen((value){
      // If this value is still false then emit the same value to the localStream
      if(!hasNavigated) {
        _localStream.add(value);
      }
    });
  }

  Widget build() {
    return StreamBuilder(
      // subscribe to the local stream NOT the firebase stream
      stream: _localStream,
      // handle the same way as you were before
      builder: (context, snapshot) {
         return YourWidgets();
      }
    );
  }
}

Попробуйте разбить все на виджеты

Выполнение запроса должно кэшировать его, даже если вы полностью закроете свое приложение (я полагаю, что кэшировать его можно только в полностью закрытом состоянии до 30 минут, но если вы остаетесь без подключения к Интернету, у вас все еще есть доступ к прошлым предыдущим кэшированным запросам из Firestore)

Попробуйте что-то вроде этого:

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Please work')),
      body: _buildStream(context),
    );
  }

  Widget _buildStream(BuildContext context) {
     return StreamBuilder(
      stream: yourFireStoreStream,
      builder: (context, snapshot) {
        if (!snapshot.hasData) return LinearProgressIndicator();

        return _buildAnotherwidget(context, snapshot.data.documents);
      },
    );
  }
  Widget _buildAnotherwidget(Buildcontext context, List<DocumentSnapshot> snaps){
    return ListView.Builder(
      itemCount: snaps.length,
      itemBuilder:(context, index) {
      ..dostuff here...display your cards etc..or build another widget to display cards
         }
     );
 }

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

При обновлении streambuilder обновляются нижние виджеты.

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

мы поместили streambuilder в его собственный виджет верхнего уровня...

Я надеюсь, что я имел какой-то смысл:(

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

Другие вопросы по тегам