The LayoutBuilder always reset the field widget. Is there a widget instead of LayoutBuilder that maintains an input value?

89 Views Asked by At

When the screen is resized and LayoutBuilder runs, the value entered is always reset. Even if the same widget variables are used, this differs from Wrap widgets. So is there any widget that still maintains the entered values? (don't want to use the controller for each field)

video example. code example: (you can test this code on this website)

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    const appTitle = 'Form Styling Demo';
    return MaterialApp(
      title: appTitle,
      home: Scaffold(
        appBar: AppBar(
          title: const Text(appTitle),
        ),
        body: const MyCustomForm(),
      ),
    );
  }
}

class MyCustomForm extends StatelessWidget {
  const MyCustomForm({super.key});

  @override
  Widget build(BuildContext context) {
    print('build');
    String bValue = '';
    const bTitle = Text('b field');
    final bField = SizedBox(
      width: 200,
      child: TextFormField(
        initialValue: bValue,
        onChanged: (text) {
          bValue = text;
        },
        decoration: const InputDecoration(
          border: UnderlineInputBorder(),
          labelText: 'Enter your username',
        ),
      ),
    );
    final bButton = TextButton(
      onPressed: () => print(bValue),
      child: Text('bValue = $bValue'),
    );
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
          child: LayoutBuilder(
            builder: (BuildContext context, BoxConstraints constraints) {
              return constraints.maxWidth < 200
                  ? Column(
                      children: [
                        aTitle,
                        aField,
                        aButton,
                      ],
                    )
                  : Row(
                      children: [
                        aTitle,
                        Flexible(child: aField),
                        aButton,
                      ],
                    );
            },
          ),
        ),
        SizedBox(height: 50),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
          child: Wrap(
            alignment: WrapAlignment.center,
            crossAxisAlignment: WrapCrossAlignment.center,
            children: [
              bTitle,
              bField,
              bButton,
            ],
          ),
        ),
      ],
    );
  }
}

//tried to move all parameters out of the `Build`
String aValue = '';
const aTitle = Text('a field');
final aField = SizedBox(
  width: 200,
  child: TextFormField(
    key: Key('1'), //tried to add Key
    initialValue: aValue,
    onChanged: (text) {
      aValue = text;
    },
    decoration: const InputDecoration(
      border: UnderlineInputBorder(),
      labelText: 'Enter your username',
    ),
  ),
);
final aButton = TextButton(
  onPressed: () => print('aValue = $aValue'),
  child: Text('print bValue'),
);

In this video, you can see this issue at aField. The input in the aField disappeared after the layout was changed(that should not disappear and the aValue still not reset). The input in the bField is still alive after the layout was changed. But the bField is in the Wrap widget which is not useful like the LayoutBuilder widget.

2

There are 2 best solutions below

1
s4nk37 On

Try giving Value key to TextFormField

8
Randal Schwartz On

Your error is doing this:

String bValue = '';

and

child: TextFormField(
    initialValue: bValue,

in your build() method. This will reinitialize bValue to an empty string on each build. You should be prepared for build to be called 60 times per second. If a value needs to persist between builds (which this does), you need to move it out into State. Typically, this is done with a TextEditingController. I believe there are Flutter codelabs on flutter.dev to demonstrate this.