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/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 を生成して
って聞いたらちゃんと出力してくれて便利。