単一選択ボタン(ラジオボタン):基礎とenumを使った実践

ラジオボタンは、複数の選択肢の中から1つのアイテムを選択(単一選択・排他的選択)できるボタンです。

この記事では、FlutterのRadioListTile()を使ったラジオボタンの作り方を解説します。

RadioListTile()で使う変数は、int型で指定するのがわかりやすい方法ですが、より実践的に使うためにenum型を使う方法と、enum型を使うメリットも紹介します。

動作確認バージョン
  • Flutter : 3.27.1
  • Dart : 3.6.0

\\ChoiceChipを使う選択ボタンはこちら//

目次

基本のラジオボタン

FlutterのRadioListTile()で作成したラジオボタン
FlutterのRadioListTile()

一般的にラジオボタンは、ボタンとテキストを組み合わせて作ることが多いので、Flutterでは、この2つをセットにして扱えるウィジェットRadioListTile()を使うのが一番簡単です。

RadioListTileで使う代表的なプロパティは、次の4つです。

RadioListTileの代表的なプロパティ
  • title:ボタンの横に表示する文字列など(ウィジェットで指定する)
  • value:このボタンに割り当てる値(int型の数値指定が簡単)
  • groupValue:選択肢の中で選ばれている値(value:と同じ型の変数を用意する)
  • onChanged:ボタンがクリックされたときの処理
int _radioValue = 1;

RadioListTile(
  title: Text('選択肢 1'),
  value: 1,
  groupValue: _radioValue,
  onChanged: (int? value) {
    setState(() {
      _radioValue = value!;
    });},
),

上記のRadioListTile()一つが、ラジオボタンの一つ分です。

ボタンが押されたときに実行されるonChanged:では、setState()の中でgroupValue:に指定した変数を更新して、画面を再描画します。

選択肢を複数個並べるときは、このRadioListTile()Column()で包みます。

複数の選択肢を作るときは、value: 1,の部分を、value: 2,value: 3,のように別の番号を指定します。

ボタンだけ表示したいときは、テキスト部分のないRadio()ウィジェットを使ってもいいですが、RadioListTiletitle:プロパティを書かなければボタンだけが表示されます。

title:プロパティは、必須プロパティではありません。

RadioListTile()の実装:3ステップ

FlutterのRadioListTile()で作成した選択ボタン3つ。
基本のRadioListTile
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _radioValue = 1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[

          // 1つめのボタン
          RadioListTile(
            title: Text('選択肢 1'),
            value: 1,
            groupValue: _radioValue,
            onChanged: (int? value) => _onRadioSelected(value),
          ),

          // 2つ目のボタン
          RadioListTile(
            title: Text('選択肢 2'),
            value: 2,
            groupValue: _radioValue,
            onChanged: (int? value) => _onRadioSelected(value),
          ),

          // 3つ目のボタン
          RadioListTile(
            title: Text('選択肢 3'),
            value: 3,
            groupValue: _radioValue,
            onChanged: (int? value) => _onRadioSelected(value),
          ),
        ],
      ),
    );
  }

  _onRadioSelected(int? value) {
    setState(() {
      _radioValue = value!;
    });
  }
}

順を追って解説します。

STEP
StatefulWidgetを作る

まず初めのポイントは、StatefulWidgetです。(行番号1)
選択結果の画面更新でsetState()を使うには、StatefulWidgetが必要です。

STEP
ラジオボタンの値を入れる変数を宣言

次は、行番号8の変数宣言です。

class _MyHomePageState extends State<MyHomePage> {
  int _radioValue = 1;

