diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index 210f5fe1f20..b045783ad79 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -157,6 +157,11 @@ class Navigator extends StatefulComponent { static void openTransaction(BuildContext context, NavigatorTransactionCallback callback) { NavigatorState navigator = context.ancestorStateOfType(const TypeMatcher()); + assert(() { + if (navigator == null) + throw new WidgetError('openTransaction called with a context that does not include a Navigator. The context passed to the Navigator.openTransaction() method must be that of a widget that is a descendant of a Navigator widget.'); + return true; + }); navigator.openTransaction(callback); } diff --git a/packages/flutter/test/widget/navigator_test.dart b/packages/flutter/test/widget/navigator_test.dart index bbed60ec24a..b441f6e5535 100644 --- a/packages/flutter/test/widget/navigator_test.dart +++ b/packages/flutter/test/widget/navigator_test.dart @@ -40,6 +40,29 @@ class SecondComponentState extends State { } } +typedef void ExceptionCallback(exception); + +class ThirdComponent extends StatelessComponent { + ThirdComponent({ this.targetKey, this.onException }); + + final Key targetKey; + final ExceptionCallback onException; + + Widget build(BuildContext context) { + return new GestureDetector( + key: targetKey, + onTap: () { + try { + Navigator.openTransaction(context, (_) { }); + } catch (e) { + onException(e); + } + }, + behavior: HitTestBehavior.opaque + ); + } +} + void main() { test('Can navigator navigate to and from a stateful component', () { testWidgets((WidgetTester tester) { @@ -73,4 +96,21 @@ void main() { expect(tester.findText('Y'), isNull); }); }); + + test('Navigator.openTransaction fails gracefully when not found in context', () { + testWidgets((WidgetTester tester) { + Key targetKey = new Key('foo'); + dynamic exception; + Widget widget = new ThirdComponent( + targetKey: targetKey, + onException: (e) { + exception = e; + } + ); + tester.pumpWidget(widget); + tester.tap(tester.findElementByKey(targetKey)); + expect(exception, new isInstanceOf()); + expect('$exception', startsWith('openTransaction called with a context')); + }); + }); }