diff --git a/packages/flutter/lib/src/widgets/hero_controller.dart b/packages/flutter/lib/src/widgets/hero_controller.dart index 3995caa36f..cf51afabfc 100644 --- a/packages/flutter/lib/src/widgets/hero_controller.dart +++ b/packages/flutter/lib/src/widgets/hero_controller.dart @@ -45,8 +45,10 @@ class HeroController { return; } _to = current; - current.offstage = true; - scheduler.requestPostFrameCallback(_updateQuest); + if (_from != _to) { + current.offstage = current.performance.status != PerformanceStatus.completed; + scheduler.requestPostFrameCallback(_updateQuest); + } } void _handleQuestFinished() { diff --git a/packages/unit/test/widget/heroes_test.dart b/packages/unit/test/widget/heroes_test.dart new file mode 100644 index 0000000000..a8dec7a8ee --- /dev/null +++ b/packages/unit/test/widget/heroes_test.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:test/test.dart'; + +import 'widget_tester.dart'; + +class TestOverlayRoute extends OverlayRoute { + List createWidgets() => [ new Text('Overlay') ]; +} + +bool _isOnStage(Element element) { + expect(element, isNotNull); + bool result = true; + element.visitAncestorElements((Element ancestor) { + if (ancestor.widget is OffStage) { + result = false; + return false; + } + return true; + }); + return result; +} + +class _IsOnStage extends Matcher { + const _IsOnStage(); + bool matches(item, Map matchState) => _isOnStage(item); + Description describe(Description description) => description.add('onstage'); +} + +class _IsOffStage extends Matcher { + const _IsOffStage(); + bool matches(item, Map matchState) => !_isOnStage(item); + Description describe(Description description) => description.add('offstage'); +} + +const Matcher isOnStage = const _IsOnStage(); +const Matcher isOffStage = const _IsOffStage(); + +void main() { + test('Can pop ephemeral route without black flash', () { + testWidgets((WidgetTester tester) { + GlobalKey containerKey = new GlobalKey(); + final Map routes = { + '/': (_) => new Container(key: containerKey, child: new Text('Home')), + '/settings': (_) => new Container(child: new Text('Settings')), + }; + + tester.pumpWidget(new MaterialApp(routes: routes)); + + expect(tester.findText('Home'), isOnStage); + expect(tester.findText('Settings'), isNull); + expect(tester.findText('Overlay'), isNull); + + NavigatorState navigator = Navigator.of(containerKey.currentContext); + + navigator.pushNamed('/settings'); + + tester.pump(); + + expect(tester.findText('Home'), isOnStage); + expect(tester.findText('Settings'), isOffStage); + expect(tester.findText('Overlay'), isNull); + + tester.pump(const Duration(milliseconds: 16)); + + expect(tester.findText('Home'), isOnStage); + expect(tester.findText('Settings'), isOnStage); + expect(tester.findText('Overlay'), isNull); + + tester.pump(const Duration(seconds: 1)); + + expect(tester.findText('Home'), isNull); + expect(tester.findText('Settings'), isOnStage); + expect(tester.findText('Overlay'), isNull); + + navigator.push(new TestOverlayRoute()); + + tester.pump(); + + expect(tester.findText('Home'), isNull); + expect(tester.findText('Settings'), isOnStage); + expect(tester.findText('Overlay'), isOnStage); + + tester.pump(const Duration(seconds: 1)); + + expect(tester.findText('Home'), isNull); + expect(tester.findText('Settings'), isOnStage); + expect(tester.findText('Overlay'), isOnStage); + + navigator.pop(); + tester.pump(); + + expect(tester.findText('Home'), isNull); + expect(tester.findText('Settings'), isOnStage); + expect(tester.findText('Overlay'), isNull); + + tester.pump(const Duration(seconds: 1)); + + expect(tester.findText('Home'), isNull); + expect(tester.findText('Settings'), isOnStage); + expect(tester.findText('Overlay'), isNull); + + navigator.pop(); + tester.pump(); + + expect(tester.findText('Home'), isOnStage); + expect(tester.findText('Settings'), isOnStage); + expect(tester.findText('Overlay'), isNull); + + tester.pump(const Duration(seconds: 1)); + + expect(tester.findText('Home'), isOnStage); + expect(tester.findText('Settings'), isNull); + expect(tester.findText('Overlay'), isNull); + + }); + }); +}