Hero: Add an example for createRectTween (#102650)

This commit is contained in:
Taha Tesser 2022-04-29 22:39:09 +03:00 committed by GitHub
parent 709b26d0b3
commit eef4aa7caa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 337 additions and 18 deletions

View file

@ -6,29 +6,26 @@
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
void main() => runApp(const HeroApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
class HeroApp extends StatelessWidget {
const HeroApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
appBar: AppBar(title: const Text('Hero Sample')),
body: const Center(
child: MyStatelessWidget(),
child: HeroExample(),
),
),
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
class HeroExample extends StatelessWidget {
const HeroExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -41,17 +38,18 @@ class MyStatelessWidget extends StatelessWidget {
ListTile(
leading: Hero(
tag: 'hero-rectangle',
child: _blueRectangle(const Size(50, 50)),
child: _box(const Size(50, 50)),
),
onTap: () => _gotoDetailsPage(context),
title:
const Text('Tap on the icon to view hero animation transition.'),
title: const Text(
'Tap on the icon to view hero animation transition.',
),
),
],
);
}
Widget _blueRectangle(Size size) {
Widget _box(Size size) {
return Container(
width: size.width,
height: size.height,
@ -63,7 +61,7 @@ class MyStatelessWidget extends StatelessWidget {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('second Page'),
title: const Text('Second Page'),
),
body: Center(
child: Column(
@ -71,7 +69,7 @@ class MyStatelessWidget extends StatelessWidget {
children: <Widget>[
Hero(
tag: 'hero-rectangle',
child: _blueRectangle(const Size(200, 200)),
child: _box(const Size(200, 200)),
),
],
),

View file

@ -0,0 +1,110 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for Hero
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() {
// Slow down time to see Hero flight animation.
timeDilation = 15.0;
runApp(const HeroApp());
}
class HeroApp extends StatelessWidget {
const HeroApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Hero Sample')),
body: const Center(
child: HeroExample(),
),
),
);
}
}
class HeroExample extends StatelessWidget {
const HeroExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ListTile(
leading: Hero(
tag: 'hero-default-tween',
child: _box(size: 50.0, color: Colors.red[700]!.withOpacity(0.5)),
),
title: const Text(
'This red icon will use a default rect tween during the hero flight.',
),
),
const SizedBox(height: 10.0),
ListTile(
leading: Hero(
tag: 'hero-custom-tween',
createRectTween: (Rect? begin, Rect? end) {
return MaterialRectCenterArcTween(begin: begin, end: end);
},
child: _box(size: 50.0, color: Colors.blue[700]!.withOpacity(0.5)),
),
title: const Text(
'This blue icon will use a custom rect tween during the hero flight.',
),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () => _gotoDetailsPage(context),
child: const Text('Tap to trigger hero flight'),
),
],
);
}
Widget _box({double? size, Color? color}) {
return Container(
color: color,
child: FlutterLogo(size: size),
);
}
void _gotoDetailsPage(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Second Page'),
),
body: Align(
alignment: Alignment.bottomRight,
child: Stack(
children: <Widget>[
Hero(
tag: 'hero-custom-tween',
createRectTween: (Rect? begin, Rect? end) {
return MaterialRectCenterArcTween(begin: begin, end: end);
},
child: _box(
size: 400.0,
color: Colors.blue[700]!.withOpacity(0.5),
),
),
Hero(
tag: 'hero-default-tween',
child: _box(
size: 400.0,
color: Colors.red[700]!.withOpacity(0.5),
),
),
],
),
),
),
));
}
}

View file

@ -0,0 +1,71 @@
// Copyright 2014 The Flutter 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/material.dart';
import 'package:flutter_api_samples/widgets/heroes/hero.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Has Hero animation', (WidgetTester tester) async {
await tester.pumpWidget(
const example.HeroApp(),
);
expect(find.text('Hero Sample'), findsOneWidget);
await tester.tap(find.byType(Container));
await tester.pump();
Size heroSize = tester.getSize(find.byType(Container));
// Jump 25% into the transition (total length = 300ms)
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container));
expect(heroSize.width.roundToDouble(), 103.0);
expect(heroSize.height.roundToDouble(), 60.0);
// Jump to 50% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container));
expect(heroSize.width.roundToDouble(), 189.0);
expect(heroSize.height.roundToDouble(), 146.0);
// Jump to 75% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container));
expect(heroSize.width.roundToDouble(), 199.0);
expect(heroSize.height.roundToDouble(), 190.0);
// Jump to 100% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container));
expect(heroSize, const Size(200.0, 200.0));
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
await tester.tap(find.byIcon(Icons.arrow_back));
await tester.pump();
// Jump 25% into the transition (total length = 300ms)
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container));
expect(heroSize.width.roundToDouble(), 199.0);
expect(heroSize.height.roundToDouble(), 190.0);
// Jump to 50% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container));
expect(heroSize.width.roundToDouble(), 189.0);
expect(heroSize.height.roundToDouble(), 146.0);
// Jump to 75% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container));
expect(heroSize.width.roundToDouble(), 103.0);
expect(heroSize.height.roundToDouble(), 60.0);
// Jump to 100% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container));
expect(heroSize, const Size(50.0, 50.0));
});
}

