Zum Hauptinhalt springen

Optimizing performance

With everything we've seen so far, we can already build a fully functional application. However, you may have questions regarding performance.

In this page, we will cover a few tips and tricks to possibly optimize your code.

caution

Before doing any optimization, make sure to benchmark your application. The added complexity of the optimizations may not be worth minor gains.

Filtering widget/provider rebuild using "select".

You have have noticed that, by default, using ref.watch causes consumers/providers to rebuild whenever any of the properties of an object changes.
For instance, watching a User and only using its "name" will still cause the consumer to rebuild if the "age" changes.

But in case you have a consumer using only a subset of the properties, you want to avoid rebuilding the widget when the other properties change.

This can be achieved by using the select functionality of providers.
When doing so, ref.watch will no-longer return the full object, but rather the selected properties.
And your consumers/providers will now rebuild only if those selected properties change.

class User {
late String firstName, lastName;
}


User example(ExampleRef ref) => User()
..firstName = 'John'
..lastName = 'Doe';

class ConsumerExample extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
// Instead of writing:
// String name = ref.watch(provider).firstName!;
// We can write:
String name = ref.watch(exampleProvider.select((it) => it.firstName));
// This will cause the widget to only listen to changes on "firstName".

return Text('Hello $name');
}
}
info

It is possible to call select as many times as you wish. You are free to call it once per property you desire.

caution

The selected properties are expected to be immutable. Returning a List and then mutating that list will not trigger a rebuild.

caution

Using select slightly slows down individual read operations and increase a tiny bit the complexity of your code.
It may not be worth using it if those "other properties" rarely change.

Selecting asynchronous properties

In case you are trying to optimize a provider listening to another provider, chances are that other provider is asynchronous.

Normally, you would ref.watch(anotherProvider.future) to get the value.
The issue is, select will apply on an AsyncValue – which is not something you can await.

For this purpose, you can instead use selectAsync. It is unique to asynchronous code, and enables performing a select operation on the data emitted by a provider.
Its usage is similar to that of select, but returns a Future instead:


Object? example(ExampleRef ref) async {
// Wait for a user to be available, and listen to only the "firstName" property
final firstName = await ref.watch(
userProvider.selectAsync((it) => it.firstName),
);

// TODO use "firstName" to fetch something else
}