[Flutter] onPressedの処理中は押せなくなるようなボタンを作る
やりたいこと
以下のように、ボタンを押すと何らかの処理が行われるという実装を考えてみます。
このサンプルのコードは以下のようになっています。
import 'package:flutter/material.dart';
class Sample extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Builder(
builder: (BuildContext context) => Center(
child: RaisedButton(
child: Text('送信'),
onPressed: () async {
await _someAsyncProcess();
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('送信しました'),
));
},
),
),
),
);
}
}
Builder()
で囲っている理由については[Flutter] SnackBarを使おうとすると Scaffold.of() called with(略)と言われるときの対応
こちらをご参照ください。
このままだと、ボタンが押されたときの処理を実行している間もボタンが押せる状態になっているため、 ボタンを連打すると処理が並行して複数回実行されてしまいます。
これは色々と困ることがありそうなので、処理中はボタンを押せなくするような実装をしたくなります。
やり方
まず、ボタンを押せなくするには、 onPressed
に null
を渡せばよいです。
- onPressed: () async {
- await _someAsyncProcess();
- Scaffold.of(context).showSnackBar(SnackBar(
- content: Text('送信しました'),
- ));
- },
+ onPressed: null,
onPressed
が null
だと、以下のようにボタンがグレーアウトして押せなくなります。
参考:dart - How do I disable a Button in Flutter? - Stack Overflow
なので、今回の例であれば RaisedButton
をラップした StatefulWidget
を作って、処理を実行している間のみ onPressed
を null
に切り替えるという実装をしてあげればよさそうです。
というわけで、そのような実装を WaitableRaisedButton
として作ってみましょう。
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class WaitableRaisedButton extends StatefulWidget {
createState() => _State();
WaitableRaisedButton({
this.onPressed,
this.child,
});
final VoidCallback onPressed;
final Widget child;
}
class _State extends State<WaitableRaisedButton> {
bool _waiting = false;
Widget build(BuildContext context) {
return RaisedButton(
onPressed: widget.onPressed == null || _waiting
? null
: () async {
setState(() => _waiting = true);
await widget.onPressed();
setState(() => _waiting = false);
},
child: widget.child,
);
}
}
こんな感じになりました。
RaisedButton
の引数のうち、とりあえず毎回使う onPressed
と child
だけをラップしています。必要に応じて他の引数も渡せるようにしてみてください。
_waiting
という状態を持たせて、 onPressed
の処理を実行している間だけ _waiting
が true
になるようにし、 _waiting
が true
の場合は onPressed
に null
が渡るようにしています。
あとは RaisedButton
の代わりにこの WaitableRaisedButton
を使うだけです👍
- child: RaisedButton(
+ child: WaitableRaisedButton(
これで、連打できないボタンが作れました🙌
本格的に使う場合はライブラリを活用しましょう
ちなみに、 pub.dev を検索してみると素晴らしいライブラリが色々配布されています。
- like_button | Flutter Package
- argon_buttons_flutter | Flutter Package
- progress_state_button | Flutter Package
今回は簡単なものを自前で用意する場合の実装例を示しましたが、本格的に使う場合はこの辺りのライブラリを活用するのがよいと思います✋
Discussion