347

I'm having trouble figuring out how to set the enabled state of a button in Flutter.

From the docs, it says to set onPressed to null to disable a button, and give it a value to enable it. This is fine if the button continues to be in the same state for the lifecycle.

I get the impression I need to create a custom Stateful widget that will allow me to update the button's enabled state (or onPressed callback) somehow.

How would I do that? This seems like a pretty straightforward requirement, but I can't find anything in the docs on how to do it.

0

17 Answers 17

341

According to the docs:

If the onPressed callback is null, then the button will be disabled and by default will resemble a flat button in the disabledColor.

So, you might do something like this:

RaisedButton(
  onPressed: calculateWhetherDisabledReturnsBool() ? null : () => whatToDoOnPressed,
  child: Text('Button text')
);
5
  • 9
    Judging from the docs, this is how it's ment to be implemented. With the accepted answer properties like disabledElevation, disabledColor and DisabledTextColor won't work as intended. Feb 11, 2019 at 10:55
  • 3
    Pff thanks for this Steve, was not planning on going through all the code of the currently accepted answer. @chris84948, consider changing this to the accepted answer.
    – CularBytes
    Mar 8, 2020 at 12:49
  • yes, but can you destroy the house from inside and still run away? or can you null the onPress from setStatus within the onPress?
    – none
    Oct 27, 2020 at 11:31
  • 3
    Although, after null safety we can not use 'null' so you can use onPressed: _isLoading ? () => {} : () => _signImWithGoogle(context),
    – Kru
    Feb 5, 2022 at 18:49
  • 1
    Here in 2023, ElevatedButton is the new widget. Use this docs link since the one in the answer 404s. "If onPressed and onLongPress callbacks are null, then the button will be disabled." Apr 6, 2023 at 15:37
267

I think you may want to introduce some helper functions to build your button as well as a Stateful widget along with some property to key off of.

  • Use a StatefulWidget/State and create a variable to hold your condition (e.g. isButtonDisabled)
  • Set this to true initially (if that's what you desire)
  • When rendering the button, don't directly set the onPressed value to either null or some function onPressed: () {}
  • Instead, conditionally set it using a ternary or a helper function (example below)
  • Check the isButtonDisabled as part of this conditional and return either null or some function.
  • When the button is pressed (or whenever you want to disable the button) use setState(() => isButtonDisabled = true) to flip the conditional variable.
  • Flutter will call the build() method again with the new state and the button will be rendered with a null press handler and be disabled.

Here's is some more context using the Flutter counter project.

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  bool _isButtonDisabled;

  @override
  void initState() {
    _isButtonDisabled = false;
  }

  void _incrementCounter() {
    setState(() {
      _isButtonDisabled = true;
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("The App"),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
            _buildCounterButton(),
          ],
        ),
      ),
    );
  }

  Widget _buildCounterButton() {
    return new RaisedButton(
      child: new Text(
        _isButtonDisabled ? "Hold on..." : "Increment"
      ),
      onPressed: _isButtonDisabled ? null : _incrementCounter,
    );
  }
}

In this example I am using an inline ternary to conditionally set the Text and onPressed, but it may be more appropriate for you to extract this into a function (you can use this same method to change the text of the button as well):

Widget _buildCounterButton() {
    return new RaisedButton(
      child: new Text(
        _isButtonDisabled ? "Hold on..." : "Increment"
      ),
      onPressed: _counterButtonPress(),
    );
  }

  Function _counterButtonPress() {
    if (_isButtonDisabled) {
      return null;
    } else {
      return () {
        // do anything else you may want to here
        _incrementCounter();
      };
    }
  }
