🤗

Flutter RadioButton

2023/02/24に公開

サンプルがわかりずらい?

Flutterのラジオボタンの使い方を見てみると、わかりにくくて、参考になる情報がなくて困った😅
enumは必要なのか?

こちらが公式
過去に試したがうまく使えない?
https://api.flutter.dev/flutter/material/Radio-class.html

import 'package:flutter/material.dart';

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

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

  static const String _title = 'Flutter Code Sample';

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: Scaffold(
        appBar: AppBar(title: const Text(_title)),
        body: const Center(
          child: MyStatefulWidget(),
        ),
      ),
    );
  }
}

enum SingingCharacter { lafayette, jefferson }

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({super.key});

  
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  SingingCharacter? _character = SingingCharacter.lafayette;

  
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        ListTile(
          title: const Text('Lafayette'),
          leading: Radio<SingingCharacter>(
            value: SingingCharacter.lafayette,
            groupValue: _character,
            onChanged: (SingingCharacter? value) {
              setState(() {
                _character = value;
              });
            },
          ),
        ),
        ListTile(
          title: const Text('Thomas Jefferson'),
          leading: Radio<SingingCharacter>(
            value: SingingCharacter.jefferson,
            groupValue: _character,
            onChanged: (SingingCharacter? value) {
              setState(() {
                _character = value;
              });
            },
          ),
        ),
      ],
    );
  }
}

でもわかりやすい使い方を教えてくれてる動画があった!

こちらを参考にString型とint型のデータをFirestoreに、保存するラジオボタンの機能を作ってみました!
用意するのは、変数だけ。

https://www.youtube.com/watch?v=mO-nSbATAwM

radio_test/radio_menu.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

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

  
  State<RadioMenu> createState() => _RadioMenuState();
}

class _RadioMenuState extends State<RadioMenu> {
  int _value = 1; // int型の変数.
  String _text = ''; // String型の変数.

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('RadioMenu'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          // int型のラジオボタン.
          Row(
            children: [
              Radio(
                  value: 1,
                  groupValue: _value,
                  onChanged: (value) {
                    setState(() {
                      _value = value!;
                    });
                  }),
              SizedBox(width: 10.0),
              Text('Radio1'),
              Radio(
                  value: 2,
                  groupValue: _value,
                  onChanged: (value) {
                    setState(() {
                      _value = value!;
                    });
                  }),
              SizedBox(width: 10.0),
              Text('Radio2'),
              Radio(
                  value: 3,
                  groupValue: _value,
                  onChanged: (value) {
                    setState(() {
                      _value = value!;
                    });
                  }),
              SizedBox(width: 10.0),
              Text('Radio3'),
            ],
          ),
          SizedBox(height: 20),
          // String型のラジオボタン.
          Row(
            children: [
              Radio(
                  value: 'Radio1',
                  groupValue: _text,
                  onChanged: (value) {
                    setState(() {
                      _text = value!;
                    });
                  }),
              SizedBox(width: 10.0),
              Text('Radio1'),
              Radio(
                  value: 'Radio2',
                  groupValue: _text,
                  onChanged: (value) {
                    setState(() {
                      _text = value!;
                    });
                  }),
              SizedBox(width: 10.0),
              Text('Radio2'),
              Radio(
                  value: 'Radio3',
                  groupValue: _text,
                  onChanged: (value) {
                    setState(() {
                      _text = value!;
                    });
                  }),
              SizedBox(width: 10.0),
              Text('Radio3'),
            ],
          ),
          ElevatedButton(
              onPressed: () async {
                // int型か調べる.
                print(_value.runtimeType);
                print(_value);
                // String型か調べる.
                print(_text.runtimeType);
                print(_text);
                // FirestoreにintとStringのデータを保存する.
                await FirebaseFirestore.instance.collection('radio').add({
                  'int': _value,
                  'String': _text,
                });
              },
              child: const Text('save'))
        ],
      ),
    );
  }
}
main.dart
import 'dart:developer';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:state_tutorial/radio_test/radio_menu.dart';
import 'package:state_tutorial/reference_page/blog_page.dart';

import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(MyApp());
}

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

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const RadioMenu(),
    );
  }
}


まとめ

お問い合わせフォームやアンケートの画面で、ラジオボタンがよくあるので試しに作ってみたいなと思いやってみました。
他にもチェックボックスやスライダーなど色々試してみて、入力フォームのバリエーションを増やしたいなと考えてます。
Riverpodで書き直すのもやってみました。

