일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 자바스크립트
- 모션비트
- pintos
- Java
- 스택
- 큐
- Vue.js
- 알고리즘
- 백준
- 4기
- defee
- TiL
- 코드트리
- HTML
- 리액트
- 자바
- 사이드프로젝트
- Flutter
- 오블완
- 소켓
- userprog
- JavaScript
- 시스템콜
- 티스토리챌린지
- 핀토스
- 크래프톤정글
- corou
- 크래프톤 정글
- 나만무
- CSS
- Today
- Total
미새문지
flutter AppBar, BottomBar 구현 본문
리액트때는 자바스크립트 문법위주로 작성되기 때문에 사용되는 기능들만 알아내서 사용하면 되는데, 플러터는 기능 자체가 너무 많고 방대해서 솔직히 외워서 쓸 수 있을지 모르겠다.
제대로 배우지 않아서 지피티로 계속 찾아보며 구현하곤 있는데, dart의 문법이 안익숙해서 그런지 참 어렵네..
import 'package:defeefront/routes.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "defee{ }",
theme: ThemeData(
fontFamily: "Pretendard",
),
routes: routes,
);
}
}
main.dart는 리액트와 비슷하게 깔끔하게 작성했다. 기본적인 플러터 구조로 작성했으며, theme은 앱의 전체적인 색상, 폰트 등을 세팅하는 기능인데 해당 기능으로 pretendard라는 폰트를 적용시켰다. fontFamily의 설정은 pubspec.yaml 파일에서 작성되는데, 일종의 eslint 설정 느낌인 것 같다.
name: defeefront
description: "A new Flutter project."
publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ^3.5.4
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^4.0.0
flutter:
uses-material-design: true
fonts:
- family: Pretendard
fonts:
- asset: assets/fonts/pretendard/Pretendard-Black.ttf
weight: 100
- asset: assets/fonts/pretendard/Pretendard-Bold.ttf
weight: 700
flutter라는 세팅에 fonts를 추가해주며 fontFamily로 가져올 이름을 family로 설정하고 assets 폴더에 있는 폰트 경로를 지정해준다.
나중에 다른 폰트도 같이 사용하고 싶으면 똑같이 추가해서 사용할 곳에 작성해주면 끝
appbar와 bottombar는 scaffold 형식의 위젯에서 사용된다. 기본적인 커스텀이 되어있는 위젯인데, 해당 위젯을 가져와서 기획대로 수정해주면 된다.
import 'package:defeefront/widgets/footer.dart';
import 'package:defeefront/widgets/header.dart';
import 'package:flutter/material.dart';
class Headline extends StatelessWidget {
const Headline({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: Header(),
body: Center(
child: Text("테스트"),
),
bottomNavigationBar: Footer(),
);
}
}
이번주 맡은 페이지인 헤드라인 페이지에 Scaffold 형식으로 구조를 짜주고 해당 코드에 appbar와 bottomNavigationBar를 추가했다. 해당 코드는 여러 페이지에서 사용되기 때문에 공용 위젯으로 만들어서 import 해왔다.
import 'package:flutter/material.dart';
class Header extends StatelessWidget implements PreferredSizeWidget {
const Header({super.key});
@override
Widget build(BuildContext context) {
return AppBar(
centerTitle: true,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios_new,
color: Colors.indigo[900],
size: 24,
),
onPressed: () {
Navigator.pop(context);
},
),
title: Text(
"헤드라인",
style: TextStyle(
color: Colors.indigo[900],
fontWeight: FontWeight.bold,
fontSize: 24),
),
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}
appbar는 현재는 해당 페이지의 설명만 들어가있기 때문에 Text만 넣어줬지만, appbar에서 제공하는 뒤로가기 아이콘이 기획과 맞지 않아 찾아본 결과 커스텀이 가능하다고 해서 지피티의 힘을 빌렸다.
지정된 위젯을 따로 파일로 빼서 사용하려면 implements를 이용해 해당 위젯이 빠졌다는걸 인식해줘야 된다고 한다.
leading은 왼쪽에 위치한 위젯을 지정하는 속성인데 leading이 정의되지 않으면 기본 옵션으로 뒤로가기 버튼이 표시된다.
기존 옵션을 커스텀하기 위해 leading을 정의해주고 onClick과 동일한 onPressed로 뒤로가기 기능을 추가해줬다. 플러터는 페이지를 스택형식으로 처리하기 때문에 pop을 사용해 최근 스택을 없애는 방식으로 구현한다. 리액트에서도 navigate(-1)과 동일한 기능이다.
import 'package:flutter/material.dart';
class Footer extends StatelessWidget {
const Footer({super.key});
@override
Widget build(BuildContext context) {
final String? currentRoute = ModalRoute.of(context)?.settings.name;
return Container(
color: Colors.grey[850],
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildNavItem(
icon: Icons.search,
label: "검색",
context: context,
route: '/search',
isSelected: currentRoute == '/search',
),
_buildNavItem(
icon: Icons.language,
label: "헤드라인",
context: context,
route: '/',
isSelected: currentRoute == '/',
),
_buildNavItem(
icon: Icons.thumb_up_outlined,
label: "추천",
context: context,
route: '/recommend',
isSelected: currentRoute == '/recommend',
),
_buildNavItem(
icon: Icons.star_border,
label: "My",
context: context,
route: '/my',
isSelected: currentRoute == '/my',
),
],
),
);
}
Widget _buildNavItem({
required IconData icon,
required String label,
required BuildContext context,
required String route,
required bool isSelected,
}) {
final ValueNotifier<bool> isHovered = ValueNotifier(false);
return MouseRegion(
onEnter: (_) => isHovered.value = true,
onExit: (_) => isHovered.value = false,
child: ValueListenableBuilder<bool>(
valueListenable: isHovered,
builder: (context, hover, child) {
final bool isActive = isSelected || hover;
return GestureDetector(
onTap: () => Navigator.pushNamed(context, route),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 25),
decoration: isActive
? BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.white38,
)
: null,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
size: 20,
color: isActive ? Colors.white : Colors.white70,
),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 14,
color: isActive ? Colors.white : Colors.white70,
),
),
],
),
),
);
},
),
);
}
}
bottombar의 경우 상태관리나 상수값을 사용하는 문법을 몰라서 지피티를 이용했으며, bar에는 4개의 아이콘이 있고 버튼을 클릭하면 해당 경로 페이지를 이동한다.
현재 경로를 파악해서 isSelected를 통해 경로가 맞는 버튼의 ui를 활성화시키며, hover를 통해 버튼에 마우스르 가져가면 ui를 변화시킨다.
지피티가 한 파일에 위젯을 나눠 작성해줬지만, 이 후 유지보수나 가독성면에서 한 파일엔 한 위젯으로 관리하는게 낫다고 하여, 따로 분리할 생각이다.
bottombar를 작성하면서 제대로 공부하지 않으면 의미가 없겠구나 생각했다. 현재 플러터 공부를 유튜브로 찾아보거나 지피티로 하고 있지만, 전체적인 학습이 아니라 내가 필요한 기능만 찾아지기 때문에 좀 아쉬운 것 같다. 그래서 인프런에서 플러터 강의를 하나 구매하려고 하는데, 맘에 든 강의 하나가 있었다.
설명에 목차가 자세히 적혀있었는데 코드를 외워서 쓰지말고 해당 기능이 어떻게 작동되는지, 로직이 어떻게 진행되는지 자세히 이해하고 작성하라는 문구가 있던걸 보고, 구입해서 강의를 학습하면 도움이 많이 될 거 같아 조만간 사려고 한다.
'개발 TIL' 카테고리의 다른 글
flutter 헤드라인 페이지 UI 구현 (1) | 2024.11.23 |
---|---|
flutter 앱 에뮬레이터 설치 (0) | 2024.11.22 |
flutter 프로젝트 시작 (0) | 2024.11.20 |
24.11.03 소프트웨어 품질 관리 (4) | 2024.11.03 |
24.10.31 flutter navigator (3) | 2024.10.31 |