【问题标题】:How do I disable a Button in Flutter?如何禁用 Flutter 中的按钮?
【发布时间】:2018-08-27 08:16:53
【问题描述】:

我刚刚开始掌握 Flutter 的窍门,但我无法弄清楚如何设置按钮的启用状态。

从文档中,它说将 onPressed 设置为 null 以禁用按钮,并给它一个值以启用它。如果按钮在生命周期内继续处于相同状态,这很好。

我觉得我需要创建一个自定义的有状态小部件,它允许我以某种方式更新按钮的启用状态(或 onPressed 回调)。

所以我的问题是我该怎么做?这似乎是一个非常简单的要求,但我在文档中找不到任何关于如何做到这一点的内容。

谢谢。

【问题讨论】:

    标签: flutter dart button disable


    【解决方案1】:

    我认为您可能想为您的按钮 build 引入一些帮助函数以及有状态小部件以及一些要关闭的属性。

    • 使用 StatefulWidget/State 并创建一个变量来保存您的条件(例如 isButtonDisabled
    • 最初将此设置为 true(如果您愿意的话)
    • 渲染按钮时,不要直接将onPressed值设置为null或某些函数onPressed: () {}
    • 改为,使用三元或辅助函数有条件地设置它(下面的示例)
    • 检查 isButtonDisabled 作为此条件的一部分并返回 null 或某些函数。
    • 当按钮被按下时(或当您想要禁用按钮时)使用setState(() => isButtonDisabled = true) 翻转条件变量。
    • Flutter 将使用新状态再次调用 build() 方法,并且按钮将使用 null 按下处理程序呈现并被禁用。

    这里是使用 Flutter 计数器项目的更多上下文。

    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,
        );
      }
    }
    

    在本例中,我使用内联三元有条件地设置TextonPressed,但将其提取到函数中可能更适合您(您可以使用相同的方法来更改按钮):

    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();
          };
        }
      }
    

    【讨论】:

    • 您需要添加粗箭头函数作为参数,否则当按钮启用时会立即调用_i​​ncrementCounter() 函数。这样它实际上会等到单击按钮: onPressed 应该如下所示:onPressed: _isButtonDisabled ? null : () =&gt; _incrementCounter
    • @vitVeres 这通常是正确的,但 _counterButtonPress() 正在返回一个函数 return () {} 所以这是故意的。我不想在这里使用粗箭头,因为我希望函数执行并返回 null 并禁用按钮。
    • @AshtonThomas 是的,在提取的方法 _counterButtonPress() 中,它与您解释的完全一样,但是在您建议提取之前,我使用三元运算符引用了代码。在您的第一个示例中,当应启用按钮时,它将导致执行 _incrementCounter() 方法。下次我会尝试更准确地指出我的意思:)
    • 使用disabled 属性有什么问题,Flutter 团队?这不直观:-/
    • 正确的方法是使用 AbsorbPointer 或 IgnorePointer。简单的小部件方式而不是逻辑,将 onPressed 设置为 null。
    【解决方案2】:

    根据文档:

    “如果 onPressed 回调为 null,则按钮将被禁用,默认情况下将类似于 disabledColor 中的平面按钮。”

    https://docs.flutter.io/flutter/material/RaisedButton-class.html

    所以,你可以这样做:

        RaisedButton(
          onPressed: calculateWhetherDisabledReturnsBool() ? null : () => whatToDoOnPressed,
          child: Text('Button text')
        );
    

    【讨论】:

    • 从文档来看,这就是它的实现方式。使用disabledElevationdisabledColorDisabledTextColor 等公认的答案属性将无法按预期工作。
    • Pff 感谢史蒂夫,不打算浏览当前接受的答案的所有代码。 @chris84948,考虑将其更改为接受的答案。
    • 是的,但是你能从里面摧毁房子然后逃跑吗?或者你可以从 onPress 中的 setStatus 中为 onPress 清空吗?
    【解决方案3】:

    简单的答案是onPressed : null 提供了一个禁用按钮。

    【讨论】:

      【解决方案4】:

      对于特定且数量有限的小部件,将它们包装在小部件IgnorePointer 中正是这样做的:当其ignoring 属性设置为true 时,子小部件(实际上是整个子树)是不可点击的。

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

      否则,如果您打算禁用整个子树,请查看 AbsorbPointer()。

      【讨论】:

      • 你如何处理代码崩溃或代码执行中的错误以释放忽略?
      【解决方案5】:

      禁用点击:

      onPressed: null
      

      启用点击:

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

      组合:

      onPressed: shouldEnable ? fooFunction : null
      

      【讨论】:

        【解决方案6】:

        你也可以使用AbsorbPointer,你可以通过以下方式使用它:

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

        如果您想了解更多关于这个小部件的信息,可以查看以下链接Flutter Docs

        【讨论】:

        • Ignore-/AbsorbPointer 不会将禁用的样式视为提醒 :-)
        【解决方案7】:

        大多数小部件的启用和禁用功能是相同的。

        例如,按钮、开关、复选框等

        如下图设置onPressed属性即可

        onPressed : null 返回禁用的小部件

        onPressed : (){}onPressed : _functionName 返回启用的小部件

        【讨论】:

          【解决方案8】:

          这是我认为最简单的方法:

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

          【讨论】:

            【解决方案9】:

            你也可以设置空白条件,代替 set null

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

            【讨论】:

              【解决方案10】:

              我喜欢为此使用 flutter_mobx 并处理状态。

              接下来我使用观察者:

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

              在控制器上:

              @observable
              bool isDisabledButton = true;
              

              然后在控件内部你可以随意操作这个变量。

              参考:Flutter mobx

              【讨论】:

                【解决方案11】:

                要在颤动中禁用任何 按钮,例如 FlatButtonRaisedButtonMaterialButtonIconButton 等,您只需设置 onPressedonLongPress null 的属性。下面是一些按钮的一些简单示例:

                平面按钮(已启用)

                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'),
                ),
                

                平面按钮(已禁用)

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

                RaisedButton(启用)

                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'),
                ),
                

                RaisedButton(已禁用)

                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'),
                ),
                

                图标按钮(已启用)

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

                图标按钮(已禁用)

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

                注意:某些按钮,例如IconButton,只有onPressed 属性。

                【讨论】:

                  【解决方案12】:

                  此答案基于更新的按钮 TextButton/ElevatedButton/OutlinedButtonFlutter 2.x

                  不过,按钮是根据onPressed 属性启用或禁用的。如果该属性为空,则按钮将被禁用。如果您将功能分配给onPressed,那么按钮将被启用。 在下面的 sn-ps 中,我展示了如何启用/禁用按钮并相应地更新其样式。

                  这个帖子也说明了如何将不同的样式应用到新的 Flutter 2.x 按钮。

                  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;
                                          });
                                        },
                                      ),
                                    ],
                                  )
                                ],
                              ),
                            ],
                          ),
                        ),
                      );
                    }
                  }
                  

                  【讨论】:

                    【解决方案13】:

                    如果您正在寻找一种快速的方法并且不关心让用户实际点击按钮不止一次。您也可以通过以下方式进行操作:

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

                    然后在 onPressed() 函数中检查用户是否已经点击了按钮。

                    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);
                        }
                    }
                    

                    【讨论】:

                      【解决方案14】:

                      您可以在您的应用中使用此代码来加载和禁用按钮:

                      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,
                          );
                        }
                      }
                      

                      希望有用?

                      【讨论】:

                        猜你喜欢
                        • 2022-12-18
                        • 1970-01-01
                        • 2021-05-02
                        • 2022-01-06
                        • 1970-01-01
                        • 2020-05-02
                        • 2021-05-20
                        • 2022-09-23
                        • 1970-01-01
                        相关资源
                        最近更新 更多