Flutter - ListView, Card, Navigator - 스크롤 가능한 목록 표시, 목록 선택시 특정 목록 내용 보여주기

반응형

 

부제 : 안드로이드에서 사용하는 'Recycler View Adapter', 플러터에서 구현하기

ListView와 Card 위젯 사용 예시를 위해 DB 연동은 제외하고 기능만을 설명합니다.

포스팅에서 설명한 예시 프로젝트는 깃허브에서 다운로드 가능합니다.

'1_base_listview_example' 폴더를 확인해주세요.

https://github.com/luvris2/flutter_memo_app

 

GitHub - luvris2/flutter_memo_app

Contribute to luvris2/flutter_memo_app development by creating an account on GitHub.

github.com


레이아웃 관련 위젯 설명

ListView

  • 선형으로 정렬된 스크롤이 가능한 위젯
  • 가장 일반적으로 사용되는 스크롤 위젯
  • 다양한 사용자 지정 옵션을 사용하여 많은 양의 데이터를 효율적으로 표시하는데 사용
  • 속성
    • scrollDirection
      • 스크롤 방향 결정
      • 세로 : Axis.vertical(기본값)
      • 가로 : Axis.horizontal
    • shrinkWrap
      • 목록을 shrinkWarp 위젯으로 래핑할지 여부를 부울 값으로 설정
      • true : 목록은 콘텐츠를 표시하는데 필요한 공간만 차지
      • false : 사용 가능한 모든 공간 차지
    •  padding
      • 전체 목록 주변의 안쪽 여백 설정
    • itemCount
      • 목록의 항목 수
      • 목록이 정적이면 수동으로 설정
      • 동적인 경우 null로 설정하여 항목이 추가될 때 동적으로 추가
    • itemBuilder
      • 목록의 각 항목에 대해 호출되는 콜백 함수
      • 내용(BuildContext)r과 인덱스(index)라는 두 가지 인수를 사용
      • 함수는 항목을 나타내는 위젯 반환
    • separatorBuilder
      • ListView의 각 항목 사이에 구분 기호를 삽입하는데 사용
      • 주로 Divider 위젯으로 목록 각 항목 사이의 구분 기호로 사용
      • 위젯을 반환하는 함수를 사용하며 목록의 모든 항목에 대해 한 번씩 호출
    • controller
      • 목록의 스크롤 동작을 제어하는데 사용
      • 목록의 특정 위치로 스크롤할 수 있는 jumpTo, animateTo 같은 메서드 제공
      • 스크롤 이벤트를 수신할 수 있는 addListener 메서드 제공, 페이징 처리를 위해 주로 사용

GridView

  • 스크롤 가능한 위젯 그리드를 2차원 레이아웃으로 표시
  • 그리드의 모양과 동작을 사용자가 지정하는 여러 속성 제공
  • 속성
    • scrollDirection
      • 스크롤 방향 결정
      • 세로 : Axis.vertical(기본값)
      • 가로 : Axis.horizontal
    • crossAxisCount
      • 각 행(또는 scrollDirection에 따라 열)의 항목수 결정
      • 기본값 : 2
    • crossAxisSpacing
      • 행(또는 scrollDirection에 따라 열)에서 항목 사이의 간격 결정
      • 기본 값 : 0
    • mainAxisSpacing
      • 행(또는 scrollDirection에 따라 열) 사이의 간격 결정
      • 기본 값 : 0
    •  childAspectRatio
      • 그리드의 각 항목의 화면 비율 결정
      • 위젯의 너비와 높이 사이의 비율을 나타내는 이중 값 사용
    • padding
      • 전체 그리드 주변의 안쪽 여백 결정
    • controller
      • 목록의 스크롤 동작을 제어하는데 사용
      • 그리드의 특정 위치로 스크롤 하는 메서드 제공
      • 스크롤 이벤트를 수신할 수 있는 메서드 제공, 페이징 처리를 위해 주로 사용
    • gridDelegate
      • 그리드의 레이아웃 알고리즘 및 기타 시각적 속성 지정
      • 행당 항목 수, 각 항목의 종횡비 및 항목 사이의 안쪽 여백(패딩)과 같은 그리드의 모양을 사용자가 다양하게 지정할 수 있는 옵션 제공

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를 제어하고 화면을 변경하거나 이전 화면으로 이동

설계 및 코드 작성

설계

  1. ListView를 이용하여 목록 출력
  2. 출력되는 목록은 항목을 구분하기 위해 Card를 사용
  3. Card의 항목을 누르면 다른 폼 호출
  4. 폼 호출시 Navigator를 이용하여 push() 메서드로 화면 추가
  5. 호출된 폼은 선택한 항목과 관련 있는 값을 출력해주어야 함

 

변수 가정

  1. 목록의 항목은 List<String> items 변수에 임의 저장한다.
  2. 카드를 누를 때 표시되는 메시지는 List<String> itemsContents 변수에 임의 저장한다.
    • 단, items, itemsContents의 인덱스는 반드시 같아야 한다.
  3. 표현되는 폼의 내용은 카드를 클릭했던 items 변수의 인덱스와 동일해야한다.

 

호출 방법

  1. item 변수를 ListView의 항목에 출력한다.
  2. 카드 클릭시 출력되는 폼의 내용은 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 항목 클릭시


참고

반응형