diff --git a/packages/flutter/lib/src/cupertino/app.dart b/packages/flutter/lib/src/cupertino/app.dart index 3140671aae..0a6fa16c22 100644 --- a/packages/flutter/lib/src/cupertino/app.dart +++ b/packages/flutter/lib/src/cupertino/app.dart @@ -42,6 +42,9 @@ const TextStyle _kDefaultTextStyle = const TextStyle( /// If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null, /// and [builder] is not null, then no [Navigator] is created. /// +/// This widget also configures the observer of the top-level [Navigator] (if +/// any) to perform [Hero] animations. +/// /// Using this widget with caution on Android since it may produce behaviors /// Android users are not expecting such as: /// @@ -171,7 +174,7 @@ class CupertinoApp extends StatefulWidget { /// When a named route is pushed with [Navigator.pushNamed], the route name is /// looked up in this map. If the name is present, the associated /// [WidgetBuilder] is used to construct a [CupertinoPageRoute] that performs - /// an appropriate transition to the new route. + /// an appropriate transition, including [Hero] animations, to the new route. /// /// If the app only has one page, then you can specify it using [home] instead. /// @@ -246,8 +249,8 @@ class CupertinoApp extends StatefulWidget { /// /// Unless a [Navigator] is provided, either implicitly from [builder] being /// null, or by a [builder] including its `child` argument, or by a [builder] - /// explicitly providing a [Navigator] of its own, APIs such as - /// [Navigator.push] and [Navigator.pop], will not function. + /// explicitly providing a [Navigator] of its own, widgets and APIs such as + /// [Hero], [Navigator.push] and [Navigator.pop], will not function. final TransitionBuilder builder; /// {@macro flutter.widgets.widgetsApp.title} @@ -317,9 +320,13 @@ class _AlwaysCupertinoScrollBehavior extends ScrollBehavior { } class _CupertinoAppState extends State { + HeroController _heroController; + List _navigatorObservers; + @override void initState() { super.initState(); + _heroController = new HeroController(); // Linear tweening. _updateNavigator(); } @@ -335,6 +342,9 @@ class _CupertinoAppState extends State { widget.routes.isNotEmpty || widget.onGenerateRoute != null || widget.onUnknownRoute != null; + _navigatorObservers = + new List.from(widget.navigatorObservers) + ..add(_heroController); } Widget defaultBuilder(BuildContext context, Widget child) { @@ -351,7 +361,7 @@ class _CupertinoAppState extends State { routes: widget.routes, onGenerateRoute: widget.onGenerateRoute, onUnknownRoute: widget.onUnknownRoute, - navigatorObservers: widget.navigatorObservers, + navigatorObservers: _navigatorObservers, ); if (widget.builder != null) { return widget.builder(context, navigator); diff --git a/packages/flutter/test/cupertino/app_test.dart b/packages/flutter/test/cupertino/app_test.dart new file mode 100644 index 0000000000..20c73542c5 --- /dev/null +++ b/packages/flutter/test/cupertino/app_test.dart @@ -0,0 +1,43 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/cupertino.dart'; + +void main() { + testWidgets('Heroes work', (WidgetTester tester) async { + await tester.pumpWidget(new CupertinoApp( + home: + new ListView( + children: [ + const Hero(tag: 'a', child: const Text('foo')), + new Builder(builder: (BuildContext context) { + return new CupertinoButton( + child: const Text('next'), + onPressed: () { + Navigator.push( + context, + new CupertinoPageRoute( + builder: (BuildContext context) { + return const Hero(tag: 'a', child: const Text('foo')); + } + ), + ); + }, + ); + }), + ], + ) + )); + + await tester.tap(find.text('next')); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 100)); + + // During the hero transition, the hero widget is lifted off of both + // page routes and exists as its own overlay on top of both routes. + expect(find.widgetWithText(CupertinoPageRoute, 'foo'), findsNothing); + expect(find.widgetWithText(Navigator, 'foo'), findsOneWidget); + }); +} \ No newline at end of file