본문 바로가기
Flutter

Flutter(플러터) 카카오뱅크 클론 앱 만들기

by 퍼즐잎 2020. 10. 12.

개요

Flutter를 공부하면서 이번에는 뭘 해볼까 하다가 자주 사용하던 카카오뱅크 앱이 생각이 났습니다.

깔끔하게 잘 만들어진 앱이라고 평소에 생각했었는데, 이번 기회에 참고하면서 UI Clone 앱을 만들어 보기로 했습니다.

 

소스 코드와 프로젝트 시작부터 끝까지 코딩하는 영상은 하단에 링크를 참고하시면 됩니다!

(카카오뱅크 앱에서 언어를 영어로 변경할 수 있는 설정을 찾지 못해서 임의로 번역기 돌려서 만들어 봤습니다.)

 

 

구현

HomePage

BottomNavigation을 포함하고 있고 다른 페이지에 접근할 수 있는 기본이 되는 페이지

flutter_bloc 패키지를 이용하여 HomeCubit을 만들었습니다.

Cubit은 간단한 상태 관리를 위해 사용할 수 있으며 Bloc보다 간단하게 구현할 수 있습니다.

 

 

 

BottomNavigationBar를 사용하는데 NavigationBar를 클릭했을 때 페이지를 이동하기 위해서 사용합니다.

HomePage에서 context로 bloc를 참조해서 쉽게 State 변경을 할 수 있습니다.

State가 변경되는 경우 BlocBuilder에서 변경 된 State 값이 전달됩니다.

 

 

 

MainPage

계좌 정보를 확인할 수 있는 페이지

CustomScrollView와 SliverAppBar, SliverList를 사용하고 있습니다.

다양한 목적으로 사용할 수 있는데, 여기서는 SliverList 아이템을 스크롤해도 SliverAppBar를 상단에 고정하는 목적으로 사용하고 있습니다.

 

CustomScrollView에서 key 값으로 PageStorageKey를 사용하고 있습니다.

다른 화면으로 이동했다가 돌아왔을 때 현재 화면의 스크롤 위치를 유지시켜 줍니다.

(PageStorageKey를 제거하면 다른 화면으로 돌아갔다 왔을 때 스크롤이 최상단으로 초기화가 됩니다.)

 

 

 

CatalogPage

판매하는 은행 상품 목록 페이지

 

 

아래 두 기능을 구현하느라 가장 시간이 오래 걸린 화면입니다.

  • TabBar를 클릭했을 때 해당 탭에 위치한 스크롤 아이템으로 이동하는 기능
  • 화면을 스크롤했을 때 현재 위치한 아이템 위치에 맞춰서 TabBar Indicator가 이동하는 기능

SliverAppbar를 두 번 사용했고 고정할 탭 영역에만 pinnedtrue로 설정해서 사용했습니다.

 

GlobalKey를 이용하여 해당 아이템의 위치로 이동할 수 있습니다.

그러나 SliverListDelegate 특성상 화면에 보이지 않은 영역에 있는 아이템의 경우 화면에 보이기 전까지는 실제 렌더링이 된 것이 아니기 때문에 GlobalKeycurrentContext로 참조하는 경우 에러가 발생하게 됩니다.

(첫 번째 아이템이 렌더링 된 상태에서 화면에 보이지 않는 네 번째 아이템의 GlobalKey를 참조하는 경우 에러 발생)

 

_onTapToScroll 함수를 보면 다음과 같이 GlobalKey 들을 배열로 만들어서 사용하고 있습니다.

스크롤의 이동 방향에 따라서 GlobalKey를 순서대로 참조하고 있습니다.

 

GlobalKey를 순서대로 참조하게 되면 하나의 아이템씩 이동하게 되면서 인접한 아이템의 GlobalKey는 참조 가능한 상태가 됩니다.

void _onTapToScroll(int index) async {
    var keys = [adKey, accountKey, loanKey, serviceKey, allianceKey];
    var previousIndex = _tabController.previousIndex;

    isTabToScroll = true;
    if (index == 0) {
      await _scrollController.animateTo(
        0,
        duration: const Duration(milliseconds: 300),
        curve: Curves.linear,
      );
    } else {
            // 스크롤의 이동 방향에 따라서 순서대로 이동
      if (previousIndex < index) {
        for (var i = previousIndex; i <= index; i++) {
          await _scrollController.position.ensureVisible(
            keys[i].currentContext.findRenderObject(),
            duration: const Duration(milliseconds: 100),
            curve: Curves.linear,
          );
        }
      } else {
        for (var i = previousIndex; i >= index; i--) {
          await _scrollController.position.ensureVisible(
            keys[i].currentContext.findRenderObject(),
            duration: const Duration(milliseconds: 100),
            curve: Curves.linear,
          );
        }
      }
    }
    isTabToScroll = false;
  }
}

화면을 스크롤 했을 때 TabBarIndicator의 위치를 맞춰 주기 위해서 ScrollController에 이벤트를 등록해서 사용합니다.

 

GlobalKey를 이용하여 각 아이템의 높이를 얻어오고, 현재 스크롤의 위치와 높이 값을 이용하여 TabBarIndicator 위치를 변경합니다.

void _onScroll() {
    if (isTabToScroll) return;

    if (adKey.currentContext != null) {
      adHeight = adKey.currentContext.size.height;
    }
    if (accountKey.currentContext != null) {
      accountHeight = accountKey.currentContext.size.height;
    }
    if (loanKey.currentContext != null) {
      loanHeight = loanKey.currentContext.size.height;
    }
    if (serviceKey.currentContext != null) {
      serviceHeight = serviceKey.currentContext.size.height;
    }
    if (allianceKey.currentContext != null) {
      allianceHeight = allianceKey.currentContext.size.height;
    }

    if (_scrollController.offset <= adHeight) {
      _tabController.animateTo(0, duration: const Duration(milliseconds: 0), curve: Curves.linear);
    } else if (_scrollController.offset > adHeight &&
        _scrollController.offset <= adHeight + accountHeight) {
      _tabController.animateTo (1, duration: const Duration(milliseconds: 0), curve: Curves.linear);
    } else if (_scrollController.offset > adHeight + accountHeight &&
        _scrollController.offset <= adHeight + accountHeight + loanHeight) {
      _tabController.animateTo(2, duration: const Duration(milliseconds: 0), curve: Curves.linear);
    } else if (_scrollController.offset >
        adHeight + accountHeight + serviceHeight &&
        _scrollController.offset <=
            adHeight + accountHeight + loanHeight + serviceHeight) {

      if (_scrollController.offset >=
          _scrollController.position.maxScrollExtent) {
        _tabController.animateTo(4, duration: const Duration(milliseconds: 0), curve: Curves.linear);
      } else {
        _tabController.animateTo(3, duration: const Duration(milliseconds: 0), curve: Curves.linear);
      }
    }
  }

 

 

 

 

Notification Page

알림 목록 페이지

More Page

추가 메뉴 페이지

나머지 두 페이지는 단순 UI로 자세한 내용은 아래 링크에서 찾을 수 있습니다.

참고

 

 

Youtube

 

 

PuzzleLeaf/flutter_kakao_bank_ui_clone

flutter로 만든 카카오 뱅크 앱 클론 프로젝트 입니다. Contribute to PuzzleLeaf/flutter_kakao_bank_ui_clone development by creating an account on GitHub.

github.com

 

 

댓글