ラベル付きのチェックボックス/ラジオボタン【Flutter 3.35 対応】

2025/08/22 22:47公開

一般的なチェックボックスのユースケース(設定画面など)においてはLabeledListTileを使うことで、ラベル付きのチェックボックスが実現できます。しかし、横方向に大量のチェックボックスを並べたいとき(画像参照)には、やはり「ラベル付きのチェックボックス」というウィジェットが欲しいところです。

image.webp

今回は、私が使っているサンプルを載せますので、適宜カスタムして使ってみてください〜

Table of Contents
  1. LabeledCheckBox
    1. 軽い説明
  2. LabeledRadio
    1. 軽い説明

LabeledCheckBox

import "package:flutter/material.dart";

class LabeledCheckBox extends StatelessWidget {
  final Widget child;
  final bool value;
  final ValueChanged<bool?>? onChanged;

  const LabeledCheckBox({
    super.key,
    required this.child,
    required this.value,
    required this.onChanged,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => onChanged?.call(!value),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          SizedBox(
            width: 32,
            height: 32,
            child: Checkbox(
              value: value,
              onChanged: onChanged,
            ),
          ),
          _animatedChild(context),
        ],
      ),
    );
  }

  Widget _animatedChild(BuildContext context) {
    final enabledStyle = Theme.of(context).textTheme.labelLarge!.copyWith(
      fontSize: 15,
    );
    final disabledStyle = enabledStyle.copyWith(
      color: Theme.of(context).disabledColor,
    );
    return AnimatedDefaultTextStyle(
      style: onChanged != null ? enabledStyle : disabledStyle,
      duration: kThemeChangeDuration,
      child: child,
    );
  }
}

軽い説明

RowCheckBoxchild(ウィジェット)を並べ、最低限必要なパラメータだけを伝搬しています。全体をGestureDetectorでラップしてonChangedを呼んでいるため、ラベル部分をタップしても反応させることができます。チェックボックスの有効・無効が切り替わったときにテキストの色がアニメーションする機能付き。

LabeledRadio

import "package:flutter/material.dart";

class LabeledRadio<T> extends StatelessWidget {
  final T value;
  final Widget? label;
  final Color? activeColor;

  const LabeledRadio({
    super.key,
    required this.value,
    this.label,
    this.activeColor,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => RadioGroup.maybeOf<T>(context)?.onChanged(value),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          SizedBox(
            width: 32,
            height: 32,
            child: Radio<T>(
              value: value,
              activeColor: activeColor,
            ),
          ),
          if (label != null) DefaultTextStyle(
            style: Theme.of(context).textTheme.labelLarge!.copyWith(
              fontSize: 15,
            ),
            child: label!,
          ),
        ],
      ),
    );
  }
}

軽い説明

こちらが今回の問題。Flutter 3.35より、RadiogroupValueonChangedパラメータが非推奨となり、代わりに親としてRadioGroupウィジェットを設置する必要があります。今まではRadioごとにonChangedコールバックを保持していたため、ラベルを押したときにラジオをチェックさせることは簡単でした。しかし、この仕様変更によって、どうにかして値の変更を通知する必要が出てきました。

結論から言うと、RadioGroup.maybeOf関数を用いてRadioGroupRegistryを取得し、そのonChangedメソッドを呼び出すことで、変更を通知できます。しかし、ここに一つ落とし穴が。Radioが機能するためにはRadioGroupの型引数Tとすべての子Radioの型引数Tが同じ型である必要がありますが、RadioGroup.maybeOf関数にもそれらと同じ型引数を渡す必要があります。 何も渡さないとnullが返却されます。ここでしばらくハマりました。