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() {
title: 'Address Book',
theme: theme,
routes: <String, RouteBuilder>{
'/': (NavigatorState navigator, Route route) => new AddressBookHome(navigator: navigator)
'/': (RouteArguments args) => new AddressBookHome(navigator: args.navigator)
}
));
}
......@@ -206,7 +206,7 @@ void main() {
title: 'Flutter Demos',
theme: _theme,
routes: {
'/': (NavigatorState navigator, Route route) => new DemoHome()
'/': (RouteArguments args) => new DemoHome()
}
));
}
......@@ -92,8 +92,6 @@ class FitnessApp extends StatefulComponent {
class FitnessAppState extends State<FitnessApp> {
UserDataImpl _userData;
Map<String, RouteBuilder> _routes;
void initState() {
super.initState();
loadFitnessData().then((UserData data) {
......@@ -102,36 +100,6 @@ class FitnessAppState extends State<FitnessApp> {
print("Failed to load data: $e");
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) {
......@@ -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) {
return new App(
theme: _theme,
theme: new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.indigo,
accentColor: Colors.pinkAccent[200]
),
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> {
);
}
Column _buildColumn(NavigatorState navigator, Route route) {
Column _buildColumn(RouteArguments args) {
return new Column([
new Flexible(child: _buildSpriteWidget()),
_buildTabBar()
......
......@@ -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);
}
Widget _buildMainScene(navigator, route) {
Widget _buildMainScene(RouteArguments args) {
return new Stack([
new SpriteWidget(new MainScreenBackground(), SpriteBoxTransformMode.fixedWidth),
new Column([
......@@ -109,10 +109,10 @@ class GameDemoState extends State<GameDemo> {
_sounds,
(lastScore) {
setState(() {_lastScore = lastScore;});
navigator.pop();
args.navigator.pop();
}
);
navigator.pushNamed('/game');
args.navigator.pushNamed('/game');
},
texture: _spriteSheetUI['btn_play_up.png'],
textureDown: _spriteSheetUI['btn_play_down.png'],
......
......@@ -36,7 +36,7 @@ main() async {
title: 'Test drawAtlas',
theme: _theme,
routes: {
'/': (NavigatorState navigator, Route route) {
'/': (RouteArguments args) {
return new SpriteWidget(
new TestDrawAtlas(),
SpriteBoxTransformMode.fixedWidth
......
......@@ -34,7 +34,7 @@ main() async {
primarySwatch: Colors.purple
),
routes: {
'/': (navigator, route) {
'/': (RouteArguments args) {
return new SpriteWidget(
new TestBed(),
SpriteBoxTransformMode.letterbox
......
......@@ -82,7 +82,7 @@ class StocksAppState extends State<StocksApp> {
if (path.length != 3)
return null;
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;
......@@ -93,8 +93,8 @@ class StocksAppState extends State<StocksApp> {
title: 'Stocks',
theme: theme,
routes: <String, RouteBuilder>{
'/': (navigator, route) => new StockHome(navigator, _stocks, _symbols, _optimismSetting, modeUpdater),
'/settings': (navigator, route) => new StockSettings(navigator, _optimismSetting, _backupSetting, settingsUpdater)
'/': (RouteArguments args) => new StockHome(args.navigator, _stocks, _symbols, _optimismSetting, modeUpdater),
'/settings': (RouteArguments args) => new StockSettings(args.navigator, _optimismSetting, _backupSetting, settingsUpdater)
},
onGenerateRoute: _getRoute
);
......
......@@ -357,7 +357,7 @@ void main() {
accentColor: Colors.redAccent[200]
),
routes: {
'/': (NavigatorState navigator, Route route) => new CardCollection(navigator: navigator),
'/': (RouteArguments args) => new CardCollection(navigator: args.navigator),
}
));
}
......@@ -133,7 +133,7 @@ void main() {
runApp(new App(
title: 'Drag and Drop Flutter Demo',
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';
import 'package:sky/widgets.dart';
final Map<String, RouteBuilder> routes = <String, RouteBuilder>{
'/': (NavigatorState navigator, Route route) => new Container(
'/': (RouteArguments args) => new Container(
padding: const EdgeDims.all(30.0),
decoration: new BoxDecoration(backgroundColor: const Color(0xFFCCCCCC)),
child: new Column([
new Text("You are at home"),
new RaisedButton(
child: new Text('GO SHOPPING'),
onPressed: () => navigator.pushNamed('/shopping')
onPressed: () => args.navigator.pushNamed('/shopping')
),
new RaisedButton(
child: new Text('START ADVENTURE'),
onPressed: () => navigator.pushNamed('/adventure')
onPressed: () => args.navigator.pushNamed('/adventure')
)],
justifyContent: FlexJustifyContent.center
)
),
'/shopping': (NavigatorState navigator, Route route) => new Container(
'/shopping': (RouteArguments args) => new Container(
padding: const EdgeDims.all(20.0),
decoration: new BoxDecoration(backgroundColor: const Color(0xFFBF5FFF)),
child: new Column([
new Text("Village Shop"),
new RaisedButton(
child: new Text('RETURN HOME'),
onPressed: () => navigator.pop()
onPressed: () => args.navigator.pop()
),
new RaisedButton(
child: new Text('GO TO DUNGEON'),
onPressed: () => navigator.pushNamed('/adventure')
onPressed: () => args.navigator.pushNamed('/adventure')
)],
justifyContent: FlexJustifyContent.center
)
),
'/adventure': (NavigatorState navigator, Route route) => new Container(
'/adventure': (RouteArguments args) => new Container(
padding: const EdgeDims.all(20.0),
decoration: new BoxDecoration(backgroundColor: const Color(0xFFDC143C)),
child: new Column([
new Text("Monster's Lair"),
new RaisedButton(
child: new Text('RUN!!!'),
onPressed: () => navigator.pop()
onPressed: () => args.navigator.pop()
)],
justifyContent: FlexJustifyContent.center
)
......
......@@ -165,7 +165,7 @@ void main() {
),
title: 'Cards',
routes: {
'/': (navigator, route) => new OverlayGeometryApp()
'/': (RouteArguments args) => new OverlayGeometryApp()
}
));
}
......@@ -165,7 +165,7 @@ void main() {
accentColor: Colors.redAccent[200]
),
routes: {
'/': (NavigatorState navigator, Route route) => new PageableListApp(navigator: navigator),
'/': (RouteArguments args) => new PageableListApp(navigator: args.navigator),
}
));
}
......@@ -145,7 +145,7 @@ class DialogRoute extends Route {
return new FadeTransition(
performance: performance,
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) {
Completer completer = new Completer();
navigator.push(new DialogRoute(
completer: completer,
builder: (navigator, route) {
builder: (RouteArguments args) {
return new Focus(
key: new GlobalObjectKey(route),
key: new GlobalObjectKey(completer),
autofocus: true,
child: builder(navigator)
child: builder(args.navigator)
);
}
));
......
......@@ -2,8 +2,6 @@
// 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.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/animated_container.dart';
......@@ -11,7 +9,6 @@ import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/gesture_detector.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/transitions.dart';
import 'package:sky/src/widgets/focus.dart';
......
......@@ -486,9 +486,11 @@ final _InactiveElements _inactiveElements = new _InactiveElements();
typedef void ElementVisitor(Element element);
abstract class BuildContext {
InheritedWidget inheritedWidgetOfType(Type targetType);
Widget get widget;
RenderObject findRenderObject();
InheritedWidget inheritedWidgetOfType(Type targetType);
void visitAncestorElements(bool visitor(Element element));
void visitChildElements(void visitor(Element element));
}
/// Elements are the instantiations of Widget configurations.
......@@ -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.
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;
/// This method is the core of the system.
......@@ -952,11 +961,11 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
/// Instantiation of StatelessComponent widgets.
class StatelessComponentElement<T extends StatelessComponent> extends BuildableElement<T> {
StatelessComponentElement(StatelessComponent widget) : super(widget) {
StatelessComponentElement(T widget) : super(widget) {
_builder = widget.build;
}
void update(StatelessComponent newWidget) {
void update(T newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_builder = widget.build;
......@@ -966,10 +975,10 @@ class StatelessComponentElement<T extends StatelessComponent> extends BuildableE
}
/// Instantiation of StatefulComponent widgets.
class StatefulComponentElement extends BuildableElement<StatefulComponent> {
StatefulComponentElement(StatefulComponent widget)
class StatefulComponentElement<T extends StatefulComponent, U extends State<T>> extends BuildableElement<T> {
StatefulComponentElement(T 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);
_state._element = this;
assert(_builder == null);
......@@ -992,10 +1001,10 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> {
assert(() { _state._debugLifecycleState = _StateLifecycle.ready; return true; });
}
State get state => _state;
State _state;
U get state => _state;
U _state;
void update(StatefulComponent newWidget) {
void update(T newWidget) {
super.update(newWidget);
assert(widget == newWidget);
StatefulComponent oldConfig = _state._config;
......
......@@ -8,7 +8,14 @@ import 'package:sky/src/widgets/focus.dart';
import 'package:sky/src/widgets/framework.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 void StateRouteCallback(StateRoute route);
typedef void NotificationCallback();
......@@ -154,6 +161,7 @@ class NavigatorState extends State<Navigator> {
}
return new Focus(child: new Stack(visibleRoutes.reversed.toList()));
}
}
......@@ -272,7 +280,7 @@ class PageRoute extends Route {
child: new FadeTransition(
performance: performance,
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() {
tester.pumpWidget(new Navigator(
routes: {
'/': (NavigatorState navigator, Route route) { return new Column([
'/': (RouteArguments args) { return new Column([
new Draggable(
navigator: navigator,
navigator: args.navigator,
data: 1,
child: new Text('Source'),
feedback: new Text('Dragging')
......
......@@ -49,8 +49,8 @@ void main() {
test('Can navigator navigate to and from a stateful component', () {
testWidgets((WidgetTester tester) {
final Map<String, RouteBuilder> routes = <String, RouteBuilder>{
'/': (navigator, route) => new FirstComponent(navigator),
'/second': (navigator, route) => new SecondComponent(navigator),
'/': (RouteArguments args) => new FirstComponent(args.navigator),
'/second': (RouteArguments args) => new SecondComponent(args.navigator),
};
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