6
  • 7
    You need to add fat arrow function as an argument, otherwise the _incrementCounter() function will be called right away when button becomes enabled. This way it will actually wait until the button is clicked: The onPressed should look like this: onPressed: _isButtonDisabled ? null : () => _incrementCounter
    – Vit Veres
    Apr 12, 2018 at 5:21
  • 3
    @vitVeres that is usually true but the _counterButtonPress() is returning a function return () {} so this is intentional. I don't want to use the fat arrow here as I want the function to execute and return null and disable the button. Apr 14, 2018 at 14:34
  • 219
    What was wrong with using a disabled property, Flutter team? This is just not intuitive :-/
    – SoftWyer
    Aug 10, 2019 at 16:19
  • 1
    the correct way is with AbsorbPointer or IgnorePointer. Simply widget way instead of logic with setting up onPressed to null.
    – ejdrian313
    Nov 18, 2019 at 13:16
  • 2
    This is not good enough because it assumes the onPress: is synchronous code while it is very possible the code is asynchronous and operational, splitting every button code to multiple function to work around this causes a maintenance nightmare Jul 2, 2020 at 20:57
115

The simple answer is onPressed : null gives a disabled button.

0
72

Disables click:

onPressed: null

Enables click:

onPressed: () => fooFunction() 
// or
onPressed: fooFunction

Combination:

onPressed: shouldEnable ? fooFunction : null
0
32

For a specific and limited number of widgets, wrapping them in a widget IgnorePointer does exactly this: when its ignoring property is set to true, the sub-widget (actually, the entire subtree) is not clickable.

IgnorePointer(
    ignoring: true, // or false
    child: RaisedButton(
        onPressed: _logInWithFacebook,
        child: Text("Facebook sign-in"),
        ),
),

Otherwise, if you intend to disable an entire subtree, look into AbsorbPointer().

2
  • how do you handle code crush or error in code execution to release the ignoring ?
    – none
    Oct 27, 2020 at 10:46
  • This is a much better option in my opinion. All of the others visually show the button as pressable, they just don't do anything. This is the only suggestion that visually indicates unpressable.
    – darkfrog
    Jan 4 at 18:09
22

This is the easiest way in my opinion:

RaisedButton(
  child: Text("PRESS BUTTON"),
  onPressed: booleanCondition
    ? () => myTapCallback()
    : null
)
0
17

Enable and Disable functionality is same for most of the widgets.

Ex, button , switch, checkbox etc.

Just set the onPressed property as shown below

onPressed : null returns Disabled widget

onPressed : (){} or onPressed : _functionName returns Enabled widget

16

You can also use the AbsorbPointer, and you can use it in the following way:

AbsorbPointer(
      absorbing: true, // by default is true
      child: RaisedButton(
        onPressed: (){
          print('pending to implement onPressed function');
        },
        child: Text("Button Click!!!"),
      ),
    ),

If you want to know more about this widget, you can check the following link Flutter Docs

1
  • 4
    Ignore-/AbsorbPointer does not consider the disabled styles just as a REMINDER :-)
    – Pascal
    Jan 31, 2020 at 21:54
16

This answer is based on updated Buttons TextButton/ElevatedButton/OutlinedButton for Flutter 2.x

Still, buttons are enabled or disabled based on onPressed property. If that property is null then button would be disabled. If you will assign function to onPressed then button would be enabled. In the below snippets, I have shown how to enable/disable button and update it's style accordingly.

This post also indicating that how to apply different styles to new Flutter 2.x buttons.

