diff --git a/sky/sdk/example/address_book/lib/main.dart b/sky/sdk/example/address_book/lib/main.dart index 5ab9490fa4c..1f24c3e296d 100644 --- a/sky/sdk/example/address_book/lib/main.dart +++ b/sky/sdk/example/address_book/lib/main.dart @@ -68,7 +68,7 @@ class AddressBookApp extends App { child: new Icon(type: 'image/photo_camera', size: 24), backgroundColor: Theme.of(this).accentColor, onPressed: () { - navigator.push(new DialogRoute(builder: (navigator, route) { + showDialog(navigator, (navigator) { return new Dialog( title: new Text("Describe your picture"), content: new ScrollableBlock([ @@ -79,9 +79,7 @@ class AddressBookApp extends App { actions: [ new FlatButton( child: new Text('DISCARD'), - onPressed: () { - navigator.pop(); - } + onPressed: navigator.pop ), new FlatButton( child: new Text('SAVE'), @@ -91,7 +89,7 @@ class AddressBookApp extends App { ), ] ); - })); + }); } ); } diff --git a/sky/sdk/example/fitness/lib/feed.dart b/sky/sdk/example/fitness/lib/feed.dart index a9e8213c260..0bdf66a39a9 100644 --- a/sky/sdk/example/fitness/lib/feed.dart +++ b/sky/sdk/example/fitness/lib/feed.dart @@ -214,10 +214,8 @@ class FeedFragment extends StatefulComponent { } void _handleActionButtonPressed() { - setState(() { - navigator.push(new DialogRoute(builder: (navigator, route) { - return new AddItemDialog(navigator); - })); + showDialog(navigator, (navigator) => new AddItemDialog(navigator)).then((route) { + navigator.pushNamed(route); }); } @@ -286,8 +284,7 @@ class AddItemDialog extends StatefulComponent { new FlatButton( child: new Text('ADD'), onPressed: () { - navigator.pop(); - navigator.pushNamed(_addItemRoute); + navigator.pop(_addItemRoute); } ), ] diff --git a/sky/sdk/example/stocks/lib/stock_settings.dart b/sky/sdk/example/stocks/lib/stock_settings.dart index 0f5d78f16f4..27432b8890e 100644 --- a/sky/sdk/example/stocks/lib/stock_settings.dart +++ b/sky/sdk/example/stocks/lib/stock_settings.dart @@ -45,26 +45,29 @@ class StockSettings extends StatefulComponent { _handleOptimismChanged(false); break; case StockMode.pessimistic: - navigator.push(new DialogRoute(builder: (navigator, route) { + showDialog(navigator, (navigator) { return new Dialog( title: new Text("Change mode?"), content: new Text("Optimistic mode means everything is awesome. Are you sure you can handle that?"), - onDismiss: navigator.pop, + onDismiss: () { + navigator.pop(false); + }, actions: [ new FlatButton( child: new Text('NO THANKS'), - onPressed: navigator.pop + onPressed: () { + navigator.pop(false); + } ), new FlatButton( child: new Text('AGREE'), onPressed: () { - _handleOptimismChanged(true); - navigator.pop(); + navigator.pop(true); } ), ] ); - })); + }).then(_handleOptimismChanged); break; } } diff --git a/sky/sdk/lib/widgets/dialog.dart b/sky/sdk/lib/widgets/dialog.dart index 67d9ec08f73..148845fd144 100644 --- a/sky/sdk/lib/widgets/dialog.dart +++ b/sky/sdk/lib/widgets/dialog.dart @@ -2,13 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:sky/theme/colors.dart' as colors; import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/default_text_style.dart'; import 'package:sky/widgets/material.dart'; +import 'package:sky/widgets/navigator.dart'; import 'package:sky/widgets/scrollable_viewport.dart'; import 'package:sky/widgets/theme.dart'; +typedef Widget DialogBuilder(Navigator navigator); + /// A material design dialog /// /// @@ -100,3 +105,14 @@ class Dialog extends Component { } } + +Future showDialog(Navigator navigator, DialogBuilder builder) { + Completer completer = new Completer(); + navigator.push(new DialogRoute( + completer: completer, + builder: (navigator, route) { + return builder(navigator); + } + )); + return completer.future; +} diff --git a/sky/sdk/lib/widgets/navigator.dart b/sky/sdk/lib/widgets/navigator.dart index 2c331bde306..327ec278fa4 100644 --- a/sky/sdk/lib/widgets/navigator.dart +++ b/sky/sdk/lib/widgets/navigator.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:sky/animation/animated_value.dart'; import 'package:sky/animation/animation_performance.dart'; import 'package:sky/animation/curves.dart'; @@ -14,7 +16,7 @@ typedef Widget RouteBuilder(Navigator navigator, RouteBase route); abstract class RouteBase { Widget build(Navigator navigator, RouteBase route); bool get isOpaque; - void popState() { } + void popState([dynamic result]) { assert(result == null); } } class Route extends RouteBase { @@ -28,17 +30,16 @@ class Route extends RouteBase { } class DialogRoute extends RouteBase { - DialogRoute({ this.builder, this.callback }); + DialogRoute({ this.completer, this.builder }); + final Completer completer; final RouteBuilder builder; - Function callback; Widget build(Navigator navigator, RouteBase route) => builder(navigator, route); bool get isOpaque => false; - void popState() { - if (callback != null) - callback(this); + void popState([dynamic result]) { + completer.complete(result); } } @@ -52,7 +53,8 @@ class RouteState extends RouteBase { Widget build(Navigator navigator, RouteBase route) => null; bool get isOpaque => false; - void popState() { + void popState([dynamic result]) { + assert(result == null); if (callback != null) callback(this); } @@ -197,10 +199,10 @@ class NavigationState { historyIndex++; } - void pop() { + void pop([dynamic result]) { if (historyIndex > 0) { HistoryEntry entry = history[historyIndex]; - entry.route.popState(); + entry.route.popState(result); entry.fullyOpaque = false; historyIndex--; } @@ -240,9 +242,9 @@ class Navigator extends StatefulComponent { }); } - void pop() { + void pop([dynamic result]) { setState(() { - state.pop(); + state.pop(result); }); } diff --git a/sky/sdk/lib/widgets/widget.dart b/sky/sdk/lib/widgets/widget.dart index 36242139220..cbafe4a77b0 100644 --- a/sky/sdk/lib/widgets/widget.dart +++ b/sky/sdk/lib/widgets/widget.dart @@ -26,7 +26,6 @@ abstract class Key { 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 { @@ -45,14 +44,25 @@ class ObjectKey extends Key { int get hashCode => identityHashCode(value); } -class UniqueKey extends Key { - UniqueKey() : super.constructor(); - String toString() => '[$hashCode]'; +abstract class GlobalKey extends Key { + GlobalKey.constructor() : super.constructor(); // so that subclasses can call us, since the Key() factory constructor shadows the implicit constructor + factory GlobalKey({ String label }) => new LabeledGlobalKey(label); + factory GlobalKey.fromObjectIdentity(Object value) => new GlobalObjectKey(value); } -class GlobalKey extends Key { - GlobalKey() : super.constructor(); - String toString() => '[Global Key $hashCode]'; +class LabeledGlobalKey extends GlobalKey { + // the label is purely for documentary purposes and does not affect the key + LabeledGlobalKey(this._label) : super.constructor(); + final String _label; + String toString() => '[GlobalKey ${_label != null ? _label : hashCode}]'; +} + +class GlobalObjectKey extends GlobalKey { + GlobalObjectKey(this.value) : super.constructor(); + final Object value; + String toString() => '[GlobalKey ${value.runtimeType}(${value.hashCode})]'; + bool operator==(other) => other is GlobalObjectKey && identical(other.value, value); + int get hashCode => identityHashCode(value); } /// A base class for elements of the widget tree