Commit 74575775 authored by Hixie's avatar Hixie

Introduce an explicit Key type.

This fixes some theoretical bugs whereby we were using hashCode to try
to get unique keys for objects, but really we wanted object identity.
It also lays the groundwork for a new GlobalKey concept.

I tried to keep the impact on the code minimal, which is why the "Key"
constructor is actually a factory that returns a StringKey. The code
has this class hierarchy:

```
   KeyBase
    |
   Key--------------+---------------+
    |               |               |
   StringKey    ObjectKey       UniqueKey
```

...where the constructors are Key and Key.stringify (StringKey),
Key.fromObjectIdentity (ObjectKey), and Key.unique (UniqueKey).

We could instead of factory methods use regular constructors with the
following hierarchy:

```
   KeyBase
    |
   LocalKey---------+---------------+
    |               |               |
   Key      ObjectIdentityKey   UniqueKey
```

...with constructors Key, Key.stringify, ObjectIdentityKey, and
UniqueKey, but I felt that that was maybe a more confusing hierarchy.
I don't have a strong opinion on this.
parent bb0c8bb1
......@@ -60,9 +60,9 @@ class SkyDemo {
this.description,
this.textTheme,
this.decoration
}) : name = name, key = name;
}) : name = name, key = new Key(name);
final String name;
final String key;
final Key key;
final String href;
final String bundle;
final String description;
......
......@@ -27,7 +27,7 @@ import 'fitness_types.dart';
import 'measurement.dart';
class MeasurementList extends Component {
MeasurementList({ String key, this.measurements, this.onDismissed }) : super(key: key);
MeasurementList({ Key key, this.measurements, this.onDismissed }) : super(key: key);
final List<Measurement> measurements;
final MeasurementHandler onDismissed;
......@@ -49,7 +49,7 @@ class MeasurementList extends Component {
class MeasurementRow extends Component {
MeasurementRow({ Measurement measurement, this.onDismissed }) : this.measurement = measurement, super(key: measurement.when.toString());
MeasurementRow({ Measurement measurement, this.onDismissed }) : this.measurement = measurement, super(key: new Key.stringify(measurement.when));
final Measurement measurement;
final MeasurementHandler onDismissed;
......@@ -74,7 +74,7 @@ class MeasurementRow extends Component {
];
return new Dismissable(
key: measurement.when.toString(),
key: new Key.stringify(measurement.when),
onDismissed: () => onDismissed(measurement),
child: new Card(
child: new Container(
......
// 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.
......@@ -57,6 +56,20 @@ class MineDiggerApp extends App {
// |uiState| keeps track of the visible player progess.
List<List<CellState>> uiState;
Game(this.app) {
randomSeed = 22;
// Colors for each mine count:
// 0 - none, 1 - blue, 2-green, 3-red, 4-black, 5-dark red .. etc.
textStyles.add(new TextStyle(color: const Color(0xFF555555), fontWeight: bold));
textStyles.add(new TextStyle(color: const Color(0xFF0094FF), fontWeight: bold));
textStyles.add(new TextStyle(color: const Color(0xFF13A023), fontWeight: bold));
textStyles.add(new TextStyle(color: const Color(0xFFDA1414), fontWeight: bold));
textStyles.add(new TextStyle(color: const Color(0xFF1E2347), fontWeight: bold));
textStyles.add(new TextStyle(color: const Color(0xFF7F0037), fontWeight: bold));
textStyles.add(new TextStyle(color: const Color(0xFFE93BE9), fontWeight: bold));
initialize();
}
void resetGame() {
alive = true;
hasWon = false;
......@@ -148,13 +161,14 @@ class MineDiggerApp extends App {
} else if (state == CellState.flagged) {
row.add(new CoveredMineNode(
flagged: true,
posX: ix, posY: iy)
);
posX: ix,
posY: iy
));
} else {
row.add(new ExposedMineNode(
state: state,
count: count)
);
count: count
));
}
}
flexRows.add(
......@@ -162,8 +176,9 @@ class MineDiggerApp extends App {
row,
direction: FlexDirection.horizontal,
justifyContent: FlexJustifyContent.center,
key: 'flex_row($iy)'
));
key: new Key.stringify(iy)
)
);
}
if (!hasCoveredCell) {
......
......@@ -12,7 +12,7 @@ import 'package:sky/widgets/basic.dart';
class StockArrow extends Component {
StockArrow({ String key, this.percentChange }) : super(key: key);
StockArrow({ Key key, this.percentChange }) : super(key: key);
final double percentChange;
......
......@@ -10,7 +10,7 @@ import 'stock_data.dart';
import 'stock_row.dart';
class Stocklist extends Component {
Stocklist({ String key, this.stocks }) : super(key: key);
Stocklist({ Key key, this.stocks }) : super(key: key);
final List<Stock> stocks;
......
......@@ -14,7 +14,7 @@ export 'package:sky/widgets/popup_menu.dart' show PopupMenuStatus;
class StockMenu extends Component {
StockMenu({
String key,
Key key,
this.showing,
this.onStatusChanged,
this.navigator,
......
......@@ -14,7 +14,7 @@ import 'stock_data.dart';
class StockRow extends Component {
StockRow({ Stock stock }) : this.stock = stock, super(key: stock.symbol);
StockRow({ Stock stock }) : this.stock = stock, super(key: new Key(stock.symbol));
final Stock stock;
......
......@@ -115,7 +115,7 @@ class BlockViewportApp extends App {
if (index >= lengths.length)
return null;
return new Listener(
key: lengths[index].toString(),
key: new Key.stringify(lengths[index]),
child: new Container(
decoration: new BoxDecoration(
backgroundColor: new Color((0xFF000000 + 0xFFFFFF * lengths[index] / kMaxLength).round())
......
......@@ -27,13 +27,13 @@ class CardModel {
Color color;
AnimationPerformance performance;
String get label => "Item $value";
String get key => value.toString();
Key get key => new Key.fromObjectIdentity(this);
}
class ShrinkingCard extends AnimatedComponent {
ShrinkingCard({
String key,
Key key,
CardModel this.card,
Function this.onUpdated,
Function this.onCompleted
......
......@@ -24,7 +24,7 @@ void addFlexChildSolidColor(RenderFlex parent, sky.Color backgroundColor, { int
// Solid colour, Widget version
class Rectangle extends Component {
Rectangle(this.color, { String key }) : super(key: key);
Rectangle(this.color, { Key key }) : super(key: key);
final Color color;
Widget build() {
return new Flexible(
......
......@@ -52,7 +52,7 @@ HAL: This mission is too important for me to allow you to jeopardize it.''';
Component toStyledText(String name, String text) {
TextStyle lineStyle = (name == "Dave") ? daveStyle : halStyle;
return new StyledText(
key: text,
key: new Key(text),
elements: [lineStyle, [boldStyle, [underlineStyle, name], ":"], text]
);
}
......
......@@ -18,7 +18,7 @@ const _kCursorWidth = 1.0;
class EditableText extends StatefulComponent {
EditableText({
String key,
Key key,
this.value,
this.focused: false,
this.style,
......
......@@ -25,7 +25,7 @@ class Input extends StatefulComponent {
// Never makes sense to have both a localKey and a globalKey.
// Possibly a class HeroKey who functions as a UUID.
Input({String key,
Input({Key key,
this.placeholder,
this.onChanged,
this.focused})
......@@ -75,7 +75,7 @@ class Input extends StatefulComponent {
if (placeholder != null && _value.isEmpty) {
Widget child = new Opacity(
key: "placeholder",
key: new Key('placeholder'),
child: new Text(placeholder, style: textStyle),
opacity: themeData.hintOpacity
);
......
......@@ -7,7 +7,7 @@ import 'package:sky/widgets/basic.dart';
abstract class AnimatedComponent extends StatefulComponent {
AnimatedComponent({ String key }) : super(key: key);
AnimatedComponent({ Key key }) : super(key: key);
void syncFields(AnimatedComponent source) { }
......
......@@ -77,7 +77,7 @@ class ImplicitlyAnimatedValue<T> {
class AnimatedContainer extends AnimatedComponent {
AnimatedContainer({
String key,
Key key,
this.child,
this.duration,
this.constraints,
......
......@@ -22,13 +22,13 @@ import 'package:sky/widgets/widget.dart';
export 'package:sky/rendering/box.dart' show BackgroundImage, BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims;
export 'package:sky/rendering/flex.dart' show FlexDirection, FlexJustifyContent, FlexAlignItems;
export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
export 'package:sky/widgets/widget.dart' show Widget, Component, StatefulComponent, App, runApp, Listener, ParentDataNode;
export 'package:sky/widgets/widget.dart' show Key, Widget, Component, StatefulComponent, App, runApp, Listener, ParentDataNode;
// PAINTING NODES
class Opacity extends OneChildRenderObjectWrapper {
Opacity({ String key, this.opacity, Widget child })
Opacity({ Key key, this.opacity, Widget child })
: super(key: key, child: child);
final double opacity;
......@@ -43,7 +43,7 @@ class Opacity extends OneChildRenderObjectWrapper {
}
class ColorFilter extends OneChildRenderObjectWrapper {
ColorFilter({ String key, this.color, this.transferMode, Widget child })
ColorFilter({ Key key, this.color, this.transferMode, Widget child })
: super(key: key, child: child);
final Color color;
......@@ -60,7 +60,7 @@ class ColorFilter extends OneChildRenderObjectWrapper {
}
class DecoratedBox extends OneChildRenderObjectWrapper {
DecoratedBox({ String key, this.decoration, Widget child })
DecoratedBox({ Key key, this.decoration, Widget child })
: super(key: key, child: child);
final BoxDecoration decoration;
......@@ -75,7 +75,7 @@ class DecoratedBox extends OneChildRenderObjectWrapper {
}
class CustomPaint extends OneChildRenderObjectWrapper {
CustomPaint({ String key, this.callback, this.token, Widget child })
CustomPaint({ Key key, this.callback, this.token, Widget child })
: super(key: key, child: child);
final CustomPaintCallback callback;
......@@ -98,7 +98,7 @@ class CustomPaint extends OneChildRenderObjectWrapper {
}
class ClipRect extends OneChildRenderObjectWrapper {
ClipRect({ String key, Widget child })
ClipRect({ Key key, Widget child })
: super(key: key, child: child);
RenderClipRect createNode() => new RenderClipRect();
......@@ -108,7 +108,7 @@ class ClipRect extends OneChildRenderObjectWrapper {
}
class ClipRRect extends OneChildRenderObjectWrapper {
ClipRRect({ String key, this.xRadius, this.yRadius, Widget child })
ClipRRect({ Key key, this.xRadius, this.yRadius, Widget child })
: super(key: key, child: child);
final double xRadius;
......@@ -125,7 +125,7 @@ class ClipRRect extends OneChildRenderObjectWrapper {
}
class ClipOval extends OneChildRenderObjectWrapper {
ClipOval({ String key, Widget child })
ClipOval({ Key key, Widget child })
: super(key: key, child: child);
RenderClipOval createNode() => new RenderClipOval();
......@@ -138,7 +138,7 @@ class ClipOval extends OneChildRenderObjectWrapper {
// POSITIONING AND SIZING NODES
class Transform extends OneChildRenderObjectWrapper {
Transform({ String key, this.transform, Widget child })
Transform({ Key key, this.transform, Widget child })
: super(key: key, child: child);
final Matrix4 transform;
......@@ -153,7 +153,7 @@ class Transform extends OneChildRenderObjectWrapper {
}
class Padding extends OneChildRenderObjectWrapper {
Padding({ String key, this.padding, Widget child })
Padding({ Key key, this.padding, Widget child })
: super(key: key, child: child);
final EdgeDims padding;
......@@ -168,7 +168,7 @@ class Padding extends OneChildRenderObjectWrapper {
}
class Center extends OneChildRenderObjectWrapper {
Center({ String key, Widget child })
Center({ Key key, Widget child })
: super(key: key, child: child);
RenderPositionedBox createNode() => new RenderPositionedBox();
......@@ -178,7 +178,7 @@ class Center extends OneChildRenderObjectWrapper {
}
class SizedBox extends OneChildRenderObjectWrapper {
SizedBox({ String key, this.width, this.height, Widget child })
SizedBox({ Key key, this.width, this.height, Widget child })
: super(key: key, child: child);
final double width;
......@@ -203,7 +203,7 @@ class SizedBox extends OneChildRenderObjectWrapper {
}
class ConstrainedBox extends OneChildRenderObjectWrapper {
ConstrainedBox({ String key, this.constraints, Widget child })
ConstrainedBox({ Key key, this.constraints, Widget child })
: super(key: key, child: child);
final BoxConstraints constraints;
......@@ -218,7 +218,7 @@ class ConstrainedBox extends OneChildRenderObjectWrapper {
}
class AspectRatio extends OneChildRenderObjectWrapper {
AspectRatio({ String key, this.aspectRatio, Widget child })
AspectRatio({ Key key, this.aspectRatio, Widget child })
: super(key: key, child: child);
final double aspectRatio;
......@@ -233,7 +233,7 @@ class AspectRatio extends OneChildRenderObjectWrapper {
}
class ShrinkWrapWidth extends OneChildRenderObjectWrapper {
ShrinkWrapWidth({ String key, this.stepWidth, this.stepHeight, Widget child })
ShrinkWrapWidth({ Key key, this.stepWidth, this.stepHeight, Widget child })
: super(key: key, child: child);
final double stepWidth;
......@@ -250,7 +250,7 @@ class ShrinkWrapWidth extends OneChildRenderObjectWrapper {
}
class Baseline extends OneChildRenderObjectWrapper {
Baseline({ String key, this.baseline, this.baselineType: TextBaseline.alphabetic, Widget child })
Baseline({ Key key, this.baseline, this.baselineType: TextBaseline.alphabetic, Widget child })
: super(key: key, child: child);
final double baseline; // in pixels
......@@ -267,7 +267,7 @@ class Baseline extends OneChildRenderObjectWrapper {
}
class Viewport extends OneChildRenderObjectWrapper {
Viewport({ String key, this.offset: 0.0, Widget child })
Viewport({ Key key, this.offset: 0.0, Widget child })
: super(key: key, child: child);
final double offset;
......@@ -282,7 +282,7 @@ class Viewport extends OneChildRenderObjectWrapper {
}
class SizeObserver extends OneChildRenderObjectWrapper {
SizeObserver({ String key, this.callback, Widget child })
SizeObserver({ Key key, this.callback, Widget child })
: super(key: key, child: child);
final SizeChangedCallback callback;
......@@ -307,7 +307,7 @@ class SizeObserver extends OneChildRenderObjectWrapper {
class Container extends Component {
Container({
String key,
Key key,
this.child,
this.constraints,
this.decoration,
......@@ -364,7 +364,7 @@ class Container extends Component {
// LAYOUT NODES
class Block extends MultiChildRenderObjectWrapper {
Block(List<Widget> children, { String key })
Block(List<Widget> children, { Key key })
: super(key: key, children: children);
RenderBlock createNode() => new RenderBlock();
......@@ -372,7 +372,7 @@ class Block extends MultiChildRenderObjectWrapper {
}
class Stack extends MultiChildRenderObjectWrapper {
Stack(List<Widget> children, { String key })
Stack(List<Widget> children, { Key key })
: super(key: key, children: children);
RenderStack createNode() => new RenderStack();
......@@ -381,7 +381,7 @@ class Stack extends MultiChildRenderObjectWrapper {
class Positioned extends ParentDataNode {
Positioned({
String key,
Key key,
Widget child,
double top,
double right,
......@@ -398,7 +398,7 @@ class Positioned extends ParentDataNode {
class Flex extends MultiChildRenderObjectWrapper {
Flex(List<Widget> children, {
String key,
Key key,
this.direction: FlexDirection.horizontal,
this.justifyContent: FlexJustifyContent.start,
this.alignItems: FlexAlignItems.center,
......@@ -424,12 +424,12 @@ class Flex extends MultiChildRenderObjectWrapper {
}
class Flexible extends ParentDataNode {
Flexible({ String key, int flex: 1, Widget child })
Flexible({ Key key, int flex: 1, Widget child })
: super(child, new FlexBoxParentData()..flex = flex, key: key);
}
class Inline extends LeafRenderObjectWrapper {
Inline({ String key, this.text }) : super(key: key);
Inline({ Key key, this.text }) : super(key: key);
final InlineBase text;
......@@ -446,7 +446,7 @@ class StyledText extends Component {
// elements ::= "string" | [<text-style> <elements>*]
// Where "string" is text to display and text-style is an instance of
// TextStyle. The text-style applies to all of the elements that follow.
StyledText({ this.elements, String key }) : super(key: key);
StyledText({ this.elements, Key key }) : super(key: key);
final dynamic elements;
......@@ -464,7 +464,7 @@ class StyledText extends Component {
}
class Text extends Component {
Text(this.data, { String key, TextStyle this.style }) : super(key: key);
Text(this.data, { Key key, TextStyle this.style }) : super(key: key);
final String data;
final TextStyle style;
......@@ -490,7 +490,7 @@ class Text extends Component {
class Image extends LeafRenderObjectWrapper {
Image({ sky.Image image, this.width, this.height, this.colorFilter })
: image = image,
super(key: image.hashCode.toString()); // TODO(ianh): Find a way to uniquely identify the sky.Image rather than using hashCode, which could collide
super(key: new Key.fromObjectIdentity(image));
final sky.Image image;
final double width;
......@@ -510,7 +510,7 @@ class Image extends LeafRenderObjectWrapper {
}
class FutureImage extends StatefulComponent {
FutureImage({ String key, this.image, this.width, this.height, this.colorFilter })
FutureImage({ Key key, this.image, this.width, this.height, this.colorFilter })
: super(key: key);
Future<sky.Image> image;
......@@ -556,7 +556,8 @@ class FutureImage extends StatefulComponent {
class NetworkImage extends Component {
NetworkImage({ String src, this.width, this.height, this.colorFilter })
: src = src, super(key: src);
: src = src,
super(key: new Key(src));
final String src;
final double width;
......@@ -576,7 +577,7 @@ class NetworkImage extends Component {
class AssetImage extends Component {
AssetImage({ String name, this.bundle, this.width, this.height, this.colorFilter })
: name = name,
super(key: name);
super(key: new Key(name));
final String name;
final AssetBundle bundle;
......@@ -597,7 +598,7 @@ class AssetImage extends Component {
class WidgetToRenderBoxAdapter extends LeafRenderObjectWrapper {
WidgetToRenderBoxAdapter(RenderBox renderBox)
: renderBox = renderBox,
super(key: renderBox.hashCode.toString()); // TODO(ianh): Find a way to uniquely identify the RenderBox rather than using hashCode, which could collide
super(key: new Key.fromObjectIdentity(renderBox));
final RenderBox renderBox;
......
......@@ -16,7 +16,7 @@ class _Key {
const _Key(this.type, this.key);
factory _Key.fromWidget(Widget widget) => new _Key(widget.runtimeType, widget.key);
final Type type;
final String key;
final Key key;
bool operator ==(other) => other is _Key && other.type == type && other.key == key;
int get hashCode => 373 * 37 * type.hashCode + key.hashCode;
String toString() => "_Key(type: $type, key: $key)";
......@@ -78,7 +78,7 @@ class BlockViewportLayoutState {
}
class BlockViewport extends RenderObjectWrapper {
BlockViewport({ this.builder, this.startOffset, this.token, this.layoutState, String key })
BlockViewport({ this.builder, this.startOffset, this.token, this.layoutState, Key key })
: super(key: key) {
assert(this.layoutState != null);
}
......
......@@ -6,7 +6,7 @@ import 'package:sky/widgets/basic.dart';
abstract class ButtonBase extends StatefulComponent {
ButtonBase({ String key, this.highlight: false }) : super(key: key);
ButtonBase({ Key key, this.highlight: false }) : super(key: key);
bool highlight;
......
......@@ -11,7 +11,7 @@ const EdgeDims kCardMargins = const EdgeDims.all(4.0);
///
/// <https://www.google.com/design/spec/components/cards.html>
class Card extends Component {
Card({ String key, this.child, this.color }) : super(key: key);
Card({ Key key, this.child, this.color }) : super(key: key);
final Widget child;
final Color color;
......
......@@ -32,7 +32,7 @@ class Checkbox extends Toggleable {
/// * `value` determines whether the checkbox is checked.
/// * `onChanged` is called whenever the state of the checkbox should change.
Checkbox({
String key,
Key key,
bool value,
ValueChanged onChanged
}) : super(key: key, value: value, onChanged: onChanged);
......
......@@ -9,7 +9,7 @@ import 'package:sky/widgets/widget.dart';
class DefaultTextStyle extends Inherited {
DefaultTextStyle({
String key,
Key key,
this.style,
Widget child
}) : super(key: key, child: child) {
......
......@@ -14,7 +14,7 @@ import 'package:sky/widgets/theme.dart';
/// <https://www.google.com/design/spec/components/dialogs.html>
class Dialog extends Component {
Dialog({
String key,
Key key,
this.title,
this.content,
this.actions,
......
......@@ -22,7 +22,7 @@ typedef void DismissedCallback();
class Dismissable extends AnimatedComponent {
Dismissable({
String key,
Key key,
this.child,
this.onDismissed
// TODO(hansmuller): direction
......
......@@ -46,7 +46,7 @@ typedef void DrawerStatusChangedCallback(DrawerStatus status);
class Drawer extends AnimatedComponent {
Drawer({
String key,
Key key,
this.children,
this.showing: false,
this.level: 0,
......
......@@ -6,7 +6,7 @@ import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/theme.dart';
class DrawerDivider extends Component {
DrawerDivider({ String key }) : super(key: key);
DrawerDivider({ Key key }) : super(key: key);
Widget build() {
return new Container(
......
......@@ -12,7 +12,7 @@ import 'package:sky/widgets/theme.dart';
class DrawerHeader extends Component {
DrawerHeader({ String key, this.children }) : super(key: key);
DrawerHeader({ Key key, this.children }) : super(key: key);
final List<Widget> children;
......
......@@ -15,7 +15,7 @@ import 'package:sky/widgets/theme.dart';
import 'package:sky/widgets/widget.dart';
class DrawerItem extends ButtonBase {
DrawerItem({ String key, this.icon, this.children, this.onPressed, this.selected: false })
DrawerItem({ Key key, this.icon, this.children, this.onPressed, this.selected: false })
: super(key: key);
String icon;
......
......@@ -10,7 +10,7 @@ import 'package:sky/widgets/scrollable.dart';
abstract class FixedHeightScrollable extends Scrollable {
FixedHeightScrollable({ String key, this.itemHeight, this.padding })
FixedHeightScrollable({ Key key, this.itemHeight, this.padding })
: super(key: key) {
assert(itemHeight != null);
}
......
......@@ -9,7 +9,7 @@ import 'package:sky/widgets/theme.dart';
class FlatButton extends MaterialButton {
FlatButton({
String key,
Key key,
Widget child,
bool enabled: true,
Function onPressed
......
......@@ -16,7 +16,7 @@ const double _kSize = 56.0;
class FloatingActionButton extends ButtonBase {
FloatingActionButton({
String key,
Key key,
this.child,
this.backgroundColor,
this.onPressed
......
......@@ -19,7 +19,7 @@ class IconThemeData {
class IconTheme extends Inherited {
IconTheme({
String key,
Key key,
this.data,
Widget child
}) : super(key: key, child: child) {
......@@ -49,7 +49,7 @@ final AssetBundle _iconBundle = _initIconBundle();
class Icon extends Component {
Icon({
String key,
Key key,
this.size,
this.type: '',
this.color,
......
......@@ -12,7 +12,7 @@ import 'package:sky/widgets/widget.dart';
class IconButton extends Component {
IconButton({ String icon: '', this.onPressed, this.color })
: super(key: icon), icon = icon;
: super(key: new Key(icon)), icon = icon;
final String icon;
final Function onPressed;
......
......@@ -126,7 +126,7 @@ class RenderInkWell extends RenderProxyBox {
}
class InkWell extends OneChildRenderObjectWrapper {
InkWell({ String key, Widget child })
InkWell({ Key key, Widget child })
: super(key: key, child: child);
RenderInkWell get root => super.root;
......
......@@ -20,7 +20,7 @@ const Map<MaterialType, double> edges = const {
class Material extends Component {
Material({
String key,
Key key,
this.child,
this.type: MaterialType.card,
this.level: 0,
......
......@@ -11,7 +11,7 @@ import 'package:sky/widgets/material.dart';
abstract class MaterialButton extends ButtonBase {
MaterialButton({
String key,
Key key,
this.child,
this.enabled: true,
this.onPressed
......
......@@ -7,7 +7,7 @@ import 'package:sky/widgets/widget.dart';
class ModalOverlay extends Component {
ModalOverlay({ String key, this.children, this.onDismiss }) : super(key: key);
ModalOverlay({ Key key, this.children, this.onDismiss }) : super(key: key);
final List<Widget> children;
final Function onDismiss;
......
......@@ -46,7 +46,7 @@ const Point _kTransitionStartPoint = const Point(0.0, 75.0);
enum TransitionDirection { forward, reverse }
class Transition extends AnimatedComponent {
Transition({
String key,
Key key,
this.content,
this.direction,
this.onDismissed,
......@@ -190,7 +190,7 @@ class NavigationState {
class Navigator extends StatefulComponent {
Navigator(this.state, { String key }) : super(key: key);
Navigator(this.state, { Key key }) : super(key: key);
NavigationState state;
......@@ -242,7 +242,7 @@ class Navigator extends StatefulComponent {
if (content == null)
continue;
Transition transition = new Transition(
key: historyEntry.hashCode.toString(), // TODO(ianh): make it not collide
key: new Key.fromObjectIdentity(historyEntry),
content: content,
direction: (i <= state.historyIndex) ? TransitionDirection.forward : TransitionDirection.reverse,
interactive: (i == state.historyIndex),
......
......@@ -35,7 +35,7 @@ typedef void PopupMenuStatusChangedCallback(PopupMenuStatus status);
class PopupMenu extends AnimatedComponent {
PopupMenu({
String key,
Key key,
this.showing,
this.onStatusChanged,
this.items,
......
......@@ -13,7 +13,7 @@ const double kBaselineOffsetFromBottom = 20.0;
class PopupMenuItem extends Component {
PopupMenuItem({
String key,
Key key,
this.onPressed,
this.child
}) : super(key: key);
......
......@@ -17,7 +17,7 @@ typedef void ValueChanged(value);
class Radio extends ButtonBase {
Radio({
String key,
Key key,
this.value,
this.groupValue,
this.onChanged
......
......@@ -10,7 +10,7 @@ import 'package:sky/widgets/theme.dart';
class RaisedButton extends MaterialButton {
RaisedButton({
String key,
Key key,
Widget child,
bool enabled: true,
Function onPressed
......
......@@ -173,7 +173,7 @@ class RenderScaffold extends RenderBox {
class Scaffold extends RenderObjectWrapper {
Scaffold({
String key,
Key key,
Widget body,
Widget statusBar,
Widget toolbar,
......
......@@ -27,7 +27,7 @@ enum ScrollDirection { vertical, horizontal }
abstract class Scrollable extends StatefulComponent {
Scrollable({
String key,
Key key,
this.direction: ScrollDirection.vertical
}) : super(key: key);
......
......@@ -11,7 +11,7 @@ typedef Widget ItemBuilder<T>(T item);
class ScrollableList<T> extends FixedHeightScrollable {
ScrollableList({
String key,
Key key,
this.items,
this.itemBuilder,
double itemHeight,
......
......@@ -8,7 +8,7 @@ import 'package:sky/widgets/scrollable.dart';
class ScrollableViewport extends Scrollable {
ScrollableViewport({ String key, this.child }) : super(key: key);
ScrollableViewport({ Key key, this.child }) : super(key: key);
Widget child;
......@@ -54,7 +54,7 @@ class ScrollableViewport extends Scrollable {
class ScrollableBlock extends Component {
ScrollableBlock(this.children, { String key }) : super(key: key);
ScrollableBlock(this.children, { Key key }) : super(key: key);
final List<Widget> children;
......
......@@ -10,7 +10,7 @@ import 'package:sky/widgets/material.dart';
import 'package:sky/widgets/theme.dart';
class SnackBarAction extends Component {
SnackBarAction({String key, this.label, this.onPressed }) : super(key: key) {
SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) {
assert(label != null);
}
......@@ -32,7 +32,7 @@ class SnackBarAction extends Component {
class SnackBar extends Component {
SnackBar({
String key,
Key key,
this.content,
this.actions
}) : super(key: key) {
......
......@@ -26,7 +26,7 @@ class Switch extends Toggleable {
// TODO(jackson): Hit-test the switch so that it can respond to both taps and swipe gestures
Switch({
String key,
Key key,
bool value,
ValueChanged onChanged
}) : super(key: key, value: value, onChanged: onChanged);
......
......@@ -256,7 +256,7 @@ class TabBarWrapper extends MultiChildRenderObjectWrapper {
this.textAndIcons,
this.scrollable: false,
this.onLayoutChanged,
String key
Key key
}) : super(key: key, children: children);
final int selectedIndex;
......@@ -289,7 +289,7 @@ class TabLabel {
class Tab extends Component {
Tab({
String key,
Key key,
this.label,
this.selected: false
}) : super(key: key) {
......@@ -347,7 +347,7 @@ class Tab extends Component {
class TabBar extends Scrollable {
TabBar({
String key,
Key key,
this.labels,
this.selectedIndex: 0,
this.onChanged,
......@@ -381,7 +381,7 @@ class TabBar extends Scrollable {
Tab tab = new Tab(
label: label,
selected: tabIndex == selectedIndex,
key: label.text == null ? label.icon : label.text
key: new Key(label.text == null ? label.icon : label.text)
);
return new Listener(
child: tab,
......@@ -472,7 +472,7 @@ class TabNavigatorView {
class TabNavigator extends Component {
TabNavigator({
String key,
Key key,
this.views,
this.selectedIndex: 0,
this.onChanged,
......
......@@ -11,7 +11,7 @@ export 'package:sky/theme/theme_data.dart' show ThemeData, ThemeBrightness;
class Theme extends Inherited {
Theme({
String key,
Key key,
this.data,
Widget child
}) : super(key: key, child: child) {
......
......@@ -17,7 +17,7 @@ const Duration _kCheckDuration = const Duration(milliseconds: 200);
abstract class Toggleable extends AnimatedComponent {
Toggleable({
String key,
Key key,
this.value,
this.onChanged
}) : super(key: key);
......
......@@ -16,7 +16,7 @@ import 'package:sky/widgets/icon.dart';
class ToolBar extends Component {
ToolBar({
String key,
Key key,
this.left,
this.center,
this.right,
......
......@@ -12,7 +12,7 @@ export 'package:sky/widgets/block_viewport.dart' show BlockViewportLayoutState;
class VariableHeightScrollable extends Scrollable {
VariableHeightScrollable({
String key,
Key key,
this.builder,
this.token,
this.layoutState
......
......@@ -21,22 +21,54 @@ final bool _shouldLogRenderDuration = false;
typedef Widget Builder();
typedef void WidgetTreeWalker(Widget);
abstract class KeyBase {
}
abstract class Key extends KeyBase {
Key.constructor(); // so that subclasses can call us, since the Key() factory constructor shadows the implicit constructor
factory Key(String value) => new StringKey(value);
factory Key.stringify(Object value) => new StringKey(value.toString());
factory Key.fromObjectIdentity(Object value) => new ObjectKey(value);
factory Key.unique() => new UniqueKey();
}
class StringKey extends Key {
StringKey(this.value) : super.constructor();
final String value;
String toString() => value;
bool operator==(other) => other is StringKey && other.value == value;
int get hashCode => value.hashCode;
}
class ObjectKey extends Key {
ObjectKey(this.value) : super.constructor();
final Object value;
String toString() => '[Instance of ${value.runtimeType}]';
bool operator==(other) => other is ObjectKey && identical(other.value, value);
int get hashCode => identityHashCode(value);
}
class UniqueKey extends Key {
UniqueKey() : super.constructor();
String toString() => '[$hashCode]';
}
/// A base class for elements of the widget tree
abstract class Widget {
Widget({ String key }) : _key = key {
Widget({ Key key }) : _key = key {
assert(_isConstructedDuringBuild());
}
// TODO(jackson): Remove this workaround for limitation of Dart mixins
Widget._withKey(String key) : _key = key {
Widget._withKey(Key key) : _key = key {
assert(_isConstructedDuringBuild());
}
// you should not build the UI tree ahead of time, build it only during build()
bool _isConstructedDuringBuild() => this is AbstractWidgetRoot || this is App || _inRenderDirtyComponents || _inLayoutCallbackBuilder > 0;
String _key;
Key _key;
/// A semantic identifer for this widget
///
......@@ -46,7 +78,7 @@ abstract class Widget {
/// Assigning a key to a widget can improve performance by causing the
/// framework to sync widgets that share a lot of common structure and can
/// help match stateful components semantically rather than positionally.
String get key => _key;
Key get key => _key;
Widget _parent;
......@@ -243,11 +275,11 @@ abstract class Widget {
// stylistic information, etc.
abstract class TagNode extends Widget {
TagNode(Widget child, { String key })
TagNode(Widget child, { Key key })
: this.child = child, super(key: key);
// TODO(jackson): Remove this workaround for limitation of Dart mixins
TagNode._withKey(Widget child, String key)
TagNode._withKey(Widget child, Key key)
: this.child = child, super._withKey(key);
Widget child;
......@@ -284,14 +316,14 @@ abstract class TagNode extends Widget {
}
class ParentDataNode extends TagNode {
ParentDataNode(Widget child, this.parentData, { String key })
ParentDataNode(Widget child, this.parentData, { Key key })
: super(child, key: key);
final ParentData parentData;
}
abstract class Inherited extends TagNode {
Inherited({ String key, Widget child }) : super._withKey(child, key);
Inherited({ Key key, Widget child }) : super._withKey(child, key);
void _sync(Widget old, dynamic slot) {
if (old != null && syncShouldNotify(old)) {
......@@ -320,7 +352,7 @@ typedef void EventListener(sky.Event e);
class Listener extends TagNode {
Listener({
String key,
Key key,
Widget child,
EventListener onWheel,
GestureEventListener onGestureFlingCancel,
......@@ -407,7 +439,7 @@ class Listener extends TagNode {
abstract class Component extends Widget {
Component({ String key })
Component({ Key key })
: _order = _currentOrder + 1,
super._withKey(key);
......@@ -524,7 +556,7 @@ abstract class Component extends Widget {
abstract class StatefulComponent extends Component {
StatefulComponent({ String key }) : super(key: key);
StatefulComponent({ Key key }) : super(key: key);
bool _disqualifiedFromEverAppearingAgain = false;
bool _isStateInitialized = false;
......@@ -682,7 +714,7 @@ void _scheduleComponentForRender(Component c) {
// become stateful.
abstract class RenderObjectWrapper extends Widget {
RenderObjectWrapper({ String key }) : super(key: key);
RenderObjectWrapper({ Key key }) : super(key: key);
RenderObject createNode();
......@@ -766,7 +798,7 @@ abstract class RenderObjectWrapper extends Widget {
abstract class LeafRenderObjectWrapper extends RenderObjectWrapper {
LeafRenderObjectWrapper({ String key }) : super(key: key);
LeafRenderObjectWrapper({ Key key }) : super(key: key);
void insertChildRoot(RenderObjectWrapper child, dynamic slot) {
assert(false);
......@@ -780,7 +812,7 @@ abstract class LeafRenderObjectWrapper extends RenderObjectWrapper {
abstract class OneChildRenderObjectWrapper extends RenderObjectWrapper {
OneChildRenderObjectWrapper({ String key, Widget child })
OneChildRenderObjectWrapper({ Key key, Widget child })
: _child = child, super(key: key);
Widget _child;
......@@ -828,7 +860,7 @@ abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper {
// In MultiChildRenderObjectWrapper subclasses, slots are RenderObject nodes
// to use as the "insert before" sibling in ContainerRenderObjectMixin.add() calls
MultiChildRenderObjectWrapper({ String key, List<Widget> children })
MultiChildRenderObjectWrapper({ Key key, List<Widget> children })
: this.children = children == null ? const [] : children,
super(key: key) {
assert(!_debugHasDuplicateIds());
......@@ -867,7 +899,7 @@ abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper {
}
bool _debugHasDuplicateIds() {
var idSet = new HashSet<String>();
var idSet = new HashSet<Key>();
for (var child in children) {
assert(child != null);
if (child.key == null)
......@@ -922,9 +954,9 @@ abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper {
nextSibling = children[endIndex].root;
}
HashMap<String, Widget> oldNodeIdMap = null;
HashMap<Key, Widget> oldNodeIdMap = null;
bool oldNodeReordered(String key) {
bool oldNodeReordered(Key key) {
return oldNodeIdMap != null &&
oldNodeIdMap.containsKey(key) &&
oldNodeIdMap[key] == null;
......@@ -942,7 +974,7 @@ abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper {
if (oldNodeIdMap != null)
return;
oldNodeIdMap = new HashMap<String, Widget>();
oldNodeIdMap = new HashMap<Key, Widget>();
for (int i = oldStartIndex; i < oldEndIndex; i++) {
var node = oldChildren[i];
if (node.key != null)
......@@ -1047,7 +1079,7 @@ class WidgetSkyBinding extends SkyBinding {
abstract class App extends StatefulComponent {
App({ String key }) : super(key: key);
App({ Key key }) : super(key: key);
void _handleEvent(sky.Event event) {
if (event.type == 'back')
......@@ -1095,7 +1127,7 @@ abstract class AbstractWidgetRoot extends StatefulComponent {
}
class RenderViewWrapper extends OneChildRenderObjectWrapper {
RenderViewWrapper({ String key, Widget child }) : super(key: key, child: child);
RenderViewWrapper({ Key key, Widget child }) : super(key: key, child: child);
RenderView get root => super.root;
RenderView createNode() => SkyBinding.instance.renderView;
}
......
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