enter image description here

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool textBtnswitchState = true;
  bool elevatedBtnSwitchState = true;
  bool outlinedBtnState = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                TextButton(
                  child: Text('Text Button'),
                  onPressed: textBtnswitchState ? () {} : null,
                  style: ButtonStyle(
                    foregroundColor: MaterialStateProperty.resolveWith(
                      (states) {
                        if (states.contains(MaterialState.disabled)) {
                          return Colors.grey;
                        } else {
                          return Colors.red;
                        }
                      },
                    ),
                  ),
                ),
                Column(
                  children: [
                    Text('Change State'),
                    Switch(
                      value: textBtnswitchState,
                      onChanged: (newState) {
                        setState(() {
                          textBtnswitchState = !textBtnswitchState;
                        });
                      },
                    ),
                  ],
                )
              ],
            ),
            Divider(),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                ElevatedButton(
                  child: Text('Text Button'),
                  onPressed: elevatedBtnSwitchState ? () {} : null,
                  style: ButtonStyle(
                    foregroundColor: MaterialStateProperty.resolveWith(
                      (states) {
                        if (states.contains(MaterialState.disabled)) {
                          return Colors.grey;
                        } else {
                          return Colors.white;
                        }
                      },
                    ),
                  ),
                ),
                Column(
                  children: [
                    Text('Change State'),
                    Switch(
                      value: elevatedBtnSwitchState,
                      onChanged: (newState) {
                        setState(() {
                          elevatedBtnSwitchState = !elevatedBtnSwitchState;
                        });
                      },
                    ),
                  ],
                )
              ],
            ),
            Divider(),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                OutlinedButton(
                  child: Text('Outlined Button'),
                  onPressed: outlinedBtnState ? () {} : null,
                  style: ButtonStyle(
                      foregroundColor: MaterialStateProperty.resolveWith(
                    (states) {
                      if (states.contains(MaterialState.disabled)) {
                        return Colors.grey;
                      } else {
                        return Colors.red;
                      }
                    },
                  ), side: MaterialStateProperty.resolveWith((states) {
                    if (states.contains(MaterialState.disabled)) {
                      return BorderSide(color: Colors.grey);
                    } else {
                      return BorderSide(color: Colors.red);
                    }
                  })),
                ),
                Column(
                  children: [
                    Text('Change State'),
                    Switch(
                      value: outlinedBtnState,
                      onChanged: (newState) {
                        setState(() {
                          outlinedBtnState = !outlinedBtnState;
                        });
                      },
                    ),
                  ],
                )
              ],
            ),
          ],
        ),
      ),
    );
  }
}
10

For disabling any Button in flutter such as FlatButton, RaisedButton, MaterialButton, IconButton etc all you need to do is to set the onPressed and onLongPress properties to null. Below is some simple examples for some of the buttons:

FlatButton (Enabled)

FlatButton(
  onPressed: (){}, 
  onLongPress: null, // Set one as NOT null is enough to enable the button
  textColor: Colors.black,
  disabledColor: Colors.orange,
  disabledTextColor: Colors.white,
  child: Text('Flat Button'),
),

enter image description here enter image description here

FlatButton (Disabled)

FlatButton(
  onPressed: null,
  onLongPress: null,
  textColor: Colors.black,
  disabledColor: Colors.orange,
  disabledTextColor: Colors.white,
  child: Text('Flat Button'),
),

enter image description here

RaisedButton (Enabled)

RaisedButton(
  onPressed: (){},
  onLongPress: null, // Set one as NOT null is enough to enable the button
  // For when the button is enabled
  color: Colors.lightBlueAccent,
  textColor: Colors.black,
  splashColor: Colors.blue,
  elevation: 8.0,

  // For when the button is disabled
  disabledTextColor: Colors.white,
  disabledColor: Colors.orange,
  disabledElevation: 0.0,

  child: Text('Raised Button'),
),

enter image description here

RaisedButton (Disabled)

RaisedButton(
  onPressed: null,
  onLongPress: null,
  // For when the button is enabled
  color: Colors.lightBlueAccent,
  textColor: Colors.black,
  splashColor: Colors.blue,
  elevation: 8.0,

  // For when the button is disabled
  disabledTextColor: Colors.white,
  disabledColor: Colors.orange,
  disabledElevation: 0.0,

  child: Text('Raised Button'),
),

enter image description here

IconButton (Enabled)

IconButton(
  onPressed: () {},
  icon: Icon(Icons.card_giftcard_rounded),
  color: Colors.lightBlueAccent,
            
  disabledColor: Colors.orange,
),

enter image description here enter image description here

IconButton (Disabled)

IconButton(
  onPressed: null,
  icon: Icon(Icons.card_giftcard_rounded),
  color: Colors.lightBlueAccent,
            
  disabledColor: Colors.orange,
),

enter image description here

