skip to Main Content

When trying to read a collection from cloud_firestore, I’m using riverpod provider.

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../dataStructs.dart';

part 'ChargesProvider.g.dart';

@riverpod
class GetCharges extends _$GetCharges {
  @override
  Future<Charges> build({required Layers layer}) async {
    final layerNumber = layer.toDBName();
    final layerDoc = 'Layers-$layerNumber';

    final chargeSnapshot =
        await FirebaseFirestore.instance.collection('charge').doc(layerDoc).get();

    final chargeMap = chargeSnapshot.data()!;

    return Charges.fromMap(chargeMap);
  }
}

The code inside above build function works, tested by writing it directly in onPressed of a button. So it gets the data without any errors.

But when called like below in onPressed of a button using ref.read the provider immediately disposes.

                    Align(
                      alignment: Alignment.centerRight,
                      child: CustomOutlinedButton(
                        text: 'Continue',
                        onPressed: () {
                                if (chosenProduct != null) {
                                  Charges charges;
                                  ref.read(GetChargesProvider(layer: chosenProduct.layers))
                                   .when(data: (chrg) {
                                    charges = chrg;
                                    print(charges);
                                    return AsyncValue.data(chrg);
                                  }, error: (err, stTrc) {
                                    print('ERROR: $err');
                                    return AsyncValue.data(defaultValue);
                                  }, loading: () {
                                    print('isLOADING');
                                    return AsyncValue.data(defaultValue);
                                  });
                                }
                              },
                      ),
                    ),

Below is the output on terminal.

Provider getChargesProvider:GetChargesProvider#53198(null) was initialized with AsyncLoading<Charges>()
isLOADING
Provider getChargesProvider:GetChargesProvider#53198(null) was disposed

I have tried saving my ref.read statement in a variable and then use it but got same results, it disposes. I am not using this provider anywhere else. Please suggest how to use the value of this provider. I’m using riverpod here because in case on the same page if this provider is required again, then it does not re-pull the data from firestore.

2

Answers


  1. Chosen as BEST ANSWER

    Below solution works for me but better solutions are welcome.

    ref.read as per docs read the current state of the provider. So, directly reading a new provider (not watched before/having no state) in a onPressed function was causing it to dispose the provider (correct me if I'm wrong). I changed my function to -

    onPressed: () async {
        if (chosenProduct != null) {
            Charges? charges;
            charges = await ref
                              .read(GetChargesProvider(layer:chosenProduct.layers).future)
                              .then((Charges chrg) {
                                       // DO ALL Operations using chrg here
            return chrg;
         });
    

    Above function works on single click of button, i.e. waits for provider to return the value and perform required operations post that. Also provider is updated as below -

    @riverpod
    class GetCharges extends _$GetCharges {
      @override
      Future<Charges> build({required Layers layer}) async {
        ref.cacheFor(Duration(minutes: 5)); // <<<<< added this
        final layerNumber = layer.toDBName();
        final layerDoc = 'Layers-$layerNumber';
    
        final chargeSnapshot =
            await FirebaseFirestore.instance.collection('charge').doc(layerDoc).get();
    
        final chargeMap = chargeSnapshot.data()!;
    
        return Charges.fromMap(chargeMap);
      }
    }
    

    Also, used below directly from riverpod docs

    extension CacheForExtension on AutoDisposeRef<Object?> {
      /// Keeps the provider alive for [duration].
      void cacheFor(Duration duration) {
        // Immediately prevent the state from getting destroyed.
        final link = keepAlive();
        // After duration has elapsed, we re-enable automatic disposal.
        final timer = Timer(duration, link.close);
    
        // Optional: when the provider is recomputed (such as with ref.watch),
        // we cancel the pending timer.
        onDispose(timer.cancel);
      }
    }
    

  2. To prevent auto dispose, call ref.keepAlive(); in Provider.build

    Or set keepAlive to true in riverpod annotation.

    @Riverpod(keepAlive: true)
    class GetCharges extends _$GetCharges {
      ...
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search