title: How do avoid markNeedsBuilder() error using Flutter_Riverpod and TextEditingControllers on TextFormFields?
body: <p>The form below is using ConsumerWidget from the flutter_riverpod package to watch for updates on first/last name fields in a firebase stream provider. Then using TextEditingControllers I am both setting the <code>watch</code>ed text values in the fields and also getting the text values when I update the account in Firebase.</p>
  <p>This all works great until I change a value in the first or last name fields directly in Firebase, which causes a rebuild in the ui. While the UI does display the update Firebase value I get the following Exception in the run logs.</p>
  <p>Appears riverpod is battling with the TextEditingControllers over state, which makes sense, but how do I overcome this?</p>
  <blockquote>
  <p>======== Exception caught by foundation library ====================================================
  The following assertion was thrown while dispatching notifications for TextEditingController:
  setState() or markNeedsBuild() called during build.</p>
  </blockquote>
  <blockquote>
  <p>This Form widget cannot be marked as needing to build because the framework is already in the process of building widgets.  A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
  The widget on which setState() or markNeedsBuild() was called was: Form-[LabeledGlobalKey#78eaf]
  state: FormState#7d070
  The widget which was currently being built when the offending call was made was: FirstLastName
  dirty
  dependencies: [UncontrolledProviderScope]</p>
  </blockquote>
  <h3>Can I use the <a href="https://pub.dev/packages/flutter_riverpod" rel="nofollow noreferrer">flutter_riverpod</a> package when I am using a Stateful Widget that is required for using TextEditingControllers? Or do I need to look at using the <a href="https://pub.dev/packages/hooks_riverpod" rel="nofollow noreferrer">hooks_riverpod</a> package or just <a href="https://pub.dev/packages/riverpod" rel="nofollow noreferrer">riverpod</a> package so that I can use TextEditingControllers to set values in fields and read values from fields?</h3>
  <h3>Code excerpts below:</h3>
  <p><strong>account_setup.dart</strong></p>
  <pre><code>class AccountSetup extends StatefulWidget {
    @override
    _AccountSetupState createState() =&gt; _AccountSetupState();
  }
  
  class _AccountSetupState extends State&lt;AccountSetup&gt; {
    final TextEditingController _firstNameController = TextEditingController();
    final TextEditingController _lastNameController = TextEditingController();
  
    @override
    void initState() {
      super.initState();
    }
  
    @override
    void dispose() {
      _firstNameController.dispose();
      _lastNameController.dispose();
      super.dispose();
    }
  
    final _formKey = GlobalKey&lt;FormState&gt;();
  
    @override
    Widget build(BuildContext context) {
      return SafeArea(
        child: Scaffold(
          backgroundColor: Colors.white,
          body: Form(
            key: _formKey,
            child: ListView(
              children: [
                AccountSettingsTitle(
                  title: 'Account Setup',
                ),
                FirstLastName(_firstNameController, _lastNameController),
                SizedBox(
                  height: 24.0,
                ),
              ],
            ),
          ),
        ),
      );
    }
  }
  
  class FirstLastName extends ConsumerWidget {
    FirstLastName(
      this.firstNameController,
      this.lastNameController,
    );
    final TextEditingController firstNameController;
    final TextEditingController lastNameController;
  
    @override
    Widget build(BuildContext context, ScopedReader watch) {
      final account = watch(accountStreamProvider);
      return account.when(
        data: (data) {
          firstNameController.text = data.firstName;
          lastNameController.text = data.lastName;
          return Column(
            children: [
              Center(
                child: Padding(
                  padding: EdgeInsets.only(top: 10.0, left: 24.0, right: 24.0),
                  child: TextFormField(
                    controller: firstNameController,
                    decoration: kInputStringFields.copyWith(
                      hintText: 'First Name',
                    ),
                    autocorrect: false,
                    validator: (String value) {
                      if (value.isEmpty) {
                        return 'Enter first name';
                      }
  
                      return null;
                    },
                  ),
                ),
              ),
              SizedBox(
                height: 14.0,
              ),
              Center(
                child: Padding(
                  padding: EdgeInsets.only(top: 10.0, left: 24.0, right: 24.0),
                  child: TextFormField(
                    controller: lastNameController,
                    decoration: kInputStringFields.copyWith(
                      hintText: 'Last Name',
                    ),
                    autocorrect: false,
                    validator: (String value) {
                      if (value.isEmpty) {
                        return 'Enter last name';
                      }
  
                      return null;
                    },
                  ),
                ),
              ),
            ],
          );
        },
        loading: () =&gt; Container(),
        error: (_, __) =&gt; Container(),
      );
    }
  }
  </code></pre>
  <p><strong>top_level_providers.dart</strong></p>
  <pre><code>final accountStreamProvider = StreamProvider.autoDispose&lt;Account&gt;((ref) {
    final database = ref.watch(databaseProvider);
    return database != null ? database.accountStream() : const Stream.empty();
  });
  </code></pre>