在小部件中使用 FutureProvider 和 StreamProvider

问题描述 投票:0回答:1

我有两个数据模型 一个产品 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 读取。

注意:这些提供程序在许多其他小部件中使用,之前没有注意到这种行为。

flutter google-cloud-firestore flutter-provider
1个回答
0
投票

我已经能够使用 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 文档中的某些内容,请原谅我,因为它有很多需要掌握的内容。

© www.soinside.com 2019 - 2024. All rights reserved.