Study Record

[Flutter] 스크롤바 (Scrollbar) 본문

Flutter/widget_scrollView

[Flutter] 스크롤바 (Scrollbar)

초코초코초코 2023. 3. 9. 18:46
728x90

✍ Scrollbar

스크롤할 수 있는 위젯의 상위 위젯에 Scrollbar 위젯을 넣으면 스크롤 바가 생긴다. 일반적으로 ListView, GridView CustomScrollView 등의 상위 위젯에 Scrollbar 를 사용하는데 리스트 뷰의 항목 개수가 명확하지 않을 경우(itemCount, childCount 가 명시되지 않은 경우 무한 스크롤) 스크롤 바가 보이지 않는다. 

Scrollbar({   
  Key? key,  
  required Widget child,
  ScrollController? controller,  
  bool? thumbVisibility, 
  bool? trackVisibility, 
  double? thickness,
  Radius? radius, 
  bool Function(ScrollNotification)? notificationPredicate, 
  bool? interactive,  
  ScrollbarOrientation? scrollbarOrientation,
})

 

스크롤 시 스크롤바 위젯에서 직접적으로 움직이는 상자를 thumb 라고 부르고 thumb 가 움직이는 길을 track 이라고 한다. thumbVisibility 가 true 이면 thumb 가 항상 보이고 false 이면 스크롤 시에만 보인다. trackVisibility 가 true 이면 항상 track 이 보이고 false 이면 항상 track 이 안보인다. thickness 는 thumb 두께를 나타내고 radius 는 thumb 의 둥근 정도를 나타낼 수 있다. interactive 가 true 면 사용자가 직접 track 을 클릭해 스크롤을 이동할 수 있고 false 이면 track 을 클릭해 이동할 수 없다.

 

 

 

😶 ScrollbarThemeData

이외에 스크롤 바를 꾸밀려면 theme 데이터로 넣어야 한다.

ScrollbarThemeData({   
  MaterialStateProperty ? thumbVisibility,
  MaterialStateProperty ? thickness, 
  MaterialStateProperty ? trackVisibility,
  Radius? radius, 
  MaterialStateProperty ? thumbColor, 
  MaterialStateProperty ? trackColor,  
  MaterialStateProperty ? trackBorderColor,  
  double? crossAxisMargin,  
  double? mainAxisMargin,
  double? minThumbLength,
  bool? interactive,
})

 

인자 이름을 보면 어떤 역할을 하는지 유추할 수 있다. crossAxisMargin 과 MainAxisMargin 은 다음과 같다.

 

속성 값이 MaterialStateProperty 로 된 것들이 있는데 Flutter 에는 Material State 가 있다. 

Material State 설명
pressed  호버링 상태 (마우스 커서를 올려놓은 상태)
focused  포커스 됐을 때 (텍스트 필드)
hovered  눌렸을 때 
dragged 드래그 됐을 때
selected  선택됐을 때 (체크박스, 라디오 버튼 등)
scrollUnder  다른 컴포넌트 밑으로 스크롤링 됐을 때
disabled  비활성화 됐을 때 ex. ElevatedButton에 onPressed: null 이면 비활성화 상태
error 에러 상태

앱을 만들 때는 호버링 상태는 고려하지 않아도 된다. Material State에 따라 값을 적용할 수 있어 좀 더 섬세한 컨트롤이 가능하다.

 

+ MaterialStateProperty 적용 방법

// 모든 Material State 상태들에 대해 동일한 값을 적용한다.
MaterialStateProperty.all(T value)

// resolveWith()는 상태를 인자로 받는 이벤트에서 각 상태에 대한 값을 다르게 리턴해주면 된다.
// 예시) backgroundColor 를 설정하는 경우
MaterialStateProperty.resolveWith((states){
    if(states == MaterialState.pressed) {
      // 눌렀을 경우
      return Colors.pink;
    }
    // 그 외의 경우
    return Colors.black;
})

 

 

theme + Scrollbar 예시)

import 'package:flutter/material.dart';

void main() => runApp(
  MaterialApp(
    theme: ThemeData(
      scrollbarTheme: ScrollbarThemeData(
        thumbColor: MaterialStateProperty.resolveWith((states){
          if(states.contains(MaterialState.dragged)){
            // thumb 를 직접 눌러 스크롤할 경우
            return Colors.pink;
          }
          return Colors.black.withOpacity(0.5);
        }),
        interactive: true,
        crossAxisMargin: 20.0,
        mainAxisMargin: 40.0
      )
    ),
    home: ScrollBarScreen(),
  ),
);

class ScrollBarScreen extends StatelessWidget {
  const ScrollBarScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MainLayout(
      title: "ScrollBarScreen",
      body: Scrollbar(
        thumbVisibility: true,
        trackVisibility: true,
        child: SingleChildScrollView(
          child: Column(
            children: List.generate(
              100,
              (index) => Container(
                color: Colors.greenAccent,
                height: 100,
                margin: EdgeInsets.all(16.0),
                child: Center(child: Text("$index")),
              ),
            ),
          ),
        ),
      ),
    );
  }
}
728x90