【Flutter】ボタンの種類と使い方|押せなくする・色を変える

当サイトで紹介する商品・サービス等の外部リンクは、アフィリエイト広告を含む場合があります。
スポンサーリンク
本記事を読むと以下の実行ができます

Flutter の5種類のボタンの違いを動かして確かめ、状態で色とアイコンが変わるマイクボタン(FloatingActionButton)と、押せなくする切り替えを作れます。

アプリには、必ずと言っていいほどボタンがあります。Flutter にはボタンの種類がいくつもあり、「どれを使えばいいのか」「押せなくするにはどうするのか」と迷いやすい部品です。

本記事では、よく使う5種類のボタンを並べて押し比べたあと、音声入力の開始と停止に使うマイクボタンを作ります。聞き取り中は赤くなり、スイッチで押せなくもできます。

本記事は次の人におすすめ
  • Flutter のボタンの種類と使い分けを知りたい人
  • 押したときの処理(onPressed)の書き方を知りたい人
  • 状態によってボタンの色やアイコンを変えたい人
  • ボタンを押せなくする方法を知りたい人
スポンサーリンク

Flutterのボタンの種類

よく使うボタンは、次の5つです。(FilledButton・ElevatedButton・OutlinedButton・TextButton・IconButton)どれも、押したときの処理を onPressed に渡すと動きます。

ボタン見た目使いどころ
FilledButton色で塗りつぶされているその画面でいちばん大事な操作
ElevatedButton影があり、浮いて見える目立たせたい操作。背景と区別したいとき
OutlinedButton枠線だけ補助的な操作
TextButton文字だけ控えめな操作
IconButtonアイコンだけツールバーなど、場所を取りたくないとき

使い分けの目安は、「目立たせたい操作ほど、塗りつぶしや影のあるボタンにする」ことです。1つの画面に塗りつぶしのボタンを並べすぎると、どれが大事か分からなくなります。

古い記事に出てくる RaisedButton、FlatButton などは、いまは使われていません。新しく書くときは、上の5つを使ってください。

押せなくする:onPressed に null

どのボタンも、onPressed に null を渡すと、押せなくなります。公式の説明でも、onPressed が null のボタンは無効になります。

ElevatedButton(
  onPressed: isReady ? () { /* 処理 */ } : null,
  child: const Text('送信'),
)

条件によって、処理の関数と null を切り替える書き方です。

FloatingActionButton

FloatingActionButton は、画面の上に浮いて見える丸いボタンです。画面でいちばん大事な操作(作る、共有する、録音を始めるなど)に使います。公式の説明では、1つの画面に置くのは多くても1つにするよう案内されています。

Scaffold の floatingActionButton に渡すと、通常は画面の右下に置かれます。

画面の枠組みを作る Scaffold について解説しています。

主なプロパティは次のとおりです。

プロパティ役割
onPressed押したときの処理。null だと押せなくなる
childボタンの中身。ふつうはアイコン
tooltip長押ししたときに出る、ボタンの説明
backgroundColorボタンの色

参考:FloatingActionButton(Flutter公式)

この記事では、次の画面を作ります。

  • 右下のマイクボタンを押すと、聞き取り中(赤)と停止中を切り替わる。アイコンと説明文も変わる
  • 「マイクボタン許可」スイッチをオフにすると、マイクボタンを押せなくなる
  • 5種類のボタンを押すと、押した回数が増える

ソースコード

  • mic_button.dart

マイクボタンの部品です。状態で色とアイコンを変えます。

import 'package:flutter/material.dart';

/// 音声入力の開始/停止ボタン。聞き取り中は赤くなる。
/// onPressed が null のときは押せない。
class MicButton extends StatelessWidget {
  const MicButton({
    super.key,
    required this.isListening,
    required this.onPressed,
  });

  final bool isListening;
  final VoidCallback? onPressed;

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      onPressed: onPressed,
      tooltip: isListening ? '停止' : '音声入力開始',
      backgroundColor: isListening ? Colors.red : null,
      child: Icon(isListening ? Icons.mic : Icons.mic_none),
    );
  }
}
  • button_page.dart

画面です。状態と、5種類のボタンを持ちます。

import 'package:flutter/material.dart';

import '../widgets/mic_button.dart';

class ButtonPage extends StatefulWidget {
  const ButtonPage({super.key, required this.title});

  final String title;

