[英]Flutter/Firestore/Provider - Error shown for split second then stream displayed, how can I load stream values on startup?
I am using a Stream Provider to access Firestore data and pass it around my app.我正在使用 Stream 提供程序来访问 Firestore 数据并将其传递给我的应用程序。 The problem I am facing starts when I first run the app.
当我第一次运行应用程序时,我面临的问题就开始了。 Everything starts as normal but as I navigate to the screen where I am using the Stream values in a list view, I initially get an error before the UI rebuilds and the list items appear after a split second.
一切正常开始,但是当我导航到在列表视图中使用 Stream 值的屏幕时,我最初在 UI 重建之前收到错误,并且列表项会在一瞬间出现。 This is the error I get:
这是我得到的错误:
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building OurInboxPage(dirty, dependencies: [_InheritedProviderScope<List<InboxItem>>]):
The getter 'length' was called on null.
Receiver: null
Tried calling: length
I'm guessing this has something to do with the load time to access the values and add them to the screen?我猜这与访问值并将它们添加到屏幕的加载时间有关吗? How can I load all stream values when the app starts up to avoid this?
如何在应用启动时加载所有 stream 值以避免这种情况?
Here is my Stream code:这是我的 Stream 代码:
Stream<List<InboxItem>> get inboxitems {
return orderCollection
.where("sendTo", isEqualTo: FirebaseAuth.instance.currentUser.email)
.snapshots()
.map(
(QuerySnapshot querySnapshot) => querySnapshot.docs
.map(
(document) => InboxItem.fromFirestore(document),
)
.toList(),
);
}
I then add this to my list of Providers:然后我将其添加到我的提供者列表中:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MultiProvider(
providers: [
StreamProvider<List<InboxItem>>.value(value: OurDatabase().inboxitems),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<OurUser>(
builder: (_, user, __) {
return MaterialApp(
title: 'My App',
theme: OurTheme().buildTheme(),
home: HomepageNavigator(),
);
},
);
}
}
And finally the page I want to display the stream items:最后是我要显示 stream 项目的页面:
class OurInboxPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
List<InboxItem> inboxList = Provider.of<List<InboxItem>>(context);
return Scaffold(
body: Center(
child: ListView.builder(
itemCount: inboxList.length,
itemBuilder: (context, index) {
final InboxItem document = inboxList[index];
return Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(document.event),
Icon(Icons.arrow_forward_ios)
],
),
);
},
),
),
);
}
}
Thanks谢谢
Yeah its trying to build before the data is populated, hence the null error.是的,它试图在填充数据之前构建,因此出现 null 错误。
Wrap your ListView.builder
in a StreamBuilder
and having it show a loading indicator if there's no data.将您的
ListView.builder
包装在StreamBuilder
中,并在没有数据时显示加载指示器。
StreamBuilder<List<InboxItem>>(
stream: // your stream here
builder: (context, snapshot) {
if (snapshot.hasData) {
return // your ListView here
} else {
return CircularProgressIndicator();
}
},
);
I'm assuming your not using the latest version of provider
because the latest version requires StreamProvider
to set initialData
.我假设您没有使用最新版本的
provider
,因为最新版本需要StreamProvider
设置initialData
。
If you really want to use StreamProvider
and don't want a null
value, just set its initialData
property.如果你真的想使用
StreamProvider
并且不想要null
值,只需设置它的initialData
属性。
FROM:从:
StreamProvider<List<InboxItem>>.value(value: OurDatabase().inboxitems),
TO:至:
StreamProvider<List<InboxItem>>.value(
value: OurDatabase().inboxitems,
initialData: <InboxItem>[], // <<<<< THIS ONE
),
If you want to display some progress indicator while getter function inboxitems
is executed initially.如果您想在最初执行 getter function
inboxitems
时显示一些进度指示器。 You don't need to modify the StreamProvider
, and just add a null
checking in your OurInboxPage
widget.您无需修改
StreamProvider
,只需在您的OurInboxPage
小部件中添加一个null
检查。
class OurInboxPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final List<InboxItem>? inboxList =
Provider.of<List<InboxItem>?>(context, listen: false);
return Scaffold(
body: inboxList == null
? const CircularProgressIndicator()
: ListView.builder(
itemCount: inboxList.length,
itemBuilder: (_, __) => Container(
height: 100,
color: Colors.red,
),
),
);
}
}
There are 2 ways to solve the issue.有2种方法可以解决这个问题。
Use the progress bar while the data is loading.加载数据时使用进度条。
StreamBuilder<int>( stream: getStream(), builder: (_, snapshot) { if (snapshot.hasError) { return Text('${snapshot.error}'); } else if (snapshot.hasData) { return Text('${snapshot.data}'); } return Center(child: CircularProgressIndicator()); // <-- Use Progress bar }, )
Provide dummy data initially.最初提供虚拟数据。
StreamBuilder<int>( initialData: 0, // <-- Give dummy data stream: getStream(), builder: (_, snapshot) { if (snapshot.hasError) return Text('${snapshot.error}'); return Text('${snapshot.data}'); }, )
Here, getStream()
return Stream<int>
.在这里,
getStream()
返回Stream<int>
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.