Note: Some of buttons such as IconButton have only the onPressed property.

8

This is the easiest way to disable a button in Flutter is assign the null value to the onPressed

ElevatedButton(
  style: ElevatedButton.styleFrom(
    primary: Colors.blue, // background
    onPrimary: Colors.white, // foreground
  ),
  onPressed: null,
  child: Text('ElevatedButton'),
),
6

There are two ways of doing this:

1- https://stackoverflow.com/a/49354576/5499531

2- You can use a MaterialStatesController:

final _statesController = MaterialStatesController();

and then change the state to:

_statesController.update(
   MaterialState.disabled,
   true, // or false depending on your logic
);

On your button

ElevatedButton(
    onPressed: _onPressed,
    statesController: _statesController,
    child: Text("Awesome"),
),

In addition you can change the button style when is disable: in your theme setup:

....
elevatedButtonTheme: ElevatedButtonThemeData(
              style: ElevatedButton.styleFrom(
                backgroundColor: colors.primary500, // set your own color
                textStyle: button, // set your own style
                onPrimary: colors.onPrimary100, // set your own color
                enableFeedback: true,
                disabledBackgroundColor: colors.primary300, // set your own color
                disabledForegroundColor: colors.primary300, // set your own color
                disabledMouseCursor: SystemMouseCursors.forbidden, // when is disable the change the cursor type
              ),
            ),
...
3

You can use this code in your app for button with loading and disable:

class BtnPrimary extends StatelessWidget {
  bool loading;
  String label;
  VoidCallback onPressed;

  BtnPrimary(
      {required this.label, required this.onPressed, this.loading = false});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton.icon(
      icon: loading
          ? const SizedBox(
              child: CircularProgressIndicator(
                color: Colors.white,
              ),
              width: 20,
              height: 20)
          : const SizedBox(width: 0, height: 0),
      label: loading ? const Text('Waiting...'): Text(label),
      onPressed: loading ? null : onPressed,
    );
  }
}

I hope useful 😊

0

You can set also blank condition, in place of set null

         var isDisable=true;

   

          RaisedButton(
              padding: const EdgeInsets.all(20),
              textColor: Colors.white,
              color: Colors.green,
              onPressed:  isDisable
                  ? () => (){} : myClickingData(),
              child: Text('Button'),
            )
0

see below of the possible solution, add a 'ValueListenableBuilder' of 'TextEditingValue' listening to controller (TextEditingController) and return your function call if controller.text is not empty and return 'null' if it is empty.

// valuelistenablebuilder wraped around button

  ValueListenableBuilder<TextEditingValue>(
                  valueListenable: textFieldController,
                  builder: (context, ctrl, __) => ElevatedButton(                    
                    onPressed: ctrl.text.isNotEmpty ? yourFunctionCall : null,
                    child: Text(
                      'SUBMIT',
                      style: GoogleFonts.roboto(fontSize: 20.0),
                    ),
                  ),
                ),

//texfield

 TextField(controller: textFieldController,
                 onChanged: (newValue) {
                  textFieldText = newValue;
                },
              ),

builder will listen to controller and enables button only when textfield is in use. I hope this answers the question. let me know..

-2

I like to use flutter_mobx for this and work on the state.

Next I use an observer:

Container(child: Observer(builder: (_) {
  var method;
  if (!controller.isDisabledButton) method = controller.methodController;
  return RaiseButton(child: Text('Test') onPressed: method);
}));

On the Controller:

@observable
bool isDisabledButton = true;

Then inside the control you can manipulate this variable as you want.

Refs.: Flutter mobx

-6

If you are searching for a quick way and don't care about letting the user actually clicking more then once on a button. You could do it also the following way:

// Constant whether button is clicked
bool isClicked = false;

and then checking in the onPressed() function whether the user has already clicked the button or not.

onPressed: () async {
    if (!isClicked) {
       isClicked = true;
       // await Your normal function
    } else {
       Toast.show(
          "You click already on this button", context,
          duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
    }
}

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.