【Flutter】FilledButtonとMaterial 3 での各ボタン

対象者

  • FlutterでのMaterial 3が気になる人
  • Material 3 で導入されたFilledButtonが気になる人

はじめに

FlutterにMaterial 3 が導入されて、ボタンが丸くなり、お洒落になりました(多分、センスないんで、断言できない)。また原色を指定しても、いい感じになっています(ような気がする)。実際どうなのかと思い比較してみます。
また、Material 3になって導入されたFilledButtonがどんな感じかいていきたいと思います。

外観

Material 3 の設定と色の設定が異なるだけの、同じソースの結果を表示してます。
並べてみるとやっぱり、色がちょっと違いますね。
ボタンも丸くなっているだけかと思いましたが、テキストの色が変わってます。
ElevatedButtonのボタンの色が弱くなってますね。元々のスタイルはFilledButtonに取って代わられた感じがします。特にダーク時にボタンの色が黒っぽくて、ぱっと見ボタンと気づかないかも。

Material3ではない Material 3 (ライト) Material 3 (ダーク)
全体 Mateerial2 Mateerial2 Mateerial3
差分 useMaterial3: false,
primarySwatch: Colors.red,
useMaterial3: true,
colorSchemeSeed: Colors.red,
useMaterial3: true,
colorSchemeSeed: Colors.red,

FilledButton

さて、今回は新たに導入されたFilledButtonを見ていきましょう。(onPressは省略してます)
ElevatedButtonと同じようにボタンに色を塗りますが、浮き上がり(影)がない点が異なります。

FilledButton(child: Text('FilledButton')),
FilledButton.tonal(
    child: Text('FilledButton.tonal'),
),
FilledButton.icon(
    icon: Icon(Icons.add),
    label: Text('FilledButton.icon'),
),
FilledButton.tonalIcon(
    icon: Icon(Icons.add),
    label: Text('FilledButton.tonalIcon'),
),

ボタンの種類としては「デフォルト」「tonal」の2種類 X アイコンの有り無し2種類、で4種類になります。
アイコンの有り無しは、ラベルの左側にアイコンを表示できるか、できないか、だけです。
そして、「デフォルト」「tonal」ではそれぞれ色が異なります。デフォルトがプライマリの色で、tonalがセカンダリの色です。プライマリはCTA(Call To Action: なにか実行を呼びかける)ボタンのため、なにを実行するためのボタン(購入、とか開発者側が押させたいボタン)です。セカンダリはCTA以外です(キャンセル、とか開発者側は押させたくないけど、ないとダメだよねというボタン)。
Material3的には、OutlinedButtonとElevatedButtonではなく、FilledButtonのデフォルトとtonalで使い分けていくのかな。

まとめ

Material 3 を使ったときのボタン、特にFilledButtonを見てきました。
ElevatedButtonとOutlinedButtonの角がデフォルトではとんがっているので、いつも角丸の設定にしていました。FilledButtonでその設定は不要になりました。
あとは、角を少し丸くするか、横側を半円にするか、の選択ですね。どっちがいいんだろ。

参考

全ソース

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  var _brightness = Brightness.light;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        useMaterial3: true,
        //primarySwatch: Colors.red,
        colorSchemeSeed: Colors.red,
        brightness: _brightness,
      ),
      home: MyHomePage(
        title: 'Flutter Demo Home Page',
        changeBrightness: changeBrightness,
      ),
    );
  }

  void changeBrightness() {
    setState(() {
      _brightness =
          _brightness == Brightness.light ? Brightness.dark : Brightness.light;
    });
  }
}

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

  final String title;

  final void Function() changeBrightness;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _radioValue = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: SingleChildScrollView(
          child: Column(
            children: [
              FilledButton(onPressed: () {}, child: Text('FilledButton')),
              FilledButton.tonal(
                onPressed: () {},
                child: Text('FilledButton.tonal'),
              ),
              FilledButton(
                  onPressed: null, child: Text('FilledButton disabled')),
              FilledButton.tonal(
                onPressed: null,
                child: Text('FilledButton.tonal disabled'),
              ),
              FilledButton.icon(
                onPressed: () {},
                icon: Icon(Icons.add),
                label: Text('FilledButton.icon'),
              ),
              FilledButton.tonalIcon(
                onPressed: () {},
                icon: Icon(Icons.add),
                label: Text('FilledButton.tonalIcon'),
              ),
              FilledButton(
                  onPressed: () {},
                  child: Text(
                    'LargeFont',
                    style: TextStyle(fontSize: 48),
                  )),
              Divider(),
              ElevatedButton(onPressed: () {}, child: Text('ElevatedButton')),
              OutlinedButton(onPressed: () {}, child: Text('OutlinedButton')),
              TextButton(onPressed: () {}, child: Text('TextButton')),
              Text('Just text (Not Button)'),
              Checkbox(value: true, onChanged: (_) {}),
              TextField(),
              Switch(value: true, onChanged: (_) {}),
              DropdownMenu<int>(
                  dropdownMenuEntries: [1, 2, 3]
                      .map((e) =>
                          DropdownMenuEntry(value: e, label: e.toString()))
                      .toList()),
              Radio(
                value: true,
                groupValue: _radioValue,
                onChanged: (_) {},
              )
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.dark_mode), onPressed: widget.changeBrightness),
    );
  }
}