1

I have this widget:

   TextInputSettingsTile(
      title: 'Valor actual',
      settingKey: 'current_value',
      initialValue: '0',
      keyboardType: isNumber
          ? TextInputType.numberWithOptions()
          : TextInputType.text,
      inputFormatters: [
        isNumber
            ? FilteringTextInputFormatter.digitsOnly
            : FilteringTextInputFormatter.singleLineFormatter,
      ],
      onChange: (value) {
        if (isNumber) {
          Settings.setValue<int>(
            'current_int_value',
            int.tryParse(value) ?? 0,
          );
        } else {
          Settings.setValue<String>('current_string_value', value);
        }
      },
    ),

This is similar to TextFormField and, in fact, it has almost the same properties.

Look at this fragment:

      keyboardType: isNumber
          ? TextInputType.numberWithOptions()
          : TextInputType.text,
      inputFormatters: [
        isNumber
            ? FilteringTextInputFormatter.digitsOnly
            : FilteringTextInputFormatter.singleLineFormatter,
      ],

where isNumber is a boolean variable declared and assigned somewhere else.

When isNumber is true, I want to allow only digits to be inputted, otherwise, any character.

The problem is that it does not work when isNumber value changes. In order to make it work, I need to quit and restart the application.

isNumber variable is set in an onChange event of other control:

      onChange: (value) {
        isNumber = value;
      },
3
  • I have updated the question, however, I did not think it was relevant because it is just using isNumber = value in the onChange event of a CheckboxSettingsTile widget. Commented Nov 6 at 21:50
  • well, you are not rebuilding your widget, simply call setState when your widget extends StatefulWidget (or use StatefulBuilder - you could also use ValueListenableBuilder but it requires ValueListenable<bool> like ValueNotifier<bool> for example) Commented Nov 7 at 6:13
  • so in short: if you are extending StatefulWidget just use setState() and when extending StatelessWidget wrap your widgets tree with StatefulBuilder and call its setState function passed to builder callback Commented Nov 7 at 8:13

2 Answers 2

3

The issue is that changing isNumber alone doesn’t rebuild your widget tree — Flutter widgets are immutable and only update when a rebuild is triggered.

When you set isNumber in your onChange callback like this:

onChange: (value) {
  isNumber = value;
},

it updates the variable but doesn’t call setState(), so your widget doesn’t get rebuilt with the new keyboardType and inputFormatters.
That’s why it only updates after you restart the app.


Fix

Wrap the assignment in setState():

onChange: (value) {
  setState(() {
    isNumber = value;
  });
},

This triggers a rebuild, so your TextInputSettingsTile re-evaluates the keyboardType and inputFormatters with the new value.

Example

class MySettingsPage extends StatefulWidget {
  const MySettingsPage({super.key});

  @override
  State<MySettingsPage> createState() => _MySettingsPageState();
}

class _MySettingsPageState extends State<MySettingsPage> {
  bool isNumber = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        SwitchListTile(
          title: const Text('Use number input'),
          value: isNumber,
          onChanged: (value) {
            setState(() {
              isNumber = value;
            });
          },
        ),
        TextInputSettingsTile(
          title: 'Valor actual',
          settingKey: 'current_value',
          initialValue: '0',
          keyboardType: isNumber
              ? const TextInputType.numberWithOptions()
              : TextInputType.text,
          inputFormatters: [
            isNumber
                ? FilteringTextInputFormatter.digitsOnly
                : FilteringTextInputFormatter.singleLineFormatter,
          ],
          onChange: (value) {
            if (isNumber) {
              Settings.setValue<int>(
                'current_int_value',
                int.tryParse(value) ?? 0,
              );
            } else {
              Settings.setValue<String>('current_string_value', value);
            }
          },
        ),
      ],
    );
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Helo.... thanks, but I used other way to set the state, by using ValueNotifier
Yeah, the value notifier is better than setState.
0

Finally the way to go is to use ValueNotifier to monitor changes in isNumber value.

I am declaring the isNumber this way now:

late ValueNotifier<bool> isNumber = ValueNotifier(false);

Then wrapped the widget with ValueListenableBuilder . That way, to change isNumber value I had to use:

              onChange: (on) {
                isNumber.value = on;
              },

When that was used, the state of the widget changes so that the new property value is applied.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.