i’m using state notifier and state notifier provider with select to only apply rebuild to specific field in the object. but the whole widget rebuild whether i selected or not.
i have the following example code to simplify my problem:
final counterProvider =
StateNotifierProvider<CounterState, Counter>((ref) => CounterState());
class Counter {
int count1;
int count2;
Counter(this.count1, this.count2);
}
class CounterState extends StateNotifier<Counter> {
CounterState() : super(Counter(0, 0));
void inc1() => state = Counter(state.count1 + 1, state.count2);
void inc2() => state = Counter(state.count1, state.count2 + 1);
}
and the following consumer widget:
class TestWidget extends ConsumerWidget {
const TestWidget({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) => Scaffold(
body: Column(
children: [
Text(ref.watch(counterProvider.select((value) {
print("rebuilt counter 1 Text with val: ${value.count1}");
return value.count1.toString();
}))),
Text(ref.watch(counterProvider.select((value) {
print("rebuilt Counter 2 Text with val: ${value.count2}");
return value.count2.toString();
}))),
ElevatedButton(
onPressed: () => ref.read(counterProvider.notifier).inc1(),
child: const Text("Inc 1")),
ElevatedButton(
onPressed: () => ref.read(counterProvider.notifier).inc2(),
child: const Text("Inc 2"))
],
));
}
i’m excpecting when pressing the inc1()
button to not rebuild the second text. only the first.
but the output in the console when i press inc1 for 3 times is the following:
I/flutter (19394): rebuilt counter 1 Text with val: 0
I/flutter (19394): rebuilt Counter 2 Text with val: 0
D/EGL_emulation(19394): app_time_stats: avg=417.64ms min=8.70ms max=4924.00ms count=13
I/flutter (19394): rebuilt counter 1 Text with val: 1
I/flutter (19394): rebuilt Counter 2 Text with val: 0
I/flutter (19394): rebuilt counter 1 Text with val: 1
I/flutter (19394): rebuilt Counter 2 Text with val: 0
D/EGL_emulation(19394): app_time_stats: avg=78.42ms min=2.63ms max=1171.43ms count=18
I/flutter (19394): rebuilt counter 1 Text with val: 2
I/flutter (19394): rebuilt Counter 2 Text with val: 0
I/flutter (19394): rebuilt counter 1 Text with val: 2
I/flutter (19394): rebuilt Counter 2 Text with val: 0
D/EGL_emulation(19394): app_time_stats: avg=34.74ms min=2.47ms max=721.10ms count=25
I/flutter (19394): rebuilt counter 1 Text with val: 3
I/flutter (19394): rebuilt Counter 2 Text with val: 0
I/flutter (19394): rebuilt counter 1 Text with val: 3
I/flutter (19394): rebuilt Counter 2 Text with val: 0
and i’m expecting in the console:
I/flutter (19394): rebuilt counter 1 Text with val: 0
I/flutter (19394): rebuilt Counter 2 Text with val: 0
D/EGL_emulation(19394): app_time_stats: avg=417.64ms min=8.70ms max=4924.00ms count=13
I/flutter (19394): rebuilt counter 1 Text with val: 1
D/EGL_emulation(19394): app_time_stats: avg=78.42ms min=2.63ms max=1171.43ms count=18
I/flutter (19394): rebuilt counter 1 Text with val: 2
D/EGL_emulation(19394): app_time_stats: avg=34.74ms min=2.47ms max=721.10ms count=25
I/flutter (19394): rebuilt counter 1 Text with val: 3
so what am i not understanding correctly about select()
function?
and why the first text is rebuilt twice although the change occur once?
3
Answers
Thank you @Ruble, @Rémi Rousselet i did this solution before and it worked as i said.. but a problem occurs when i have a situation like this:
and i can change the color the same way i did with
inc1()
andinc2()
, through a procedure..so where is the problem? there is still some situations for example where i want to watch a decoration of an object through a composite state notifier like this example above. the solution of nested consumer widgets doesn't always work, because in the situation above the
style
property doesn't accept a value of type consumer because it is not a widget.what can i do in situations like this?
i need change the text of the counter and it's color using state notifier provider, without any redundant rebuilts.
The
select
function is unrelated to the problem described.select
is for skipping some updates which you don’t care about. In your care about the update, but want to rebuild fewer widgets.To do that the solution is to either:
ConnsumerWidget
Consumer
Like:
This way, an update on
fooProvider
will not rebuild the text which listens tobarProvider
try like this:
This is because you are using
ref
, which is in the scope ofTestWidget
. You must use a separate widget or use theConsumer
wrapper. This was described by @Rémi Rousselet