Foggy day
[Flutter] AppBar 사용법 본문
이번 포스팅에서는 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 |