すらぼうの開発ノート

モバイルアプリエンジニアのメモ

【Flutter】Radioウィジェットを使ってシンプルなRadioボタングループを作成する

以下のスクリーンショットのように、ユーザーに複数の選択肢の中から一つを選んでもらう際にRadioボタンを作成したいケースがある。

material.io

radio buttons sample

このようなケースではRadioウィジェットを使うと簡単に作成できる。

本エントリではRadioを用いたシンプルなRadioボタンの作成方法を説明する。

Radioウィジェットとは

api.flutter.dev

RadioウィジェットMaterial DesignのRadio Buttonsを実装するためのクラス。 上記リンクでも説明されている通り、単独ではなくListTileウィジェットと合わせて使うことが多い。

アプリの設定画面などで使用頻度の高いウィジェットで、選択された値は providerなどを用いてアプリ内でシェアするといった使い方もある。 (light / dark テーマの設定など)

Radioボタンの作成方法

次のスクリーンショットのようなサンプルアプリを作成して説明する。

radio buttons sample app

選択した値をTextで表示するというシンプルなアプリ。

では作り方を説明する。

1. 選択値用の変数作成

Radioウィジェットで選択した値を格納する変数を用意する。

int _currentValue = 1;

2. RadioウィジェットをListTileウィジェットでラップ

上述した通り、RadioウィジェットListTileウィジェットと合わせて使うことが多い。 各選択肢を次のように記述する。

ListTile(
  title: Text("1"),
  leading: Radio<int>(
      value: 1,
      groupValue: _currentValue,
      onChanged: (val) {
        setState(() {
          _currentValue = val ?? 1;
        });
      }),
),

上記のコードでRadioボタンが一つできる。

radio in listtile

これを選択肢の数だけ作成する。ポイントはgroupValueを揃えること。 これで同じグループに属したRadioボタンであることがわかる。 逆に異なるグループであればgroupValueは異なる変数を渡す必要がある。

valueプロパティはかく選択肢に割り振りたい値を渡す。 文字列でも数値でもよい。

またonChangedには、選択された際の処理をコールバック形式で記述する。 引数にはRadioウィジェットvalueが渡されるので、setState()で状態を更新する。

ListTileを3つに増やすと次の様になる。

ListTile(
  title: Text("1"),
  leading: Radio<int>(
      value: 1,
      groupValue: _currentValue,
      onChanged: (val) {
        setState(() {
          _currentValue = val ?? 1;
        });
      }),
),
ListTile(
  title: Text("2"),
  leading: Radio<int>(
      value: 2,
      groupValue: _currentValue,
      onChanged: (val) {
        setState(() {
          _currentValue = val ?? 2;
        });
      }),
),
ListTile(
  title: Text("3"),
  leading: Radio<int>(
      value: 3,
      groupValue: _currentValue,
      onChanged: (val) {
        setState(() {
          _currentValue = val ?? 3;
        });
      }),
),

この記述でRadioボタンが3つ作成できる。

3 radio buttons

3. Radioウィジェットで選択した値を表示する

選択した値が格納されている_currentValueを表示する。

Text(
  "Selected Number: $_currentValue",
  style: TextStyle(fontSize: 24, fontWeight: FontWeight.w800),

あとはデザインを少し整えて冒頭のgif画像の様なアプリができる。

radio buttons sample app

最終的なコードは次の様になる。

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'radio group app',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: HomeWidget());
  }
}

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

  @override
  State<HomeWidget> createState() => _HomeWidgetState();
}

class _HomeWidgetState extends State<HomeWidget> {
  int _currentValue = 1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Radio Example App")),
      body: SafeArea(
        child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  "Select Number",
                  style: TextStyle(fontSize: 24, fontWeight: FontWeight.w800),
                ),
                ListTile(
                  title: Text("1"),
                  leading: Radio<int>(
                      value: 1,
                      groupValue: _currentValue,
                      onChanged: (val) {
                        setState(() {
                          _currentValue = val ?? 1;
                        });
                      }),
                ),
                ListTile(
                  title: Text("2"),
                  leading: Radio<int>(
                      value: 2,
                      groupValue: _currentValue,
                      onChanged: (val) {
                        setState(() {
                          _currentValue = val ?? 1;
                        });
                      }),
                ),
                ListTile(
                  title: Text("3"),
                  leading: Radio<int>(
                      value: 3,
                      groupValue: _currentValue,
                      onChanged: (val) {
                        setState(() {
                          _currentValue = val ?? 3;
                        });
                      }),
                ),
                SizedBox(
                  height: 16,
                ),
                Text(
                  "Selected Number: $_currentValue",
                  style: TextStyle(fontSize: 24, fontWeight: FontWeight.w800),
                ),
              ],
            )),
      ),
    );
  }
}