Como desativo um botão no Flutter?

115

Estou apenas começando a pegar o jeito do Flutter, mas estou tendo problemas para descobrir como definir o estado ativado de um botão.

Nos documentos, ele diz para definir onPressedcomo nulo para desativar um botão e dar a ele um valor para ativá-lo. Isso é bom se o botão continuar no mesmo estado durante o ciclo de vida.

Tenho a impressão de que preciso criar um widget Stateful personalizado que me permitirá atualizar o estado ativado do botão (ou retorno de chamada onPressed) de alguma forma.

Então, minha pergunta é como eu faria isso? Parece um requisito bastante simples, mas não consigo encontrar nada nos documentos sobre como fazer isso.

Obrigado.

chris84948
fonte
Você pode esclarecer o que quer dizer com "Tudo bem se o botão continuar no mesmo estado durante o ciclo de vida". ?
Seth Ladd

Respostas:

127

Acho que você pode querer introduzir algumas funções auxiliares em buildseu botão, bem como um widget Stateful junto com algumas propriedades para desligar.

  • Use um StatefulWidget / State e crie uma variável para manter sua condição (por exemplo isButtonDisabled)
  • Defina isso como verdadeiro inicialmente (se for o que você deseja)
  • Ao renderizar o botão, não defina diretamente oonPressed valor para uma nullou outra funçãoonPressed: () {}
  • Em vez disso , defina-o condicionalmente usando uma função ternária ou auxiliar (exemplo abaixo)
  • Verifique o isButtonDisabledcomo parte desta condição e retorne uma nullou alguma função.
  • Quando o botão é pressionado (ou sempre que você deseja desabilitar o botão), use setState(() => isButtonDisabled = true)para inverter a variável condicional.
  • Flutter irá chamar o build()método novamente com o novo estado e o botão será renderizado com um nullmanipulador de pressão e desabilitado.

Aqui está um pouco mais de contexto usando o projeto Contador de oscilação.

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

Neste exemplo, estou usando um ternário in-line para definir condicionalmente o Texte onPressed, mas pode ser mais apropriado para você extrair isso em uma função (você pode usar o mesmo método para alterar o texto do botão também):

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();
      };
    }
  }
Ashton Thomas
fonte
3
Você precisa adicionar a função seta grande como um argumento, caso contrário, a função _incrementCounter () será chamada imediatamente quando o botão for habilitado. Dessa forma, ele realmente aguardará até que o botão seja clicado: O onPressed deve ser semelhante a:onPressed: _isButtonDisabled ? null : () => _incrementCounter
Vit Veres
2
@vitVeres isso geralmente é verdade, mas o _counterButtonPress () está retornando uma função, return () {}então isso é intencional. Não quero usar a seta grande aqui, pois quero que a função seja executada e retorne nulle desative o botão.
Ashton Thomas de
@AshtonThomas Sim, no método extraído _counterButtonPress () é exatamente como você explicou, mas eu estava me referindo ao código com o operador ternário antes de você sugerir a extração. Em seu primeiro exemplo, isso causará a execução do método _incrementCounter () quando o botão deve ser habilitado. Da próxima vez, tentarei apontar o que quero dizer com mais precisão :)
Vit Veres
30
O que havia de errado em usar uma disabledpropriedade, equipe Flutter? Isso não é intuitivo: - /
Curly
1
a maneira correta é com AbsorbPointer ou IgnorePointer. Simplesmente forma de widget em vez de lógica com a configuração de onPressed para null.
ejdrian313
93

De acordo com a documentação:

"Se o retorno de chamada onPressed for nulo, o botão será desativado e, por padrão, se parecerá com um botão plano na disabledColor."

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

Então, você pode fazer algo assim:

    RaisedButton(
      onPressed: calculateWhetherDisabledReturnsBool() ? null : () => whatToDoOnPressed,
      child: Text('Button text')
    );
Steve Alexander
fonte
2
A julgar pelos documentos, é assim que ele deve ser implementado. Com as propriedades de resposta aceitas como disabledElevation, disabledColore DisabledTextColornão funcionará como esperado.
Joel Broström
Pff obrigado por isso Steve, não estava planejando passar por todo o código da resposta aceita atualmente. @ chris84948, considere mudar para a resposta aceita.
CularBytes
42

A resposta simples é onPressed : nullfornecer um botão desativado.

BINAY THAPA MAGAR
fonte
17

Configuração

onPressed: null // disables click

e

onPressed: () => yourFunction() // enables click
CopsOnRoad
fonte
1
Nesta solução, o valor de onPressedé sempre uma função, então o botão é renderizado como 'clicável', embora ele ignore o evento de clique se a isEnabledpropriedade for definida. Para realmente desabilitar o botão, useRaisedButton(onPressed: isEnabled ? _handleClick : null
Curly
15

Para um número específico e limitado de widgets, envolvê-los em um widget IgnorePointer faz exatamente isso: quando sua ignoringpropriedade é definida como true, o sub-widget (na verdade, a subárvore inteira) não é clicável.

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

Caso contrário, se você pretende desabilitar uma subárvore inteira, procure em AbsorbPointer ().

Edmond
fonte
9

A funcionalidade Habilitar e Desabilitar é a mesma para a maioria dos widgets.

Ex: botão, interruptor, caixa de seleção etc.

Basta definir a onPressedpropriedade conforme mostrado abaixo

onPressed : nullretorna widget desativado

onPressed : (){}ou onPressed : _functionNameretorna widget Ativado

Vicky Salunkhe
fonte
6

Você também pode usar o AbsorbPointer da seguinte maneira:

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

Se você quiser saber mais sobre este widget, você pode verificar o seguinte link Flutter Docs

Juanes30
fonte
2
Ignore- / AbsorbPointer não considera os estilos desativados apenas como um LEMBRETE :-)
Pascal
4

Esta é a maneira mais fácil na minha opinião:

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