Skip to content

Commit eef4aa7

Browse files
authored
Hero: Add an example for createRectTween (#102650)
1 parent 709b26d commit eef4aa7

File tree

6 files changed

+337
-18
lines changed

6 files changed

+337
-18
lines changed

examples/api/lib/widgets/heroes/hero.0.dart

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,26 @@
66

77
import 'package:flutter/material.dart';
88

9-
void main() => runApp(const MyApp());
9+
void main() => runApp(const HeroApp());
1010

11-
class MyApp extends StatelessWidget {
12-
const MyApp({Key? key}) : super(key: key);
13-
14-
static const String _title = 'Flutter Code Sample';
11+
class HeroApp extends StatelessWidget {
12+
const HeroApp({Key? key}) : super(key: key);
1513

1614
@override
1715
Widget build(BuildContext context) {
1816
return MaterialApp(
19-
title: _title,
2017
home: Scaffold(
21-
appBar: AppBar(title: const Text(_title)),
18+
appBar: AppBar(title: const Text('Hero Sample')),
2219
body: const Center(
23-
child: MyStatelessWidget(),
20+
child: HeroExample(),
2421
),
2522
),
2623
);
2724
}
2825
}
2926

30-
class MyStatelessWidget extends StatelessWidget {
31-
const MyStatelessWidget({Key? key}) : super(key: key);
27+
class HeroExample extends StatelessWidget {
28+
const HeroExample({Key? key}) : super(key: key);
3229

3330
@override
3431
Widget build(BuildContext context) {
@@ -41,17 +38,18 @@ class MyStatelessWidget extends StatelessWidget {
4138
ListTile(
4239
leading: Hero(
4340
tag: 'hero-rectangle',
44-
child: _blueRectangle(const Size(50, 50)),
41+
child: _box(const Size(50, 50)),
4542
),
4643
onTap: () => _gotoDetailsPage(context),
47-
title:
48-
const Text('Tap on the icon to view hero animation transition.'),
44+
title: const Text(
45+
'Tap on the icon to view hero animation transition.',
46+
),
4947
),
5048
],
5149
);
5250
}
5351

54-
Widget _blueRectangle(Size size) {
52+
Widget _box(Size size) {
5553
return Container(
5654
width: size.width,
5755
height: size.height,
@@ -63,15 +61,15 @@ class MyStatelessWidget extends StatelessWidget {
6361
Navigator.of(context).push(MaterialPageRoute<void>(
6462
builder: (BuildContext context) => Scaffold(
6563
appBar: AppBar(
66-
title: const Text('second Page'),
64+
title: const Text('Second Page'),
6765
),
6866
body: Center(
6967
child: Column(
7068
mainAxisAlignment: MainAxisAlignment.center,
7169
children: <Widget>[
7270
Hero(
7371
tag: 'hero-rectangle',
74-
child: _blueRectangle(const Size(200, 200)),
72+
child: _box(const Size(200, 200)),
7573
),
7674
],
7775
),
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Flutter code sample for Hero
6+
7+
import 'package:flutter/material.dart';
8+
import 'package:flutter/scheduler.dart';
9+
10+
void main() {
11+
// Slow down time to see Hero flight animation.
12+
timeDilation = 15.0;
13+
runApp(const HeroApp());
14+
}
15+
16+
class HeroApp extends StatelessWidget {
17+
const HeroApp({Key? key}) : super(key: key);
18+
19+
@override
20+
Widget build(BuildContext context) {
21+
return MaterialApp(
22+
home: Scaffold(
23+
appBar: AppBar(title: const Text('Hero Sample')),
24+
body: const Center(
25+
child: HeroExample(),
26+
),
27+
),
28+
);
29+
}
30+
}
31+
32+
class HeroExample extends StatelessWidget {
33+
const HeroExample({Key? key}) : super(key: key);
34+
35+
@override
36+
Widget build(BuildContext context) {
37+
return Column(
38+
children: <Widget>[
39+
ListTile(
40+
leading: Hero(
41+
tag: 'hero-default-tween',
42+
child: _box(size: 50.0, color: Colors.red[700]!.withOpacity(0.5)),
43+
),
44+
title: const Text(
45+
'This red icon will use a default rect tween during the hero flight.',
46+
),
47+
),
48+
const SizedBox(height: 10.0),
49+
ListTile(
50+
leading: Hero(
51+
tag: 'hero-custom-tween',
52+
createRectTween: (Rect? begin, Rect? end) {
53+
return MaterialRectCenterArcTween(begin: begin, end: end);
54+
},
55+
child: _box(size: 50.0, color: Colors.blue[700]!.withOpacity(0.5)),
56+
),
57+
title: const Text(
58+
'This blue icon will use a custom rect tween during the hero flight.',
59+
),
60+
),
61+
const SizedBox(height: 10),
62+
ElevatedButton(
63+
onPressed: () => _gotoDetailsPage(context),
64+
child: const Text('Tap to trigger hero flight'),
65+
),
66+
],
67+
);
68+
}
69+
70+
Widget _box({double? size, Color? color}) {
71+
return Container(
72+
color: color,
73+
child: FlutterLogo(size: size),
74+
);
75+
}
76+
77+
void _gotoDetailsPage(BuildContext context) {
78+
Navigator.of(context).push(MaterialPageRoute<void>(
79+
builder: (BuildContext context) => Scaffold(
80+
appBar: AppBar(
81+
title: const Text('Second Page'),
82+
),
83+
body: Align(
84+
alignment: Alignment.bottomRight,
85+
child: Stack(
86+
children: <Widget>[
87+
Hero(
88+
tag: 'hero-custom-tween',
89+
createRectTween: (Rect? begin, Rect? end) {
90+
return MaterialRectCenterArcTween(begin: begin, end: end);
91+
},
92+
child: _box(
93+
size: 400.0,
94+
color: Colors.blue[700]!.withOpacity(0.5),
95+
),
96+
),
97+
Hero(
98+
tag: 'hero-default-tween',
99+
child: _box(
100+
size: 400.0,
101+
color: Colors.red[700]!.withOpacity(0.5),
102+
),
103+
),
104+
],
105+
),
106+
),
107+
),
108+
));
109+
}
110+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter_api_samples/widgets/heroes/hero.0.dart' as example;
7+
import 'package:flutter_test/flutter_test.dart';
8+
9+
void main() {
10+
testWidgets('Has Hero animation', (WidgetTester tester) async {
11+
await tester.pumpWidget(
12+
const example.HeroApp(),
13+
);
14+
15+
expect(find.text('Hero Sample'), findsOneWidget);
16+
await tester.tap(find.byType(Container));
17+
await tester.pump();
18+
19+
Size heroSize = tester.getSize(find.byType(Container));
20+
21+
// Jump 25% into the transition (total length = 300ms)
22+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
23+
heroSize = tester.getSize(find.byType(Container));
24+
expect(heroSize.width.roundToDouble(), 103.0);
25+
expect(heroSize.height.roundToDouble(), 60.0);
26+
27+
// Jump to 50% into the transition.
28+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
29+
heroSize = tester.getSize(find.byType(Container));
30+
expect(heroSize.width.roundToDouble(), 189.0);
31+
expect(heroSize.height.roundToDouble(), 146.0);
32+
33+
// Jump to 75% into the transition.
34+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
35+
heroSize = tester.getSize(find.byType(Container));
36+
expect(heroSize.width.roundToDouble(), 199.0);
37+
expect(heroSize.height.roundToDouble(), 190.0);
38+
39+
// Jump to 100% into the transition.
40+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
41+
heroSize = tester.getSize(find.byType(Container));
42+
expect(heroSize, const Size(200.0, 200.0));
43+
44+
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
45+
await tester.tap(find.byIcon(Icons.arrow_back));
46+
await tester.pump();
47+
48+
// Jump 25% into the transition (total length = 300ms)
49+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
50+
heroSize = tester.getSize(find.byType(Container));
51+
expect(heroSize.width.roundToDouble(), 199.0);
52+
expect(heroSize.height.roundToDouble(), 190.0);
53+
54+
// Jump to 50% into the transition.
55+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
56+
heroSize = tester.getSize(find.byType(Container));
57+
expect(heroSize.width.roundToDouble(), 189.0);
58+
expect(heroSize.height.roundToDouble(), 146.0);
59+
60+
// Jump to 75% into the transition.
61+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
62+
heroSize = tester.getSize(find.byType(Container));
63+
expect(heroSize.width.roundToDouble(), 103.0);
64+
expect(heroSize.height.roundToDouble(), 60.0);
65+
66+
// Jump to 100% into the transition.
67+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
68+
heroSize = tester.getSize(find.byType(Container));
69+
expect(heroSize, const Size(50.0, 50.0));
70+
});
71+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter_api_samples/widgets/heroes/hero.1.dart' as example;
7+
import 'package:flutter_test/flutter_test.dart';
8+
9+
void main() {
10+
testWidgets('Hero flight animation with default rect tween', (WidgetTester tester) async {
11+
await tester.pumpWidget(
12+
const example.HeroApp(),
13+
);
14+
15+
expect(find.text('Hero Sample'), findsOneWidget);
16+
await tester.tap(find.byType(ElevatedButton));
17+
await tester.pump();
18+
19+
Size heroSize = tester.getSize(find.byType(Container).first);
20+
expect(heroSize, const Size(50.0, 50.0));
21+
22+
// Jump 25% into the transition (total length = 300ms)
23+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
24+
heroSize = tester.getSize(find.byType(Container).first);
25+
expect(heroSize.width.roundToDouble(), 171.0);
26+
expect(heroSize.height.roundToDouble(), 73.0);
27+
28+
// Jump to 50% into the transition.
29+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
30+
heroSize = tester.getSize(find.byType(Container).first);
31+
expect(heroSize.width.roundToDouble(), 371.0);
32+
expect(heroSize.height.roundToDouble(), 273.0);
33+
34+
// Jump to 75% into the transition.
35+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
36+
heroSize = tester.getSize(find.byType(Container).first);
37+
expect(heroSize.width.roundToDouble(), 398.0);
38+
expect(heroSize.height.roundToDouble(), 376.0);
39+
40+
// Jump to 100% into the transition.
41+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
42+
heroSize = tester.getSize(find.byType(Container).first);
43+
expect(heroSize, const Size(400.0, 400.0));
44+
45+
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
46+
await tester.tap(find.byIcon(Icons.arrow_back));
47+
await tester.pump();
48+
49+
// Jump 25% into the transition (total length = 300ms)
50+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
51+
heroSize = tester.getSize(find.byType(Container).first);
52+
expect(heroSize.width.roundToDouble(), 398.0);
53+
expect(heroSize.height.roundToDouble(), 376.0);
54+
55+
// Jump to 50% into the transition.
56+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
57+
heroSize = tester.getSize(find.byType(Container).first);
58+
expect(heroSize.width.roundToDouble(), 371.0);
59+
expect(heroSize.height.roundToDouble(), 273.0);
60+
61+
// Jump to 75% into the transition.
62+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
63+
heroSize = tester.getSize(find.byType(Container).first);
64+
expect(heroSize.width.roundToDouble(), 171.0);
65+
expect(heroSize.height.roundToDouble(), 73.0);
66+
67+
// Jump to 100% into the transition.
68+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
69+
heroSize = tester.getSize(find.byType(Container).first);
70+
expect(heroSize, const Size(50.0, 50.0));
71+
});
72+
73+
testWidgets('Hero flight animation with custom rect tween', (WidgetTester tester) async {
74+
await tester.pumpWidget(
75+
const example.HeroApp(),
76+
);
77+
78+
expect(find.text('Hero Sample'), findsOneWidget);
79+
await tester.tap(find.byType(ElevatedButton));
80+
await tester.pump();
81+
82+
Size heroSize = tester.getSize(find.byType(Container).last);
83+
expect(heroSize, const Size(50.0, 50.0));
84+
85+
// Jump 25% into the transition (total length = 300ms)
86+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
87+
heroSize = tester.getSize(find.byType(Container).last);
88+
expect(heroSize.width.roundToDouble(), 133.0);
89+
expect(heroSize.height.roundToDouble(), 133.0);
90+
91+
// Jump to 50% into the transition.
92+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
93+
heroSize = tester.getSize(find.byType(Container).last);
94+
expect(heroSize.width.roundToDouble(), 321.0);
95+
expect(heroSize.height.roundToDouble(), 321.0);
96+
97+
// Jump to 75% into the transition.
98+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
99+
heroSize = tester.getSize(find.byType(Container).first);
100+
expect(heroSize.width.roundToDouble(), 398.0);
101+
expect(heroSize.height.roundToDouble(), 376.0);
102+
103+
// Jump to 100% into the transition.
104+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
105+
heroSize = tester.getSize(find.byType(Container).last);
106+
expect(heroSize, const Size(400.0, 400.0));
107+
108+
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
109+
await tester.tap(find.byIcon(Icons.arrow_back));
110+
await tester.pump();
111+
112+
// Jump 25% into the transition (total length = 300ms)
113+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
114+
heroSize = tester.getSize(find.byType(Container).last);
115+
expect(heroSize.width.roundToDouble(), 386.0);
116+
expect(heroSize.height.roundToDouble(), 386.0);
117+
118+
// Jump to 50% into the transition.
119+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
120+
heroSize = tester.getSize(find.byType(Container).last);
121+
expect(heroSize.width.roundToDouble(), 321.0);
122+
expect(heroSize.height.roundToDouble(), 321.0);
123+
124+
// Jump to 75% into the transition.
125+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
126+
heroSize = tester.getSize(find.byType(Container).last);
127+
expect(heroSize.width.roundToDouble(), 133.0);
128+
expect(heroSize.height.roundToDouble(), 133.0);
129+
130+
// Jump to 100% into the transition.
131+
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
132+
heroSize = tester.getSize(find.byType(Container).last);
133+
expect(heroSize, const Size(50.0, 50.0));
134+
});
135+
}

0 commit comments

Comments
 (0)