【Flutter】Multiple widgets used the same GlobalKey.

ネコニウム研究所

PCを利用したモノづくりに関連する情報や超個人的なナレッジを掲載するブログ

【Flutter】Multiple widgets used the same GlobalKey.

2024-6-12 | ,

FlutterのIflutter-multiple-widgets-used-the-same-globalkey.をなんとかしたい!

概要

今回の記事では、FlutterのMultiple widgets used the same GlobalKey.をなんとかする手順を掲載する。

仕様書

環境

  • Android Studio Giraffe | 2023.2.1 Patch 2
  • Flutter 3.19.6

手順書

このエラーの原因はGlobalKeyが必要なウェジェットに設定されてない場合に発生する。

エラーが発生する例

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: const ReorderableListViewSample1()
      )
    )
  );
}

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

  @override
  ReorderableListViewSample1State createState() => ReorderableListViewSample1State();
}

class ReorderableListViewSample1State extends State<ReorderableListViewSample1> {
  List<String> list = ["Apple", "Bear", "Cat", "Decadence", "Exp", "File", "Godspeed", "Hex", "Iron" , "Joker", "Keep", "Leek", "Opaque", "Picnic", "Que", "Resonance", "Salt", "Tooth", "Under" ,"Vanguard", "Whisky", "Xray", "Zine"];

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Center(
        child: SizedBox(
          width: double.infinity,
          child: ReorderableListView(
            onReorder: (int oldIndex, int newIndex) {
              setState(() {
                if (newIndex > oldIndex) {
                  newIndex -= 1;
                }
                final item = list.removeAt(oldIndex);
                list.insert(newIndex, item);
              });
            },
            children: List.generate(list.length, (index) {
              return Card(
                child: ListTile(
                  title: Text(list[index])
                ),
              );
            }),
          ),
        ),
      ),
    );
  }
}

ReorderableListViewの子であるCardkeyが設定されてないためにMultiple widgets used the same GlobalKey.が発生する。

エラーが発生しない例

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: const ReorderableListViewSample1()
      )
    )
  );
}

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

  @override
  ReorderableListViewSample1State createState() => ReorderableListViewSample1State();
}

class ReorderableListViewSample1State extends State<ReorderableListViewSample1> {
  List<String> list = ["Apple", "Bear", "Cat", "Decadence", "Exp", "File", "Godspeed", "Hex", "Iron" , "Joker", "Keep", "Leek", "Opaque", "Picnic", "Que", "Resonance", "Salt", "Tooth", "Under" ,"Vanguard", "Whisky", "Xray", "Zine"];

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Center(
        child: SizedBox(
          width: double.infinity,
          child: ReorderableListView(
            onReorder: (int oldIndex, int newIndex) {
              setState(() {
                if (newIndex > oldIndex) {
                  newIndex -= 1;
                }
                final item = list.removeAt(oldIndex);
                list.insert(newIndex, item);
              });
            },
            children: List.generate(list.length, (index) {
              return Card(
                key: ValueKey(list[index]),
                child: ListTile(
                  title: Text(list[index])
                ),
              );
            }),
          ),
        ),
      ),
    );
  }
}

CardkeyValueKeyを設定してる。この場合はGlobalKeyじゃなくてValueKeyでもいい。

GlobalKeyValueKeyの違いは大雑把に説明すると有効なスコープが違う。GlobalKeyはウィジェットツリーの全体で、ValueKeyはローカルな範囲で一意性を担保するために使われる。他にも違いがあるんだけども、まあ、その、大体そんな感じ。

まとめ(感想文)

内部的に要素を判別するためにkeyが必要になるんだと思われ。