Flutter/Firestore/Provider - 瞬间显示错误然后显示流,如何在启动时加载流值?
Flutter/Firestore/Provider - Error shown for split second then stream displayed, how can I load stream values on startup?
我正在使用 Stream Provider 访问 Firestore 数据并将其传递到我的应用程序。当我第一次 运行 该应用程序时,我面临的问题就开始了。一切都正常开始,但是当我导航到我在列表视图中使用 Stream 值的屏幕时,我最初在 UI 重建之前收到一个错误,列表项在一瞬间出现。这是我得到的错误:
════════ 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
我猜这与访问值并将它们添加到屏幕的加载时间有关?如何在应用程序启动时加载所有流值以避免这种情况?
这是我的流代码:
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(),
);
}
然后我将其添加到我的供应商列表中:
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(),
);
},
);
}
}
最后是我要显示流项目的页面:
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)
],
),
);
},
),
),
);
}
}
谢谢
是的,它试图在填充数据之前构建,因此出现空错误。
将您的 ListView.builder
包裹在 StreamBuilder
中,并在没有数据时显示加载指示器。
StreamBuilder<List<InboxItem>>(
stream: // your stream here
builder: (context, snapshot) {
if (snapshot.hasData) {
return // your ListView here
} else {
return CircularProgressIndicator();
}
},
);
有两种方法可以解决这个问题。
加载数据时使用进度条。
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
},
)
最初提供虚拟数据。
StreamBuilder<int>(
initialData: 0, // <-- Give dummy data
stream: getStream(),
builder: (_, snapshot) {
if (snapshot.hasError) return Text('${snapshot.error}');
return Text('${snapshot.data}');
},
)
这里,getStream()
return Stream<int>
.
我假设你没有使用最新版本的 provider
,因为最新版本需要 StreamProvider
来设置 initialData
。
如果您真的想使用 StreamProvider
而不想使用 null
值,只需设置它的 initialData
属性.
发件人:
StreamProvider<List<InboxItem>>.value(value: OurDatabase().inboxitems),
收件人:
StreamProvider<List<InboxItem>>.value(
value: OurDatabase().inboxitems,
initialData: <InboxItem>[], // <<<<< THIS ONE
),
如果您想在 getter 函数 inboxitems
最初执行时显示一些进度指示器。您不需要修改 StreamProvider
,只需添加一个 null
检查您的 OurInboxPage
小部件。
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,
),
),
);
}
}
我正在使用 Stream Provider 访问 Firestore 数据并将其传递到我的应用程序。当我第一次 运行 该应用程序时,我面临的问题就开始了。一切都正常开始,但是当我导航到我在列表视图中使用 Stream 值的屏幕时,我最初在 UI 重建之前收到一个错误,列表项在一瞬间出现。这是我得到的错误:
════════ 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
我猜这与访问值并将它们添加到屏幕的加载时间有关?如何在应用程序启动时加载所有流值以避免这种情况?
这是我的流代码:
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(),
);
}
然后我将其添加到我的供应商列表中:
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(),
);
},
);
}
}
最后是我要显示流项目的页面:
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)
],
),
);
},
),
),
);
}
}
谢谢
是的,它试图在填充数据之前构建,因此出现空错误。
将您的 ListView.builder
包裹在 StreamBuilder
中,并在没有数据时显示加载指示器。
StreamBuilder<List<InboxItem>>(
stream: // your stream here
builder: (context, snapshot) {
if (snapshot.hasData) {
return // your ListView here
} else {
return CircularProgressIndicator();
}
},
);
有两种方法可以解决这个问题。
加载数据时使用进度条。
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 }, )
最初提供虚拟数据。
StreamBuilder<int>( initialData: 0, // <-- Give dummy data stream: getStream(), builder: (_, snapshot) { if (snapshot.hasError) return Text('${snapshot.error}'); return Text('${snapshot.data}'); }, )
这里,getStream()
return Stream<int>
.
我假设你没有使用最新版本的 provider
,因为最新版本需要 StreamProvider
来设置 initialData
。
如果您真的想使用 StreamProvider
而不想使用 null
值,只需设置它的 initialData
属性.
发件人:
StreamProvider<List<InboxItem>>.value(value: OurDatabase().inboxitems),
收件人:
StreamProvider<List<InboxItem>>.value(
value: OurDatabase().inboxitems,
initialData: <InboxItem>[], // <<<<< THIS ONE
),
如果您想在 getter 函数 inboxitems
最初执行时显示一些进度指示器。您不需要修改 StreamProvider
,只需添加一个 null
检查您的 OurInboxPage
小部件。
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,
),
),
);
}
}