본문 바로가기
flutter/Widget of the Week

Flutter[플러터] / LinearProgressIndicator 를 사용하여 다운로드 중임을 나타내는 화면 보여주기 (download bar, 리니어, 선형, 프로그래스 인디케이터, 바, 진행률 표시) LinearProgressIndicator (Flutter Widget of the Week)

by ch5c 2025. 7. 1.
반응형

LinearProgressIndicator class

Material Design의 선형 진행률 표시기, 진행률 막대라고도 합니다.

https://youtu.be/O-rhXZLtpv0

공식 문서 코드

 

 


화면에서 어떠한 콘텐츠가 다운로드 중이라는 것을 표시하기 위해서는 어떤 방식이 가장 좋을까? 서큘러 인디케이터? 아니면 대놓고 다운중이라고 글자로 표시하기? 아마도 가장 좋은 방법으로는 선형의 인디케이터를 사용하여 그 인디케이터의 차지 공간에 따라 다운로드가 어느 정도 되었는지 표시하는 방법일 것이다. 이러한 선형의 인디케이터를 쉽게 사용할 수 있게 만들어주는 위젯이 있는데 바로 LinearProgressIndicator다.

LinearProgressIndicator는 선형 진행 표시줄(linear progress bar)을 구현할 때 사용하는 Material Design 위젯이다. 사용자에게 어떤 작업이 진행 중임을 선형 형태로 시각적으로 보여주는 데 사용된다. 바로 한번 알아보자.

하위 속성
속성명 타입 기본값 설명
key Key? null 위젯의 고유 식별자
value double? null 0.0에서 1.0 사이의 진행값, null이면 애니메이션 진행 표시
backgroundColor Color? null 트랙의 배경색, 지정하지 않으면 테마 기반 색상 사용
color Color? null 진행 색상, 지정하면 valueColor보다 우선됨
valueColor Animation<Color?>? null 애니메이션 가능한 진행 색상
semanticsLabel String? null 접근성을 위한 라벨 텍스트
semanticsValue String? null 접근성을 위한 진행 값 텍스트
minHeight double? null 진행 표시줄의 최소 높이
borderRadius BorderRadiusGeometry? null 트랙과 진행 표시줄의 모서리 반경
stopIndicatorColor Color? null Material 3 스타일의 종료 표시자 색상
stopIndicatorRadius double? null 종료 표시자의 반지름, 0이면 숨김
trackGap double? null 진행 표시줄과 트랙 사이의 간격

 

사용하는 방법은 엄청 간단한데

Center(
  child: LinearProgressIndicator(),
)

그냥 바로 LinearProgressIndicator를 사용해 주면 된다.

이렇게 사용을 해주면 무한히 반복하고 있는 인디케이터를 볼 수 있을 것이다.

어째서 무한히 반복하고 있냐 한다면 그 이유는 파라미터인 value에 있다. value에는 double 값이 들어갈 수 있어 그 값으로 진행바의 위치를 나타낼 수 있는데 valuenull 인 상태라면 어느 한 지점에 고정되지 못하고 계속해서 로딩되게 된다.

LinearProgressIndicator(
  value: 0.7,
)

그래서 value를 고정값으로 설정한다면 아래와 같은 모습이 되게 된다.

근데 다들 아시다시피 다운로드하는데 이러고 있으면 진짜 석 다 나가니 다운로드 중인 콘텐츠의 진행 상황에 맞게 표시해 주자.

일단 먼저 Image.network 에서 이미지를 불러오는 중에 사용되는 로더의 방식으로 사용해 보자.

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 LinearProgressIndicator(value: progress);
  },
)

이 코드는 Image.network에서 이미지가 로드되는 동안 진행 상태에 따라 로딩 인디케이터를 보여주는 loadingBuilder 예제이다.

