【Flutter】 riverpodでDropdownButton選択時に値を保持して表示を変える

# はじめに
https://api.flutter.dev/flutter/material/DropdownButton-class.html


実現したい事としては、`DropdownButton`で値を選択した時に
①選択した値に表示を変える
②選択した値をriverpodで保持する

よく`StatefulWidget`を使って`DropdownButton`を実装するサンプルは探せばたくさん出てきますが、riverpodを使って同じことをできないかやってみました。

# イメージはこんな感じ
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">riverpodでDropdownButton選択時に値を保持して表示を変える <a href="https://t.co/PFXTFjmfmf">pic.twitter.com/PFXTFjmfmf</a></p>&mdash; Gento アプリ&amp;web (@gento34165638) <a href="https://twitter.com/gento34165638/status/1614455955068178432?ref_src=twsrc%5Etfw">January 15, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 環境と前提

```console
$ flutter --version                                                                   (git)-[master]
Flutter 3.3.9 • channel stable • https://github.com/flutter/flutter.git
Framework • revision b8f7f1f986 (7 weeks ago) • 2022-11-23 06:43:51 +0900
Engine • revision 8f2221fbef
Tools • Dart 2.18.5 • DevTools 2.15.0
```

https://riverpod.dev/ja/docs/getting_started


使うパッケージは`flutter_riverpod`です。
hooksと併用の`hooks_riverpod`ではありません。


```yaml:pubspec.yaml 
dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  flutter_riverpod: ^2.1.1
```

ProviderScopeで`MyApp`を囲む部分は省いています！



# コード全文

```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// DropdownMenuItemの初期値を設定
final numberProvider = StateProvider<int?>((ref) {
  return 5;
});


class NumberMembersScreen extends ConsumerStatefulWidget {
  const NumberMembersScreen({super.key});

  @override
  ConsumerState<ConsumerStatefulWidget> createState() => _NumberMembersScreen();
}

class _NumberMembersScreen extends ConsumerState<NumberMembersScreen> {
  // DropdownButtonで表示される選択肢
  List<int> items = [3, 4, 5];
  // DropdownButtonで選択した値
  int? _selectedItem;

  @override
  Widget build(BuildContext context) {
    var _screenSize = MediaQuery.of(context).size;
    // 選択した値をwatchで監視する
    final numberOfMember = ref.watch(numberProvider);

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text(
              '人数を入力してな',
            ),
            // 監視しているnumberOfMemberが変更されるたびに再描画される
            Text(
              numberOfMember.toString(),
            ),
            const SizedBox(height: 15),
            SizedBox(
                width: _screenSize.width * 0.4,
                child: DropdownButton(
                  hint: const Text('人数を選んで'),
                  isExpanded: true,
                  value: _selectedItem,
                  items: items
                      .map((item) => DropdownMenuItem<int>(
                            alignment: AlignmentDirectional.center,
                            value: item,
                            child: Text(item.toString()),
                          ))
                      .toList(),
                  onChanged: (int? value) {
                    // riverpodで保持している値を選択した値に更新する
                    ref.read(numberProvider.notifier).state = value;
                    // 画面に表示させるDropdownButtonの値を動的に変える
                    setState(() {
                      _selectedItem = value;
                    });
                    print(ref.read(numberProvider.notifier).state);
                  },
                )),
          ],
        ),
      ),
    );
  }
}
```

ちなみに、もし`DropdownButton`の`hint`を消すと、初期値は何も表示されなくなる
![Screenshot 2023-01-15 at 11.55.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/35376cae-2de0-20a2-0091-10efbef85f3f.png)

これで`DropdownButton`選択時にriverpodを使って値をグローバルに保持することができました！！:blush: