[Flutter] 심화 ScrollView

Comely·2024년 11월 15일

Flutter

목록 보기
9/26

ReorderableListView

유저가 Item 위치를 스크롤하여 변경할 때
DB의 순서를 함께 수정할 수 있습니다.

class ReorderableListViewScreen extends StatefulWidget {
  const ReorderableListViewScreen({Key? key}) : super(key: key);

  
  State<ReorderableListViewScreen> createState() =>
      _ReorderableListViewScreenState();
}

class _ReorderableListViewScreenState extends State<ReorderableListViewScreen> {
  List<int> numbers = List.generate(100, (index) => index);

  
  Widget build(BuildContext context) {
    return MainLayout(
      title: 'ReorderableListViewScreen',
      body: ReorderableListView.builder(
        itemBuilder: (context, index) {
          return renderContainer(
          
          //index 를 numbers[index]로 변경해서 색깔과 인덱스로 위치 변경시 바뀜 
            color: rainbowColors[numbers[index] % rainbowColors.length],
            index: numbers[index],
          );
        },
        itemCount: numbers.length,
        onReorder: (int oldIndex, int newIndex) {
          setState(() {
            // oldIndex와 newIndex 모두
            // 이동이 되기 전에 산정한다.
            //
            //
            // [red, orange, yellow]
            // [0, 1, 2]
            //
            // red를 yellow 다음으로 옮기고싶다.
            // red : 0 oldIndex -> 3 newIndex
            // [orange, yellow, red]
            //
            // [red, orange, yellow]
            // yellow를 맨 앞으로 옮기고싶다.
            // yellow : 2 oldIndex -> 0 newIndex
            // [yellow, red, orange]
            if (oldIndex < newIndex) {
              newIndex -= 1;
            }

            final item = numbers.removeAt(oldIndex);
            numbers.insert(newIndex, item);
          });
        },
      ),
    );
  }

  Widget renderDefault() {
    return ReorderableListView(
      children: numbers
          .map(
            (e) => renderContainer(
              color: rainbowColors[e % rainbowColors.length],
              index: e,
            ),
          )
          .toList(),
      onReorder: (int oldIndex, int newIndex) {
        setState(() {
          // oldIndex와 newIndex 모두
          // 이동이 되기 전에 산정한다.
          //
          //
          // [red, orange, yellow]
          // [0, 1, 2]
          //
          // red를 yellow 다음으로 옮기고싶다.
          // red : 0 oldIndex -> 3 newIndex
          // [orange, yellow, red]
          //
          // [red, orange, yellow]
          // yellow를 맨 앞으로 옮기고싶다.
          // yellow : 2 oldIndex -> 0 newIndex
          // [yellow, red, orange]
          if (oldIndex < newIndex) {
            newIndex -= 1;
          }

          final item = numbers.removeAt(oldIndex);
          numbers.insert(newIndex, item);
        });
      },
    );
  }

  Widget renderContainer({
    required Color color,
    required int index,
    double? height,
  }) {
    print(index);
    return Container(
      //Key는 서로 다른 Container로 인식
      key: Key(index.toString()),
      height: height ?? 300,
      color: color,
      child: Center(
        child: Text(
          index.toString(),
          style: TextStyle(
            color: Colors.white,
            fontWeight: FontWeight.w700,
            fontSize: 30.0,
          ),
        ),
      ),
    );
  }
}

SliverList

// ListView.builder 생성자와 유사함.
  SliverList renderBuilderSliverList() {
    return SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) {
          return renderContainer(
            color: rainbowColors[index % rainbowColors.length],
            index: index,
          );
        },
        childCount: 15,
      ),
    );
  }

SliverGrid

// GridView.builder 와 비슷함
  SliverGrid renderSliverGridBuilder() {
    return SliverGrid(
      delegate: SliverChildBuilderDelegate(
        (context, index) {
          return renderContainer(
            color: rainbowColors[index % rainbowColors.length],
            index: index,
          );
        },
        childCount: 30,
      ),
      gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 150,
      ),
    );
  }

SliverAppBar

SliverAppBar renderSliverAppbar() {
  return SliverAppBar(
    // 스크롤 했을때 리스트의 중간에도 AppBar가 내려오게 할 수 있음.
    floating: true,
    
    // 완전 고정, 헤더가 여러 개라면 위에 쌓인다.
    pinned: false,
    
    // 자석 효과
    // floating true 에만 사용가능
    // false면 중간이 존재
    snap: true, 
    
    // 맨 위에서 한계 이상으로 스크롤 했을때
    // 앱바가 스크롤 한 남는 공간을 차지
    stretch: false,
    
    // 앱바 최대 사이즈
    expandedHeight: 200,
    
    // 최소 사이즈
    collapsedHeight: 150,
    
    flexibleSpace: FlexibleSpaceBar(
      background: Image.asset(
        'asset/img/image_1.jpeg',
        fit: BoxFit.cover,
      ),
      title: Text('FlexibleSpace'),
    ),
    title: Text('CustomScrollViewScreen'),
  );
}

shouldRebuild (언제 build를 해야 할지 결정)

class _SliverFixedHeaderDelegate extends SliverPersistentHeaderDelegate {
  final Widget child;
  final double maxHeight;
  final double minHeight;

  _SliverFixedHeaderDelegate({
    required this.child,
    required this.maxHeight,
    required this.minHeight,
  });

  
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return SizedBox.expand(
      child: child,
    );
  }

  
  // 최대 높이
  double get maxExtent => maxHeight;

  
  // 최소 높이
  double get minExtent => minHeight;

  
  // covariant - 해당 클래스를 상속한 클래스도 사용 가능
  // ex) bool shouldRebuild(covariant SliverPersistHeaderDelegate oldDelegate) {

  // oldDelegate - build가 실행이 됐을때 이전 Delegate
  // this - 새로운 delegate
  // shouldRebuild - 새로 build를 해야할지 말지 결정. false - build 안함 true - 빌드 다시함
  
  // child, maxHeight, minHeight 값이 바뀔 때마다 Rebuild
  bool shouldRebuild(_SliverFixedHeaderDelegate oldDelegate) {
    return oldDelegate.minHeight != minHeight ||
        oldDelegate.maxHeight != maxHeight ||
        oldDelegate.child != child;
  }
}

화면

SliverPersistentHeader renderHeader() {
    return SliverPersistentHeader(
      pinned: true,
      delegate: _SliverFixedHeaderDelegate(
        child: Container(
          color: Colors.black,
          child: Center(
            child: Text(
              '신기하지요',
              style: TextStyle(
                color: Colors.white,
              ),
            ),
          ),
        ),
        minHeight: 50,
        maxHeight: 200,
      ),
    );
  }

Scrollbar 위젯 추가

body: Scrollbar(
        child: SingleChildScrollView(
          child: Column(
            children: numbers.map(
              (e) => renderContainer(
                color: rainbowColors[e % rainbowColors.length],
                index: e,
              ),
            ).toList(),
          ),
        ),
      ),

Refresh Indicator

await Future.delayed(Duration(seconds: 3));

3초 만큼 RefreshIndicator가 보여집니다.
이후 서버 요청 코드를 추가할 수 있습니다.

body: RefreshIndicator(
        onRefresh: () async {
          // 서버 요청
          await Future.delayed(Duration(seconds: 3));
        },
        child: ListView(
          children: numbers
              .map(
                (e) => renderContainer(
              color: rainbowColors[e % rainbowColors.length],
              index: e,
            ),
          )
              .toList(),
        ),
      ),
profile
App, Web Developer

0개의 댓글