  @override
  Widget build(...

この変数には「どのラジオボタンが選ばれているか」という値が入ります。
ここでは、int型で_radioValueという変数を作成して、初期値を1にしました。

groupValue: _radioValueとしたラジオボタン群が、「排他的選択ができるボタンのグループ」になります。

STEP
RadioListTileウィジェットの記述
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          
          // 1つめのボタン
          RadioListTile(
            title: Text('選択肢 1'),
            value: 1,
            groupValue: _radioValue,
            onChanged: (int? value) => _onRadioSelected(value),
          ),
          
          // 2つ目のボタン
          RadioListTile(
            title: Text('選択肢 2'),
            value: 2,
            groupValue: _radioValue,
            onChanged: (int? value) => _onRadioSelected(value),
          ),

          // 3つ目のボタン
          RadioListTile(
            title: Text('選択肢 3'),
            value: 3,
            groupValue: _radioValue,
			...
			...

RadioListTile()value:の値は、各ボタンごとに違う値にします。

groupValue:で指定する変数がint型なら、順番に1、2、3という感じです。(行番号19, 27, 35)

横並びにするならRow()で包みますが、スマホの幅に収まりきらないとエラーになるので、Row()をさらにWrap()で包むと、入りきらないラジオボタンは次の行へ自動で送られます。

下記のChoiceChipを使った単一選択ボタンの書き方では、Wrap()を使った例を紹介しています。

応用:enumで選択値を管理する

選択肢が多い場合などは、RadioListTile()の選択値をint型ではなく、自分独自のenum型を作って管理すると、あとのメンテナンスの手間の軽減が期待できます。

enum型は、日本語では「列挙型」と訳されるように、使用する値を独自に列挙しておける「型」です。

列挙する値は自分独自の単語で指定できるので、単に数値で管理するよりも直感的でわかりやすくなります。

FlutterのRadioListTile()で選択ボタンに割り当てる値にenum型の変数を作って、色名で管理する。
選択肢の色名をenumで管理するには…
STEP
enum型を作成

まずは、色名を列挙しておくenumを作ります。

値は、カンマで区切って並べます。
リストに似ていますが、[ ]ではなく{ }で囲むことに注意です。
文末に;は入りません。

enum RadioValue { red, blue, green }

このコードで、RadioValueという自分独自の新しい型が作成できます。

STEP
enum型を使う

作成したenum型で変数を作ります。

ここでは、初期画面で赤が選択されるよう、初期値にRadioEnumValue.redを代入しました。(行番号25)

enum RadioValue { red, blue, green }

class _MyHomePageState extends State<MyHomePage> {
  RadioValue _radioValue = RadioValue.red;

  @override
  Widget build(BuildContext context) {
    return ...
STEP
RadioListTile()のvalue:プロパティに指定
          RadioListTile(
            title: Text("赤", style: TextStyle(color: Colors.red)),
            value: RadioValue.red,
            groupValue: _radioValue,
            onChanged: (RadioValue? value) => _onRadioSelectedEnum(value),
          ),

これで、タイトルに「赤」と表示する選択肢はRadioValue.redという値で扱えるようになります。

下記コードが、enumを使った全文です。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

enum RadioValue { red, blue, green }

class _MyHomePageState extends State<MyHomePage> {
  RadioValue _radioValue = RadioValue.red;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          SizedBox(height: 100.0),

          // 1つめのボタン
          RadioListTile(
            title: Text("赤", style: TextStyle(color: Colors.red)),
            value: RadioValue.red,
            groupValue: _radioValue,
            onChanged: (RadioValue? value) => _onRadioSelectedEnum(value),
          ),

          // 2つめのボタン
          RadioListTile(
            title: Text("青", style: TextStyle(color: Colors.blue)),
            value: RadioValue.blue,
            groupValue: _radioValue,
            onChanged: (RadioValue? value) => _onRadioSelectedEnum(value),
          ),

          // 3つめのボタン
          RadioListTile(
            title: Text("緑", style: TextStyle(color: Colors.green)),
            value: RadioValue.green,
            groupValue: _radioValue,
            onChanged: (RadioValue? value) => _onRadioSelectedEnum(value),
          ),
        ],
      ),
    );
  }

  _onRadioSelectedEnum(RadioValue? value) {
    setState(() {
      _radioValue = value!;
    });
  }
}

enumを使うメリット・デメリット

enumは、使うのに慣れないと難しく感じます。

Flutter公式のサンプルコードでenumを使っているのを見ても、複雑な印象を受けるかもしれません。

でも、enumでRadioButtonを管理するメリットは、ちゃんとあるんです。

メリット その1

enumを使うメリットがあるケースの一つ目は、選択肢が「たくさんある」ときや、作成後に「追加削除」や「並び順の変更」が考えられるときです。

選択項目がたくさんあるラジオボタングループ。
「たくさんの選択肢」が並んでいて、今後「追加・削除」や「順番変更」がありそう…

ボタンに指定するvalue: 1のint数値は、上からカウントアップ指定する必要はありません、削除・追加・位置の入れ替えで番号が飛び飛びになったり欠番があると、メンテナンス時の混乱の原因になります。

メリット その2

二つ目のケースは、title:の表示内容が「長文」だったり、テキスト表示の代わりに「アイコン」や「イメージ」を使っている場合など、内容が一目でわかりにくいときです。

こういうときは、単語1つで内容がわかるenumを使うと、「2番はなんだっけ?」といちいちタイトルのテキストを確認する必要がなくなります。

選択項目の内容が一目で認識できないラジオボタンの例
タイトルをパッと見ても内容が把握しにくい
            RadioListTile(
              title: Text("はるはあけぼの やうやうしろく..."),
              value: RadioValue.spring,
              groupValue: _radioValue,
              onChanged: (RadioValue? value) => _onRadioSelectedEnum(value),
            ),

            RadioListTile(
              title: Text("ふゆはつとめて ゆきのふりたるは..."),
              value: RadioValue.winter,
              groupValue: _radioValue,
              onChanged: (RadioValue? value) => _onRadioSelectedEnum(value),
            ),

            RadioListTile(
              title: Text("あきはゆうぐれ ゆうひのさして..."),
              value: RadioValue.autumn,
              groupValue: _radioValue,
              onChanged: (RadioValue? value) => _onRadioSelectedEnum(value),
            ),

            RadioListTile(
              title: Text("なつはよる つきのころはさらなり..."),
              value: RadioValue.summer,
              groupValue: _radioValue,
              onChanged: (RadioValue? value) => _onRadioSelectedEnum(value),
            ),

メリット その3

アプリ全体で何らかのステータス管理をenumで行っていて、そのenumにラジオボタンを連動させることができるとき。

実は、これはメリットというよりも、より実践的なアプリでは必然的に導入することになる手法の一つです。

ラジオボタンで値が選択されたあとの処理は、条件ごとに処理分岐させるケースが多いので、選択値をenum型として明示して管理するとコードがわかりやすくなるのです。

デメリット

RadioListTile()で、enumを使うデメリットがあるケースがあるとしたら、選択内容が単純でわかりやすいときだと思います。

ラジオボタンのためだけに、わざわざenumを作るのは手間が増えるだけですから、簡単な選択ボタンだけならint型で十分でしょう。

サブタイトルをつけるには

この記事の冒頭の動画で表示しているRadioListTile()では、タイトルの下にサブタイトルを出しています。

RadioListTile()にサブタイトルを付けるには、subtitle:プロパティを使います。

            RadioListTile(
              title: Text("サブテキスト付き 1"),
              subtitle: Text("選択肢1を説明"),
              value: RadioEnumValue.first,
              groupValue: _radioEnumValue,
              onChanged: (RadioEnumValue? value) => _onRadioSelectedEnum(value),
            ),
よかったらシェアしてね!
  • URLをコピーしました!

コメント

コメントする

目次