👀

Flutterでスクロールに合わせてヘッダーバーの色を変化させる方法

2024/03/20に公開

はじめに

23卒で自社開発企業に入社した新米エンジニアです。仕事で使っているのJavaがメインですが、Flutterの開発も行っています。

初記事にはなりますが、Flutterを使用して、スクロールに応じてヘッダーバーの色を変化させる方法を紹介します。

スクロールに応じて色を変化させる実装方法

まず、スクロールに合わせてヘッダーバーの色を変化させるためには、ScrollControllerを使用します。ScrollControllerを介してスクロール位置を監視し、その位置に応じて色を変化させます。以下はその実装例です。

  • maxScrollExtent
    _scrollController.position.maxScrollExtent は、スクロールビューのコンテンツ全体の高さから表示領域の高さを引いたものです。つまり、スクロールビューが最大限までスクロールできる距離を表しています。

  • scrollRate
    現在のスクロール位置を maxScrollExtent で割った値です。これにより、スクロール位置がスクロール可能な最大量に対してどれだけ進んでいるかを表す割合が得られます。

import 'package:flutter/material.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateMixin {
  late TabController _tabController;
  final ScrollController _scrollController = ScrollController();

  
  void initState(){
    _tabController = TabController(length: 4, vsync: this, initialIndex: 0);
    _tabController.addListener(_handleTabSelection);
    _scrollController.addListener(() {
      setState(() {});
    });
    super.initState();
  }

  _handleTabSelection(){
    if(_tabController.indexIsChanging){
      setState(() {});
    }
  }

  
  void dispose(){
    _tabController.dispose();
    super.dispose();
  }

  /// スクロールコントローラーの現在のスクロール位置に基づいてスクロールレートを計算します。
  ///
  /// [colorChangeSpeed] パラメーターは色の変化の速度を制御します。
  /// 1.0 の値は通常の速度を意味し、
  /// 1.0 より大きい値は色の変化を加速させ、1.0 より小さい値は遅くします。
  double _calculateScrollRate(ScrollController scrollController,
      {double colorChangeSpeed = 1.0}) {

    final maxScrollExtent = scrollController.hasClients ?
    scrollController.position.maxScrollExtent : 0.0;
    final scrollRate = scrollController.hasClients ?
    scrollController.position.pixels / maxScrollExtent : 0.0;
    return scrollRate * colorChangeSpeed;
  }

  
  Widget build(BuildContext context) {
    final scrollRate =
    _calculateScrollRate(_scrollController, colorChangeSpeed: 0.8);
    final headerBarColor =
    Color.lerp(Colors.black, Colors.brown.shade300, scrollRate.clamp(0.0, 1.0));
    final textColor =
    Color.lerp(Colors.white, Colors.black, scrollRate.clamp(0.0, 1.0));
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            Container(
              color: headerBarColor,
              child: Column(
                children: [
                  const SizedBox(height: 30,),
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 15),
                    child: SizedBox(
                      width: MediaQuery.of(context).size.width,
                      child: Text("It's a Great Day for Coffee",
                        style: TextStyle(
                          color: textColor,
                          fontSize: 28,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: 10),
                  Container(
                    color: headerBarColor,
                    child: TabBar(
                      controller: _tabController,
                      labelColor: const Color(0xFFE57734),
                      unselectedLabelColor: textColor?.withOpacity(0.5),
                      isScrollable: false,
                      indicator: const UnderlineTabIndicator(
                        borderSide: BorderSide(
                          width: 3,
                          color: Color(0xFFE57734),
                        ),
                        insets: EdgeInsets.symmetric(horizontal: 80),
                      ),
                      labelStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
                      labelPadding: const EdgeInsets.symmetric(horizontal: 5),
                      tabs: const [
                        Tab(text: "Hot Coffee",),
                        Tab(text: "Cold Coffee",),
                        Tab(text: "Hot Tea",),
                        Tab(text: "Cold Tea",),
                      ],
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 10),
            Expanded(
              child: ListView(
                controller: _scrollController,
                children: [
                  /// 以下省略、、、
                  Center(child: ItemsWidget(),),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

実際の動き

画面をスクロールできるようにサンプルでコーヒーのメニューをListViewで表示しています。
スクロールに合わせてヘッダーバーを黒色から茶色に変化させています。

最後に

今回初めて記事を書いてみましたが、今後もアウトプットをたくさんしていこうと思います。
まだ、エンジニアとして経験は浅いので、コードや記事に関して、もっとこうした方がいいことや間違っていることなどがあればぜひコメントで教えてください。

Discussion