main.dart
import 'dart:developer';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:state_tutorial/radio_test/radio_menu.dart';

import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(ProviderScope(child: MyApp()));
}

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

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const RadioMenu(),
    );
  }
}
radio_test/radio_menu.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final intProvider = StateProvider<int>((ref) => 0);
final stringProvider = StateProvider<String>((ref) => '');

class RadioMenu extends ConsumerWidget {
  const RadioMenu({Key? key}) : super(key: key);

  
  Widget build(BuildContext context, WidgetRef ref) {
    // 状態管理している値を取得するプロバイダー.
    final _value = ref.watch(intProvider);
    final _text = ref.watch(stringProvider);
    // 状態管理している値を操作するプロバイダー.
    final _intValue = ref.watch(intProvider.notifier);
    final _textValue = ref.watch(stringProvider.notifier);

    return Scaffold(
      appBar: AppBar(
        title: const Text('RadioMenu'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          // int型のラジオボタン.
          Row(
            children: [
              Radio(
                  value: 1,
                  groupValue: _value, // 状態を表示する.
                  onChanged: (value) {
                    _intValue.state = value!; // 状態を操作する.
                  }),
              SizedBox(width: 10.0),
              Text('Radio1'),
              Radio(
                  value: 2,
                  groupValue: _value,
                  onChanged: (value) {
                    _intValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio2'),
              Radio(
                  value: 3,
                  groupValue: _value,
                  onChanged: (value) {
                    _intValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio3'),
            ],
          ),
          SizedBox(height: 20),
          // String型のラジオボタン.
          Row(
            children: [
              Radio(
                  value: 'Radio1',
                  groupValue: _text,
                  onChanged: (value) {
                    _textValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio1'),
              Radio(
                  value: 'Radio2',
                  groupValue: _text,
                  onChanged: (value) {
                    _textValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio2'),
              Radio(
                  value: 'Radio3',
                  groupValue: _text,
                  onChanged: (value) {
                    _textValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio3'),
            ],
          ),
          ElevatedButton(
              onPressed: () async {
                // FirestoreにintとStringのデータを保存する.
                await FirebaseFirestore.instance.collection('radio').add({
                  'int': _value,
                  'String': _text,
                });
              },
              child: const Text('save'))
        ],
      ),
    );
  }
}

追加記事

StateNotifierを使用して、状態管理をする方法を試してみました。hooks_riverpodを使用していたときに作りましたが、flutter_riverpodでもコピペして使えます。

🕵️‍♀️状態を管理するコード

enumに、男性・女性のどちらかを選択する定数を作り、StateNotifierで状態管理しています。

radio_state.dart
// Gender enum to represent the radio button state
import 'package:hooks_riverpod/hooks_riverpod.dart';

enum Gender { male, female }

class GenderNotifier extends StateNotifier<Gender> {
  GenderNotifier() : super(Gender.male);

  void updateGender(Gender value) {
    state = value;
  }
}

final genderProvider = StateNotifierProvider<GenderNotifier, Gender>((ref) => GenderNotifier());

📻ラジオボタンのUI

Riverpodで状態管理をした、ラジオボタンのロジックは、valueのところで、enumを呼び出し、groupValueのところで、性別の監視をして、onChangedのところで、ロジックを呼び出します。

radio.dart
import 'package:custom_hook_api/radio_state.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class Home extends HookConsumerWidget {
  const Home({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    // ref.watchで、genderProviderを監視
    final gender = ref.watch(genderProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('Gender Selection'),
      ),
      body: Column(
        children: <Widget>[
          // ListTileを使って、RadioListTileを作成
          ListTile(
            title: const Text('男性'),
            leading: Radio<Gender>(
              value: Gender.male,// ここでenumの値を指定
              groupValue: gender,// ref.watchで、男性か女性かを監視
              // ref.readで、updateGenderを呼び出す
              onChanged: (Gender? value) {
                ref.read(genderProvider.notifier).updateGender(value!);
              },
            ),
          ),
          ListTile(
            title: const Text('女性'),
            leading: Radio<Gender>(
              value: Gender.female,// ここでenumの値を指定
              groupValue: gender,// ref.watchで、男性か女性かを監視
              onChanged: (Gender? value) {
                // ref.readで、updateGenderを呼び出す
                ref.read(genderProvider.notifier).updateGender(value!);
              },
            ),
          ),
        ],
      ),
    );
  }
}

あとは、main.dartでimportして、ビルドすれば使えます。

Discussion