Riverpod doesn't change ListView Item state inside Consumer Widget

Viewed 20

I am just starting out with Riverpod state management.

Image of my ListView: enter image description here

Let me introduce you to my Setup.

First I created a class that defines my individual items.

class CategoryItem {
  CategoryItem({required this.onTap, required this.name, required this.icon});
  final String name;
  final Icon icon;
  final Function onTap;
}

The class is then used to build my items inside my StateNotifier.

class CategoryNotifier extends StateNotifier<List<CategoryItem>> {
  CategoryNotifier()
      : super([
          CategoryItem(
              name: "sports", icon: const Icon(Icons.sports), onTap: () {}),
          CategoryItem(
              name: "drink", icon: const Icon(Icons.water), onTap: () {}),
          CategoryItem(
              name: "summer", icon: const Icon(Icons.sunny), onTap: () {}),
          CategoryItem(
              name: "home", icon: const Icon(Icons.home), onTap: () {}),
          CategoryItem(
              name: "car", icon: const Icon(Icons.car_rental), onTap: () {}),
        ]);

  String selected = "sports";

  selectCategoryItem(String name) {
    selected = name;
  }
}

After creating my State I am initialising my provider as followed.

final categoryListProvider = StateNotifierProvider((ref) {
  return CategoryNotifier();
});

Now I am ready to use my categoryListProvider inside a ConsumerWidget. The output is just a simple Listview.seperator with the elements from the CategoryNotifier.

class CategoryList extends ConsumerWidget {
  const CategoryList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // ignore: invalid_use_of_protected_member
    final watchListItem = ref.watch(categoryListProvider.notifier).state;
    final readListItem = ref.read(categoryListProvider.notifier);
    return ListView.separated(
        separatorBuilder: (BuildContext context, int index) {
          return const SizedBox(width: 16);
        },
        shrinkWrap: true,
        scrollDirection: Axis.horizontal,
        itemCount: watchListItem.length,
        itemBuilder: (context, index) {
          return InkWell(
            onTap: () {
              readListItem.selectCategoryItem(watchListItem[index].name);
            },
            child: Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8),
                color: watchListItem[index].name == readListItem.selected
                    ? Colors.blueAccent
                    : Colors.grey.withOpacity(0.2),
              ),
              child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 16.0),
                child: Center(
                  child: Row(children: [
                    watchListItem[index].icon,
                    const SizedBox(
                      width: 8,
                    ),
                    Text(watchListItem[index].name)
                  ]),
                ),
              ),
            ),
          );
        });
  }
}

If I now press on one item, it correctly changes the selected variable to the name of the index. But it doesn't change the Color. I always have to Hotrestart to take affect of the changes.

Problem: Do you know why my StateNotifier doesn't rebuild the State after triggering the function selectCategoryItem()?

1 Answers

I recommend creating another provider to store the selected value.

final selectedProvider = StateProvider<String>((ref) => 'sports');

And add selected in your build method:

bool selected = ref.watch(selectedProvider) == watchListItem[index].name

A simple way to change state our selectedProvider:

ref.read(selectedProvider.update((_) => watchListItem[index].name));

Try it should work.

Related