Commit dbaf12b8 authored by Hans Muller's avatar Hans Muller Committed by GitHub

Do not read or write state if PageStorageKeys cannot be found (#10612)

parent 81eb1404
...@@ -39,21 +39,19 @@ class PageStorageKey<T> extends ValueKey<T> { ...@@ -39,21 +39,19 @@ class PageStorageKey<T> extends ValueKey<T> {
} }
class _StorageEntryIdentifier { class _StorageEntryIdentifier {
_StorageEntryIdentifier(this.clientType, this.keys) { _StorageEntryIdentifier(this.keys) {
assert(clientType != null);
assert(keys != null); assert(keys != null);
} }
final Type clientType;
final List<PageStorageKey<dynamic>> keys; final List<PageStorageKey<dynamic>> keys;
bool get isNotEmpty => keys.isNotEmpty;
@override @override
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType)
return false; return false;
final _StorageEntryIdentifier typedOther = other; final _StorageEntryIdentifier typedOther = other;
if (clientType != typedOther.clientType || keys.length != typedOther.keys.length)
return false;
for (int index = 0; index < keys.length; index += 1) { for (int index = 0; index < keys.length; index += 1) {
if (keys[index] != typedOther.keys[index]) if (keys[index] != typedOther.keys[index])
return false; return false;
...@@ -62,11 +60,11 @@ class _StorageEntryIdentifier { ...@@ -62,11 +60,11 @@ class _StorageEntryIdentifier {
} }
@override @override
int get hashCode => hashValues(clientType, hashList(keys)); int get hashCode => hashList(keys);
@override @override
String toString() { String toString() {
return 'StorageEntryIdentifier($clientType, ${keys?.join(":")})'; return 'StorageEntryIdentifier(${keys?.join(":")})';
} }
} }
...@@ -94,31 +92,45 @@ class PageStorageBucket { ...@@ -94,31 +92,45 @@ class PageStorageBucket {
} }
_StorageEntryIdentifier _computeIdentifier(BuildContext context) { _StorageEntryIdentifier _computeIdentifier(BuildContext context) {
return new _StorageEntryIdentifier(context.widget.runtimeType, _allKeys(context)); return new _StorageEntryIdentifier(_allKeys(context));
} }
Map<Object, dynamic> _storage; Map<Object, dynamic> _storage;
/// Write the given data into this page storage bucket using an identifier /// Write the given data into this page storage bucket using the
/// computed from the given context. The identifier is based on the keys /// specified identifier or an identifier computed from the given context.
/// found in the path from context to the root of the widget tree for this /// The computed identifier is based on the [PageStorageKey]s
/// page. Keys are collected until the widget tree's root is reached or /// found in the path from context to the [PageStorage] widget that
/// a GlobalKey is found. /// owns this page storage bucket.
/// ///
/// An explicit identifier can be used in cases where the list of keys /// If an explicit identifier is not provided and no [PageStorageKey]s
/// is not stable. For example if the path concludes with a GlobalKey /// are found, then the `data` is not saved.
/// that's created by a stateful widget, if the stateful widget is
/// recreated when it's exposed by [Navigator.pop], then its storage
/// identifier will change.
void writeState(BuildContext context, dynamic data, { Object identifier }) { void writeState(BuildContext context, dynamic data, { Object identifier }) {
_storage ??= <Object, dynamic>{}; _storage ??= <Object, dynamic>{};
_storage[identifier ?? _computeIdentifier(context)] = data; if (identifier != null) {
_storage[identifier] = data;
} else {
final _StorageEntryIdentifier contextIdentifier = _computeIdentifier(context);
if (contextIdentifier.isNotEmpty)
_storage[contextIdentifier] = data;
}
} }
/// Read given data from into this page storage bucket using an identifier /// Read given data from into this page storage bucket using the specified
/// computed from the given context. More about [identifier] in [writeState]. /// identifier or an identifier computed from the given context.
/// The computed identifier is based on the [PageStorageKey]s
/// found in the path from context to the [PageStorage] widget that
/// owns this page storage bucket.
///
/// If an explicit identifier is not provided and no [PageStorageKey]s
/// are found, then null is returned.
dynamic readState(BuildContext context, { Object identifier }) { dynamic readState(BuildContext context, { Object identifier }) {
return _storage != null ? _storage[identifier ?? _computeIdentifier(context)] : null; if (_storage == null)
return null;
if (identifier != null)
return _storage[identifier];
final _StorageEntryIdentifier contextIdentifier = _computeIdentifier(context);
return contextIdentifier.isNotEmpty ? _storage[contextIdentifier] : null;
} }
} }
......
...@@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; ...@@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
void main() { void main() {
testWidgets('PageStorage read and write', (WidgetTester tester) async { testWidgets('PageStorage read and write', (WidgetTester tester) async {
final Key builderKey = const Key('builderKey'); final Key builderKey = const PageStorageKey<String>('builderKey');
StateSetter setState; StateSetter setState;
int storedValue = 0; int storedValue = 0;
......
...@@ -407,6 +407,7 @@ void main() { ...@@ -407,6 +407,7 @@ void main() {
new PageStorage( new PageStorage(
bucket: bucket, bucket: bucket,
child: new PageView( child: new PageView(
key: const PageStorageKey<String>('PageView'),
controller: controller, controller: controller,
children: <Widget>[ children: <Widget>[
const Placeholder(), const Placeholder(),
...@@ -431,6 +432,7 @@ void main() { ...@@ -431,6 +432,7 @@ void main() {
new PageStorage( new PageStorage(
bucket: bucket, bucket: bucket,
child: new PageView( child: new PageView(
key: const PageStorageKey<String>('PageView'),
controller: controller, controller: controller,
children: <Widget>[ children: <Widget>[
const Placeholder(), const Placeholder(),
...@@ -447,7 +449,7 @@ void main() { ...@@ -447,7 +449,7 @@ void main() {
new PageStorage( new PageStorage(
bucket: bucket, bucket: bucket,
child: new PageView( child: new PageView(
key: const Key('Check it again against your list and see consistency!'), key: const PageStorageKey<String>('Check it again against your list and see consistency!'),
controller: controller2, controller: controller2,
children: <Widget>[ children: <Widget>[
const Placeholder(), const Placeholder(),
......
...@@ -18,6 +18,7 @@ class ThePositiveNumbers extends StatelessWidget { ...@@ -18,6 +18,7 @@ class ThePositiveNumbers extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new ListView.builder( return new ListView.builder(
key: const PageStorageKey<String>('ThePositiveNumbers'),
itemExtent: 100.0, itemExtent: 100.0,
controller: _controller, controller: _controller,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
......
...@@ -266,12 +266,15 @@ void main() { ...@@ -266,12 +266,15 @@ void main() {
Widget buildFrame(ScrollController controller) { Widget buildFrame(ScrollController controller) {
return new PageStorage( return new PageStorage(
bucket: bucket, bucket: bucket,
child: new ListView( child: new KeyedSubtree(
key: new UniqueKey(), // it's a different ListView every time key: const PageStorageKey<String>('ListView'),
controller: controller, child: new ListView(
children: new List<Widget>.generate(50, (int index) { key: new UniqueKey(), // it's a different ListView every time
return new Container(height: 100.0, child: new Text('Item $index')); controller: controller,
}).toList(), children: new List<Widget>.generate(50, (int index) {
return new Container(height: 100.0, child: new Text('Item $index'));
}).toList(),
),
), ),
); );
} }
......
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