Flutter でスクロールするとヘッダー背景がいい感じになるやつ
Flutter には SliverAppBar というものがあり、これを使うとヘッダーの AppBar をいい感じに表示することができる。
Twitter iOS アプリのプロフィールページなどのように、PullToRefresh するとヘッダー画像が blur していい感じに表示される、というのも SliverAppBar で簡単に実装できる。
なのだが、ちょっと複雑なことをしようとするとうまくいかず、自力で ScrollController の offset を取得してヘッダー背景がいい感じになるような Widget を作った。やってることは難しくなくてスクロールの offset をみて画像の位置をいじったり AppBar の opacity を変化させている。(iOS アプリ開発の時によくやっていたので、モバイルアプリとしてはあるあるな実装だと思う)
これで一応動いているのだが、スクロールのたびに Widget の更新が走るため、パフォーマンスは不安である。
こんな感じで動く。
class HeaderScrollPage extends StatefulWidget { const HeaderScrollPage({Key? key}) : super(key: key); @override HeaderScrollPageState createState() => HeaderScrollPageState(); } class HeaderScrollPageState extends State<HeaderScrollPage> { double _appBarOpacity = 1.0; double _transform = 0.0; double _backgroundHeight = 200; final _scrollController = ScrollController(); final _topMargin = 80; @override void initState() { super.initState(); _scrollController.addListener( () { final offset = _scrollController.offset; setState(() { _transform = offset < 0 ? 0 : -offset; _backgroundHeight = offset < 0 ? 200 - offset : 200; _appBarOpacity = max(((_topMargin - -_transform)), 0) / _topMargin; }); }, ); } @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( extendBodyBehindAppBar: true, appBar: _appBarOpacity == 0.0 ? null : AppBar( foregroundColor: Colors.black.withOpacity(_appBarOpacity), backgroundColor: Colors.transparent, title: const Text('AppBar'), ), body: Stack( children: [ Container( transform: Matrix4.translationValues(0.0, _transform, 0.0), height: _backgroundHeight, width: double.infinity, child: Image.network( // https://min-chi.material.jp/fm/bg_c/facility2/ をお借りしています 'https://min-chi.material.jp/mc/materials/background-c/facility2/_facility2_1.jpg', fit: BoxFit.cover, ), ), Center( child: SingleChildScrollView( controller: _scrollController, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( height: _topMargin + 20, ), const SizedBox( width: 200, height: 200, child: CircleAvatar(child: Text('Avatar')), ), const SizedBox(height: 40), Column( children: List.generate(100, (index) => Text("Text $index")), ) ], ), ), ), ], ), ); } }
ChatGPT に Flutter の scrollcontroller の使い方を教えて
って聞いたらただ動くだけじゃなく dispose ちゃんとしろよって教えてくれた。flutter で 100この Text widget を生成して
って聞いたらちゃんと出力してくれて便利。