Flutter 스터디 13 플러터 ListView builder와 Dialog
순한맛 시즌2-2 리스트뷰 빌더(ListView builder)와 다이어로그(Dialog) 팝업창 띄우기
- GestureDetector
: 제스처 기능을 지원하지 않는 위젯을 이것으로 감싸면 onTap 메서드로 제스터 기능 사용가능하게 해주는 위젯
+ Inkwell 위젯도 같은 기능을 함. 하지만 잉크가 퍼지는듯한 애니메이션이 조금 다름
- Card class
- ListView에 나올 card들의 크기를 지정할 때, Container / SizedBox 두 가지를 사용할 수 있음.
Container를 써도 무방하지만 Container 위젯은 컬러, 패딩, 데코레이션 등 다양한 것들을 지정할 수 있기 때문에 크기 조절만 할 거라면 SizedBox가 좋음.
SizedBox는 사이즈 지정하는 것 외에 아무기능도 없기 때문에 조금더 위젯 비용이 적게 들어가서 효율적일 수 있음
- description 데이터는 가로 방향일 때도 일정하게 배치되어야하기 떄문에 반응형으로 만들기
reponsive 앱은 사용자가 화면에 방향을 바꿀떄 마다 그에 맞춰서 화면이 재배치 되어야하고 재배치될 때마다 위젯이 리빌드 됨
> MediaQuery class 사용
- MediaQuery class
해당 클래스를 통해서 사용중인 디바이스 크기를 알아낼 수 있음.
double width = MediaQuery.of(context).size.width * 0.6; //현재 디바이스 크기를 알아내서 화면의 60%만 차지하도록
ListView 기본
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: ListViewPage(), //리스트뷰 띄우기
);
}
}
class ListViewPage extends StatefulWidget {
const ListViewPage({Key? key}) : super(key: key);
@override
State<ListViewPage> createState() => _ListViewPageState();
}
class _ListViewPageState extends State<ListViewPage> {
var titleList =[
'Dentist',
'Pharmacies',
'Schoold teacher',
'It manager',
'Account',
'Lawyer',
'Hairdersser',
'Physician',
'web developter',
'Medical Secretary'
];
var imageList = [
'image/1.PNG',
'image/2.PNG',
'image/3.PNG',
'image/4.PNG',
'image/5.PNG',
'image/6.PNG',
'image/7.PNG',
'image/8.PNG',
'image/9.PNG',
'image/10.PNG'
];
var description = [
'1. There are different types of careers you can pursue in your life. Which one will ti be?',
'2. There are different types of careers you can pursue in your life. Which one will ti be?',
'3. There are different types of careers you can pursue in your life. Which one will ti be?',
'4. There are different types of careers you can pursue in your life. Which one will ti be?',
'5. There are different types of careers you can pursue in your life. Which one will ti be?',
'6. There are different types of careers you can pursue in your life. Which one will ti be?',
'7. There are different types of careers you can pursue in your life. Which one will ti be?',
'8. There are different types of careers you can pursue in your life. Which one will ti be?',
'9. There are different types of careers you can pursue in your life. Which one will ti be?',
'10-. There are different types of careers you can pursue in your life. Which one will ti be?',
];
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width * 0.6; //현재 디바이스 크기를 알아내서 화면의 60%만 차지하도록
return Scaffold(
appBar: AppBar(
title: const Text(
'Listview',
style: TextStyle(color: Colors.grey), // 리스트를 클릭하면 직업 출력
),
backgroundColor: Colors.white,
elevation: 0,
),
body: ListView.builder(
itemCount: titleList.length,
itemBuilder: (context, index){
return GestureDetector( // 제스처 기능 추가
onTap: (){
debugPrint(titleList[index]);
},
child: Card(
child: Row(
children: [
SizedBox( // Container 써도 무방함. 하지만 Container 컬러, 패딩 등 다양한 것들을 지정할 수 있기 때문에 크기 조절만 할 거라면 SizedBox가 좋음
width: 100,
height: 100,
child: Image.asset(imageList[index]),
),
Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
Text(titleList[index],
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.grey),
),
const SizedBox(
height: 10,
),
SizedBox(
width: width,
child: Text(
description[index],
style: TextStyle(
fontSize: 15,
color: Colors.grey[500]
),
),
)
],
),)
],
),
),
);
},
),
);
}
}
// popup 메소드. 리스트를 클릭했을때 화면 위에 떠지는 팝업창
void showPopup(context, title, image, description){
showDialog(
context: context,
builder: (context){
return Dialog(
child: Container(
width: MediaQuery.of(context).size.width * 0.7,
height: 380,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
child: Column(
children: [
ClipRRect( //이미지를 모양에 맞게 출력
borderRadius: BorderRadius.circular(10),
child: Image.asset(
image,
width: 200,
height: 200,
),
),
const SizedBox(
height: 10,
),
Text(title, style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Colors.grey
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(
description,
maxLines: 3, //최대 몇줄까지 표시하는지 지정
style: TextStyle(
fontSize: 15,
color: Colors.grey[500]
),
textAlign: TextAlign.center,
),),
ElevatedButton.icon(onPressed: () { //닫기 버튼
Navigator.pop(context);
},
icon: const Icon(Icons.close),
label: const Text('close'),
)
],
),
),
);
}
);
}
전체 코드
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: ListViewPage(), //리스트뷰 띄우기
);
}
}
class ListViewPage extends StatefulWidget {
const ListViewPage({Key? key}) : super(key: key);
@override
State<ListViewPage> createState() => _ListViewPageState();
}
class _ListViewPageState extends State<ListViewPage> {
var titleList =[
'Dentist',
'Pharmacies',
'Schoold teacher',
'It manager',
'Account',
'Lawyer',
'Hairdersser',
'Physician',
'web developter',
'Medical Secretary'
];
var imageList = [
'image/1.PNG',
'image/2.PNG',
'image/3.PNG',
'image/4.PNG',
'image/5.PNG',
'image/6.PNG',
'image/7.PNG',
'image/8.PNG',
'image/9.PNG',
'image/10.PNG'
];
var description = [
'1. There are different types of careers you can pursue in your life. Which one will ti be?',
'2. There are different types of careers you can pursue in your life. Which one will ti be?',
'3. There are different types of careers you can pursue in your life. Which one will ti be?',
'4. There are different types of careers you can pursue in your life. Which one will ti be?',
'5. There are different types of careers you can pursue in your life. Which one will ti be?',
'6. There are different types of careers you can pursue in your life. Which one will ti be?',
'7. There are different types of careers you can pursue in your life. Which one will ti be?',
'8. There are different types of careers you can pursue in your life. Which one will ti be?',
'9. There are different types of careers you can pursue in your life. Which one will ti be?',
'10-. There are different types of careers you can pursue in your life. Which one will ti be?',
];
// popup 메소드. 리스트를 클릭했을때 화면 위에 떠지는 팝업창
void showPopup(context, title, image, description){
showDialog(
context: context,
builder: (context){
return Dialog(
child: Container(
width: MediaQuery.of(context).size.width * 0.7,
height: 380,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
child: Column(
children: [
ClipRRect( //이미지를 모양에 맞게 출력
borderRadius: BorderRadius.circular(10),
child: Image.asset(
image,
width: 200,
height: 200,
),
),
const SizedBox(
height: 10,
),
Text(title, style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Colors.grey
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(
description,
maxLines: 3, //최대 몇줄까지 표시하는지 지정
style: TextStyle(
fontSize: 15,
color: Colors.grey[500]
),
textAlign: TextAlign.center,
),),
ElevatedButton.icon(onPressed: () { //닫기 버튼
Navigator.pop(context);
},
icon: const Icon(Icons.close),
label: const Text('close'),
)
],
),
),
);
}
);
}
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width * 0.6; //현재 디바이스 크기를 알아내서 화면의 60%만 차지하도록
return Scaffold(
appBar: AppBar(
title: const Text(
'Listview',
style: TextStyle(color: Colors.grey), // 리스트를 클릭하면 직업 출력
),
backgroundColor: Colors.white,
elevation: 0,
),
body: ListView.builder(
itemCount: titleList.length,
itemBuilder: (context, index){
return GestureDetector( // 제스처 기능 추가
onTap: (){
debugPrint(titleList[index]);
showPopup(context, titleList[index], imageList[index],
description[index]);
},
child: Card(
child: Row(
children: [
SizedBox( // Container 써도 무방함. 하지만 Container 컬러, 패딩 등 다양한 것들을 지정할 수 있기 때문에 크기 조절만 할 거라면 SizedBox가 좋음
width: 100,
height: 100,
child: Image.asset(imageList[index]),
),
Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
Text(titleList[index],
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.grey),
),
const SizedBox(
height: 10,
),
SizedBox(
width: width,
child: Text(
description[index],
style: TextStyle(
fontSize: 15,
color: Colors.grey[500]
),
),
)
],
),)
],
),
),
);
},
),
);
}
}
실습으로 리스트 데이터를 만들고 각 리스트 아이템에 데이터를 출력했음.
하지만 출력할 데이터가 10000개라면?
- ListView vs. ListView.builder
1. 공통점
- 스크롤이 가능한 배열형 위젯
2. 차이점
- ListView : 리스트뷰 안에 모든 차일드를 생성해서 보여줌. 데이터가 적을때 용이
- ListView.builder : 그때그때 필요한 만큼만 데이터를 저장소나 서버에서 불러옴.
즉, 화면에 보이는 만큼의 데이터들을 그때그때 불러온다.