【Flutter】ドロップダウンの作り方|選択肢を作り選べなくする

当サイトで紹介する商品・サービス等の外部リンクは、アフィリエイト広告を含む場合があります。
スポンサーリンク
本記事を読むと以下の実行ができます

言語などの選択肢から1つを選べるドロップダウンを作り、選んだ値の取り出しと、スイッチで選べなくする切り替えまでを DropdownButtonFormField で実装できます。

アプリで言語や国を選んでもらうとき、一覧から1つを選ぶドロップダウン(プルダウン)がよく使われます。

Flutter でドロップダウンを作る部品はいくつかあります。

本記事では、フォームの仕組みにも組み込める DropdownButtonFormField を使います。画面に出す文字と、プログラムが受け取る値を分ける方法、最初に選んでおく値の決め方、選べなくする方法を、動くコードで説明します。

本記事は次の人におすすめ
  • Flutter でドロップダウン(プルダウン)を作りたい人
  • 選択肢をリストから作る方法を知りたい人
  • 画面に出す名前と、プログラムが使う値を分けたい人
  • ドロップダウンを、条件によって選べなくしたい人
スポンサーリンク

DropdownButtonFormField(ドロップダウン)

DropdownButtonFormField は、押すと選択肢の一覧が開き、1つを選べる部品です。公式の説明では、ドロップダウンの部品である DropdownButton を、フォームの入力欄として使えるように包んだものです。複数の入力欄をまとめて検証したいときに使う Form の中に置けますが、Form がなくても使えます。

この記事で使うプロパティは3つです。

プロパティ役割
items選択肢の一覧(DropdownMenuItem のリスト)。必須
initialValue最初に選んでおく値
onChanged選び直したときに呼ばれる関数

古い記事では、最初に選んでおく値に value を使っています。公式では value は非推奨になっており、initialValue を使うよう案内されています(v3.33.0-1.0.pre の後に非推奨)。新しく書くときは initialValue を使ってください。

参考:DropdownButtonFormField

本記事では、次の動きをする画面を作ります。

  • 言語の一覧から1つを選べる
  • 選んだ言語の名前と ID を、ドロップダウンの下に表示する
  • 「変更を許可する」スイッチをオフにすると、ドロップダウンが押せなくなる

ソースコード

  • language.dart

選択肢1つ分のクラスと、一覧のデータです。

/// 選択肢1つ分。id は選んだときに渡る値、name は画面に出る文字。
class Language {
  const Language(this.id, this.name);

  final String id;
  final String name;
}

const languages = [
  Language('ja-JP', '日本語 (日本)'),
  Language('en-US', 'English (United States)'),
  Language('fr-FR', 'Français (France)'),
];
  • language_dropdown.dart

ドロップダウン部分の部品です。

import 'package:flutter/material.dart';

import '../models/language.dart';

class LanguageDropdown extends StatelessWidget {
  const LanguageDropdown({
    super.key,
    required this.languages,
    required this.selectedId,
    required this.enabled,
    required this.onChanged,
  });

  final List<Language> languages;
  final String? selectedId;
  final bool enabled;
  final ValueChanged<String?> onChanged;

  @override
  Widget build(BuildContext context) {
    return DropdownButtonFormField<String>(
      initialValue: selectedId,
      decoration: const InputDecoration(
        border: OutlineInputBorder(),
        labelText: '言語',
      ),
      items: [
        for (final language in languages)
          DropdownMenuItem(value: language.id, child: Text(language.name)),
      ],
      // onChanged が null のとき、ドロップダウンは押せなくなる。
      onChanged: enabled ? onChanged : null,
    );
  }
}
  • dropdown_page.dart

画面です。選んだ値と、変更できるかどうかの状態を持ちます。

import 'package:flutter/material.dart';

import '../models/language.dart';
import '../widgets/language_dropdown.dart';

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

  final String title;

  @override
  State<DropdownPage> createState() => _DropdownPageState();
}

class _DropdownPageState extends State<DropdownPage> {
  String? _selectedId = languages.first.id;
  bool _enabled = true;

