안드로이드에서 리스트를 표현하기 위해서 사용하는 뷰는 RecyclerView입니다.
플러터에서는 ListView 위젯을 사용해서 동일한 동작을 구현할 수 있습니다.
ListView 위젯 만들기
플러터에서 ListView 위젯은 다음과 같은 방법으로 만들 수 있습니다.
ListView makeWebtoonList(AsyncSnapshot<List<WebtoonModel>> snapshot) {
return ListView.separated(
scrollDirection: Axis.vertical,
itemCount: snapshot.data!.length,
// itemBuilder: 현재 사용자가 보고 있는 부분에 대한 데이터만 보여준다.
itemBuilder: (context, index) {
var webtoon = snapshot.data![index];
return Text(webtoon.title);
},
// separatorBuilderf: 각 item 사이에 들어갈 위젯을 지정
separatorBuilder: (context, index) => const SizedBox(
height: 10,
),
);
}
}
알아보기 쉽게 하기 위해 함수를 정의해서 해당 함수 안에 ListView 위젯을 정의했습니다.
ListView의 각 요소는 다음과 같은 역할을 합니다.
- separated: 사용자가 보고 있는 부분에 대해서만 데이터를 로드하고 각 아이템 사이에 지정한 위젯을 넣을 수 있는 ListView 위젯 빌더
생성한 ListView 위젯 사용하기
ListView 위젯을 Column에 그냥 넣으면 다음과 같은 에러가 발생한다.
Vertical viewport was given unbounded height.
해당 에러가 발생하는 이유는
ListView 위젯의 경우 높이가 무제한이기 때문이다.
Column 위젯의 경우 높이를 예상할 수 있어야 하는데 그냥 ListView 위젯만 넣을 경우 해당 위젯의 높이를 예상할 수 없다.
그렇기 때문에 Expanded 위젯으로 감싸줘야한다.
Column(
children: [
const SizedBox(
height: 50,
),
Expanded(
child: makeWebtoonList(
snapshot,
),
)
],
);
Expanded 위젯은 현재 차지할 수 있는 공간을 가득히 채워주는 위젯이다.
Dismissible을 사용해서 Slide를 통해 리스트뷰의 아이템 삭제하기
Dismissible을 사용하여 itemBuilder를 정의하면 Slide를 사용해서 아이템을 삭제할 수 있다.
다음과 같이 ListView의 itemBuilder에 Dismissible 위젯을 정의한다.
Expanded(
child: ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
// Dismissible을 사용하면 swipe를 사용해서 리스트뷰에 있는 아이템을 삭제할 수 있다.
// 하지만 이 삭제는 UI 상에서만 이루어지며 실제 동작은 onDismissed에서 실시한다.
return Dismissible(
// 실제로 제거되야하는 데이터를 key로 지정한다.
key: ValueKey(testList[index]),
// List 아이템을 슬라이드 했을 때 보여줄 배경
background: Container(
color: Theme.of(context).colorScheme.error.withOpacity(0.5),
margin: Theme.of(context).cardTheme.margin,
alignment: Alignment.center,
child: const Text(
"Delete",
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
// 클릭 시 행동 정의
child: Card(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
const Icon(Icons.lunch_dining),
const SizedBox(width: 16),
Text(testList[index]),
],
),
),
),
onDismissed: (direction) {
setState(() {
testList.removeAt(index);
});
},
);
},
separatorBuilder: (context, index) => Divider(color: Colors.deepPurpleAccent.withOpacity(0.5)),
itemCount: testList.length,
),
)
Dismissible의 각 인자의 의미는 다음과 같다.
- Key
- 실제로 제거되어야 하는 데이터를 key로 하여 Key 객체를 반환해 주면 된다.
- background
- item을 slide 했을 때 보여줄 background를 의미한다.
- Widget 객체를 반환해 주면 된다.
- onDismissed
- UI 상에서 삭제를 한 후 실제로 어떤 처리를 할지 여기에 정의한다.
- 주로 데이터 상의 삭제를 여기서 진행하고 setState를 통해 UI를 갱신한다.
listView의 아이템 클릭 이벤트 넣기
GestureDetector 위젯을 사용하여 아이템 클릭에 따른 클릭 이벤트를 넣을 수 있다.
다음과 같이 아이템 widget 부분을 GestureDetector로 감싼다.
GestureDetector(
onTap: () {
Fluttertoast.showToast(
msg: "submitted text: ${testList[index]}",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 2,
backgroundColor: Colors.grey,
textColor: Colors.white,
fontSize: 16.0,
);
},
// ListView의 아이템은 Card 위젯으로 만드는게 예쁘게 만들 수 있음
child: Card(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
const Icon(Icons.lunch_dining),
const SizedBox(width: 16),
Text(testList[index]),
],
),
),
),
)
- onTap
- 클릭했을 때 어떤 이벤트를 실시할지 여기에 정의한다.
전체 listView 위젯의 코드
위에서 정의한 listView widget의 전체 코드는 다음과 같다.
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
class ListWidget extends StatefulWidget {
const ListWidget({super.key});
@override
State<ListWidget> createState() => _ListWidgetState();
}
class _ListWidgetState extends State<ListWidget> {
final List<String> testList = List.generate(100, (index) => "Test Text $index");
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("List Widget"),
),
body: Column(
children: [
const Text(
"Dummy Text",
style: TextStyle(fontSize: 20),
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Text(
"Dummy Text2",
style: TextStyle(fontSize: 20),
),
),
Expanded(
child: ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
// Dismissible을 사용하면 swipe를 사용해서 리스트뷰에 있는 아이템을 삭제할 수 있다.
// 하지만 이 삭제는 UI 상에서만 이루어지며 실제 동작은 onDismissed에서 실시한다.
return Dismissible(
// 실제로 제거되야하는 데이터를 key로 지정한다.
key: ValueKey(testList[index]),
// List 아이템을 슬라이드 했을 때 보여줄 배경
background: Container(
color: Theme.of(context).colorScheme.error.withOpacity(0.5),
margin: Theme.of(context).cardTheme.margin,
alignment: Alignment.center,
child: const Text(
"Delete",
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
// 클릭 시 행동 정의
child: GestureDetector(
onTap: () {
Fluttertoast.showToast(
msg: "submitted text: ${testList[index]}",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 2,
backgroundColor: Colors.grey,
textColor: Colors.white,
fontSize: 16.0,
);
},
// ListView의 아이템은 Card 위젯으로 만드는게 예쁘게 만들 수 있음
child: Card(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
const Icon(Icons.lunch_dining),
const SizedBox(width: 16),
Text(testList[index]),
],
),
),
),
),
onDismissed: (direction) {
setState(() {
testList.removeAt(index);
});
},
);
},
separatorBuilder: (context, index) => Divider(color: Colors.deepPurpleAccent.withOpacity(0.5)),
itemCount: testList.length,
),
)
],
),
);
}
}
그리고 결과는 다음과 같다.
'플러터(flutter)' 카테고리의 다른 글
플러터 기초 - PageRouteBuilder를 사용해서 화면 전환하기 (0) | 2023.05.14 |
---|---|
플러터 기초 - HttpOverrides으로 userAgent 설정하기 (0) | 2023.05.13 |
플러터 기초 - FutureBuilder를 사용한 비동기 작업 수행 (0) | 2023.05.12 |
플러터 기초 - https로 데이터 받고 JSON으로 변환하기 (0) | 2023.04.28 |
플러터 기초 - BuildContext (0) | 2023.04.19 |
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
댓글