회고

20220915-16 TIL #Tabbar #scroll #차트만들기 #provider

paran21 2022. 9. 19. 00:53

새로운 페이지 작업을 하게 되었는데, tabbar, scroll, 차트 기능이 포함되어 있다.

 

Tabbar + scroll

Tab을 이용해서, Tab마다 다른 화면을 그릴 수 있다.

https://docs.flutter.dev/cookbook/design/tabs

 

Work with tabs

How to implement tabs in a layout.

docs.flutter.dev

 

Tabbar는 Appbar의 bottom으로 구현할 수도 있고, Appbar가 아니라 Scaffold의 body 안에도 구현할 수 있다.

 

처음에는 Appbar에 bottom에 DefaultTabController를 이용해서 구현하고, Scaffold body에 TabBarView를 구현하였다.

그런데 TabBar와 TabBarView를 모두 스크롤 영역으로 만들고 싶어서 모두 Scaffold body에 구현하였다.

이 경우에는 TabController를 넣어주어야 원하는데로 스크린이 보인다.

또, TabBarView의 범위를 알 수 없기 때문에 Expanded를 사용해서 TabBar를 제외한 나머지 모든 영역을 차지하도록 하였다.

 

그런데 이렇게 구현을 하고 나니 TabBar와 TabBarView를 SingleChildScrollView로 감쌀 수가 없었다.

방법이 있을 것 같아서 계속 구글링하면서 시도해봤는데 적절한 방법을 찾지 못했다.

또, TabBar부분은 스크롤이 안되게 해서 스크롤을 내린 상태에서도 원한다면 바로 다른 탭으로 갈 수 있게 하는게 더 좋다고 생각했다.

그래서 우선은 TabBarView 부분만 스크롤이 되도록 하였다.

 

Chart

형태가 복잡하지는 않은, 간단한 bar chart 2종류와 pie chart를 만들어야 했다.

 

bar chart는 따로 패키지를 사용하지 않아도 만들 수 있을 것 같아서, 우선 pie chart만 패키지를 사용해 만들었다.

 

먼저 pub.dev(https://pub.dev/)에서 chart로 검색해서 사람들이 많이 사용하고, 예시가 내가 사용하려는 목적에 맞다고 생각되는 chart 패키지를 찾았다.

 

이 패키지인데, 깃허브에 어떤 식으로 사용하면 되는지 설명도 자세히 되어 있다.

문서를 참고해서 pie chart는 금방 구현할 수 있었다.

https://pub.dev/packages/fl_chart

 

fl_chart | Flutter Package

A powerful Flutter chart library, currently supporting Line Chart, Bar Chart and Pie Chart.

pub.dev

 

내가 원하는 pie chart는 차트 가운데에 총합이 같이 표시되는 형태인데, 이건 Stack을 사용해서 별도로 구현했다.

차트가 차지하는 크기가 포함된 위젯에 Stack을 사용해서 가운데에 Text 위젯을 넣어서 원하는 위치로 조정하였다.

 

bar chart는 두 가지를 만들어야 하는데, 하나는 항목별로 값을 가로 막대로 나타내는 차트이고, 다른 하나는 하나의 막대에 두 항목이 비율만큼 차지하도록 만드는 것이다(이건 아직 만드는 중!)

 

형태는 Container의 boxDecoration을 사용해서 금방 잡았는데, 생각보다 원하는 비율만큼 차지하게 하는 부분이 어려웠다.

우선 Bar의 전체 크기를 몇으로 하냐는 문제가 있었는데, 이건 일단 값들 중 최대값으로 잡았다.

그리고 Row 안에 색깔이 있는 Container와(실질적으로 chart가 되는 부분), Colors.transparent로 투명한 부분이 나머지를 차지하게 만들었다.

 

두 개의 비율을 value와 maxValue - value로 잡아야 했는데 이부분 구현이 생각만큼 원하는대로 바로 나오지 않았다.

크기를 지정해놓고 만드는게 아니라 이 Row 자체도 Expanded로 구현을 해놓아서 더 어려웠던 것 같다.

 

Expanded 안에 flex를 백분율로 환산해서 value만큼만 bar가 그려지도록 하였다.

Expanded의 flex는 int이기 때문에 백분율로 환산하지 않으면 toInt()를 할 때 소수점 아래는 모두 버려져서 원하는 비율이 나오지 않는다.

 

예를 들어, value가 3이고 maxValue가 6이면 50%를 차지해야한다.

3 / 6은 0.5이므로 toInt()를 하면 무조건 0이 된다.

따라서 100을 곱해서 50으로 만들어줘야 한다.

 

Expanded가 적용된 같은 depth의 flex들의 총 합이 전체 범위가 되고, 그 안에서 각각 flex만큼의 범위를 차지하게 된다.

Expanded(
  child: Row(
  	children: [
      Expanded(
        flex: (value / maxValue * 100).toInt(),
        //bar 부분
        child: Container(
        ...
        ),
      ),
      Expanded(
        flex: ((1 - value / maxValue) * 100).toInt(),
        //bar외 나머지 부분
        child: Container(
          color: Colors.transparent,
        ...
        ),
      ),
    ],
  ),
);

 

값이 0일 경우에는 디자인에 맞춰서 구현한 별도의 메서드가 실행되도록 하였다.

 

다 구현하고 나니 디자인과 다른 부분이 있었다.

value가 각 bar의 맨 끝에 표시되도록 구현하였는데(즉 위의 Row의 childeren안에 구현), 디자인을 다시 보니 bar 바로 오른쪽에 표시가 되는 형태였다.

투명으로 만들어 놓은 Container를 Stack으로 감싸서 그 안에 Text위젯을 넣어서 처리하였다.

 

처음에 생각한 것보다 시간이 많이 걸렸지만 Expanded와 관련해서 공부가 많이 되었다.

 

provider

처음에 ui를 그릴 때는 우선 하드코딩으로 처리했는데, 차트를 그리면서 provider로 데이터를 바꿔주었다.

우선 서버와 연동하기 전이라서 dummy data를 하나 만들었다.

 

tabview에 따라 다른 데이터로 ui를 그려야 하고, 해당 tabview 내에서도 조건에 따라 다른 데이터를 받아와야해서 tabview별로 다른 provider를 만들었다.

데이터 모델을 완전히 같아서 어떻게 해야하는지 고민이었는데, 다른 api를 사용하고 또 tabview내에서도 조회 조건을 변경하면 다시 데이터를 받아와야 해서 다른 provider로 하는게 맞는 것 같다.

provider를 사용하면서 아직 provider의 정말 기본 개념밖에 모르는 것 같아서 다음주에는 provider를 더 공부해야겠다.