page_storage.dart 4.1 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'package:meta/meta.dart';

7 8 9 10 11
import 'framework.dart';

class _StorageEntryIdentifier {
  Type clientType;
  List<Key> keys;
12

13 14 15 16 17 18
  void addKey(Key key) {
    assert(key != null);
    assert(key is! GlobalKey);
    keys ??= <Key>[];
    keys.add(key);
  }
19

20
  GlobalKey scopeKey;
21 22

  @override
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
  bool operator ==(dynamic other) {
    if (other is! _StorageEntryIdentifier)
      return false;
    final _StorageEntryIdentifier typedOther = other;
    if (clientType != typedOther.clientType ||
        scopeKey != typedOther.scopeKey ||
        keys?.length != typedOther.keys?.length)
      return false;
    if (keys != null) {
      for (int index = 0; index < keys.length; index += 1) {
        if (keys[index] != typedOther.keys[index])
          return false;
      }
    }
    return true;
  }
39 40

  @override
41
  int get hashCode => hashValues(clientType, scopeKey, hashList(keys));
42

43
  @override
44 45 46
  String toString() {
    return 'StorageEntryIdentifier($clientType, $scopeKey, ${keys?.join(":")})';
  }
47 48
}

49 50 51 52
/// A storage bucket associated with a page in an app.
///
/// Useful for storing per-page state that persists across navigations from one
/// page to another.
53 54 55 56 57 58
class PageStorageBucket {
  _StorageEntryIdentifier _computeStorageIdentifier(BuildContext context) {
    _StorageEntryIdentifier result = new _StorageEntryIdentifier();
    result.clientType = context.widget.runtimeType;
    Key lastKey = context.widget.key;
    if (lastKey is! GlobalKey) {
59 60
      if (lastKey != null)
        result.addKey(lastKey);
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
      context.visitAncestorElements((Element element) {
        if (element.widget.key is GlobalKey) {
          lastKey = element.widget.key;
          return false;
        } else if (element.widget.key != null) {
          result.addKey(element.widget.key);
        }
        return true;
      });
      return result;
    }
    assert(lastKey is GlobalKey);
    result.scopeKey = lastKey;
    return result;
  }

77
  Map<Object, dynamic> _storage;
78 79

  /// Write the given data into this page storage bucket using an identifier
80 81 82 83 84 85 86 87 88 89 90 91 92
  /// computed from the given context. The identifier is based on the keys
  /// found in the path from context to the root of the widget tree for this
  /// page. Keys are collected until the widget tree's root is reached or
  /// a GlobalKey is found.
  ///
  /// An explicit identifier can be used in cases where the list of keys
  /// is not stable. For example if the path concludes with a GlobalKey
  /// 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 }) {
    _storage ??= <Object, dynamic>{};
    _storage[identifier ?? _computeStorageIdentifier(context)] = data;
93
  }
94 95

  /// Read given data from into this page storage bucket using an identifier
96 97 98
  /// computed from the given context. More about [identifier] in [writeState].
  dynamic readState(BuildContext context, { Object identifier }) {
    return _storage != null ? _storage[identifier ?? _computeStorageIdentifier(context)] : null;
99 100 101
  }
}

102
/// A widget that establishes a page storage bucket for this widget subtree.
103
class PageStorage extends StatelessWidget {
104 105 106
  /// Creates a widget that provides a storage bucket for its descendants.
  ///
  /// The [bucket] argument must not be null.
107 108
  PageStorage({
    Key key,
109 110 111 112 113
    @required this.bucket,
    this.child
  }) : super(key: key) {
    assert(bucket != null);
  }
114

115
  /// The widget below this widget in the tree.
116
  final Widget child;
117 118

  /// The page storage bucket to use for this subtree.
119 120
  final PageStorageBucket bucket;

121 122
  /// The bucket from the closest instance of this class that encloses the given context.
  ///
123
  /// Returns `null` if none exists.
124
  static PageStorageBucket of(BuildContext context) {
Ian Hickson's avatar
Ian Hickson committed
125
    PageStorage widget = context.ancestorWidgetOfExactType(PageStorage);
Hixie's avatar
Hixie committed
126
    return widget?.bucket;
127 128
  }

129
  @override
130 131
  Widget build(BuildContext context) => child;
}