- 테마 세팅은 기존에 제공 받았던 파일을 활용함(노가다가 많아서) --> core에 넣어서 main.dart에 적용해줌.
- main screen에서 app과 navigation bar 작업 했음. 대부분 UI 적인 요소.
- bottom directory cubit을 main directory에 생성, main screen 파일에서 bottom navigation cubit 등록.
enum BottomNav{home, category, search, user}
class BottomNavCubit extends Cubit<BottomNav> {
BottomNavCubit() : super(BottomNav.home);
void changeIndex(int index) => emit(BottomNav.values[index]);
}
- BlocBuilder로 감싸서 페이지네이션 구현함:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../core/theme/constant/app_icons.dart';
import '../pages/category/category_page.dart';
import '../pages/home/home_page.dart';
import '../pages/search/search_page.dart';
import '../pages/user/user_page.dart';
import 'cubit/bottom_nav_cubit.dart';
class MainScreen extends StatelessWidget {
const MainScreen({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => BottomNavCubit(),
child: const MainScreenView(),
);
}
}
class MainScreenView extends StatelessWidget {
const MainScreenView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(44),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
color: Theme.of(context).colorScheme.primary,
child: AppBar(
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset(AppIcons.mainLogo),
),
title: Text(
'Home',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
backgroundColor: Colors.transparent,
centerTitle: true,
leadingWidth: 86,
),
),
),
body: BlocBuilder<BottomNavCubit, BottomNav>(
builder: (_, state) {
switch (state) {
case BottomNav.home:
return const HomePage();
case BottomNav.category:
return const CategoryPage();
case BottomNav.search:
return const SearchPage();
case BottomNav.user:
return const UserPage();
}
},
),
bottomNavigationBar: BlocBuilder<BottomNavCubit, BottomNav>(
builder: (_, state) {
return BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: SvgPicture.asset(AppIcons.navHome),
label: 'home',
activeIcon: SvgPicture.asset(AppIcons.navHomeOn),
),
BottomNavigationBarItem(
icon: SvgPicture.asset(AppIcons.navCategory),
label: 'category',
activeIcon: SvgPicture.asset(AppIcons.navCategoryOn),
),
BottomNavigationBarItem(
icon: SvgPicture.asset(AppIcons.navSearch),
label: 'search',
activeIcon: SvgPicture.asset(AppIcons.navSearchOn),
),
BottomNavigationBarItem(
icon: SvgPicture.asset(AppIcons.navUser),
label: 'user',
activeIcon: SvgPicture.asset(AppIcons.navUserOn),
),
],
onTap: (index) => context.read<BottomNavCubit>().changeIndex(index),
currentIndex: state.index,
type: BottomNavigationBarType.fixed,
showSelectedLabels: false,
showUnselectedLabels: false,
);
},
),
);
}
}
- 이후 중복되는 코드는 extension을 통해 별도의 리팩토링을 했음.
- 예컨대, bottom_nav_cubit파일에 아래와 같이 작성하여 main파일을 개선함:
extension BottomNavX on BottomNav {
String get icon {
switch(this) {
case BottomNav.home:
return AppIcons.navHome;
case BottomNav.category:
return AppIcons.navCategory;
case BottomNav.search:
return AppIcons.navSearch;
case BottomNav.user:
return AppIcons.navUser;
}
}
String get activeIcon {
switch(this) {
case BottomNav.home:
return AppIcons.navHomeOn;
case BottomNav.category:
return AppIcons.navCategoryOn;
case BottomNav.search:
return AppIcons.navSearchOn;
case BottomNav.user:
return AppIcons.navUserOn;
}
}
String get toName {
switch(this) {
case BottomNav.home:
return 'home';
case BottomNav.category:
return 'category';
case BottomNav.search:
return 'search';
case BottomNav.user:
return 'user';
}
}
}
- main 파일:
bottomNavigationBar: BlocBuilder<BottomNavCubit, BottomNav>(
builder: (_, state) {
return BottomNavigationBar(
items: List.generate(
BottomNav.values.length,
(index) => BottomNavigationBarItem(
icon: SvgPicture.asset(BottomNav.values[index].icon),
label: BottomNav.values[index].toName,
activeIcon:
SvgPicture.asset(BottomNav.values[index].activeIcon),
),
),
onTap: (index) => context.read<BottomNavCubit>().changeIndex(index),
currentIndex: state.index,
type: BottomNavigationBarType.fixed,
showSelectedLabels: false,
showUnselectedLabels: false,
);
},
),
- presentation/main/component/top_app_bar를 만들어 위젯 파일들을 모음.
- 해당 위젯 파일내 DefaultAppBar, home, top app bar 만들어서 main screen에 대체하여 적용하고자 하니 요런 에러가 나옴: Error: The argument type 'DefaultAppBar' can't be assigned to the parameter type 'PreferredSizeWidget?'.
이러면 해당 위젯 파일에서 implements해주고 하면 됨.
- bottom navigation 구현시 보통 indexed stack을 활용하지만 이번 프로젝트에선 cubit과 bloc를 활용함. (향후 더 간단하게 리팩토링要...!!!!!!!!!)
- Firebase 연동 시작함, firebase local emulator도 정상적으로 연결하였음.
- 일단 무지성으로 firebase core, cloud storage, firebase auth, firebase storage 연동하였음. (위의 emulator는 storage랑 firestore 외 아직 추가하지 않았을 때의 캡처임)
- 카카오 로그인 작업을 시작함.
- 카카오계정(이메일)의 경우 biz app으로 등록을 해야 필수 동의 받을 수 있다고 함.
- iOS 플랫폼 등록시 xcode에서 bundle ID 별도로 확인해야함. (걍 Runner 실행해서 general tab에서 바로 확인 가능)
- Android 플랫폼은 키 해시도 등록해야 하는데, 이 키 해시는 안드로이드 앱 배포시 보안을 위해 서명키를 해시한 값임. 카카오의 경우 api호출시 인증된 사용자인지 확인하기 위한 용도로 요구한다함. (6.9.1)
- 키 해시는 디버그용, 릴리즈용, 구글 플레이용으로 총 3가지 있다고 하는데, debug를 가지고 우선은 시작함: 주로 앱 개발을 할 때 사용되고, release key hash는 앱 개발을 완료 후 apk 파일을 추출했을 때 사용되는 키 해시라고 함.
- 무튼 ~/.android에서 아래 명령어 터미널에 치니까 생성됐음.
keytool -exportcert -alias androiddebugkey \
-keystore ~/.android/debug.keystore \ -storepass android \
-keypass android | openssl sha1 -binary | openssl base64
참고로 확인하고자 하면 이거 치면됨:
keytool -v -list -alias androiddebugkey -keystore debug.keystore
-storepass android -keypass android
- 생성된 디버그용 키해시 등록하면 카카오에 클라이언트 정보 등록은 완료된 것.
- 이후 kakao developers 문서에서 AndroidManifest.xml(android/app/src/main) 설정해주고, 네이티브앱 키 입력해주면 됨.
- ios는 xcode에서 설정해주는데, 추가로 앱 실행 허용 목록도 추가해줘야 함.
- 로그인 화면 라우팅 작업 및 구현하였음. 우선적으로 빈껍데기임(카카오는 천천히 적용해볼 예정... 일단 환경설정은 해두었으니 나중에 캐치업하자...):
- 아... 대대적으로 바꾸기로 마음 먹음...! 시간낭비 5졌음 ㅠㅠ
기 빨려서 다음 글에 계속...
- 현재까지의 dependencies:
version: 1.0.0+1
environment:
sdk: '>=3.3.3 <4.0.0'
dependencies:
dart_code_metrics: ^5.7.6
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
flutter_riverpod: ^2.5.1
google_sign_in: ^6.2.1
image_picker: ^1.1.1
intl: ^0.19.0
freezed_annotation: ^2.4.1
json_annotation: ^4.9.0
flutter_image_compress: ^2.2.0
file_picker: ^8.0.3
go_router: ^14.1.0
flutter_svg: ^2.0.9
flutter_bloc: ^8.1.5
firebase_core: ^2.31.0
firebase_auth: ^4.19.5
firebase_storage: ^11.7.5
cloud_firestore: ^4.17.3
firebase_database: ^10.5.5
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.9
freezed: ^2.5.2
json_serializable: ^6.8.0
flutter:
uses-material-design: true
assets:
- assets/
- assets/svg/
- assets/icon/
- assets/image/
- tip) 노란색 줄은 source code 정리가 필요한 경우로, 보통 예를 들어 scaffold 생성 당시 작성된 순서대로 property를 정리하라는 의미임. alt enter하고 argument 정리해주고 trailing comma들 찍어주고 라인 정리하다 보면 풀림.
[Donfo] 1차 폭발 (1) | 2024.05.15 |
---|---|
[Donfo] 환경설정 및 구조 잡기 (0) | 2024.05.13 |
[React Native] Expo CLI / npx 명령어 (0) | 2024.04.11 |