Reputation: 445
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?
Upvotes: 9
Views: 14412
Reputation: 790
Since StateNotifierProvider
state is immutable, you need to replace the data on CRUD operations by using state =
- which is what will trigger UI updates as per docs.
Then assign new data using state = <newData>
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.
Whilst your query does not need to update data, here is an example of how an update could be done to retain the original sort order of the list;
void updateHabit(Habit newHabit) {
List<Habit> newState = [...state];
int index = newState.indexWhere((habit) => habit.id == newHabit.id);
newState[index] = newHabit;
state = newState;
}
Upvotes: 17
Reputation: 73
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.
Upvotes: 4
Reputation: 445
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.
Upvotes: 5