Flutter Riverpod – using read() inside build method

Issue

Suppose I want to initialize a text field by using the initialValue: property on a TextFormField, and I need my initial value to come from a provider. I read on the docs that calling read() from inside the build method is considered bad practice, but calling from handlers is fine (like onPressed). So I’m wondering if its fine to call read from the initialValue property like shown below?

enter image description here

Solution

No, you should use useProvider if you are using hooks, or a ConsumerWidget / Consumer if you are not.

The difference being, the initialValue field is a part of the build method, and like you said, onPressed is a handler, outside of the build method.

A core aspect of providers is optimizing rebuilds as provided values change. Using context.read in the build method is nullifying this benefit as you aren’t listening to the provided value.

Using context.read is highly recommended in anonymous functions (onChanged, onPressed, onTap, etc.) because those functions are retrieving the provided value at the time the function is executed. This means the function will always execute with the current value of that provider, without having to listen to the provider. The other methods for reading providers use a listener which is more expensive and unnecessary in the case of anonymous functions.

In your example, you wanted to set initialValue of a TextFormField. The following is how you could use hooks_riverpod and flutter_hooks to accomplish that.

class HooksExample extends HookWidget {
  const HooksExample({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      initialValue: useProvider(loginStateProv).email,
    );
  }
}

And for readers who prefer to not use hooks:

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

  @override
  Widget build(BuildContext context, ScopedReader watch) {
    return TextFormField(
      initialValue: watch(loginStateProv).email,
    );
  }
}

Or:

class ConsumerExample extends StatelessWidget {
  const ConsumerExample({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer(
      builder: (context, watch, child) {
        return TextFormField(
          initialValue: watch(loginStateProv).email,
        );
      },
    );
  }
}

The primary difference being that Consumer will only rebuild its children because only they are relying on provided data.

Answered By – Alex Hartford

Answer Checked By – Willingham (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.