Merge pull request #1490 from Hixie/navigator-ensure-pop
Handle a route being dismissed before being popped
diff --git a/examples/address_book/lib/main.dart b/examples/address_book/lib/main.dart
index c12a7d2..2bca9be 100644
--- a/examples/address_book/lib/main.dart
+++ b/examples/address_book/lib/main.dart
@@ -101,7 +101,7 @@
title: 'Address Book',
theme: theme,
routes: <String, RouteBuilder>{
- '/': (NavigatorState navigator, Route route) => new AddressBookHome(navigator: navigator)
+ '/': (RouteArguments args) => new AddressBookHome(navigator: args.navigator)
}
));
}
diff --git a/examples/demo_launcher/lib/main.dart b/examples/demo_launcher/lib/main.dart
index 4fc089c..2bbef23 100644
--- a/examples/demo_launcher/lib/main.dart
+++ b/examples/demo_launcher/lib/main.dart
@@ -206,7 +206,7 @@
title: 'Flutter Demos',
theme: _theme,
routes: {
- '/': (NavigatorState navigator, Route route) => new DemoHome()
+ '/': (RouteArguments args) => new DemoHome()
}
));
}
diff --git a/examples/fitness/lib/main.dart b/examples/fitness/lib/main.dart
index 1f12e11..98f87db 100644
--- a/examples/fitness/lib/main.dart
+++ b/examples/fitness/lib/main.dart
@@ -92,8 +92,6 @@
class FitnessAppState extends State<FitnessApp> {
UserDataImpl _userData;
- Map<String, RouteBuilder> _routes;
-
void initState() {
super.initState();
loadFitnessData().then((UserData data) {
@@ -102,36 +100,6 @@
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 @@
});
}
- 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
+ );
+ }
+ }
);
}
}
diff --git a/examples/game/example_effect_line.dart b/examples/game/example_effect_line.dart
index 4a6e7cc..05323d0 100644
--- a/examples/game/example_effect_line.dart
+++ b/examples/game/example_effect_line.dart
@@ -62,7 +62,7 @@
);
}
- Column _buildColumn(NavigatorState navigator, Route route) {
+ Column _buildColumn(RouteArguments args) {
return new Column([
new Flexible(child: _buildSpriteWidget()),
_buildTabBar()
diff --git a/examples/game/lib/main.dart b/examples/game/lib/main.dart
index a93038d..2cb407a 100644
--- a/examples/game/lib/main.dart
+++ b/examples/game/lib/main.dart
@@ -92,11 +92,11 @@
);
}
- 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 @@
_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'],
diff --git a/examples/game/test_drawatlas.dart b/examples/game/test_drawatlas.dart
index 135334e..dba1748 100644
--- a/examples/game/test_drawatlas.dart
+++ b/examples/game/test_drawatlas.dart
@@ -36,7 +36,7 @@
title: 'Test drawAtlas',
theme: _theme,
routes: {
- '/': (NavigatorState navigator, Route route) {
+ '/': (RouteArguments args) {
return new SpriteWidget(
new TestDrawAtlas(),
SpriteBoxTransformMode.fixedWidth
diff --git a/examples/game/test_physics.dart b/examples/game/test_physics.dart
index a7c23f9..aabb773 100644
--- a/examples/game/test_physics.dart
+++ b/examples/game/test_physics.dart
@@ -34,7 +34,7 @@
primarySwatch: Colors.purple
),
routes: {
- '/': (navigator, route) {
+ '/': (RouteArguments args) {
return new SpriteWidget(
new TestBed(),
SpriteBoxTransformMode.letterbox
@@ -83,6 +83,7 @@
void myCallback(PhysicsContactType type, PhysicsContact contact) {
print("CONTACT type: $type");
+ contact.nodeB.removeFromParent();
}
bool handleEvent(SpriteBoxEvent event) {
diff --git a/examples/stocks/lib/main.dart b/examples/stocks/lib/main.dart
index 4b2c1e8..a931d54 100644
--- a/examples/stocks/lib/main.dart
+++ b/examples/stocks/lib/main.dart
@@ -82,7 +82,7 @@
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 @@
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
);
diff --git a/examples/widgets/card_collection.dart b/examples/widgets/card_collection.dart
index 0e7a06c..fb0458b 100644
--- a/examples/widgets/card_collection.dart
+++ b/examples/widgets/card_collection.dart
@@ -357,7 +357,7 @@
accentColor: Colors.redAccent[200]
),
routes: {
- '/': (NavigatorState navigator, Route route) => new CardCollection(navigator: navigator),
+ '/': (RouteArguments args) => new CardCollection(navigator: args.navigator),
}
));
}
diff --git a/examples/widgets/drag_and_drop.dart b/examples/widgets/drag_and_drop.dart
index c7a6187..f35934c 100644
--- a/examples/widgets/drag_and_drop.dart
+++ b/examples/widgets/drag_and_drop.dart
@@ -133,7 +133,7 @@
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)
}
));
}
diff --git a/examples/widgets/navigation.dart b/examples/widgets/navigation.dart
index c9dd69b..7ade991 100644
--- a/examples/widgets/navigation.dart
+++ b/examples/widgets/navigation.dart
@@ -6,46 +6,46 @@
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
)
diff --git a/examples/widgets/overlay_geometry.dart b/examples/widgets/overlay_geometry.dart
index 8a52520..6aa66e9 100644
--- a/examples/widgets/overlay_geometry.dart
+++ b/examples/widgets/overlay_geometry.dart
@@ -165,7 +165,7 @@
),
title: 'Cards',
routes: {
- '/': (navigator, route) => new OverlayGeometryApp()
+ '/': (RouteArguments args) => new OverlayGeometryApp()
}
));
}
diff --git a/examples/widgets/pageable_list.dart b/examples/widgets/pageable_list.dart
index c00da4b..e68ebbc 100644
--- a/examples/widgets/pageable_list.dart
+++ b/examples/widgets/pageable_list.dart
@@ -165,7 +165,7 @@
accentColor: Colors.redAccent[200]
),
routes: {
- '/': (NavigatorState navigator, Route route) => new PageableListApp(navigator: navigator),
+ '/': (RouteArguments args) => new PageableListApp(navigator: args.navigator),
}
));
}
diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart
index d5dbb44..22f7d3e 100644
--- a/packages/flutter/lib/src/widgets/basic.dart
+++ b/packages/flutter/lib/src/widgets/basic.dart
@@ -712,6 +712,11 @@
}
bool updateShouldNotify(DefaultTextStyle old) => style != old.style;
+
+ void debugFillDescription(List<String> description) {
+ super.debugFillDescription(description);
+ '$style'.split('\n').forEach(description.add);
+ }
}
class Text extends StatelessComponent {
@@ -738,6 +743,13 @@
text = new StyledTextSpan(combinedStyle, [text]);
return new Paragraph(text: text);
}
+
+ void debugFillDescription(List<String> description) {
+ super.debugFillDescription(description);
+ description.add('"$data"');
+ if (style != null)
+ '$style'.split('\n').forEach(description.add);
+ }
}
class Image extends LeafRenderObjectWidget {
diff --git a/packages/flutter/lib/src/widgets/dialog.dart b/packages/flutter/lib/src/widgets/dialog.dart
index 54c2a6f..4efe02b 100644
--- a/packages/flutter/lib/src/widgets/dialog.dart
+++ b/packages/flutter/lib/src/widgets/dialog.dart
@@ -145,7 +145,7 @@
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 @@
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)
);
}
));
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index c0cce2b..05ce1e6 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -182,10 +182,15 @@
Element createElement();
String toString() {
- if (key == null)
- return '$runtimeType';
- return '$runtimeType-$key';
- }
+ final String name = key == null ? '$runtimeType' : '$runtimeType-$key';
+ final List<String> data = <String>[];
+ debugFillDescription(data);
+ if (data.isEmpty)
+ return 'name';
+ return 'name(${data.join("; ")})';
+ }
+
+ void debugFillDescription(List<String> description) { }
}
/// RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
@@ -486,9 +491,11 @@
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 +547,13 @@
/// 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.
@@ -786,6 +800,8 @@
description.add('no depth');
if (widget == null)
description.add('no widget');
+ else
+ widget.debugFillDescription(description);
}
String toStringDeep([String prefixLineOne = '', String prefixOtherLines = '']) {
@@ -952,11 +968,11 @@
/// 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 +982,10 @@
}
/// 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 +1008,10 @@
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;
diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart
index 212b37f..d69d75a 100644
--- a/packages/flutter/lib/src/widgets/navigator.dart
+++ b/packages/flutter/lib/src/widgets/navigator.dart
@@ -8,7 +8,14 @@
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();
@@ -159,6 +166,7 @@
}
return new Focus(child: new Stack(visibleRoutes.reversed.toList()));
}
+
}
@@ -282,7 +290,7 @@
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))
)
);
}
diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart
index cf1693a..da4d940 100644
--- a/packages/flutter/lib/src/widgets/scrollable.dart
+++ b/packages/flutter/lib/src/widgets/scrollable.dart
@@ -318,7 +318,7 @@
});
}
void _updateScrollBehaviour() {
- // if you don't call this from build() or syncConstructorArguments(), you must call it from setState().
+ // if you don't call this from build(), you must call it from setState().
scrollTo(scrollBehavior.updateExtents(
contentExtent: _childSize,
containerExtent: _viewportSize,
diff --git a/packages/flutter_sprites/lib/physics_body.dart b/packages/flutter_sprites/lib/physics_body.dart
index 99c8ea4..e9446b6 100644
--- a/packages/flutter_sprites/lib/physics_body.dart
+++ b/packages/flutter_sprites/lib/physics_body.dart
@@ -120,7 +120,7 @@
void _detach() {
if (_attached) {
- _physicsNode.b2World.destroyBody(_body);
+ _physicsNode._bodiesScheduledForDestruction.add(_body);
_attached = false;
}
}
diff --git a/packages/flutter_sprites/lib/physics_node.dart b/packages/flutter_sprites/lib/physics_node.dart
index bc19c6a..12ea4f8 100644
--- a/packages/flutter_sprites/lib/physics_node.dart
+++ b/packages/flutter_sprites/lib/physics_node.dart
@@ -31,6 +31,8 @@
_ContactHandler _contactHandler;
+ List<box2d.Body> _bodiesScheduledForDestruction = [];
+
double b2WorldToNodeConversionFactor = 500.0;
Offset get gravity {
@@ -57,6 +59,9 @@
}
void _stepPhysics(double dt) {
+ // Remove bodies that were marked for destruction during the update phase
+ _removeBodiesScheduledForDestruction();
+
// Calculate a step in the simulation
b2World.stepDt(dt, 10, 10);
@@ -71,6 +76,16 @@
body._node._setRotationFromPhysics(degrees(b2Body.getAngle()));
}
+
+ // Remove bodies that were marked for destruction during the simulation
+ _removeBodiesScheduledForDestruction();
+ }
+
+ void _removeBodiesScheduledForDestruction() {
+ for (box2d.Body b2Body in _bodiesScheduledForDestruction) {
+ b2World.destroyBody(b2Body);
+ }
+ _bodiesScheduledForDestruction.clear();
}
void _updatePosition(PhysicsBody body, Point position) {
@@ -257,6 +272,11 @@
b2Contact.isTouching(),
b2Contact.isEnabled()
);
+
+ if (type == PhysicsContactType.postSolve) {
+
+ }
+
// Make callback
info.callback(type, contact);
diff --git a/packages/unit/test/widget/draggable_test.dart b/packages/unit/test/widget/draggable_test.dart
index 31120bc..8e8b224 100644
--- a/packages/unit/test/widget/draggable_test.dart
+++ b/packages/unit/test/widget/draggable_test.dart
@@ -13,9 +13,9 @@
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')
diff --git a/packages/unit/test/widget/navigator_test.dart b/packages/unit/test/widget/navigator_test.dart
index 5da23d0..a9bfcd5 100644
--- a/packages/unit/test/widget/navigator_test.dart
+++ b/packages/unit/test/widget/navigator_test.dart
@@ -49,8 +49,8 @@
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));