チェックボックスは簡単便利なCheckboxListTile()で作ろう

チェックボックスは、ユーザーにon/off設定を切り替えて欲しいときに使います。

FlutterのCheckboxListTile()は、「チェックボックスの四角い枠」と「表示する文字(ラベル)」がセットで扱える便利なウィジェットです。

Checkbox()Text()を個別に組み合わせなくていいし、ラベル部分をクリックしてもチェック操作ができるので、アプリユーザーも使いやすいWidgetです。

この記事では、FlutterのCheckboxListTile()の基本的な作り方や装飾方法のほか、自分独自の仕様でカスタムウィジェットにしたり、複数のチェックボックスを動的に作成する方法など、少し実践的な事例も紹介します。

動作確認バージョン
  • Flutter : 3.27.1
  • Dart : 3.6.0
  • Android エミュレーター : Pixel 6a Android13.0 (API33)
  • iOSシミュレーター : iPhone 15 Pro
目次

チェックボックスの作り方

FlutterのCheckboxListTile()で作成したチェックボックス(左がAndroid、右がiOS)
CheckboxListTile()で作成したチェックボックス

上記のようなチェックボックスをCheckboxListTile()を使って作っていきます。

STEP

StatefulWidgetを用意

チェックボックスは、クリック後のチェックマークの再描画が必要です。

画面を再描画するには、FlutterではsetStateを使うのがまずは一番の基本ですから、setStateを使えるStatefulWidgetを作ります。

ここでは、FlutterのサンプルコードをもとにしたMyHomePage()を使っていきます。

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(
      title: 'Checkbox Try',
      home: MyHomePage(),
    );
  }
}

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

