CircularProgressIndicator class
애플리케이션이 실행 중임을 나타내기 위해 회전하는 Material Design 원형 진행률 표시기입니다.
공식 문서 코드
화면에서 어떠한 동작이 로딩 중이라는 것을 표시하기 위해선 어떤 방법이 가장 좋을까? 단연콘데 로더 동작을 만들어 놓으면 좋을 것이다. 근데 이제 여기에서 일반 사용자에게 가장 익숙한 로더는 무엇인가 하면 바로 원형 프로그래스 인디케이터일 것이다. 근데 또 이런 인디케이터를 직접 만들려고 하면 골치 아플 것인데 이것을 아주 간단하게 바로 사용할 수 있도록 만든 위젯이 바로 CircularProgressIndicator이다.
이 CircularProgressIndicator 위젯은 원형으로 로딩 상태를 나타내는 데 사용되는 인디케이터이다. 보통 비동기 작업이 진행 중임을 사용자에게 시각적으로 알릴 때 사용된다. 바로 알아보자.
하위 속성
| 속성명 | 타입 | 기본값 | 설명 |
| value | double? | null | 0.0에서 1.0 사이의 진행률 값 |
| backgroundColor | Color? | null | 트랙(배경)의 색상 |
| color | Color? | null | 진행 표시 선의 색상 |
| valueColor | Animation<Color?>? | null | 애니메이션 가능한 색상. `value`가 null일 때 사용 |
| strokeWidth | double? | 4.0 | 원을 그릴 때 사용하는 선의 두께 |
| strokeAlign | double? | 상황에 따라 다름 | 선의 위치: 내부(-1.0), 중심(0.0), 외부(1.0) |
| strokeCap | StrokeCap? | null | 선 끝의 스타일. round, butt, square 중 선택 가능 |
| constraints | BoxConstraints? | null | 최소/최대 크기 지정 |
| trackGap | double? | 4 | 원형 트랙과 진행 표시 사이의 간격 |
| year2023 | bool? | true | Material 3 스타일 사용 여부 (deprecated) |
| padding | EdgeInsetsGeometry? | Material 3: zero | 트랙과 외곽 사이의 여백 |
| semanticsLabel | String? | null | 접근성을 위한 레이블 텍스트 |
| semanticsValue | String? | null | 접근성을 위한 값 설명 |
사용하는 방법은 정말 간단한데 그냥 CircularProgressIndicator 위젯을 사용해 주면 끝이다.
Center(
child: CircularProgressIndicator(),
)
이렇게 적고 실행을 하면 무한히 회전하고 있는 인디케이터를 볼 수 있을 것이다.

어째서 무한히 회전하고 있냐고 한다면 그 비밀은 value에 있다.
value의 기본값은 null이다. 즉 원래 value는 0.0에서 1.0 사이의 double 값을 넣어줘야 정적 진행 상태 표시가 되게 되는데 그 어떤 값도 아닌 null이 기본으로 들어가 있음으로 완료가 되지 않고 계속 회전하는 것이다.
그러니 진행률을 표시하고 싶다면 value를 사용하면 될 것이다.
CircularProgressIndicator(
value: 0.7,
)
이렇게 value에 원하는 값을 넣으면 그 값에 따라 인디케이터가 딱 그 자리에 멈춰버린다.

당연하겠지만 이렇게는 사용 안 한다.. 하지만 이렇게 값을 지정해 줘서 사용하면 로더에 사용하는 것이 아니라 다른 방면에 사용할 수도 있을 것이다. 여러 가지 디자인이 가능하니 말이다.
예를 들어 영화 사이트가 있다고 하면 영화의 관객 만족도 같은 것을 이 위젯으로 표시할 수도 있을 것이다.
먼저 색상을 바꿔보자. 만약 API에서 관객의 만족도가 70% 라는 값을 얻고 그것을 value로 치환한다고 가정해 본다면 색상은 초록이 좋을 것 같다. 70% 정도면 나쁘지 않은 편 아닌가?
CircularProgressIndicator(
value: 0.7,
color: Colors.green,
)

여기다가 조금 더 그래프 느낌이 나게 선두께를 키우고 현재 비어있는 인디케이터 부분을 다른 색상으로 채워주면 좋을 것이다.
CircularProgressIndicator(
value: 0.7,
color: Colors.green,
strokeWidth: 8,
strokeCap: StrokeCap.round, // 선 끝의 스타일
backgroundColor: Colors.grey[300], // 2차 색상 (비어있는 부분)
)

이제 안에다가 Stack으로 뭔가 숫자 같은 거 넣어주면 좀 이상한 방향(?)으로 이 CircularProgressIndicator 위젯을 사용해 볼 수 있게 된다.

자 그렇다면 이제 진짜 용도인 로더에 사용해 보자.
먼저 대표적으로 사용되는 곳은 Image.network 속 일 것이다.
여기서 CircularProgressIndicator를 로더로 사용하려면 Image.network의 파라미터인 loadingBuilder를 사용해 주면 된다.
Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg',
loadingBuilder: (context, child, loadingProgress) {
return loadingProgress == null ? child : CircularProgressIndicator();
},
)
이렇게 작성해 주면 엄청 쉽게 로더 기능을 완성할 수 있는데 그래도 가볍게 설명을 하고 넘어가겠다.
일단 loadingBuilder는 이미지가 로딩중일 때와 로딩 완료 후를 구분해 UI를 보여주는 빌더 함수이다.
여기서 child는 이미지 로딩이 완료되면 보여줄 이미지 위젯이고 loadingProgress는 로딩 중이면 non-null, 완료되면 null 상태를 가지게 돼서 로딩이 끝나게 된다.
즉 로딩 중일 때는 CircularProgressIndicator를 표시하게 되고 로딩이 완료된다면 child(이미지)를 표시하게 되는 것이다.

근데 이것 또한 조금은 불편한 감이 없지 않아 있다. 무엇이 불편한가 하면 유저에게 이 사진이 얼마나 로딩되었는지를 알릴 수 없다는 점 말이다. 그렇다면 이 이미지가 로딩되는 것에 맞춰 인디케이터가 채워진다면 좋지 않겠는가? 그것 또한 쉽게 만들 수 있다.
Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg',
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
double progress = loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!;
return CircularProgressIndicator(value: progress);
},
)
이렇게 하면 Image.network에서 이미지가 로드되는 동안 진행 상태에 따라 로딩 인디케이터를 보여주는 loadingBuilder를 완성시킬 수 있다.
자 간단하게 살펴보자면
if (loadingProgress == null) return child;
먼저 로딩이 끝났으면 이미지를 보여주게 되어 있다.
이제 그다음이 문제일 것인데
double progress = loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!;
이것은 현재까지 다운로드한 바이트 수를 전체 이미지 크기로 나눠 0.0 ~ 1.0 사이의 로딩 비율로 계산한 것이다.
그리고 이제 이 값을 CircularProgressIndicator의 value로 사용해 주면 완성이 되는 것이다.
이렇게 CircularProgressIndicator 위젯의 사용법을 간단히 알아보았다. 보통은 후자보다는 전자의 로더 코드를 많이 사용하곤 한다지만 그래도 후자의 코드도 알아두면 분명 좋을 것이다. 도움이 되었길 바라며 마치겠다.