【Flutter】ReorderableListViewでThe following _TypeError was thrown building Builder(dirty)
FlutterのReorderableListView
で発生するThe following _TypeError was thrown building Builder(dirty)
をなんとかしたい!
概要
今回の記事では、FlutterのReorderableListView
で発生するThe following _TypeError was thrown building Builder(dirty)
をなんとかする手順を掲載する。
私のケースではReorderableListView
の子の要素の順番を入れ替えてると時に稀に発生するという再現性の低い感じで原因が分かるまで時間がかかった。
エラーが発生しても一瞬で復帰したり、タップすると復帰したり謎。
下記はエラーメッセージの一部抜粋。
The following _TypeError was thrown building Builder(dirty):
Null check operator used on a null value
When the exception was thrown, this was the stack:
#0 RenderSliverMultiBoxAdaptor.childMainAxisPosition (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:568:36)
#1 RenderSliverHelpers.applyPaintTransformForBoxChild (package:flutter/src/rendering/sliver.dart:1803:20)
#2 RenderSliverMultiBoxAdaptor.applyPaintTransform (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:597:7)
...
仕様書
環境
- Android Studio Jellyfish | 2023.3.1 Patch 2
- Flutter 3.19.6
手順書
いろいろやっててReorderableListView
の子のウェジェットのkey
の設定が原因であることが分かった。
エラーが発生する例
クラスItem
のList
をReorderableListView
の子のウェジェットにする例。
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
class Item {
String value = "";
String uuid = const Uuid().v4();
Item({required this.value, required this.order})
}
class ReorderableListViewSample1 extends StatefulWidget {
const ReorderableListViewSample1({super.key});
@override
ReorderableListViewSample1State createState() => ReorderableListViewSample1State();
}
class ReorderableListViewSample1State extends State<ReorderableListViewSample1> {
List<String> items = loadItems();
List<String> loadItems() {
// ItemのListを返す
}
@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(items.length, (index) {
return Card(
key: ValueKey(items[index].uuid),
child: ListTile(
title: Text(items[index].name)
),
);
}),
),
),
),
);
}
}
key
にItem
のプロパティuuid
を使ったValueKey
設定してる。
key: ValueKey(items[index].uuid)
uuid
はコンストラクターで生成したユニーバサルユニークIDを設定してる。これが駄目だったみたい。
エラーが発生しない例
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
class Item {
String value = "";
String uuid = const Uuid().v4();
Item({required this.value, required this.order})
}
class ReorderableListViewSample1 extends StatefulWidget {
const ReorderableListViewSample1({super.key});
@override
ReorderableListViewSample1State createState() => ReorderableListViewSample1State();
}
class ReorderableListViewSample1State extends State<ReorderableListViewSample1> {
List<String> items = loadItems();
List<String> loadItems() {
// ItemのListを返す
}
@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(items.length, (index) {
return Card(
key: ValueKey(index),
child: ListTile(
title: Text(items[index].name)
),
);
}),
),
),
),
);
}
}
key
にList
の添字(インデックス)を使ったValueKey
設定するとエラーが発生しなくなる。
key: ValueKey(index)
まとめ(感想文)
なんでだ!どちらもReorderableListView
の中ではユニークなるはずなのに!