0

I have a FutureProvider with Family parameters as below:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class I_Rem_Params {
  final String adminID;
  final String companyID;
  final int month;
  I_Rem_Params(
      {required this.adminID, required this.companyID, required this.month});
}

final iRemReportsFamily =
    FutureProvider.family<double, I_Rem_Params>((ref, iremparams) async {
  final db = FirebaseFirestore.instance;
  final admins = db.collection("Admins");
  final admin = admins.doc(iremparams.adminID);
  final companies = admin.collection("Companies");
  final company = companies.doc(iremparams.companyID);
  final imports = company.collection("Imports");
  final getImports = await imports.get();
  final val = getImports.docs.length.toDouble();
  print("value at provider : " + val.toString());
  return val;
});

I am printing the value before sent by FutureProvider as below:

value at provider : 2

I am getting and printing the AsyncValue at widget side as below:

AsyncValue<double> juneTotalValue = ref.watch(iRemReportsFamily(
    I_Rem_Params(
        adminID: adminID!, companyID: widget.companyID, month: 5)));

print("value at widget : " + juneTotalValue.value.toString());
print("runtimetype at widget : " + juneTotalValue.runtimeType.toString());

AsyncValue printed as below on widget side:

value at widget : null
runtimetype at widget : AsyncLoading<double>

And injecting the AsyncValue as follows:

....... children: [
                        juneTotalValue.when(
                            data: (data) => Text(data.toString()),
                            error: (error, stack) => Text(error.toString()),
                            loading: () => const CircularProgressIndicator()),
                        const SizedBox(
                          height: 38,
                        ),........

Handling of AsyncValue not display value of 2 sent by FutureProvider but only display loading widget of CircularProgressIndicator().

QUESTION: Why AsyncValue is Null despite there is a number sent by FutureProvider ?

isa
  • 398
  • 1
  • 3
  • 13
  • 2
    Do you have an Equatable or other == override on I_Rem_Params? If not, you aren't fulfilling the contract. .family stores into a Map that needs == to work for deep-equal items. – Randal Schwartz Jun 26 '22 at 13:26
  • You are right Randal. I have solved the problem with implementation of equatable package. – isa Jun 26 '22 at 14:24

2 Answers2

1

This mainly has to do with how you are creating your FutureProvider.family.

Since the .family modifier creates a new provider for each unique value that is passed to it, you need to confirm that the hashcode of the input value is the same each time you call the provider. If it's not, a new provider will be created each time. If you're using ref.watch, you can get caught in a loop:

  1. ref.watch creates FutureProvider1 (using input "1")
  2. When FutureProvider1 completes, it triggers the ref.watch line again
  3. ref.watch creates FutureProvider2 (using input "2")
  4. When FutureProvider2 completes, it triggers the ref.watch line again
  5. Repeat

This is also pointed out in the Parameter restrictions section of the Riverpod docs. In general, you should create your .family providers using a primitive data type (like int, bool, double, or string).

If you need to use something else, then you'll need to override the hashcode method of your object using a package like equatable.

class Person {
  const Person(this.name);

  final String name;

  @override
  bool operator ==(Object other) =>
    identical(this, other) ||
    other is Person &&
    runtimeType == other.runtimeType &&
    name == other.name;

  @override
  int get hashCode => name.hashCode;
}
Code on the Rocks
  • 11,488
  • 3
  • 53
  • 61
0

Randal's solution has solved the problem.

isa
  • 398
  • 1
  • 3
  • 13