  @override
  State<ButtonPage> createState() => _ButtonPageState();
}

class _ButtonPageState extends State<ButtonPage> {
  bool _isListening = false;
  bool _enabled = true;
  int _tapCount = 0;

  void _toggleListening() => setState(() => _isListening = !_isListening);

  void _countUp() => setState(() => _tapCount++);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(_isListening ? '聞き取り中(デモ)' : '停止中'),
            SwitchListTile(
              title: const Text('マイクボタン許可'),
              value: _enabled,
              onChanged: (value) => setState(() => _enabled = value),
            ),
            const Divider(),
            Text('ボタンの種類(押した回数: $_tapCount)'),
            const SizedBox(height: 12),
            Wrap(
              spacing: 12,
              runSpacing: 12,
              children: [
                FilledButton(onPressed: _countUp, child: const Text('Filled')),
                ElevatedButton(onPressed: _countUp, child: const Text('Elevated')),
                OutlinedButton(onPressed: _countUp, child: const Text('Outlined')),
                TextButton(onPressed: _countUp, child: const Text('Text')),
                IconButton(
                  onPressed: _countUp,
                  tooltip: 'Icon',
                  icon: const Icon(Icons.thumb_up),
                ),
              ],
            ),
          ],
        ),
      ),
      floatingActionButton: MicButton(
        isListening: _isListening,
        onPressed: _enabled ? _toggleListening : null,
      ),
    );
  }
}
  • main.dart

動かすための最小の入口です。mic_button.dart は lib/widgets/、button_page.dart は lib/pages/ に置きます。

import 'package:flutter/material.dart';

import 'pages/button_page.dart';

void main() {
  runApp(
    const MaterialApp(home: ButtonPage(title: 'ボタン')),
  );
}

解説

画面(ButtonPage)が、「聞き取り中か」「マイクボタンを押せるか」「押した回数」の3つの状態を持ちます。マイクボタンの見た目は、MicButton が受け取った値だけで決まります。

MicButton:状態で色とアイコンを変える

isListening が true のときだけ赤くし、アイコンとツールチップも切り替えています。

backgroundColor: isListening ? Colors.red : null,
child: Icon(isListening ? Icons.mic : Icons.mic_none),

条件が true か false かで値を選ぶ書き方です。backgroundColor に null を渡すと、テーマで決まっている標準の色になります。

色だけでなくアイコンも変えているので、色の違いが見分けにくい人にも、状態が伝わります。

onPressed に null を渡す

MicButton の onPressed は、VoidCallback? という型で受け取ります。? は、null も受け取れるという意味です。

画面側では、スイッチの状態で、処理と null を切り替えます。

onPressed: _enabled ? _toggleListening : null,

公式の説明では、FloatingActionButton の onPressed を null にすると押せなくなりますが、見た目で押せないことが分かりにくいので、おすすめしないとされています。押せなくするときは、あわせて backgroundColor を変えて、押せないことが見た目で分かるようにしてください。例は次のとおりです。

backgroundColor: onPressed == null
    ? Colors.grey
    : (isListening ? Colors.red : null),

部品に切り出す理由

マイクボタンを MicButton として別のクラスに切り出すと、見た目の決まりごと(赤くする、アイコンを変える)を1か所にまとめられます。画面側は、状態を渡すだけで済みます。同じボタンを別の画面で使うときにも、そのまま使えます。

_countUp:5種類のボタンに同じ処理を渡す

5種類のボタンには、すべて同じ _countUp を渡しています。押した回数が増えることで、どのボタンも同じように onPressed で動くことが確かめられます。

実装

  1. Flutter のプロジェクトを作る(追加するパッケージはありません)
  2. 2つのファイルを、上の場所に置く
  3. main.dart を書き換えて、実行する

マイクボタンを押すと赤くなり、もう一度押すと元に戻ります。スイッチをオフにすると、マイクボタンを押せなくなります。

まとめ

  • ボタンは FilledButton・ElevatedButton・OutlinedButton・TextButton・IconButton の5種類を使い分ける
  • 押したときの処理は onPressed に渡し、null を渡すと押せなくなる
  • FloatingActionButton は、画面でいちばん大事な操作に1つだけ置く
  • 押せなくするときは、見た目も変えて、押せないと分かるようにする
  • 状態で変わる見た目は、部品に切り出して1か所にまとめる
タイトルとURLをコピーしました