在页面跳转过程中给用户加以引导是非常有用的。实现引导的一种通用做法是在页面切换时为某个组件加上转场动画,从而在两个页面间建立视觉上的锚定关联。

在 Flutter 中,可以通过 Hero 组件实现页面切换时组件的转场动画。

步骤

  • 创建两个页面,展示相同的图片

  • 在第一个页面中加入 Hero 组件

  • 在第二个页面中加入 Hero 组件

1. 创建两个页面,展示相同的图片

在这个示例中,将在两个页面中展示相同的图片。当用户在第一个页面点击图片,会通过一个转场动画切换到第二个页面。现在,我们将会创建页面的视觉结构,并在后续步骤中处理动画。

提示: 这个示例建立在 导航到一个新页面和返回处理点击事件 这两个章节的基础上。

  1. class MainScreen extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return Scaffold(
  5. appBar: AppBar(
  6. title: Text('Main Screen'),
  7. ),
  8. body: GestureDetector(
  9. onTap: () {
  10. Navigator.push(context, MaterialPageRoute(builder: (_) {
  11. return DetailScreen();
  12. }));
  13. },
  14. child: Image.network(
  15. 'https://picsum.photos/250?image=9',
  16. ),
  17. ),
  18. );
  19. }
  20. }
  21. class DetailScreen extends StatelessWidget {
  22. @override
  23. Widget build(BuildContext context) {
  24. return Scaffold(
  25. body: GestureDetector(
  26. onTap: () {
  27. Navigator.pop(context);
  28. },
  29. child: Center(
  30. child: Image.network(
  31. 'https://picsum.photos/250?image=9',
  32. ),
  33. ),
  34. ),
  35. );
  36. }
  37. }

2. 在第一个页面中加入 Hero 组件

为了通过动画在两个页面间建立联系,需要把每个页面的 Image 组件都包裹进 Hero 组件里面。Hero 组件有两个参数:

  • tag: 作为 Hero 组件的标识,在这两个页面中必须相同。

  • child: 被施予动画的组件

  1. Hero(
  2. tag: 'imageHero',
  3. child: Image.network(
  4. 'https://picsum.photos/250?image=9',
  5. ),
  6. );

3. 在第二个页面中加入 Hero 组件

为了完善与第一个页面的关联,同样需要把第二个页面中的 Image 组件包裹进 Hero 组件里面。它的 tag 也必须和第一个页面相同。

Hero 组件被应用到第二个页面后,页面的转场动画就生效了。

  1. Hero(
  2. tag: 'imageHero',
  3. child: Image.network(
  4. 'https://picsum.photos/250?image=9',
  5. ),
  6. );

提示: 这份代码和第一个页面中的代码是相同的。实际上,可以创建一个可复用的组件来代替这些重复的代码。但是在这个示例中,重复的代码会更易于讲解和演示。

Complete example

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(HeroApp());
  3. class HeroApp extends StatelessWidget {
  4. @override
  5. Widget build(BuildContext context) {
  6. return MaterialApp(
  7. title: 'Transition Demo',
  8. home: MainScreen(),
  9. );
  10. }
  11. }
  12. class MainScreen extends StatelessWidget {
  13. @override
  14. Widget build(BuildContext context) {
  15. return Scaffold(
  16. appBar: AppBar(
  17. title: Text('Main Screen'),
  18. ),
  19. body: GestureDetector(
  20. child: Hero(
  21. tag: 'imageHero',
  22. child: Image.network(
  23. 'https://picsum.photos/250?image=9',
  24. ),
  25. ),
  26. onTap: () {
  27. Navigator.push(context, MaterialPageRoute(builder: (_) {
  28. return DetailScreen();
  29. }));
  30. },
  31. ),
  32. );
  33. }
  34. }
  35. class DetailScreen extends StatelessWidget {
  36. @override
  37. Widget build(BuildContext context) {
  38. return Scaffold(
  39. body: GestureDetector(
  40. child: Center(
  41. child: Hero(
  42. tag: 'imageHero',
  43. child: Image.network(
  44. 'https://picsum.photos/250?image=9',
  45. ),
  46. ),
  47. ),
  48. onTap: () {
  49. Navigator.pop(context);
  50. },
  51. ),
  52. );
  53. }
  54. }

Hero Demo