rrousselGit / riverpod

A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
https://riverpod.dev
MIT License
6.2k stars 948 forks source link

Cannot use "ref" after the widget was disposed #3644

Open SdxCoder opened 3 months ago

SdxCoder commented 3 months ago

Describe the bug In our production app from firebase crashlytics, we are getting this exception being logged .i.e Bad state: Cannot use "ref" after the widget was disposed.

Crash Log

  Non-fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError: Bad state: Cannot use "ref" after the widget was disposed.
       at ConsumerStatefulElement._assertNotDisposed(consumer.dart:550)
       at ConsumerStatefulElement.read(consumer.dart:619)
       at StoresMapComponent.build.<fn>(stores_map_component.dart:35)
       at _GoogleMapState.onPlatformViewCreated(google_map.dart:441)

To Reproduce We are setting google map controller on onMapCreated callback

 GoogleMap(
            onMapCreated: (controller) {
              ref
                  .read(
                    getMapControllerProvider(MapToLoad.stores).notifier,
                  )
                  .update(IMapController.googleaMaps(controller));
            },
            zoomControlsEnabled: false,
            myLocationButtonEnabled: false,
            buildingsEnabled: false,
            myLocationEnabled: true,
            markers: markers.values.map((e) => e.toGoogleMapMarker()).toSet(),
            initialCameraPosition: cameraPosition.toGoogleMapCameraPosition(),
          )

// Map Controller Provider 
final getMapControllerProvider = NotifierProvider.autoDispose
    .family<GetMapControllerNotifier, IMapController?, MapToLoad>(
  () {
    return GetMapControllerNotifier();
  },
);

class GetMapControllerNotifier
    extends AutoDisposeFamilyNotifier<IMapController?, MapToLoad> {
  @override
  IMapController? build(MapToLoad args) {
    return null;
  }

  void update(IMapController controller) {
    state = controller;
  }
}

And its being used in another provider


final mapControllerProvider = NotifierProvider.autoDispose
    .family<MapControllerNotifier, IMapController?, StoresTypeToFetch>(
  () {
    return MapControllerNotifier();
  },
);

class MapControllerNotifier
    extends AutoDisposeFamilyNotifier<IMapController?, StoresTypeToFetch> {
  @override
  IMapController? build(StoresTypeToFetch args) {
    state = ref.watch(getMapControllerProvider(MapToLoad.stores));

    ref.listen(
      selectedLocationCameraPositionProvider(args),
      fireImmediately: true,
      (previous, next) {
        if (previous != next) {
          state?.animateCamera(next);
        }
      },
    );

    return state;
  }
}

Is there any thing wrong with this approach which is causing this error? Can i get help on this ?

Expected behavior This bug shouldn't appear.

rrousselGit commented 3 months ago

The problem is in your widget, but you haven't shared your widget, so I cannot know.

SdxCoder commented 2 months ago

@rrousselGit i here is the attached widget can you have a look kindly.

class StoresMapComponent extends ConsumerWidget {
  final StoresTypeToFetch arg;
  final bool enableMyLocationButton;
  const StoresMapComponent({
    super.key,
    required this.arg,
    this.enableMyLocationButton = true,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    ref.listen(mapControllerProvider(arg), (previous, next) {});
    final cameraPosition =
        ref.watch(selectedLocationCameraPositionProvider(arg));
    final markers = ref.watch(addMarkersProvider(arg));
    final isPlayServicesAvailable =
        ref.watch(googleServicesProvider).valueOrNull;

    return Stack(
      children: [
        if (isPlayServicesAvailable == null)
          const Offstage()
        else if (isPlayServicesAvailable)
          GoogleMap(
            onMapCreated: (controller) {
              ref
                  .read(
                    getMapControllerProvider(MapToLoad.stores).notifier,
                  )
                  .update(IMapController.googleaMaps(controller));
            },
            zoomControlsEnabled: false,
            myLocationButtonEnabled: false,
            buildingsEnabled: false,
            myLocationEnabled: true,
            markers: markers.values.map((e) => e.toGoogleMapMarker()).toSet(),
            initialCameraPosition: cameraPosition.toGoogleMapCameraPosition(),
          )
        else
          HuaweiMap(
            onMapCreated: (controller) {
              ref
                  .read(
                    getMapControllerProvider(MapToLoad.stores).notifier,
                  )
                  .update(IMapController.huaweiMaps(controller));
            },
            zoomControlsEnabled: false,
            myLocationButtonEnabled: false,
            buildingsEnabled: false,
            myLocationEnabled: true,
            markers: markers.values.map((e) => e.toHuaweiMapMarker()).toSet(),
            initialCameraPosition: cameraPosition.toHuaweiMapCameraPosition(),
          ),
        if (enableMyLocationButton)
          Align(
            alignment: Alignment.bottomRight,
            child: Container(
              margin: const EdgeInsets.all(Dimens.padding16),
              decoration: const BoxDecoration(
                shape: BoxShape.circle,
                boxShadow: [
                  BoxShadow(
                    color: Color(0x14000000),
                    blurRadius: 10,
                  ),
                ],
              ),
              child: FloatingActionButtonWidget(
                key: UniqueKey(),
                svg: AssetNames.loactionCenterIcon,
                color: AppTheme.white,
                svgColor: AppTheme.darkGray,
                elevation: 0,
                onPressed: () {
                  ref
                      .read(shouldSetCurrentLocationProvider.notifier)
                      .update(true);
                  ref
                      .read(currentLocationProvider.notifier)
                      .getCurrentLocation();
                },
              ),
            ),
          ),
      ],
    );
  }
}
SdxCoder commented 2 months ago

@rrousselGit Can you kindly have a look at the widget and share ur thoughts kindly.