class _MyHomePageState extends State<MyHomePage> {

STEP

表示する文字列を設定(title:)

title:は必須項目(required)ではないので、何も表示しない場合は設定しなくてもエラーにはなりません。

でも、ボックスだけならCheckbox()を使えばいいので、CheckboxListTile()を使うなら、title:は用途的には必須項目みたいなもんだと思います。

class _MyHomePageState extends State<MyHomePage> {


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.only(top: 100.0),
        child: Column(
          children: <Widget>[
            CheckboxListTile(
              title: Text("Checkbox ListTile"),

STEP

値を設定(value:)

チェックボックスがクリックされたときの値を、value:プロパティ(行番号37)に設定します。
value:required(必須項目)です。

設定する値は、trueまたはfalseのbool型で、build()の前で変数を作ります。(行番号26)

ここでは、初期値をfalse(チェックなし)にして、_isCheckedという名前の変数を作りました。

class _MyHomePageState extends State<MyHomePage> {
  bool _isChecked = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.only(top: 100.0),
        child: Column(
          children: <Widget>[
            CheckboxListTile(
              title: Text("Checkbox ListTile"),
              value: _isChecked,
STEP

クリックされたときの処理を作成(onChanged:)

ボックスがクリックされたときの処理をonChanged:に書きます。(行番号38)

クリック結果(on/off:truefalse)を、onChanged:の引数(value)(行番号38)で受け取って、setStateに渡します。

setStateでは、(value)の値を_isCheckedに代入します。
_isCheckedは、value: _isCheckedで表示する値に設定しているので、新しい値で画面が更新されるというわけです。

ここでは_isCheckedへの値の代入しかしてませんが、実際にはこれに続けて何らかの処理を書くことになります。

            CheckboxListTile(
              title: Text("Checkbox ListTile"),
              value: _isChecked,
              onChanged: (value) => setState(() {
                _isChecked = value!;
              }),

行番号39のvalue!についている!は、null safety対応の!で、「絶対にnullにはならない」という意味です。

「null safety」は、値がもしnullだったときにアプリがフリーズしてしまうことを避けるためにとる対応です。

STEP

基本体裁:ボックスの位置(controlAffinity:)

FlutterのCheckboxListTile()でボックス位置を先頭に変更したAndroidスマホの画面
ボックスの表示位置を先頭に変更

CheckboxListTile()で表示するボックスは、何も指定しなければ末尾に表示されますが、controlAffinity:で先頭表示に変更できます。(行番号41)

            CheckboxListTile(
              title: Text("Checkbox ListTile"),
              value: _isChecked,
              onChanged: (value) => setState(() {
                _isChecked = value!;
              }),
              controlAffinity: ListTileControlAffinity.leading,
ボックス位置の指定:ListTileControlAffinity

*.leading:先頭
*.trailing:末尾
*.platform:表示しているプラットフォーム(AndroidとかiOS)の仕様に依存

STEP

基本体裁:アイコン(secondary:)

FlutterのCheckboxListTile()で表示したチェックボックスでListTileControlAffinityの設定を変更して、アイコンを表示したAndroidスマホ画面。 表示位置は、常にボックスの反対側になっている。
アイコンはボックスの反対側に表示される

secondary:を使うと、ボックスの反対側にアイコン表示ができます。(行番号42)
ボックスが末尾表示なら、アイコンの表示位置は先頭です。

アイコン表示も必須項目ではないので、指定しなければ何も表示されません。

            CheckboxListTile(
              title: Text("Checkbox ListTile"),
              value: _isChecked,
              onChanged: (value) => setState(() {
                _isChecked = value!;
              }),
              controlAffinity: ListTileControlAffinity.leading,
              secondary: Icon(Icons.supervisor_account_sharp),

少し実践的な実装

カスタムWidgetにする方法

FlutterのHomeScreen()クラスから、カスタムウィジェットとして作ったCheckItem()を呼び出して表示した2つのチェックボックス。
HomeScreen()からCheckItem()を呼び出す

CheckboxListTile()を自分仕様にカスタマイズして、別クラスに作成しておくと、どこからでも呼び出せて便利です。

カスタムウィジェットとして別クラスにしておくメリットは、呼び出し元に同じコードをずらずらと並べなくていいことや、仕様変更のときには、カスタムウィジェットのクラスだけ変更すればいいので、メンテナンスが効率的になります。

下のコードでは、[main.dart]のHomeScreen()の中から、CheckItem()というクラス名で、2つのCheckboxListTile()を呼び出しています。(行番号33〜40)

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.only(top:100.0),
        child: Column(
          children: <Widget>[
            CheckItem(
              title: "チェックボックス",
              secondary: Icon(Icons.account_circle),
            ),
            CheckItem(
              title: "checkbox",
              secondary: Icon(Icons.account_circle_outlined),
            ),
          ],
        ),
      ),
    );
  }
}

[main.dart]では、カスタムウィジェットとして作成したCheckItem()を呼び出すときに、titlesecondaryの値を引数で渡しています。

カスタムウィジェット内に作成するプロパティの名称(ここでは、title:secondary:)を、実際のWidgetのプロパティ名と同じにしておくと、わかりやすいです。

下記のコードは、カスタムウィジェットとして別クラスに作成した[check_item.dart]ファイルです。

カスタムウィジェットのCheckItem()では、引数で受け取ったtitlesecondaryCheckboxListTile()(行番号22)のプロパティに設定して、setStateで再描画しています。

import 'package:flutter/material.dart';

class CheckItem extends StatefulWidget {
  const CheckItem({
    super.key,
    required this.title,// コンストラクタに title を作成
    required this.secondary,// 同様に secondary も作成
  });

  final String title; //コンストラクタに入れる変数を宣言
  final Icon secondary; //

  @override
  State<CheckItem> createState() => _CheckItemState();
}

class _CheckItemState extends State<CheckItem> {
  bool isChecked =  false;

  @override
  Widget build(BuildContext context) {
    return CheckboxListTile(
      title: Text(widget.title),// コンストラクタで受け取ったtitleを指定
      value: isChecked,
      onChanged: (value) => setState(() {
        isChecked = value!;
      }),
      controlAffinity: ListTileControlAffinity.leading,
      secondary: widget.secondary,// コンストラクタで受け取ったtitleを指定
    );
  }
}

StatefulWidgetで受け取った値をStateの中で使うときは、widget.titlewidget.secondaryのように、引数名の前にwidget.をつけて呼び出します。

上記の例では、onChanged:の処理内容もカスタムウィジェット内で実行するように作成しています。

onChanged:の内容を、呼び出し元で実行したい場合はこちらを参照

ListView.builder()と組み合わせる方法

CheckboxListTile()に表示するチェック項目を、あらかじめリスト形式で作成しておくと、ListView.builder()を使ってリスト項目の数だけ動的に生成することができます。

ListView.builder()を使うメリットは、動的に作成できるということだけでなく、「画面表示されるときにはじめて、ウィジェットが作られる」ことです。

言い換えると、画面に表示されていないリスト項目はWidgetとして未生成なので、メモリ上にも存在しません。

なので、リスト項目が多い場合、画面表示されるかどうかわからない(スクロール表示されないかもしれない)Widgetをメモリ上に持たないので、メモリの圧迫を最小限に抑えられるという利点があります。

特に、TODOリストでたくさんのリスト項目を表示する場合などは、ListView.builder()を使うと良いです。

次の例では、ItemData()というクラスを作って、そのクラスをdataというリストに20件登録しました。

class ItemData{
  const ItemData({required this.title});
  final String title;
}

List<ItemData> data =[
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
  ItemData(title: "checkbox"),
];

上記のdataを、[main.dart]から呼び出して使います。

呼び出し元の[main.dart]

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.only(top:50.0),
        child: ListView.builder(
          itemCount: data.length,
          itemBuilder: (context, index) {
            return CheckItem(title: "${data[index].title} ($index)");
          },
        ),
      ),
    );
  }
}

ListView.builder()の引数itemCount:に、dataリストの個数を指定します。(8行目)

itemBuilder:は、実際にWidgetを生成している行です。(9行目)

(context, index)contextBuildContextindexはint型で任意の変数名を指定します。(9行目)

このindexが、リスト内の何番目のデータなのかをカウントします。

itemBuilder:プロパティが返す値として、CheckItem()を指定します。
CheckItem()を呼び出すのは、この1文だけです。(10行目)

CheckItem()の引数title:に、"${data[index].title} ($index)"とすることで、indexがカウントアップしながらdataリストの値にアクセスしています。

変数に$を付けているのは、変数の値を文字列として連結するためです。
ここでは、($index)として、リスト内の何番目なのかを表示しています。

カスタムウィジェット[check_item.dart]

import 'package:flutter/material.dart';

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

  @override
  State<CheckItem> createState() => _CheckItemState();
}

class _CheckItemState extends State<CheckItem> {
  bool isChecked = false;

  @override
  Widget build(BuildContext context) {
    return CheckboxListTile(
      title: Text(widget.title),
      value: isChecked,
      onChanged: (value) => setState(() {
        isChecked = value!;
      }),
    );
  }
}
FlutterのListView.builder()を使って、複数の項目を画面表示しているAndroidスマホ画面
ListView.builder()で動的に作成

画面表示では20件のデータのうち先頭の13件が描画されました。
これ以降はスクロールすると表示されます。

よく使われるプロパティ(色・罫線・角丸)

CheckboxListTile()にはたくさんのプロパティがあります。

その中から、色や形状を変えるなどよく使うプロパティの使用例を示しました。

Android

FlutterのCheckboxListTile()の色と形状を、プロパティを使って変更し表示したAndroidスマホの画面
CheckboxListTile()の色と形状を変更

iPhone

FlutterのCheckboxListTile()の色と形状を、プロパティを使って変更し表示した iOSスマホの画面
iPhoneでも同様
CheckboxListTile(
  title: Text("タイトル"),
  value: _isCheckedJp,
  onChanged: (value) => setState(() {
    _isCheckedJp = value!;
  }),
  // アイコン
  secondary: Icon(Icons.account_circle),
  // チェックボックスの位置(leading・trailing・platform)
  controlAffinity: ListTileControlAffinity.leading,
  // ラベルの下に表示する補助ラベル
  subtitle: Text("サブタイトル"),
  // チェックボックスタイル全体の装飾
  shape: RoundedRectangleBorder(
    // 枠囲み罫線をつける
    side: BorderSide(),
    // 枠を角丸にする
    borderRadius: BorderRadius.circular(8.0),
  ),
  // チェックマークのボックスを角丸にする
  checkboxShape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(4.0),
  ),
  // カラー関連
  //  全体の背景色
  tileColor: Colors.lightGreen,
  //  ボックスをチェックしたときの「レ」の色
  checkColor: Colors.black,
  //  ボックスをチェックしたときの「ボックス」の色
  activeColor: Colors.red,
)
よかったらシェアしてね!
  • URLをコピーしました!

コメント

コメントする

目次