Foggy day

[Flutter] AppBar 사용법 본문

Flutter/Flutter widget

[Flutter] AppBar 사용법

jinhan38 2023. 3. 17. 18:25

이번 포스팅에서는 AppBar의 사용법을 알아보겠습니다. 

AppBar는 대부분 Scaffold위젯과 함께 사용됩니다. 새로운 페이지로 이동할 때 뒤로가기 버튼과 페이지 이름, 필요하다면 추가적인 버튼들을 추가해서 사용합니다. 

앱바에는 이 외에도 몇 가지 특성들이 더 있습니다. 그래서 자주 사용하는 특성들과 자주 사용하지 않는 특성들을 함께 살펴보겠습니다.

 

우선적으로 알아야 할 것은 leading, title, action 위젯들입니다. leading은 앱바의 왼쪽, title은 앱바의 가운데, action은 앱바의 오른쪽에 위치하는 위젯들입니다.

이때 leading위젯을 선언하지 않거나 null을 입력하면 뒤로가기 버튼이 기본적으로 생성되는데 automaticallyImplyLeading특성을 사용하면 back 버튼을 보여줄지 말지 설정할 수 있습니다.  각 특성들의 설명은 주석으로 추가했습니다. 

 

기본 앱바 

final double toolbarHeight = 60;
double _heightPercent = 1;
  
AppBar _appBar() {
    return AppBar(
      elevation: 10,
      backgroundColor: Colors.green,
      shadowColor: Colors.blue,

      /// 왼쪽에 들어갈 위젯
      leading: IconButton(
        onPressed: () {},
        icon: const Icon(
          Icons.arrow_back_rounded,
          color: Colors.white,
        ),
      ),

      /// leading 위젯에 back버튼을 자동으로 생성해줄지 말지 결정
      /// 기본값이 true,
      automaticallyImplyLeading: false,

      /// 가운데 들어갈 위젯
      title: const Text("AppBarScreen"),

      /// 오른쪽에 들어갈 위젯
      actions: const [
        Icon(Icons.settings, color: Colors.white),
      ],

      titleTextStyle: const TextStyle(
        color: Colors.white,
        fontSize: 18,
        fontWeight: FontWeight.bold,
      ),

      /// 툴바의 높이
      toolbarHeight: toolbarHeight * _heightPercent,

      /// true로 주면 title 위젯이 가운데로 이동한다.
      centerTitle: false,

      /// leading과 title 위젯 간의 거리
      titleSpacing: 30,

      /// 아이콘들의 컬러를 지정하지 않는다면 변경할 수 있음
      foregroundColor: Colors.black,

    );
  }

 

 

위 코드를 구현하면 아래와 같이 그려집니다. 

 

 

 

여기서 다른 특성들을 좀 더 사용해보겠습니다. SliverAppBar를 활용해서 스크롤을 할 때 앱바의 모양이 변경되는 애니메이션을 구현할 수 있는데, 그와 비슷한 애니메이션을 기본 AppBar로 구현해보겠습니다.

이를 위해서는 bottom 특성에 PrefferredSizeWidget을 넘겨줘야 합니다. PrefferredSizeWidget으로는 대표적으로 AppBar와 TabBar가 있는데, 이번에는 PrefferredSizeWidget을 상속한 CustomAppBar 위젯을 만들어서 사용하겠습니다. 

PrefferredSizeWidget을 상속받으면 Size를 리턴하는 getter를 꼭 구현해줘야 합니다. 이때 return하는 Size를 CustomAppBar의 사이즈로 사용하고 있습니다. 

 

CustomAppBar

class CustomAppBar extends StatelessWidget implements PreferredSizeWidget{
   const CustomAppBar({
    required this.child,
    required this.size,
    this.minHeight = 50,
    this.maxHeight = 100,
    Key? key,
  }) : super(key: key);

   final Widget child;
   final Size size;
   final double minHeight;
   final double maxHeight;

   @override
   Size get preferredSize {
     double height = size.height;
     if (height < minHeight) {
       height = minHeight;
     }
     if (height > maxHeight) {
       height = maxHeight;
     }
     return Size(size.width, height);
   }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: preferredSize.height,
      width: preferredSize.width,
      child: child,
    );
  }

}

 

 

SingleChildScrollView

위젯을 새로 만들었으니 이와 연동시킬 스크롤뷰를 만들겠습니다.

파란 배경에 검은색 글자가 들어가있는 스크롤 위젯을 만들었습니다. 

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _appBar(),
      body: SingleChildScrollView(
        child: Column(
          children: [
            ...List.generate(6, (index) => _childWidget(index)),
          ],
        ),
      ),
    );
  }

  Widget _childWidget(int index) {
    return Container(
      height: 200,
      width: double.infinity,
      color: Colors.blue,
      margin: const EdgeInsets.all(20),
      alignment: Alignment.center,
      child: Text(
        index.toString(),
        style: const TextStyle(fontSize: 60, fontWeight: FontWeight.bold),
      ),
    );
  }

 

 

 

 

Full code

이제 앱바와 스크롤뷰, CustomAppBar를 연동시키겠습니다.

bottom 특성에 CustomAppBar를 넘겨주고, 스크롤을 할 때 마다CustomAppBar의 사이즈가 변경됩니다. 

