Ограничение 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 в его собственный виджет верхнего уровня...
Я надеюсь, что я имел какой-то смысл:(
Я написал код без тестирования, но я уверен, что вы можете заставить его работать