flutterriverpodrebuild

screen rebuild not happening on state changes in flutter and riverpod


I am using Riverpod 2.x for state management in Flutter shopping app. CartItem qty is not changing until after a screen switch or a manual action that causes a screen refresh. The same is true when adding a product to the cart screen, the visual checkmark showing that item is added is not shown. However, when I switch screen that rebuilds the widget, the correct and expected actions are displayed. I have set up providers with state notifiers to ensure instant change/rebuild but no luck. Here are the relevant code snippets:

updateItemQuantity() and addToCart():

void updateItemQuantity(String pId, int newQuantity, WidgetRef ref) {
if (cartItems.containsKey(pId)) {
  final updatedQuantity = newQuantity;
  // print('Updated Qty: $updatedQuantity');
  if (updatedQuantity >= 1) {
    state.update(
      pId,
      ((value) => Cart(
            cartId: value.cartId,
            imageUrl: value.imageUrl,
            title: value.title,
            price: value.price,
            quantity: updatedQuantity,
          )),
    );
  } else {
    state.remove(pId);
  }
  // state = {...state};
  // ref.watch(cartProvider);
  ref.read(cartProvider.notifier).state = {...state};
}

}

void addToCart(
      String pId, String imageUrl, double price, String title, WidgetRef ref) {
    if (cartItems.containsKey(pId)) {
      state.update(
        pId,
        ((value) => Cart(
              cartId: value.cartId,
              imageUrl: value.imageUrl,
              title: value.title,
              price: value.price,
              quantity: value.quantity + 1,
            )),
      );
    } else {
      state.putIfAbsent(
        pId,
        () => Cart(
          cartId: DateTime.now().toIso8601String(),
          imageUrl: imageUrl,
          title: title,
          price: price,
          quantity: 1,
        ),
      );
    }
    // state = {...state};
    // ref.watch(cartProvider);
    ref.read(cartProvider.notifier).state = {...state};
  }

cartController:

final cartProvider = StateNotifierProvider<CartListNotifier, Map<String, Cart>>(
  (ref) => CartListNotifier(),
);

class CartListNotifier extends StateNotifier<Map<String, Cart>> {
  CartListNotifier() : super(<String, Cart>{});

  Map<String, Cart> get cartItems => {...state};

  double get totalAmount {
    double total = 0.0;
    state.forEach((key, value) {
      total += value.quantity * value.price;
    });
    return total;
  }
TextButton(
                                // onPressed: productAttribute.quantity < 2
                                onPressed: cartItem?.quantity == null ||
                                        cartItem?.quantity == 1
                                    ? () {}
                                    : () {
                                        ref
                                            .read(cartProvider.notifier)
                                            .updateItemQuantity(
                                                productAttribute.id,
                                                cartItem!.quantity - 1,
                                                ref);
                                        
                                      },
                                child: Text(
                                  '-',
                                  style: TextStyle(
                                    fontSize:
                                        Device.screenType == ScreenType.mobile
                                            ? 18.sp
                                            : 20.sp,
                                  ),
                                ),
                              ),
                              Text(
                                cartItem?.quantity.toString() ?? '1',
                               
                                style: TextStyle(
                                  fontSize:
                                      Device.screenType == ScreenType.mobile
                                          ? 18.sp
                                          : 20.sp,
                                ),
                              ),
                              TextButton(
                                onPressed: () {
                                  ref
                                      .read(cartProvider.notifier)
                                      .updateItemQuantity(productAttribute.id,
                                          (cartItem?.quantity ?? 0) + 1, ref
                                          // productAttribute.quantity + 1,
                                          );
                                  
                                },
                                // },
                                child: Text(
                                  '+',
                                  style: TextStyle(
                                    fontSize:
                                        Device.screenType == ScreenType.mobile
                                            ? 18.sp
                                            : 20.sp,
                                  ),
                                ),
                              ),


    @override
  Widget build(BuildContext context, WidgetRef ref) {
    final productAttribute = ref.watch(productProvider.notifier).getById(pId);
    final cartList = ref.watch(cartProvider.notifier);
    final cartItem = cartList.cartItems[productAttribute.id];
    double subTotal = cartItem!.quantity * productAttribute.price;

Solution

  • After weeks of research, review and refactoring, I finally stumbled upon the answer to my question. It was in an obvious line that I totally missed. State changes are now working as expected. I made changes to the variable initialization to simply watch for state changes without applying a notifier to it. E.g. final catList = ref.watch(cartProvider); and not final cartList = ref.watch(cartProvider.notifier); Understanding the difference between ref.watch and ref.read is crucial when working with Riverpod. Remember to use ref.watch to listen to state changes and rebuild the widget, and ref.read to access the state or call methods on the notifier without causing a rebuild.