아직 설명을 안한 특성중 notificationPredicate가 있는데, 이 특성은 스크롤을 할 때 마다 현재 스크롤의 포지션을 알려줍니다.

전체 스크롤에서 현재 몇퍼센트에 도달했는지를 계산해서 _heightPercent(0~1)의 값을 구했습니다.  _heightPercent값과 toolbarHeight값을 사용해서 CustomAppBar 위젯의 사이즈를 입력해주고 있습니다. 

그리고 toolbarHeight특성을 사용해서 툴바의 사이즈도 변경시키고 있습니다. 

import 'package:flutter/material.dart';

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

  @override
  State<AppBarScreen> createState() => _AppBarScreenState();
}

class _AppBarScreenState extends State<AppBarScreen> {

  final double toolbarHeight = 60;
  double _heightPercent = 1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _appBar(),
      body: SingleChildScrollView(
        child: Column(
          children: [
            ...List.generate(6, (index) => _childWidget(index)),
          ],
        ),
      ),
    );
  }

  Widget _childWidget(int index) {
    return Container(
      height: 200,
      width: double.infinity,
      color: Colors.blue,
      margin: const EdgeInsets.all(20),
      alignment: Alignment.center,
      child: Text(
        index.toString(),
        style: const TextStyle(fontSize: 60, fontWeight: FontWeight.bold),
      ),
    );
  }


  AppBar _appBar() {
    return AppBar(
      elevation: 10,
      backgroundColor: Colors.green,
      shadowColor: Colors.blue,

      /// 왼쪽에 들어갈 위젯
      leading: IconButton(
        onPressed: () {
          Navigator.pop(context);
        },
        icon: const Icon(
          Icons.arrow_back_rounded,
          color: Colors.white,
        ),
      ),

      /// leading 위젯에 back버튼을 자동으로 생성해줄지 말지 결정
      /// 기본값이 true,
      automaticallyImplyLeading: false,

      /// 가운데 들어갈 위젯
      title: const Text("AppBarScreen"),

      /// 오른쪽에 들어갈 위젯
      actions: const [
        Icon(Icons.settings, color: Colors.white),
      ],

      titleTextStyle: const TextStyle(
        color: Colors.white,
        fontSize: 18,
        fontWeight: FontWeight.bold,
      ),

      /// 툴바의 투명도, backgroundColor의 투명도는 변하지 않는다.
      /// leading, title, actions 의 위젯들에 투명도가 적용된다.
      toolbarOpacity: 1,

      /// 툴바의 높이
      toolbarHeight: toolbarHeight * _heightPercent,

      /// true로 주면 title 위젯이 가운데로 이동한다.
      centerTitle: false,

      /// leading과 title 위젯 간의 거리
      titleSpacing: 30,

      /// 아이콘들의 컬러를 지정하지 않는다면 변경할 수 있음
      foregroundColor: Colors.black,

      /// 앱바의 하단에 들어갈 위젯
      bottom: CustomAppBar(
        size: Size(
          double.infinity,
          toolbarHeight * _heightPercent + toolbarHeight,
        ),
        minHeight: 0,
        maxHeight: 200,
        child: Container(
          alignment: Alignment.centerLeft,
          color: Colors.purple,
          child: const Text(
            "   Bottom Widget",
            style: TextStyle(
                color: Colors.white, fontSize: 30, fontWeight: FontWeight.bold),
          ),
        ),
      ),

      /// 하단 위젯의 투명도 설정
      bottomOpacity: 1,

      notificationPredicate: (notification) {
        double maxScrollExtent = notification.metrics.maxScrollExtent;

        /// 스크롤의 현재 포지션, 시작값은 0, 끝 값은 maxScrollExtent
        double current = notification.metrics.pixels;

        _heightPercent = 1 - ((current / maxScrollExtent) * 2);
        if (_heightPercent < 0) {
          _heightPercent = 0;
        }
        if (_heightPercent > 1) {
          _heightPercent = 1;
        }

        setState(() {});

        return true;
      },
    );
  }
}

class CustomAppBar extends StatelessWidget implements PreferredSizeWidget{
   const CustomAppBar({
    required this.child,
    required this.size,
    this.minHeight = 50,
    this.maxHeight = 100,
    Key? key,
  }) : super(key: key);

   final Widget child;
   final Size size;
   final double minHeight;
   final double maxHeight;

   @override
   Size get preferredSize {
     double height = size.height;
     if (height < minHeight) {
       height = minHeight;
     }
     if (height > maxHeight) {
       height = maxHeight;
     }
     return Size(size.width, height);
   }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: preferredSize.height,
      width: preferredSize.width,
      child: child,
    );
  }

}

 

 

최종 이미지, 동영상

 

 

 

 

 

 

'Flutter > Flutter widget' 카테고리의 다른 글

[Flutter] Row - 사용법  (0) 2023.03.20
[Flutter] Column - 사용법  (0) 2023.03.19
[Flutter] Container widget 사용법  (0) 2023.03.15
[Flutter] 기본 Dialog 사용법  (0) 2023.03.14
[Flutter] ListTile Widget 사용법  (0) 2023.03.11