View file

@ -0,0 +1,135 @@
// Copyright 2014 The Flutter 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/material.dart';
import 'package:flutter_api_samples/widgets/heroes/hero.1.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Hero flight animation with default rect tween', (WidgetTester tester) async {
await tester.pumpWidget(
const example.HeroApp(),
);
expect(find.text('Hero Sample'), findsOneWidget);
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
Size heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize, const Size(50.0, 50.0));
// Jump 25% into the transition (total length = 300ms)
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize.width.roundToDouble(), 171.0);
expect(heroSize.height.roundToDouble(), 73.0);
// Jump to 50% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize.width.roundToDouble(), 371.0);
expect(heroSize.height.roundToDouble(), 273.0);
// Jump to 75% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize.width.roundToDouble(), 398.0);
expect(heroSize.height.roundToDouble(), 376.0);
// Jump to 100% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize, const Size(400.0, 400.0));
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
await tester.tap(find.byIcon(Icons.arrow_back));
await tester.pump();
// Jump 25% into the transition (total length = 300ms)
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize.width.roundToDouble(), 398.0);
expect(heroSize.height.roundToDouble(), 376.0);
// Jump to 50% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize.width.roundToDouble(), 371.0);
expect(heroSize.height.roundToDouble(), 273.0);
// Jump to 75% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize.width.roundToDouble(), 171.0);
expect(heroSize.height.roundToDouble(), 73.0);
// Jump to 100% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize, const Size(50.0, 50.0));
});
testWidgets('Hero flight animation with custom rect tween', (WidgetTester tester) async {
await tester.pumpWidget(
const example.HeroApp(),
);
expect(find.text('Hero Sample'), findsOneWidget);
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
Size heroSize = tester.getSize(find.byType(Container).last);
expect(heroSize, const Size(50.0, 50.0));
// Jump 25% into the transition (total length = 300ms)
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).last);
expect(heroSize.width.roundToDouble(), 133.0);
expect(heroSize.height.roundToDouble(), 133.0);
// Jump to 50% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).last);
expect(heroSize.width.roundToDouble(), 321.0);
expect(heroSize.height.roundToDouble(), 321.0);
// Jump to 75% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).first);
expect(heroSize.width.roundToDouble(), 398.0);
expect(heroSize.height.roundToDouble(), 376.0);
// Jump to 100% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).last);
expect(heroSize, const Size(400.0, 400.0));
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
await tester.tap(find.byIcon(Icons.arrow_back));
await tester.pump();
// Jump 25% into the transition (total length = 300ms)
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).last);
expect(heroSize.width.roundToDouble(), 386.0);
expect(heroSize.height.roundToDouble(), 386.0);
// Jump to 50% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).last);
expect(heroSize.width.roundToDouble(), 321.0);
expect(heroSize.height.roundToDouble(), 321.0);
// Jump to 75% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).last);
expect(heroSize.width.roundToDouble(), 133.0);
expect(heroSize.height.roundToDouble(), 133.0);
// Jump to 100% into the transition.
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
heroSize = tester.getSize(find.byType(Container).last);
expect(heroSize, const Size(50.0, 50.0));
});
}

View file

@ -71,7 +71,6 @@ enum HeroFlightDirection {
pop,
}
/// A widget that marks its child as being a candidate for
/// [hero animations](https://flutter.dev/docs/development/ui/animations/hero-animations).
///
@ -116,6 +115,13 @@ enum HeroFlightDirection {
/// ** See code in examples/api/lib/widgets/heroes/hero.0.dart **
/// {@end-tool}
///
/// {@tool dartpad}
/// This sample shows [Hero] flight animations using default tween
/// and custom rect tween.
///
/// ** See code in examples/api/lib/widgets/heroes/hero.1.dart **
/// {@end-tool}
///
/// ## Discussion
///
/// Heroes and the [Navigator]'s [Overlay] [Stack] must be axis-aligned for

View file

@ -2649,7 +2649,6 @@ Future<void> main() async {
end: const Size(100, 100),
).chain(CurveTween(curve: Curves.fastOutSlowIn));
await tester.pumpWidget(
MaterialApp(
navigatorKey: navigator,