상세 컨텐츠

본문 제목

[Donfo] BottomNavigationBar, Firebase 환경설정, UI 개발

projects/Flutter

by 서울의볼 2024. 5. 14. 02:39

본문

- 테마 세팅은 기존에 제공 받았던 파일을 활용함(노가다가 많아서) --> 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 파일을 추출했을 때 사용되는 키 해시라고 함.

(참고: https://velog.io/@iamjm29/Android-%ED%82%A4%ED%95%B4%EC%8B%9Ckey-hash-%EC%B6%94%EC%B6%9C%ED%95%98%EA%B8%B0#:~:text=debug%20key%20hash%EB%8A%94%20%EC%A3%BC%EB%A1%9C,%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94%20%ED%82%A4%ED%95%B4%EC%8B%9C%EC%9D%B4%EB%8B%A4.)

- 무튼 ~/.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들 찍어주고 라인 정리하다 보면 풀림.

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

[Donfo] 1차 폭발  (1) 2024.05.15
[Donfo] 환경설정 및 구조 잡기  (0) 2024.05.13
[React Native] Expo CLI / npx 명령어  (0) 2024.04.11

관련글 더보기