Commit 90a0f630 authored by Hixie's avatar Hixie

Simplify the usage of Navigator's routes argument

(These are changes cherry-picked from in-flight branches since they are
more independent and could be helpful even without those changes.)

- Change RouteBuilder's signature to take a single argument in which the
  other fields are placed, so that we can keep iterating on those
  arguments without having to break compatibility each time. Also, this
  makes defining route builders much simpler (only one argument to
  ignore rather than a variable number).

- Expose the next performance to RouteBuilders, since sometimes the
  route itself might not be where it's used.

- Allow BuildContext to be used to walk children, just like it can for
  ancestors

- Allow BuildContext to be used to get the Widget of the current
  BuildContext

- Allow StatefulComponentElement to be referenced with a type
  specialisation so that you don't have to cast when you know what the
  type you're dealing with actually is.
parent 3fb3ec5a
...@@ -101,7 +101,7 @@ void main() { ...@@ -101,7 +101,7 @@ void main() {
title: 'Address Book', title: 'Address Book',
theme: theme, theme: theme,
routes: <String, RouteBuilder>{ routes: <String, RouteBuilder>{
'/': (NavigatorState navigator, Route route) => new AddressBookHome(navigator: navigator) '/': (RouteArguments args) => new AddressBookHome(navigator: args.navigator)
} }
)); ));
} }
...@@ -206,7 +206,7 @@ void main() { ...@@ -206,7 +206,7 @@ void main() {
title: 'Flutter Demos', title: 'Flutter Demos',
theme: _theme, theme: _theme,
routes: { routes: {
'/': (NavigatorState navigator, Route route) => new DemoHome() '/': (RouteArguments args) => new DemoHome()
} }
)); ));
} }
...@@ -92,8 +92,6 @@ class FitnessApp extends StatefulComponent { ...@@ -92,8 +92,6 @@ class FitnessApp extends StatefulComponent {
class FitnessAppState extends State<FitnessApp> { class FitnessAppState extends State<FitnessApp> {
UserDataImpl _userData; UserDataImpl _userData;
Map<String, RouteBuilder> _routes;
void initState() { void initState() {
super.initState(); super.initState();
loadFitnessData().then((UserData data) { loadFitnessData().then((UserData data) {
...@@ -102,36 +100,6 @@ class FitnessAppState extends State<FitnessApp> { ...@@ -102,36 +100,6 @@ class FitnessAppState extends State<FitnessApp> {
print("Failed to load data: $e"); print("Failed to load data: $e");
setState(() => _userData = new UserDataImpl()); setState(() => _userData = new UserDataImpl());
}); });
_routes = {
'/': (NavigatorState navigator, Route route) {
return new FeedFragment(
navigator: navigator,
userData: _userData,
onItemCreated: _handleItemCreated,
onItemDeleted: _handleItemDeleted
);
},
'/meals/new': (navigator, route) {
return new MealFragment(
navigator: navigator,
onCreated: _handleItemCreated
);
},
'/measurements/new': (NavigatorState navigator, Route route) {
return new MeasurementFragment(
navigator: navigator,
onCreated: _handleItemCreated
);
},
'/settings': (navigator, route) {
return new SettingsFragment(
navigator: navigator,
userData: _userData,
updater: settingsUpdater
);
}
};
} }
void _handleItemCreated(FitnessItem item) { void _handleItemCreated(FitnessItem item) {
...@@ -158,17 +126,43 @@ class FitnessAppState extends State<FitnessApp> { ...@@ -158,17 +126,43 @@ class FitnessAppState extends State<FitnessApp> {
}); });
} }
final ThemeData _theme = new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.indigo,
accentColor: Colors.pinkAccent[200]
);
Widget build(BuildContext) { Widget build(BuildContext) {
return new App( return new App(
theme: _theme, theme: new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.indigo,
accentColor: Colors.pinkAccent[200]
),
title: 'Fitness', title: 'Fitness',
routes: _routes routes: {
'/': (RouteArguments args) {
return new FeedFragment(
navigator: args.navigator,
userData: _userData,
onItemCreated: _handleItemCreated,
onItemDeleted: _handleItemDeleted
);
},
'/meals/new': (RouteArguments args) {
return new MealFragment(
navigator: args.navigator,
onCreated: _handleItemCreated
);
},
'/measurements/new': (RouteArguments args) {
return new MeasurementFragment(
navigator: args.navigator,
onCreated: _handleItemCreated
);
},
'/settings': (RouteArguments args) {
return new SettingsFragment(
navigator: args.navigator,
userData: _userData,
updater: settingsUpdater
);
}
}
); );
} }
} }
......
...@@ -62,7 +62,7 @@ class TestAppState extends State<TestApp> { ...@@ -62,7 +62,7 @@ class TestAppState extends State<TestApp> {
); );
} }
Column _buildColumn(NavigatorState navigator, Route route) { Column _buildColumn(RouteArguments args) {
return new Column([ return new Column([
new Flexible(child: _buildSpriteWidget()), new Flexible(child: _buildSpriteWidget()),
_buildTabBar() _buildTabBar()
......
...@@ -92,11 +92,11 @@ class GameDemoState extends State<GameDemo> { ...@@ -92,11 +92,11 @@ class GameDemoState extends State<GameDemo> {
); );
} }
Widget _buildGameScene(NavigatorState navigator, Route route) { Widget _buildGameScene(RouteArguments args) {
return new SpriteWidget(_game, SpriteBoxTransformMode.fixedWidth); return new SpriteWidget(_game, SpriteBoxTransformMode.fixedWidth);
} }
Widget _buildMainScene(navigator, route) { Widget _buildMainScene(RouteArguments args) {
return new Stack([ return new Stack([
new SpriteWidget(new MainScreenBackground(), SpriteBoxTransformMode.fixedWidth), new SpriteWidget(new MainScreenBackground(), SpriteBoxTransformMode.fixedWidth),
new Column([ new Column([
...@@ -109,10 +109,10 @@ class GameDemoState extends State<GameDemo> { ...@@ -109,10 +109,10 @@ class GameDemoState extends State<GameDemo> {
_sounds, _sounds,
(lastScore) { (lastScore) {
setState(() {_lastScore = lastScore;}); setState(() {_lastScore = lastScore;});
navigator.pop(); args.navigator.pop();
} }
); );
navigator.pushNamed('/game'); args.navigator.pushNamed('/game');
}, },
texture: _spriteSheetUI['btn_play_up.png'], texture: _spriteSheetUI['btn_play_up.png'],
textureDown: _spriteSheetUI['btn_play_down.png'], textureDown: _spriteSheetUI['btn_play_down.png'],
......
...@@ -36,7 +36,7 @@ main() async { ...@@ -36,7 +36,7 @@ main() async {
title: 'Test drawAtlas', title: 'Test drawAtlas',
theme: _theme, theme: _theme,
routes: { routes: {
'/': (NavigatorState navigator, Route route) { '/': (RouteArguments args) {
return new SpriteWidget( return new SpriteWidget(
new TestDrawAtlas(), new TestDrawAtlas(),
SpriteBoxTransformMode.fixedWidth SpriteBoxTransformMode.fixedWidth
......
...@@ -34,7 +34,7 @@ main() async { ...@@ -34,7 +34,7 @@ main() async {
primarySwatch: Colors.purple primarySwatch: Colors.purple
), ),
routes: { routes: {
'/': (navigator, route) { '/': (RouteArguments args) {
return new SpriteWidget( return new SpriteWidget(
new TestBed(), new TestBed(),
SpriteBoxTransformMode.letterbox SpriteBoxTransformMode.letterbox
......
...@@ -82,7 +82,7 @@ class StocksAppState extends State<StocksApp> { ...@@ -82,7 +82,7 @@ class StocksAppState extends State<StocksApp> {
if (path.length != 3) if (path.length != 3)
return null; return null;
if (_stocks.containsKey(path[2])) if (_stocks.containsKey(path[2]))
return (navigator, route) => new StockSymbolViewer(navigator, _stocks[path[2]]); return (RouteArguments args) => new StockSymbolViewer(args.navigator, _stocks[path[2]]);
return null; return null;
} }
return null; return null;
...@@ -93,8 +93,8 @@ class StocksAppState extends State<StocksApp> { ...@@ -93,8 +93,8 @@ class StocksAppState extends State<StocksApp> {
title: 'Stocks', title: 'Stocks',
theme: theme, theme: theme,
routes: <String, RouteBuilder>{ routes: <String, RouteBuilder>{
'/': (navigator, route) => new StockHome(navigator, _stocks, _symbols, _optimismSetting, modeUpdater), '/': (RouteArguments args) => new StockHome(args.navigator, _stocks, _symbols, _optimismSetting, modeUpdater),
'/settings': (navigator, route) => new StockSettings(navigator, _optimismSetting, _backupSetting, settingsUpdater) '/settings': (RouteArguments args) => new StockSettings(args.navigator, _optimismSetting, _backupSetting, settingsUpdater)
}, },
onGenerateRoute: _getRoute onGenerateRoute: _getRoute
); );
......
...@@ -357,7 +357,7 @@ void main() { ...@@ -357,7 +357,7 @@ void main() {
accentColor: Colors.redAccent[200] accentColor: Colors.redAccent[200]
), ),
routes: { routes: {
'/': (NavigatorState navigator, Route route) => new CardCollection(navigator: navigator), '/': (RouteArguments args) => new CardCollection(navigator: args.navigator),
} }
)); ));
} }
...@@ -133,7 +133,7 @@ void main() { ...@@ -133,7 +133,7 @@ void main() {
runApp(new App( runApp(new App(
title: 'Drag and Drop Flutter Demo', title: 'Drag and Drop Flutter Demo',
routes: { routes: {
'/': (NavigatorState navigator, Route route) => new DragAndDropApp(navigator: navigator) '/': (RouteArguments args) => new DragAndDropApp(navigator: args.navigator)
} }
)); ));
} }
...@@ -6,46 +6,46 @@ import 'package:sky/material.dart'; ...@@ -6,46 +6,46 @@ import 'package:sky/material.dart';
import 'package:sky/widgets.dart'; import 'package:sky/widgets.dart';
final Map<String, RouteBuilder> routes = <String, RouteBuilder>{ final Map<String, RouteBuilder> routes = <String, RouteBuilder>{
'/': (NavigatorState navigator, Route route) => new Container( '/': (RouteArguments args) => new Container(
padding: const EdgeDims.all(30.0), padding: const EdgeDims.all(30.0),
decoration: new BoxDecoration(backgroundColor: const Color(0xFFCCCCCC)), decoration: new BoxDecoration(backgroundColor: const Color(0xFFCCCCCC)),
child: new Column([ child: new Column([
new Text("You are at home"), new Text("You are at home"),
new RaisedButton( new RaisedButton(
child: new Text('GO SHOPPING'), child: new Text('GO SHOPPING'),
onPressed: () => navigator.pushNamed('/shopping') onPressed: () => args.navigator.pushNamed('/shopping')
), ),
new RaisedButton( new RaisedButton(
child: new Text('START ADVENTURE'), child: new Text('START ADVENTURE'),
onPressed: () => navigator.pushNamed('/adventure') onPressed: () => args.navigator.pushNamed('/adventure')
)], )],
justifyContent: FlexJustifyContent.center justifyContent: FlexJustifyContent.center
) )
), ),
'/shopping': (NavigatorState navigator, Route route) => new Container( '/shopping': (RouteArguments args) => new Container(
padding: const EdgeDims.all(20.0), padding: const EdgeDims.all(20.0),
decoration: new BoxDecoration(backgroundColor: const Color(0xFFBF5FFF)), decoration: new BoxDecoration(backgroundColor: const Color(0xFFBF5FFF)),
child: new Column([ child: new Column([
new Text("Village Shop"), new Text("Village Shop"),
new RaisedButton( new RaisedButton(
child: new Text('RETURN HOME'), child: new Text('RETURN HOME'),
onPressed: () => navigator.pop() onPressed: () => args.navigator.pop()
), ),
new RaisedButton( new RaisedButton(
child: new Text('GO TO DUNGEON'), child: new Text('GO TO DUNGEON'),
onPressed: () => navigator.pushNamed('/adventure') onPressed: () => args.navigator.pushNamed('/adventure')
)], )],
justifyContent: FlexJustifyContent.center justifyContent: FlexJustifyContent.center
) )
), ),
'/adventure': (NavigatorState navigator, Route route) => new Container( '/adventure': (RouteArguments args) => new Container(
padding: const EdgeDims.all(20.0), padding: const EdgeDims.all(20.0),
decoration: new BoxDecoration(backgroundColor: const Color(0xFFDC143C)), decoration: new BoxDecoration(backgroundColor: const Color(0xFFDC143C)),
child: new Column([ child: new Column([
new Text("Monster's Lair"), new Text("Monster's Lair"),
new RaisedButton( new RaisedButton(
child: new Text('RUN!!!'), child: new Text('RUN!!!'),
onPressed: () => navigator.pop() onPressed: () => args.navigator.pop()
)], )],
justifyContent: FlexJustifyContent.center justifyContent: FlexJustifyContent.center
) )
......
...@@ -165,7 +165,7 @@ void main() { ...@@ -165,7 +165,7 @@ void main() {
), ),
title: 'Cards', title: 'Cards',
routes: { routes: {
'/': (navigator, route) => new OverlayGeometryApp() '/': (RouteArguments args) => new OverlayGeometryApp()
} }
)); ));
} }
...@@ -165,7 +165,7 @@ void main() { ...@@ -165,7 +165,7 @@ void main() {
accentColor: Colors.redAccent[200] accentColor: Colors.redAccent[200]
), ),
routes: { routes: {
'/': (NavigatorState navigator, Route route) => new PageableListApp(navigator: navigator), '/': (RouteArguments args) => new PageableListApp(navigator: args.navigator),
} }
)); ));
} }
...@@ -145,7 +145,7 @@ class DialogRoute extends Route { ...@@ -145,7 +145,7 @@ class DialogRoute extends Route {
return new FadeTransition( return new FadeTransition(
performance: performance, performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut), opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
child: builder(navigator, this) child: builder(new RouteArguments(navigator: navigator, previousPerformance: this.performance, nextPerformance: nextRoutePerformance))
); );
} }
...@@ -159,11 +159,11 @@ Future showDialog(NavigatorState navigator, DialogBuilder builder) { ...@@ -159,11 +159,11 @@ Future showDialog(NavigatorState navigator, DialogBuilder builder) {
Completer completer = new Completer(); Completer completer = new Completer();
navigator.push(new DialogRoute( navigator.push(new DialogRoute(
completer: completer, completer: completer,
builder: (navigator, route) { builder: (RouteArguments args) {
return new Focus( return new Focus(
key: new GlobalObjectKey(route), key: new GlobalObjectKey(completer),
autofocus: true, autofocus: true,
child: builder(navigator) child: builder(args.navigator)
); );
} }
)); ));
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'package:sky/animation.dart'; import 'package:sky/animation.dart';
import 'package:sky/material.dart'; import 'package:sky/material.dart';
import 'package:sky/src/widgets/animated_container.dart'; import 'package:sky/src/widgets/animated_container.dart';
...@@ -11,7 +9,6 @@ import 'package:sky/src/widgets/framework.dart'; ...@@ -11,7 +9,6 @@ import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/basic.dart'; import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/gesture_detector.dart'; import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/navigator.dart'; import 'package:sky/src/widgets/navigator.dart';
import 'package:sky/src/widgets/scrollable.dart';
import 'package:sky/src/widgets/theme.dart'; import 'package:sky/src/widgets/theme.dart';
import 'package:sky/src/widgets/transitions.dart'; import 'package:sky/src/widgets/transitions.dart';
import 'package:sky/src/widgets/focus.dart'; import 'package:sky/src/widgets/focus.dart';
......
...@@ -486,9 +486,11 @@ final _InactiveElements _inactiveElements = new _InactiveElements(); ...@@ -486,9 +486,11 @@ final _InactiveElements _inactiveElements = new _InactiveElements();
typedef void ElementVisitor(Element element); typedef void ElementVisitor(Element element);
abstract class BuildContext { abstract class BuildContext {
InheritedWidget inheritedWidgetOfType(Type targetType); Widget get widget;
RenderObject findRenderObject(); RenderObject findRenderObject();
InheritedWidget inheritedWidgetOfType(Type targetType);
void visitAncestorElements(bool visitor(Element element)); void visitAncestorElements(bool visitor(Element element));
void visitChildElements(void visitor(Element element));
} }
/// Elements are the instantiations of Widget configurations. /// Elements are the instantiations of Widget configurations.
...@@ -540,6 +542,13 @@ abstract class Element<T extends Widget> implements BuildContext { ...@@ -540,6 +542,13 @@ abstract class Element<T extends Widget> implements BuildContext {
/// Calls the argument for each child. Must be overridden by subclasses that support having children. /// Calls the argument for each child. Must be overridden by subclasses that support having children.
void visitChildren(ElementVisitor visitor) { } void visitChildren(ElementVisitor visitor) { }
/// Wrapper around visitChildren for BuildContext.
void visitChildElements(void visitor(Element element)) {
// don't allow visitChildElements() during build, since children aren't necessarily built yet
assert(BuildableElement._debugStateLockLevel == 0);
visitChildren(visitor);
}
bool detachChild(Element child) => false; bool detachChild(Element child) => false;
/// This method is the core of the system. /// This method is the core of the system.
...@@ -952,11 +961,11 @@ abstract class BuildableElement<T extends Widget> extends Element<T> { ...@@ -952,11 +961,11 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
/// Instantiation of StatelessComponent widgets. /// Instantiation of StatelessComponent widgets.
class StatelessComponentElement<T extends StatelessComponent> extends BuildableElement<T> { class StatelessComponentElement<T extends StatelessComponent> extends BuildableElement<T> {
StatelessComponentElement(StatelessComponent widget) : super(widget) { StatelessComponentElement(T widget) : super(widget) {
_builder = widget.build; _builder = widget.build;
} }
void update(StatelessComponent newWidget) { void update(T newWidget) {
super.update(newWidget); super.update(newWidget);
assert(widget == newWidget); assert(widget == newWidget);
_builder = widget.build; _builder = widget.build;
...@@ -966,10 +975,10 @@ class StatelessComponentElement<T extends StatelessComponent> extends BuildableE ...@@ -966,10 +975,10 @@ class StatelessComponentElement<T extends StatelessComponent> extends BuildableE
} }
/// Instantiation of StatefulComponent widgets. /// Instantiation of StatefulComponent widgets.
class StatefulComponentElement extends BuildableElement<StatefulComponent> { class StatefulComponentElement<T extends StatefulComponent, U extends State<T>> extends BuildableElement<T> {
StatefulComponentElement(StatefulComponent widget) StatefulComponentElement(T widget)
: _state = widget.createState(), super(widget) { : _state = widget.createState(), super(widget) {
assert(_state._debugTypesAreRight(widget)); assert(_state._debugTypesAreRight(widget)); // can't use T and U, since normally we don't actually set those
assert(_state._element == null); assert(_state._element == null);
_state._element = this; _state._element = this;
assert(_builder == null); assert(_builder == null);
...@@ -992,10 +1001,10 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> { ...@@ -992,10 +1001,10 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> {
assert(() { _state._debugLifecycleState = _StateLifecycle.ready; return true; }); assert(() { _state._debugLifecycleState = _StateLifecycle.ready; return true; });
} }
State get state => _state; U get state => _state;
State _state; U _state;
void update(StatefulComponent newWidget) { void update(T newWidget) {
super.update(newWidget); super.update(newWidget);
assert(widget == newWidget); assert(widget == newWidget);
StatefulComponent oldConfig = _state._config; StatefulComponent oldConfig = _state._config;
......
...@@ -8,7 +8,14 @@ import 'package:sky/src/widgets/focus.dart'; ...@@ -8,7 +8,14 @@ import 'package:sky/src/widgets/focus.dart';
import 'package:sky/src/widgets/framework.dart'; import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/transitions.dart'; import 'package:sky/src/widgets/transitions.dart';
typedef Widget RouteBuilder(NavigatorState navigator, Route route); class RouteArguments {
const RouteArguments({ this.navigator, this.previousPerformance, this.nextPerformance });
final NavigatorState navigator;
final PerformanceView previousPerformance;
final PerformanceView nextPerformance;
}
typedef Widget RouteBuilder(RouteArguments args);
typedef RouteBuilder RouteGenerator(String name); typedef RouteBuilder RouteGenerator(String name);
typedef void StateRouteCallback(StateRoute route); typedef void StateRouteCallback(StateRoute route);
typedef void NotificationCallback(); typedef void NotificationCallback();
...@@ -154,6 +161,7 @@ class NavigatorState extends State<Navigator> { ...@@ -154,6 +161,7 @@ class NavigatorState extends State<Navigator> {
} }
return new Focus(child: new Stack(visibleRoutes.reversed.toList())); return new Focus(child: new Stack(visibleRoutes.reversed.toList()));
} }
} }
...@@ -272,7 +280,7 @@ class PageRoute extends Route { ...@@ -272,7 +280,7 @@ class PageRoute extends Route {
child: new FadeTransition( child: new FadeTransition(
performance: performance, performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut), opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
child: builder(navigator, this) child: builder(new RouteArguments(navigator: navigator, previousPerformance: this.performance, nextPerformance: nextRoutePerformance))
) )
); );
} }
......
...@@ -13,9 +13,9 @@ void main() { ...@@ -13,9 +13,9 @@ void main() {
tester.pumpWidget(new Navigator( tester.pumpWidget(new Navigator(
routes: { routes: {
'/': (NavigatorState navigator, Route route) { return new Column([ '/': (RouteArguments args) { return new Column([
new Draggable( new Draggable(
navigator: navigator, navigator: args.navigator,
data: 1, data: 1,
child: new Text('Source'), child: new Text('Source'),
feedback: new Text('Dragging') feedback: new Text('Dragging')
......
...@@ -49,8 +49,8 @@ void main() { ...@@ -49,8 +49,8 @@ void main() {
test('Can navigator navigate to and from a stateful component', () { test('Can navigator navigate to and from a stateful component', () {
testWidgets((WidgetTester tester) { testWidgets((WidgetTester tester) {
final Map<String, RouteBuilder> routes = <String, RouteBuilder>{ final Map<String, RouteBuilder> routes = <String, RouteBuilder>{
'/': (navigator, route) => new FirstComponent(navigator), '/': (RouteArguments args) => new FirstComponent(args.navigator),
'/second': (navigator, route) => new SecondComponent(navigator), '/second': (RouteArguments args) => new SecondComponent(args.navigator),
}; };
tester.pumpWidget(new Navigator(routes: routes)); tester.pumpWidget(new Navigator(routes: routes));
......
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