我有两个数据模型 一个产品 b) 工作
我在 main.dart _MyAppState 类(最顶层的小部件)中创建 Stream Provider。它从 firestore 读取集合。
MultiProvider(
providers: [
StreamProvider<List<Job>?>(
create: (_) => JobsProvider.readJobs(),
initialData: [],
catchError: (_, __) => null,
)
],
根据用户角色,我重定向到另一个页面,该页面在构建函数中创建 FutureProvider
MultiProvider(
providers: [
FutureProvider<List<Product>>(
create: (_) => fetchProductsFromBase(), initialData: []),
//.. other providers
],
现在,我创建一个小部件来显示 StatefulWidget 的 State 类中结合来自 firestore 的两个提供程序的特定字段(来自作业数据模型的 int 'qty' 和来自产品数据模型的双 'dim_x' 和 'dim_y')的一些结果。
List<Product>? productList;
@override
Widget build(BuildContext context) {
productList = context.watch<List<Product>>();
print('NUM PRODUCTS = ${productList?.length}\n');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Delivery Schedule',
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.black87)),
const SizedBox(
height: 15.0,
),
Selector<List<JobDetailWithID>, List<Map<String, dynamic>>>(
selector: (BuildContext context,
List<JobDetailWithID> jobs) =>
getDeliveriesByDeliveryDate(jobs, numDaysSchedule, productList ?? []),
builder: (BuildContext context, List<Map<String, dynamic>> toBeDeliveredJobs, _) {
return Wrap(
// ... My Widget UI ... toBeDeliveredJobs used in here
);
},
),
],
);
}
List<Map<String, dynamic>> getDeliveriesByDeliveryDate(
List<JobDetailWithID> jobs, int numDaysSchedule, List<Product> productList) {
List<Map<String, dynamic>> deliveries = [];
// ... Do Some Calculations and add to the empty list
// ... Part of code includes below snippet
// areaForTheDay = {
// 'delivery_date': date.add(Duration(days: i)),
// 'target': productList.length == 0
// ? 'NA'
// : GetSomeDoubleValueFromJobs()
return deliveries;
}
问题:上面代码中的打印语句最初给出的产品列表长度为零。因此选择器
请建议如何解决此错误,以便小部件在拥有来自提供商的所有数据后构建,并且如果我正确使用提到的特定三个字段来最大程度地减少 firebase 读取。
注意:这些提供程序在许多其他小部件中使用,之前没有注意到这种行为。
我已经能够使用 Riverpod 解决我的问题,如下所示。但是,我仍然面临一些性能问题。欢迎更多合适的答案来停止重建。更多详细信息敬请期待。
解决方案:
通过 - 在我的应用程序中启用 Riverpod 将 flutter_riverpod 添加到我的 pubspec.yaml 以及文档中提到的其他必要包(https://riverpod.dev/docs/introduction/getting_started) & 用 ProviderScope 包装 MyApp
runApp(
ProviderScope(child: MyApp()),
);
将我未来的提供商转变为 Riverpod 提供商
part 'ProductsProvider.g.dart';
@riverpod
Future<List<Product>> fetchProducts(
FetchProductsRef ref, {
required String? specificProductId,
// In some part of my app I need to fetch specific product, but this field is not relevant for this problem
required String? clientUid,
}) async {
List<Product> _productList = [];
if (specificProductId != null) {
final productDataSnapshot = await FirebaseFirestore.instance
.collection('product')
.doc(specificProductId.trim())
.get();
if (productDataSnapshot.exists && productDataSnapshot.data() != null) {
_productList.add(Product.fromMap(productDataSnapshot.data()!));
}
} else if (clientUid != null) {
final productDataSnapshot = await FirebaseFirestore.instance
.collection('product')
.where('client_uid', isEqualTo: clientUid)
.get();
for (var pdt in productDataSnapshot.docs) {
_productList.add(Product.fromMap(pdt.data()));
}
} else {
final productDataSnapshot = await FirebaseFirestore.instance.collection('product').get();
for (var pdt in productDataSnapshot.docs) {
_productList.add(Product.fromMap(pdt.data()));
}
}
return _productList;
}
使用我的 StreamProvider,因为将完整的应用程序迁移到 Riverpod 需要一些时间(最终会这样做)。
MultiProvider(
providers: [
StreamProvider<List<Job>?>(
create: (_) => JobsProvider.readJobs(),
initialData: [],
catchError: (_, __) => null,
)
],
相关小部件的状态类写为 -
class _MyWidgetState extends ConsumerState<MyWidget> {
List<Product>? productList;
final double dailyCapacity = 18.5;
@override
Widget build(BuildContext context) {
final productList =
ref.watch(fetchProductsProvider(clientUid: null, specificProductId: null));
final int numDaysSchedule = 15;
print('NUM PRODUCTS = ${productList.value?.length}\n');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Delivery Schedule',
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.black87)),
const SizedBox(
height: 15.0,
),
// prov is alias for provider library
prov.Consumer<List<Job>?>(
builder: (BuildContext context, List<Job>? jobs, _) {
return switch (productList) {
AsyncError(:final error) => Center(child: Text('Error: $error')),
AsyncData(:final value) => Builder(
builder: (context) {
if (jobs != null && jobs.isNotEmpty) {
final List<Map<String, dynamic>> toBeDeliveredJobs =
getMaterialAreaByDeliveryDate(jobs, numDaysSchedule, value);
return Wrap(
//... My Widget UI, toBeDeliveredJobs used in here
);
} else
return CircularProgressIndicator();
},
),
_ => CircularProgressIndicator()
};
},
),
],
);
}
剩下要解决的问题是:这个小部件(MyWidget)是父 StatefulWidget - Scaffold 的一部分。此脚手架中的任何其他小部件重建都会触发此小部件的重建,并且我假设它不会根据我从文档中学到的概念一次又一次地从 firestore 获取数据(这将更加灾难性) - 如果我错了,请纠正我。即使关闭父脚手架页面,MyWidget 也会重建两次。请发布有关如何彻底解决该问题的更好答案。如果我错过了 Riverpod 文档中的某些内容,请原谅我,因为它有很多需要掌握的内容。