【Flutter】ReorderableListViewでThe following _TypeError was thrown building Builder(dirty)

ネコニウム研究所

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

【Flutter】ReorderableListViewでThe following _TypeError was thrown building Builder(dirty)

2024-6-17 | ,

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の設定が原因であることが分かった。

エラーが発生する例

クラスItemListReorderableListViewの子のウェジェットにする例。

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)
                ),
              );
            }),
          ),
        ),
      ),
    );
  }
}

keyItemのプロパティ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)
                ),
              );
            }),
          ),
        ),
      ),
    );
  }
}

keyListの添字(インデックス)を使ったValueKey設定するとエラーが発生しなくなる。

key: ValueKey(index)

まとめ(感想文)

なんでだ!どちらもReorderableListViewの中ではユニークなるはずなのに!