Commit 14440b14 authored by Hans Muller's avatar Hans Muller

Merge pull request #2846 from HansMuller/two_level_list

TwoLevelList fails to restore sublist expanded state
parents a8c108de 67a18fa5
...@@ -154,11 +154,14 @@ class _TwoLevelSublistState extends State<TwoLevelSublist> { ...@@ -154,11 +154,14 @@ class _TwoLevelSublistState extends State<TwoLevelSublist> {
} }
class TwoLevelList extends StatelessWidget { class TwoLevelList extends StatelessWidget {
TwoLevelList({ Key key, this.items, this.type: MaterialListType.twoLine }) : super(key: key); TwoLevelList({ Key key, this.scrollableKey, this.items, this.type: MaterialListType.twoLine }) : super(key: key);
final List<Widget> items; final List<Widget> items;
final MaterialListType type; final MaterialListType type;
final Key scrollableKey;
@override @override
Widget build(BuildContext context) => new Block(children: items); Widget build(BuildContext context) {
return new Block(children: KeyedSubtree.ensureUniqueKeysForList(items), scrollableKey: scrollableKey);
}
} }
...@@ -7,6 +7,7 @@ import 'dart:ui' as ui show Image; ...@@ -7,6 +7,7 @@ import 'dart:ui' as ui show Image;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'debug.dart';
import 'framework.dart'; import 'framework.dart';
export 'package:flutter/animation.dart'; export 'package:flutter/animation.dart';
...@@ -2575,6 +2576,27 @@ class KeyedSubtree extends StatelessWidget { ...@@ -2575,6 +2576,27 @@ class KeyedSubtree extends StatelessWidget {
/// The widget below this widget in the tree. /// The widget below this widget in the tree.
final Widget child; final Widget child;
/// Wrap each item in a KeyedSubtree whose key is based on the item's existing key or
/// its list index + baseIndex.
static List<Widget> ensureUniqueKeysForList(Iterable<Widget> items, { int baseIndex: 0 }) {
if (items == null || items.isEmpty)
return items;
List<Widget> itemsWithUniqueKeys = <Widget>[];
int itemIndex = baseIndex;
for(Widget item in items) {
itemsWithUniqueKeys.add(new KeyedSubtree(
key: item.key != null ? new ValueKey<Key>(item.key) : new ValueKey<int>(itemIndex),
child: item
));
itemIndex += 1;
}
assert(!debugItemsHaveDuplicateKeys(itemsWithUniqueKeys));
return items;
}
@override @override
Widget build(BuildContext context) => child; Widget build(BuildContext context) => child;
} }
......
...@@ -25,22 +25,39 @@ bool debugCheckHasMediaQuery(BuildContext context) { ...@@ -25,22 +25,39 @@ bool debugCheckHasMediaQuery(BuildContext context) {
return true; return true;
} }
bool debugHasDuplicateKeys(Widget parent, Iterable<Widget> children) { Key _firstNonUniqueKey(Iterable<Widget> widgets) {
Set<Key> keySet = new HashSet<Key>();
for (Widget widget in widgets) {
assert(widget != null);
if (widget.key == null)
continue;
if (!keySet.add(widget.key))
return widget.key;
}
return null;
}
bool debugChildrenHaveDuplicateKeys(Widget parent, Iterable<Widget> children) {
assert(() { assert(() {
Set<Key> keySet = new HashSet<Key>(); final Key nonUniqueKey = _firstNonUniqueKey(children);
for (Widget child in children) { if (nonUniqueKey != null) {
assert(child != null); throw new FlutterError(
if (child.key == null) 'Duplicate keys found.\n'
continue; 'If multiple keyed nodes exist as children of another node, they must have unique keys.\n'
if (!keySet.add(child.key)) { '$parent has multiple children with key $nonUniqueKey.'
throw new FlutterError( );
'Duplicate keys found.\n'
'If multiple keyed nodes exist as children of another node, they must have unique keys.\n'
'$parent has multiple children with key "${child.key}".'
);
}
} }
return true; return true;
}); });
return false; return false;
} }
bool debugItemsHaveDuplicateKeys(Iterable<Widget> items) {
assert(() {
final Key nonUniqueKey = _firstNonUniqueKey(items);
if (nonUniqueKey != null)
throw new FlutterError('Duplicate key found: $nonUniqueKey.\n');
return true;
});
return false;
}
...@@ -1937,7 +1937,7 @@ class SingleChildRenderObjectElement extends RenderObjectElement { ...@@ -1937,7 +1937,7 @@ class SingleChildRenderObjectElement extends RenderObjectElement {
/// Instantiation of RenderObjectWidgets that can have a list of children /// Instantiation of RenderObjectWidgets that can have a list of children
class MultiChildRenderObjectElement extends RenderObjectElement { class MultiChildRenderObjectElement extends RenderObjectElement {
MultiChildRenderObjectElement(MultiChildRenderObjectWidget widget) : super(widget) { MultiChildRenderObjectElement(MultiChildRenderObjectWidget widget) : super(widget) {
assert(!debugHasDuplicateKeys(widget, widget.children)); assert(!debugChildrenHaveDuplicateKeys(widget, widget.children));
} }
@override @override
......
...@@ -173,8 +173,9 @@ abstract class VirtualViewportElement extends RenderObjectElement { ...@@ -173,8 +173,9 @@ abstract class VirtualViewportElement extends RenderObjectElement {
Key key = child.key != null ? new ValueKey<Key>(child.key) : new ValueKey<int>(childIndex); Key key = child.key != null ? new ValueKey<Key>(child.key) : new ValueKey<int>(childIndex);
newWidgets[i] = new RepaintBoundary(key: key, child: child); newWidgets[i] = new RepaintBoundary(key: key, child: child);
} }
assert(!debugHasDuplicateKeys(widget, newWidgets));
_materializedChildren = updateChildren(_materializedChildren, newWidgets); assert(!debugChildrenHaveDuplicateKeys(widget, newWidgets));
_materializedChildren = updateChildren(_materializedChildren, newWidgets.toList());
} }
@override @override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment