FlutterサンプルアプリにGalleryがあるが、このアプリの縮小(拡大)するヘッダがカッコよかったので、どうやってるのか調べてみた。
どうやらCustomScrollViewに、SliberAppBarとSliberListを指定して実装するらしい。SliberAppBarのbackgroundにはAnimatedBuilderを使用したWidgetも指定しアニメーションさせている様子。
チュートリアルのBuilding Layouts in Flutterにあるlakesのレイアウトに移植して試してみる。
まず、バックグランド関連ソースをコピペし、背景画像のasset名を変更する。
lib/main.dart
const double _kFlexibleSpaceMaxHeight = 256.0;
class _BackgroundLayer {
_BackgroundLayer({int level, double parallax})
: assetName = 'images/lake.jpg',
parallaxTween = new Tween<double>(begin: 0.0, end: parallax);
final String assetName;
final Tween<double> parallaxTween;
}
final List<_BackgroundLayer> _kBackgroundLayers = <_BackgroundLayer>[
new _BackgroundLayer(level: 0, parallax: _kFlexibleSpaceMaxHeight),
new _BackgroundLayer(level: 1, parallax: _kFlexibleSpaceMaxHeight),
new _BackgroundLayer(level: 2, parallax: _kFlexibleSpaceMaxHeight / 2.0),
new _BackgroundLayer(level: 3, parallax: _kFlexibleSpaceMaxHeight / 4.0),
new _BackgroundLayer(level: 4, parallax: _kFlexibleSpaceMaxHeight / 2.0),
new _BackgroundLayer(level: 5, parallax: _kFlexibleSpaceMaxHeight)
];
class _AppBarBackground extends StatelessWidget {
_AppBarBackground({Key key, this.animation}) : super(key: key);
final Animation<double> animation;
@override
Widget build(BuildContext context) {
return new AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return new Stack(
children: _kBackgroundLayers.map((_BackgroundLayer layer) {
return new Positioned(
top: -layer.parallaxTween.evaluate(animation),
left: 0.0,
right: 0.0,
bottom: 0.0,
child: new Image.asset(layer.assetName,
fit: BoxFit.cover, height: _kFlexibleSpaceMaxHeight));
}).toList());
});
}
}
次に、Scaffoldに渡していたappBarを削除し、bodyをCustomScrollViewに差し替える。画像はバックグランドの背景で指定したので、SliverListに渡す配列から取り除いておく。
lib/main.dart
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new Scaffold(
body: new CustomScrollView(slivers: <Widget>[
new SliverAppBar(
pinned: true,
expandedHeight: _kFlexibleSpaceMaxHeight,
flexibleSpace: new FlexibleSpaceBar(
title: new Text('Top Lakes'),
// TODO(abarth): Wire up to the parallax in a way that doesn't pop during hero transition.
background:
new _AppBarBackground(animation: kAlwaysDismissedAnimation),
),
),
new SliverList(
delegate: new SliverChildListDelegate(<Widget>[
titleSection,
buttonSection,
textSection,
])),
])));
あと、直接関係ないけど、ヘッダが縮小しきるにはコンテンツが足りないので、テキストの文字列を適当に追加した。
で、できあがり。ソースはここ。