Skip to content

ReorderableListView destroys children even if Key-Type of children wasn't changed #58364

@MariaMelnik

Description

@MariaMelnik

Hi,
I noticed ReorderableListView rebuilds children every time even if Key-Type of children wasn't changed.

demo: https://dartpad.dartlang.org/d5cdcdbd79081a06f0b5bdacfeb55c33
(https://gist.github.com/MariaMelnik/d5cdcdbd79081a06f0b5bdacfeb55c33)

Steps to reproduce:

  • Create new flutter project with main.dart copied from example above.
  • Run on device (I used Chrome(web) and simulator iPhone 11 Pro max).
  • Hit refresh icon button at the AppBar.

What do I expect:
Nothing changed. Because keys which I set to widget didn't changed (I used ValueKey with int inside. int will always be equal same int). So I expect my _MyListTileState keeps ms values but not drops it.

In this demo case I use empty setState just to demonstrate issue. In real world it is supposed to be meaningfull update. And ms changes in real world could be animation changes.

What is happening:
Every child of the ReorderableListView was destroyed and created again with new state. They lost all states.

Why it is happening (imho):
Reordable list under the hood wraps children I gave with new widget and assign new keys to that widget. That keys is GlobalObjectKey that use children's I gave keys as inner value.
To check if two GlobalObjectKey object is equal identical is used. And because ValueKey I assign to my tiles every time is new object (with same int inside though) GlobalObjectKey that stores them every time fail equal checks. And flutter destroy my MyListTile widget with state and create new one with initial state.

reorderable_list.dart

  // Wraps one of the widget's children in a DragTarget and Draggable.
  // Handles up the logic for dragging and reordering items in the list.
  Widget _wrap(Widget toWrap, int index, BoxConstraints constraints) {
    assert(toWrap.key != null);
    final GlobalObjectKey keyIndexGlobalKey = GlobalObjectKey(toWrap.key);
    // We pass the toWrapWithGlobalKey into the Draggable so that when a list
    // item gets dragged, the accessibility framework can preserve the selected
    // state of the dragging item.
   ...
    // We pass toWrap with a GlobalKey into the Draggable so that when a list
    // item gets dragged, the accessibility framework can preserve the selected
    // state of the dragging item.
    //
    // We also apply the relevant custom accessibility actions for moving the item
    // up, down, to the start, and to the end of the list.
    return KeyedSubtree(
      key: keyIndexGlobalKey,
      child: MergeSemantics(
        child: Semantics(
            customSemanticsActions: semanticsActions,
            child: toWrap,
         ),
       ),
     );
  }

framework.dart

@optionalTypeArgs
class GlobalObjectKey<T extends State<StatefulWidget>> extends GlobalKey<T> {
  /// Creates a global key that uses [identical] on [value] for its [operator==].
  const GlobalObjectKey(this.value) : super.constructor();

  /// The object whose identity is used by this key's [operator==].
  final Object value;

  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType)
      return false;
    return other is GlobalObjectKey<T>
        && identical(other.value, value);
  }

  @override
  int get hashCode => identityHashCode(value);

...
}

flutter doctor output:

[✓] Flutter (Channel stable, v1.17.1, on Mac OS X 10.14.6 18G4032, locale en-RU)
    • Flutter version 1.17.1 at {$pathToFlutter}
    • Framework revision f7a6a7906b (12 days ago), 2020-05-12 18:39:00 -0700
    • Engine revision 6bc433c6b6
    • Dart version 2.8.2

[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at {$pathToAndroidSdk}
    • Platform android-28, build-tools 28.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.3.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.3.1, Build version 11C504
    • CocoaPods version 1.8.4

[✓] Android Studio (version 3.6)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 45.1.1
    • Dart plugin version 192.7761
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)

[!] IntelliJ IDEA Community Edition (version 2019.2.3)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • For information about installing plugins, see
      https://flutter.dev/intellij-setup/#installing-the-plugins

[✓] Connected device (1 available)
    • iPhone 11 Pro Max • 112D9763-364B-48C1-9B0F-BA92AEE6B5F8 • ios • com.apple.CoreSimulator.SimRuntime.iOS-13-3
      (simulator)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listcustomer: crowdAffects or could affect many people, though not necessarily a specific customer.f: material designflutter/packages/flutter/material repository.found in release: 1.19Found to occur in 1.19found in release: 1.21Found to occur in 1.21frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions