🧩

FilterChipsで二つの情報を扱う

2023/03/02に公開

大きな目標

Flutterで蒐集したデータをMySQLでDatabase化、Flutterから検索した結果をUnity上で三次元表示し、開架の図書館内を歩きながら本を手に取ってみるような表現を目指す。

中期的な目標

データベースに無関心なuserが入力しても適切なデータを蒐集できるフロントをFlutterで作る。

今日の目標

FilterChipにはふつう単一の情報が示されている。文字列のことも、数字のこともあるだろう。だが今回は、表示されているのは検索結果の文字列だけ、けれど背後でその文字列のIDも保持して、最終的にIDを使った中間tableにinsertする、という方法に挑戦する。

まず、検索結果としてIDと文字列を取得して、文字列のみをChipに表示する。

class _PaysPageState extends State<PaysPage> {

  List<Map<String, String>> displayListPays = [];
  final List<String> _filtersPays = <String>[];
  final List<String> _filtersPaysId = <String>[];

  Future<void> _paysInvolved() async {
    // create connection
    final conn = await MySQLConnection.createConnection(
      host: "127.0.0.1",
      port: 3306,
      userName: NAME,
      password: PASSWORD,
      databaseName: DATABASE,
    );

    await conn.connect();

    // select countries pays involved
    var result = await conn.execute("SELECT id,pays FROM Pays");

    // make list with query result
    List<Map<String, String>> paysList = [];
    for (final row in result.rows) {
      final data = {
        'selectedPaysId': row.colAt(0)!,
        'selectedPays': row.colAt(1)!,
      };
      paysList.add(data);
    }

    setState(() {
      displayListPays = paysList;
    });

    // close all connections
    await conn.close();
  }

複数選択が可能なFilterChipで検索結果をさらに絞り込む。

                      Expanded(
                        flex: 1,
                        child: SingleChildScrollView(
                          child: Column(
                            children: [
                              Padding(
                                padding: const EdgeInsets.all(20.0),
                                child: OutlinedButton(
                                  onPressed: _paysInvolved,
                                  child: const Text('Show and Select Countries Involved'),
                                ),
                              ),
                              Text(
                                'Selected: ${_filtersPays.join(', ')}',
                                style: const TextStyle(
                                  fontSize: 20,
                                  color: Colors.yellow,
                                ),
                              ),
                              Padding(
                                padding: const EdgeInsets.all(8.0),
                                child: Wrap(
                                  spacing: 5.0,
                                  children: displayListPays.map<Widget>((data) {
                                    return FilterChip(
                                      label: Text(data['selectedPays'] ?? ""),
                                      selected: _filtersPays.contains(data['selectedPays']!),
                                      onSelected: (bool value) {
                                        setState(() {
                                          if (value) {
                                            if (!_filtersPays.contains(data['selectedPays']!)) {
                                              _filtersPays.add(data['selectedPays']!);
                                            }
                                            if (!_filtersPaysId.contains(data['selectedPaysId']!)) {
                                              _filtersPaysId.add(data['selectedPaysId']!);
                                            }

                                          } else {
                                            _filtersPays.removeWhere((String pays) {
                                              return pays == data[data['selectedPays']]!;
                                            });
                                            _filtersPaysId.removeWhere((String id) {
                                              return id == data[data['selectedPaysId']]!;
                                            });
                                          }
                                        });
                                      },
                                    );
                                  }).toList(),
                                ),
                              ),
                            ],
                          ),
                        ),
                      ),

表示されているのは文字列だけだが、IDも同時に絞り込まれている。

FloatingActionButtonで、絞り込み結果を一時保管する。

      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          showDialog<void>(
              context: context,
              builder: (_){
                return AlertDialog(
                  title: const Text('Data has been temporarily stored.'),
                  content: const Text('They are not uploaded yet. please continue to fill in the other fields.'),
                  actions: <Widget>[
                    GestureDetector(
                      child: const Text('OK'),
                      onTap: () {
                        Navigator.pop(context);
                      },
                    )
                  ],
                );
              });

          confirm.selectedPays = _filtersPays;
          confirm.selectedPaysId = _filtersPaysId;
          print ("$_filtersPaysId");
        },
        label: const Text('Temporarily Save'),
      ),

確認Pageでも文字列のみを表示するので、文字列も一時保管している。

爺様と仲良く

最初、ひとつのfiltersにidと文字列を

_filtersPays.contains(data['selectedPaysId'] + data['selectedPays']!)

というふうに両方保持していた。するとIDと文字列が同時に表示されてしまうし、のちのちIDを使いたいときに文字列がじゃまになるしで、どう分離したらいいか爺様に尋ねたら、最初から分かち書き、という答えだった。なるほどね。二つ並べてるSampleなんて見たことなかったので、目からうろこでした。

Flutter大学

Discussion