Search code examples
fluttersetstateriverpodflutter-state

Riverpod Provider.family and HookConsumerWidget not refreshing the UI


NOTA BENE: I ended up using ConsumerWidget instead of HookConsumerWidget and using exclusively Riverpod 2.0 classes.


I am having a very hard time understanding why the UI is not refreshing with Riverpod and Hook, and especially while using HookConsumerWidget and ProviderFamily, passing one argument.

Some precisions: MediaThumbnail is called by MediaThumbnailAPI with a unique key set to it, MediaThumbnailAPI is inside a gridviewbuilder (can item inside a gridviewbuilder be independantly rebuilt without rebuilding the whole gridview? I assume that it is possible).

My understanding is that bool isFavorite = ref.watch(favoriteFamilyProvider(id)).isMediaFavorite(id); is not triggering a setState (equivalent with riverpod hook) when there is a long press while it should, but I have no idea what is missing in my syntax.

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../../../favorite_provider.dart';
import 'package:universal_io/io.dart' as universal_io;

class MediaThumbnail extends HookConsumerWidget {
  final String id;
  final String fileExtension;
  final universal_io.File imageFile;
  final String urlEndpoint;
  final String description;
  final double thumbnailRadius;
  final String mimeType;

  const MediaThumbnail(this.imageFile, this.urlEndpoint, this.id,
      this.fileExtension, this.description, this.thumbnailRadius, this.mimeType,
      {Key? key})
      : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    bool isFavorite = ref.watch(favoriteFamilyProvider(id)).isMediaFavorite(id); // probably the problematic line of code.
    return GestureDetector(
        behavior: HitTestBehavior.translucent,
        onLongPress: () {

          ref.read(favoriteProvider.notifier)
              .toggleFav(id, fileExtension, description); // this one works successfully and change the content of imageMetadataBox 
          isFavorite = ref.read(favoriteFamilyProvider(id)).isMediaFavorite(id); // the isFavorite value is correctly updated
          print("is favorite: $isFavorite");
        },
       
        child: Stack(children: [
          Container(
              constraints: const BoxConstraints.expand(),
              decoration: BoxDecoration(
                image: DecorationImage(
                    image: FileImage(imageFile), fit: BoxFit.cover),
              )),
          if (isFavorite) // this does not refresh
            Positioned(
              top: 5,
              left: 60,
              right: .0,
              child: Center(
                child: Stack(
                  children: [
                    _buildIcon(16, Colors.white),
                    _buildIcon(13, Colors.pinkAccent),
                  ],
                ),
              ),
            )
        ]));
  }
}

favorite_provider.dart

final favoriteFamilyProvider =
Provider.family<FavoriteFamilyNotifier, String>(
        (_, myId) => FavoriteFamilyNotifier(myId));

class FavoriteFamilyNotifier extends StateNotifier<bool> {
  FavoriteFamilyNotifier(String myId) : super(false) {}

  bool isMediaFavorite(String myId) {
    Store store = objectbox.store;

    Box<MediaMetadata> imageMetadataBox = store.box<MediaMetadata>();

    final qBuilder =
       imageMetadataBox.query(MediaMetadata_.myId.equals(myId));
    final query = qBuilder.build();
    var results = query.find();
    bool isFavorite = results.isNotEmpty;
    print("$myId is favorite? $isFavorite");
    return isFavorite;
  }

Many thanks to those experienced with this library.

NB: I use "myId" because ObjectBox reserve the "id" keyword for its internal database, so MediaMetadata_.myId is like a 32 long String, while MediaMetadata_.id is an unsigned integer.


Solution

  • It seems your problem is that you are using Provider to implement a class that extends StateNotifier. To solve the problem, change Provider to StateNotifierProvider.

    I also want to point out that StateNotifier is a deprecated practice. Use Notifier to implement your FavoriteFamilyNotifier.

    It also seems more logical to use the myId field in the isMediaFavorite() method, which you pass as a field when creating FavoriteFamilyNotifier(myId). Or get rid of .family, because it's not at all clear what role it plays if you create a class with FavoriteFamilyNotifier(String myId) and then don't use that field at all.

    And it's not very clear why you use HookConsumerWidget, which is not used inside the widget in this example.