  String get _selectedName =>
      languages.firstWhere((l) => l.id == _selectedId).name;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            LanguageDropdown(
              languages: languages,
              selectedId: _selectedId,
              enabled: _enabled,
              onChanged: (id) => setState(() => _selectedId = id),
            ),
            const SizedBox(height: 12),
            Text('選んだ言語: $_selectedName(ID: $_selectedId)'),
            const SizedBox(height: 12),
            SwitchListTile(
              title: const Text('変更を許可する'),
              value: _enabled,
              onChanged: (value) => setState(() => _enabled = value),
            ),
          ],
        ),
      ),
    );
  }
}
  • main.dart

動かすための最小の入口です。

import 'package:flutter/material.dart';

import 'pages/dropdown_page.dart';

void main() {
  runApp(
    const MaterialApp(home: DropdownPage(title: 'ドロップダウン')),
  );
}

解説

選択肢のデータ(language.dart)、ドロップダウンの部品(language_dropdown.dart)、画面(dropdown_page.dart)を分けています。ドロップダウンの部品は、選択肢の一覧、いま選んでいる値、選べるかどうか、選び直したときの処理を、外から受け取るだけです。選んだ値は画面側が持ちます。

Language:選ぶ値と画面の文字を分ける

選択肢を、ただの文字列のリストにすることもできます。この記事では、id(プログラムが使う値)と name(画面に出す文字)の2つを持つクラスにしました。

分けておくと、画面の表示を変えても、プログラムが受け取る値が変わりません。たとえば表示を「日本語 (日本)」から「Japanese」に変えても、受け取る値は ja-JP のままです。

items:リストから選択肢を作る

items: [
  for (final language in languages)
    DropdownMenuItem(value: language.id, child: Text(language.name)),
],

リストの中に for を書くと、一覧のデータから選択肢を作れます。DropdownMenuItem の value が、選んだときに受け取る値、child が画面に出る文字です。一覧に項目を足すだけで、選択肢も増えます。

initialValue:最初に選んでおく値

initialValue に、最初に選んでおく値を渡します。ここに渡す値は、items の value のどれか1つと一致している必要があります。

initialValue は、最初の1回に使う値です。選び直したあとの値は、onChanged で受け取って画面側で持ちます。

onChanged:選び直したときの処理と、選べなくする方法

onChanged には、選び直したときに呼ばれる関数を渡します。引数が、選ばれた value です。この画面では setState で _selectedId を更新し、下の表示を変えています。

ドロップダウンを選べなくしたいときは、onChanged に null を渡します。公式の説明では、onChanged が null のとき、ドロップダウンは無効になります。この画面では、スイッチの状態 enabled が false のときに null を渡しています。

onChanged: enabled ? onChanged : null,

無効のときに出す案内文は、disabledHint で指定できます。

実装

  1. Flutter のプロジェクトを作る(追加するパッケージはありません)
  2. 3つのファイルを、上の場所に置く
  3. main.dart を書き換えて、実行する

ドロップダウンを押して言語を選ぶと、下の表示が変わります。スイッチをオフにすると、ドロップダウンを押せなくなります。

エラーと解決策・対策

以下では、公式の説明で示されているエラーと対処を紹介します。実際に起きたエラーではなく、起こりやすい点をまとめたものです。

There should be exactly one item with [DropdownButton]’s value

initialValue に、items の value に存在しない値を渡すと、このメッセージで止まります。公式の説明では、一致する項目がないとき、または2つ以上あるときに出ます。

  • initialValue の値が、items の value のどれかと一致しているか確認する
  • 同じ value の項目が2つ以上ないか確認する
  • 最初は何も選ばない状態にしたいときは、initialValue を null にする

まとめ

DropdownButtonFormField は、items、initialValue、onChanged の3つを渡せば動きます。

  • 選択肢は、プログラムが使う値と画面に出す文字を分けたクラスで作る
  • 選択肢のリストは、リスト内の for で作る
  • 最初に選んでおく値は initialValue(古い value は非推奨)
  • onChanged に null を渡すと、ドロップダウンを選べなくできる
タイトルとURLをコピーしました