I have a Hero animation that should animate an image from a ListView to the background of a FlexibleSpaceBar inside of a SliverAppBar.
This works all fine, except that the hero animation takes place on top of the SliverAppBar and when the animation is finished, the SliverAppBarsuddenly pops in on top of the background, which doesn't look so nice.
How can I either a) include the SliverAppBar in the hero transition so that it fades in on top of the image as the transition progresses or b) make the hero transition take place under the SliverAppBar without covering it?
Sample Code:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomePage(),
'/details': (context) => DetailsPage(),
},
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: AppBar().preferredSize,
child: Hero(
tag: 'appbar',
child: AppBar(
title: Text('title'),
actions: <Widget>[
PopupMenuButton<String>(
itemBuilder: (BuildContext context) {
return [
PopupMenuItem<String>(
child: Text('Action'),
)
];
},
),
],
),
),
),
body: ListView.builder(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
itemCount: 10,
itemBuilder: (context, index) => buildListItem(context, index),
),
);
}
Widget buildListItem(BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: InkWell(
onTap: () async {
await Navigator.of(context).pushNamed(
'/details',
arguments: index,
);
},
child: Row(
children: <Widget>[
Hero(
tag: index,
child:
SizedBox(width: 80, height: 80, child: widgetForIndex(index)),
),
],
),
),
);
}
}
Widget widgetForIndex(int index) {
return Container(color: Color.fromARGB(255, max(0, 255 - 20 * index), 0, 0));
}
class DetailsPage extends StatefulWidget {
@override
_DetailsPageState createState() => _DetailsPageState();
}
class _DetailsPageState extends State<DetailsPage> {
@override
Widget build(BuildContext context) {
final int index = ModalRoute.of(context).settings.arguments;
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
title: Text('Details'),
expandedHeight: 300,
flexibleSpace: FlexibleSpaceBar(
background: Hero(
tag: index,
child: widgetForIndex(index),
),
),
),
SliverToBoxAdapter(
child: Column(
children: [
Container(
color: Colors.green,
width: 200,
height: 200,
),
],
),
),
],
),
);
}
}