반응형
    
    
    
  ListView Widget
- ListView는 가장 기본적인 스크롤링 widget이다. ListView는 itemCount만큼의 자식 요소를 가지고, 하나의 자식 밑에 다른 자식이 스크롤 방향으로 쌓이는 형상을 하고 있다.
ListView를 생성하는 4가지 옵션
1. 명시적으로 childeren에 List<Widget>을 넘기는 방법.
- 자식요소의 수가 적은 리스트뷰에 적합하다.
- 눈에 보이는 자식 뿐만 아니라 목록 보기에 표시될 가능성이 있는(즉, 모든 자식 요소)에 대한 작업이 수행된다.
import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Sample(),
    );
  }
}
class Sample extends StatelessWidget {
  const Sample({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
  	
    return ListView(   
      // 자식요소로 List를 바로 넘겨준 것을 확인할 수 있다.
      children: [ 
        Text("1번 자식"),
        Text("2번 자식"),
        Text("3번 자식"),
        Text("4번 자식"),
        Text("5번 자식"),
        Text("6번 자식"),
      ],
    );
  }
}
2. ListView.builder를 사용하는 방법.
- 이 생성자는 IndexedWidgetBuilder를 인자로 받는다.
- 실제로 보이는 자식에 대해서만 호출하기 때문에 자식요소가 많은 ListView에 적합하다.
- 자식요소의 Index가 필요한 경우 사용하기 유용하다.
import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Sample(),
    );
  }
}
class Sample extends StatelessWidget {
  const Sample({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
        padding: const EdgeInsets.all(8),
        itemCount: 100,
        // 필수 인자로 itemBuilder를 받아야 하며 이 builder는 context, index를 인자로 가진 함수이다.
        itemBuilder: (BuildContext context, int index) {
          return Text("$index번 자식요소");
        });
  }
}
여기서 ListView.builder의 정의를 들어가 보면
ListView.builder({
    super.key,
    super.scrollDirection,
    super.reverse,
    super.controller,
    super.primary,
    super.physics,
    super.shrinkWrap,
    super.padding,
    this.itemExtent,
    this.prototypeItem,
    required IndexedWidgetBuilder itemBuilder,   // <-----------------------
    ChildIndexGetter? findChildIndexCallback,
    int? itemCount,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    super.cacheExtent,
    int? semanticChildCount,
    super.dragStartBehavior,
    super.keyboardDismissBehavior,
    super.restorationId,
    super.clipBehavior,
  })
이렇게 itemBuilder가 IndexedWidgetBuilder 타입인 것을 확인 할 수 있다.
3. ListView.separted를 사용하는 방법.
- 이 생성자는 아이템과 아이템 사이에 구분자를 넣어줄 수 있다.
import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Sample(),
    );
  }
}
class Sample extends StatelessWidget {
  const Sample({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return ListView.separated(
      itemCount: 100,
      itemBuilder: (context, index) {
        return Text("$index번 자식");
      },
      separatorBuilder: (context, index) {
        return const Divider();
      },
    );
  }
}
예제와 같이 separatorBuilder를 사용하여 item의 요소 사이사이에 내가 원하는 Widget을 넣어줄 수 있다.
4. ListView.custom를 사용하는 방법
- SliverchildDelegate를 가지며, 이를 통해서 자식요소에서의 추가적인 작업을 가능하게 한다.
- 가장 큰 장점은 목록을 출력하기 위한 상태를 분리할 수 있다는 점이다.
- custom과 SliverChildDelegate가 제공하는 속성을 직접 제어하여 ListView의 동작 방식을 새롭게 구현하거나, 최적화를 위한 로직을 추가할 수 있다
import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Sample(),
    );
  }
}
class Sample extends StatefulWidget {
  const Sample({Key? key}) : super(key: key);
  @override
  State<Sample> createState() => _SampleState();
}
class _SampleState extends State<Sample> {
  List<String> items = <String>['1', '2', '3', '4', '5'];
  void _reverse() {
    setState(() {
      items = items.reversed.toList();
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: ListView.custom(
          // 다른 생성자들과는 다르게 childrenDelegate를 인자로 사용하여 자식 모델을 제어한다.
          childrenDelegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return KeepAlive(
                  data: items[index],
                  key: ValueKey<String>(items[index]),
                );
              },
              childCount: items.length,
              findChildIndexCallback: (Key key) {
                final ValueKey<String> valueKey = key as ValueKey<String>;
                final String data = valueKey.value;
                return items.indexOf(data);
              }),
        ),
      ),
      bottomNavigationBar: BottomAppBar(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextButton(
              onPressed: () => _reverse(),
              child: const Text('Reverse items'),
            ),
          ],
        ),
      ),
    );
  }
}
class KeepAlive extends StatefulWidget {
  const KeepAlive({
    required Key key,
    required this.data,
  }) : super(key: key);
  final String data;
  @override
  State<KeepAlive> createState() => _KeepAliveState();
}
class _KeepAliveState extends State<KeepAlive>
    with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Text(widget.data);
  }
}
ListView WIdget 팁
- Null이 아닌 경우 itemExtent 속성은 자식요소가 스크롤 방향으로 지정된 범위만큼 차지하도록 강제한다.
- Null이 아닌 경우 prototypeItem 속성은 스크롤 방향으로 지정된 위젯만큼 차지하도록 강제한다.
- itemExtent나 prototypeItem 속성을 사용하는 것은 자식요소가 자신들이 차지하는 공간의 크기를 결정하는데 효과적이다. 예를 들어 스크롤의 위치가 급격하게 변하였을 때, 자식요소의 범위를 쉽게 계산할 수 있다.
- 빈 리스트 처리 예제
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('Empty List Test')),
    body: itemCount > 0  // 삼항연산자로 itemCount가 1이상일 경우에만 표현
      ? ListView.builder(
          itemCount: itemCount,
          itemBuilder: (BuildContext context, int index) {
            return ListTile(
              title: Text('Item ${index + 1}'),
            );
          },
        )
      : const Center(child: Text('No items')),
  );
}반응형
    
    
    
  'Flutter' 카테고리의 다른 글
| [Flutter] UI(1) - copyWith (1) | 2023.12.07 | 
|---|---|
| [Flutter] Widget(3) - Align (1) | 2023.12.07 | 
| [Flutter] Widget(1) - Dialog, Alert Dialog, Simple Dialog, Show Dialog (0) | 2023.12.05 | 
| [flutter] PopupMenuItem 에서 dialog 여는 법 (0) | 2023.10.31 | 
| [flutter]플러터에서 Intent로 다른 패키지 실행 시 화면 전환이 안되는 현상 (0) | 2023.10.25 | 
 
										
									