반응형
부제 : 안드로이드에서 사용하는 'Recycler View Adapter', 플러터에서 구현하기
ListView와 Card 위젯 사용 예시를 위해 DB 연동은 제외하고 기능만을 설명합니다.
포스팅에서 설명한 예시 프로젝트는 깃허브에서 다운로드 가능합니다.
'1_base_listview_example' 폴더를 확인해주세요.
https://github.com/luvris2/flutter_memo_app
레이아웃 관련 위젯 설명
ListView
- 선형으로 정렬된 스크롤이 가능한 위젯
- 가장 일반적으로 사용되는 스크롤 위젯
- 다양한 사용자 지정 옵션을 사용하여 많은 양의 데이터를 효율적으로 표시하는데 사용
- 속성
- scrollDirection
- 스크롤 방향 결정
- 세로 : Axis.vertical(기본값)
- 가로 : Axis.horizontal
- shrinkWrap
- 목록을 shrinkWarp 위젯으로 래핑할지 여부를 부울 값으로 설정
- true : 목록은 콘텐츠를 표시하는데 필요한 공간만 차지
- false : 사용 가능한 모든 공간 차지
- padding
- 전체 목록 주변의 안쪽 여백 설정
- itemCount
- 목록의 항목 수
- 목록이 정적이면 수동으로 설정
- 동적인 경우 null로 설정하여 항목이 추가될 때 동적으로 추가
- itemBuilder
- 목록의 각 항목에 대해 호출되는 콜백 함수
- 내용(BuildContext)r과 인덱스(index)라는 두 가지 인수를 사용
- 함수는 항목을 나타내는 위젯 반환
- separatorBuilder
- ListView의 각 항목 사이에 구분 기호를 삽입하는데 사용
- 주로 Divider 위젯으로 목록 각 항목 사이의 구분 기호로 사용
- 위젯을 반환하는 함수를 사용하며 목록의 모든 항목에 대해 한 번씩 호출
- controller
- 목록의 스크롤 동작을 제어하는데 사용
- 목록의 특정 위치로 스크롤할 수 있는 jumpTo, animateTo 같은 메서드 제공
- 스크롤 이벤트를 수신할 수 있는 addListener 메서드 제공, 페이징 처리를 위해 주로 사용
- scrollDirection
GridView
- 스크롤 가능한 위젯 그리드를 2차원 레이아웃으로 표시
- 그리드의 모양과 동작을 사용자가 지정하는 여러 속성 제공
- 속성
- scrollDirection
- 스크롤 방향 결정
- 세로 : Axis.vertical(기본값)
- 가로 : Axis.horizontal
- crossAxisCount
- 각 행(또는 scrollDirection에 따라 열)의 항목수 결정
- 기본값 : 2
- crossAxisSpacing
- 행(또는 scrollDirection에 따라 열)에서 항목 사이의 간격 결정
- 기본 값 : 0
- mainAxisSpacing
- 행(또는 scrollDirection에 따라 열) 사이의 간격 결정
- 기본 값 : 0
- childAspectRatio
- 그리드의 각 항목의 화면 비율 결정
- 위젯의 너비와 높이 사이의 비율을 나타내는 이중 값 사용
- padding
- 전체 그리드 주변의 안쪽 여백 결정
- controller
- 목록의 스크롤 동작을 제어하는데 사용
- 그리드의 특정 위치로 스크롤 하는 메서드 제공
- 스크롤 이벤트를 수신할 수 있는 메서드 제공, 페이징 처리를 위해 주로 사용
- gridDelegate
- 그리드의 레이아웃 알고리즘 및 기타 시각적 속성 지정
- 행당 항목 수, 각 항목의 종횡비 및 항목 사이의 안쪽 여백(패딩)과 같은 그리드의 모양을 사용자가 다양하게 지정할 수 있는 옵션 제공
- scrollDirection
Card
- 포함된 경계 영역에 콘텐츠 및 관련 작업을 표시하는데 사용
- 카드 위젯은 주로 ListView 또는 GridView와 함께 사용하여 여러 카드 표시
- 이 경우 ListView, GridView는 builder 기능을 사용하여 각 카드를 동적으로 생성
- 속성
- child : 카드 내부에 표시될 자식 위젯 지정
- color : 카드의 배경 색상 설정
- elevation : 카드의 그림자 설정
- shape : 카드의 모양 설정, RoundedRectangleBorder, CircleBorder, BeveledRectangleBorder 또는 사용자 정의 모양
- borderOnforeground : 자식 위젯 위에 카드의 테두리를 그릴지 여부 결정
- semanticContainer : 카드가 이미지에 대한 레이블 제공과 같은 의미론적 목적으로 사용되는지 여부 결정
Navigator
- 앱의 다른 화면(화면 전환)을 관리하는 데 사용되는 위젯
- push() 메서드 : 새로운 화면을 추가할 때 사용
- pop() 메서드 : 이전 화면으로 이동할 때 사용
- 일반적으로 MaterialApp 또는 CupertinoApp의 자식으로 사용
- 앱의 라우팅 및 화면 전환 기능 지원
- 속성
- initialRoute : 앱이 시작될 때 표시할 화면의 경로 설정, 기본 값 '/'
- onGenerateRoute : 라우팅 요청이 발생했을 때 호출되는 콜백함수, Route 객체 반환
- onUnknownRoute : 지정된 경로가 없는 경우 호출되는 콜백함수, Route 객체 반환
- navigatorKey : Navigator를 참조할 수 있는 GlobalKey, 이 키를 사용하여 Navigator를 제어하고 화면을 변경하거나 이전 화면으로 이동
설계 및 코드 작성
설계
- ListView를 이용하여 목록 출력
- 출력되는 목록은 항목을 구분하기 위해 Card를 사용
- Card의 항목을 누르면 다른 폼 호출
- 폼 호출시 Navigator를 이용하여 push() 메서드로 화면 추가
- 호출된 폼은 선택한 항목과 관련 있는 값을 출력해주어야 함
변수 가정
- 목록의 항목은 List<String> items 변수에 임의 저장한다.
- 카드를 누를 때 표시되는 메시지는 List<String> itemsContents 변수에 임의 저장한다.
- 단, items, itemsContents의 인덱스는 반드시 같아야 한다.
- 표현되는 폼의 내용은 카드를 클릭했던 items 변수의 인덱스와 동일해야한다.
호출 방법
- item 변수를 ListView의 항목에 출력한다.
- 카드 클릭시 출력되는 폼의 내용은 itemContents 변수로 표현한다.
코드 작성
- 해당 항목을 누를 때 특정 항목의 정보 보여주기
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
List<String> items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
List<String> itemContents = [
'Item 1 Contents',
'Item 2 Contents',
'Item 3 Contents',
'Item 4 Contents'
];
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
void cardClickEvent(BuildContext context, int index) {
String content = itemContents[index];
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ContentPage(content: content),
),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MemoApp', // 앱의 아이콘 이름
home: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('ListView Example'), // 앱 상단바 설정
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.elliptical(20, 20))),
child: ListTile(
title: Text(items[index]),
onTap: () => cardClickEvent(context, index),
),
);
},
),
),
);
}
}
class ContentPage extends StatelessWidget {
final String content;
const ContentPage({Key? key, required this.content}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Content'),
),
body: Center(
child: Text(content),
),
);
}
}
실행 화면
- 첫 실행 화면
- item 2 항목 클릭시
참고
반응형