分类 hero · 难度 2/5 · 验证于 Flutter 3.32 / 2026-06
点击网格项,元素「飞」到详情页对应位置。两端用同一个 Hero tag,框架自动接管过渡。
⏳ 在线预览未就绪:运行 node scripts/sync-gists.mjs 生成 gist 后即可内嵌。
// ✅ 推荐:列表页与详情页用同一个唯一 tag,框架自动接管共享元素飞行。
// 可直接粘进 DartPad (https://dartpad.dev) 运行。
import 'package:flutter/material.dart';
void main() => runApp(const _App());
class _App extends StatelessWidget {
const _App();
@override
Widget build(BuildContext context) => MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(useMaterial3: true),
home: const _GridPage(),
);
}
const _colors = [
Colors.red, Colors.green, Colors.blue,
Colors.orange, Colors.purple, Colors.teal,
];
class _GridPage extends StatelessWidget {
const _GridPage();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Hero gallery')),
body: GridView.count(
crossAxisCount: 3,
padding: const EdgeInsets.all(12),
mainAxisSpacing: 12,
crossAxisSpacing: 12,
children: [
for (final (i, c) in _colors.indexed)
GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (_) => _DetailPage(index: i, color: c)),
),
child: Hero(
tag: 'box-$i', // 唯一 tag,与详情页一致
child: Container(
decoration: BoxDecoration(
color: c,
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
),
);
}
}
class _DetailPage extends StatelessWidget {
const _DetailPage({required this.index, required this.color});
final int index;
final Color color;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Hero(
tag: 'box-$index', // 与列表页同一个 tag
child: Container(
width: 260,
height: 260,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(24),
),
),
),
),
);
}
}