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.
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.