앞서 말했듯이 LinearProgressIndicatorvalue값을 통하여 진행상황을 표시하게 되는데 위 코드에서는 로딩 상태를 담고 있는 객체인 loadingProgress를 사용하여 현재까지 다운로드한 바이트 수를 전체 이미지 크기로 나눠 0.0 ~ 1.0 사이의 로딩 비율 계산해주고 있다. 그런 후 LinearProgressIndicatorvalue값에 계산한 값을 넣어 이미지가 얼마나 로드되었는지 알 수 있게 만들고 있다. 일단 한번 알아보고 가겠다.

일단 loadingBuilder는 이미지가 로딩중일 때와 로딩 완료 후를 구분해 UI를 보여주는 빌더 함수이다.

여기서 child는 이미지 로딩이 완료되면 보여줄 이미지 위젯이고 loadingProgress는 로딩 중이면 non-null, 완료되면 null 상태를 가지게 돼서 로딩이 끝나게 된다. 간단하게 살펴보자면

if (loadingProgress == null) return child;

먼저 로딩이 끝났으면 이미지를 보여주게 되어 있다.

이제 그다음이 문제일 것인데

double progress = loadingProgress.cumulativeBytesLoaded /
    loadingProgress.expectedTotalBytes!;

이것은 현재까지 다운로드한 바이트 수를 전체 이미지 크기로 나눠 0.0 ~ 1.0 사이의 로딩 비율로 계산한 것인데

cumulativeBytesLoaded는 여기에서 이미지가 네트워크를 통해 지금까지 다운로드된 총 바이트 수를 나타내준다. 거기에 전체 크기를 나타내주는 expectedTotalBytes로 나눠준 것이다. 이렇게 하면 인디케이터를 쉽게 시각화할 수 있다.

이제 이미지 로더는 다 알아봤으니 다운로드 예제를 직접 제작하고 간단히 알아보자.

먼저 그냥 다운로드 뽄새(?)만 흉내 낼 것이기 때문에 5초 동안 값이 증가하는 함수를 하나 만들어주겠다.

double progress = 0.0;

먼저 증가시킬 값 하나 만들어 주고

void startProgress(Function(double value) onProgress) async {
  setState(() => progress = 0.0);
  for (int i = 1; i <= 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    onProgress(i * 0.2); // 1초마다 값을 onProgress 콜백을 통해 전달
  }
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('다운로드 성공'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text('확인'),
        ),
      ],
    ),
  );
}

5초 동안 값을 0.2씩 증가시키면서 콜백으로 전달하는 비동기 함수를 하나 만들어 줬다.

근데 그러면 진짜 값 증가 밖에 안 하니까 증가 다하면 다운로드 완료됐다고 팝업하나 띄워주게끔 했다.

LinearProgressIndicatorvalue 에는 위에서 만든 progress 값을 넣어줬다.

LinearProgressIndicator(value: progress,)

근데 이렇게 하면 사소한 문제가 하나 있는데 현재 값이 끊겨서 0.2씩 올라가고 있지 않은가? 그래서 화면에서도 진행바가 뚝뚝 끊기면서 올라가게 된다. 이제 이것을 해결하기 위해 TweenAnimationBuilder를 사용하여 간단한 애니메이션을 하나 넣어주었다.

TweenAnimationBuilder<double>(
  tween: Tween(begin: 0.0, end: progress),
  duration: Duration(milliseconds: 500),
  builder: (context, value, child) {
    return LinearProgressIndicator(value: value,);
  },
)

이제 마지막으로 만든 함수를 써주는 다운로드 버튼을 달아주면 끝이다.

floatingActionButton: FloatingActionButton(
  child: Icon(Icons.download),
  onPressed: () {
    startProgress((value) {
      setState(() {
        progress = value;
      });
    },);
  },
)

 

 

이렇게 LinearProgressIndicator 위젯에 대해서 알아보았다. 다운로드 예제는 그냥 이렇게 사용을 하면 된다~ 를 알려주고 싶어서 만든 거니 막 신경 쓰지 않아도 될 것이다. 그래도 도움이 되었길 바라며 마치겠다.

반응형