简体   繁体   中英

Flutter/Riverpod: UI not updating on state change within a StateNotifierProvider

The UI of my app is not updating when I know for a fact the state is changing. I am using the watch method from Riverpod to handle this, but the changes don't take effect unless I do a hot reload.

I have a class HabitListStateNotifier with methods to add/remove habits from the list:

class HabitListStateNotifier extends StateNotifier<List<Habit>> {
  HabitListStateNotifier(state) : super(state ?? []);

  void startAddNewHabit(BuildContext context) {
    showModalBottomSheet(
        context: context,
        builder: (_) {
          return NewHabit();
        });
  }

  void addNewHabit(String title) {
    final newHabit = Habit(title: title);
    state.add(newHabit);
  }

  void deleteHabit(String id) {
    state.removeWhere((habit) => habit.id == id);
  }
}

And here is the provider for this:

final habitsProvider = StateNotifierProvider(
  (ref) => HabitListStateNotifier(
    [
      Habit(title: 'Example Habit'),
    ],
  ),
);

Here is how the HabitList (the part of the UI not updating) is implemented:

class HabitList extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final habitList = watch(habitsProvider.state);

    /////////////not updating/////////////
    return ListView.builder(
      shrinkWrap: true,
      scrollDirection: Axis.vertical,
      itemBuilder: (context, index) {
        return HabitCard(
          habit: habitList[index],
        );
      },
      itemCount: habitList.length,
    );
    /////////////not updating/////////////
  }
}

And finally, the HabitCard (what the HabitList is comprised of):

class HabitCard extends StatelessWidget {
  final Habit habit;

  HabitCard({@required this.habit});

  @override
  Widget build(BuildContext context) {

    /////////////function in question/////////////
    void deleteHabit() {
      context.read(habitsProvider).deleteHabit(habit.id);
    }
    /////////////function in question/////////////

    return Card(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(devHeight * 0.03),
      ),
      color: Colors.grey[350],
      elevation: 3,
      child: Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              HabitTitle(
                title: habit.title,
              ),
              Consumer(
                builder: (context, watch, child) => IconButton(
                  padding: EdgeInsets.all(8),
                  icon: Icon(Icons.delete),

                  /////////////function in question/////////////
                  onPressed: deleteHabit,
                  /////////////function in question/////////////
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

When I press the delete icon in a HabitCard , I know the Habit is being removed from the list, but the change is not reflecting in the UI. However, when I do a hot reload, it disappears as expected. What am I doing wrong here?

I don't know if this is the right way to handle things, but I figured it out. In the HabitListStateNotifier , for addNewHabit and deleteHabit , I added this line of code: to the end: state = state; and it works exactly how I want it to.

I used to solve this Issue by putting state = state; at the end, but now for some reason maybe flutter or Riverpod update, It doesn't work anymore.

Anyway this is how I managed to solve it now.

  void addNewHabit(String title) {
    List<Habit> _habits = [...state]; 
    final newHabit = Habit(title: title);
    _habits.add(newHabit);
    state = _habits ; 
  }

the explanation as I understand it. when state equals an object, in order to trigger the consumer to rebuild. state must equal a new value of that object, but updating variables of the state object itself will not work.

Hope this helps anyone.

You need to write your add & delete like this:

void addNewHabit(String title) {
    state = [ ...state, Habit(title: title)];
}

void deleteHabit(String id) {
  state = state.where((Habit habit) => habit.id != id).toList();
}

You need to exchange your old list with a new one for Riverpod to fire up.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM