Flutter - 리스트에 아이템 추가

유의선·2024년 3월 6일

리스트에 아이템을 추가로 등록하는 기능을 만들어보았다.
이전 프로젝트에 이어서 진행하였다.


secondPage.dart의 SecondApp 클래스에 해당 기능을 구현하였다.

먼저 FirstApp 클래스와 같게 list를 받도록 secondPage.dart와 main.dart를 수정하였다.

// secondPage.dart

import 'package:flutter/material.dart';
import 'package:listview_example/animalItem.dart';

class SecondApp extends StatelessWidget {
  final List<Animal>? list;

  SecondApp({Key? key, this.list}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Center(
          child: Text('두 번째 페이지'),
        ),
      ),
    );
  }

}

// main.dart

...

      body: TabBarView(
        children: <Widget>[FirstApp(list: animalList), SecondApp(list: animalList)],
        controller: tabController,
      ),

...

그 후 SecondApp이 StatefulWidget을 상속하도록 만들었고, createState()에서 반환할 State를 상속한 _SecondApp 클래스를 만들었다.

class SecondApp extends StatefulWidget {
  final List<Animal>? list;

  SecondApp({Key? key, this.list}) : super(key: key);

  
  State createState() => _SecondApp();
}

class _SecondApp extends State<SecondApp> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[],
          ),
        ),
      ),
    );
  }
}

dart에서는 반환값을 한 줄로 표현하고자 할 때 => 화살표를 이용할 수 있다.

  
  State createState() => _SecondApp();

children: <Widget>[] 배열 안에 동물 이름을 입력할 텍스트 필드를 추가했다.

class _SecondApp extends State<SecondApp> {

  final nameController = TextEditingController();

		...

            children: <Widget>[
              TextField(
                controller: nameController,
                keyboardType: TextInputType.text,
                maxLines: 1,
              )
            ],
          ),

		...

keyboardType으로 키보드 유형을 text로 설정하였고,
controller로 final로 선언한 nameController를 설정하였다.


라디오 버튼을 이용해 동물의 종류를 선택할 수 있도록 코드를 추가하였다.

class _SecondApp extends State<SecondApp> {
  final nameController = TextEditingController();

  int? _radioValue = 0;

		...
        
            children: <Widget>[
              TextField( ... ),
              Row(
                children: <Widget>[
                  Radio(
                      value: 0,
                      groupValue: _radioValue,
                      onChanged: _radioChange),
                  Text('양서류'),
                  Radio(
                      value: 1,
                      groupValue: _radioValue,
                      onChanged: _radioChange),
                  Text('파충류'),
                  Radio(
                      value: 2,
                      groupValue: _radioValue,
                      onChanged: _radioChange),
                  Text('포유류'),
                ],
              )
            ],
	
    ...

  _radioChange(int? value) {
    setState(() {
      _radioValue = value;
    });
  }
}

각 위젯이 가로로 배치되도록 Row안에 작성하였다.
value는 인덱스 값이다. 각각 0, 1, 2를 넣어 인덱스를 부여했다.
groupValue는 그룹화를 담당한다. int형인 _radioValue를 선언하여 넣었다.
onChanged는 이벤트 처리를 담당한다. _radioChange() 함수를 정의하여 넣었다.


동물이 날 수 있는지 선택하는 체크박스를 추가하였다.

class _SecondApp extends State<SecondApp> {
  final nameController = TextEditingController();
  int? _radioValue = 0;
  bool? flyExist = false;

		...
        
            children: <Widget>[
              TextField( ... ),
              Row( ... ),
              Row(
                children: <Widget>[
                  Text('날 수 있나요?'),
                  Checkbox(
                      value: flyExist,
                      onChanged: (bool? check) {
                        setState(() {
                          flyExist = check;
                        });
                      })
                ],
              )
            ],
            
       ...

동물 이미지를 고를 수 있도록 만들었다.
이미지를 클릭하면 해당 이미지를 선택하도록 만들었다.

class _SecondApp extends State<SecondApp> {
  final nameController = TextEditingController();
  int? _radioValue = 0;
  bool? flyExist = false;
  String? _imagePath;

  	...
    
            children: <Widget>[
              TextField( ... ),
              Row( ... ),
              Row( ... ),
              Row(
                children: <Widget>[
                  GestureDetector(
                    child: Image.asset(
                      'repo/images/cow.png',
                      width: 80,
                    ),
                    onTap: () {
                      _imagePath = 'repo/images/cow.png';
                    },
                  ),
                  GestureDetector(
                    child: Image.asset(
                      'repo/images/fox.png',
                      width: 80,
                    ),
                    onTap: () {
                      _imagePath = 'repo/images/fox.png';
                    },
                  ),
                  GestureDetector(
                    child: Image.asset(
                      'repo/images/bee.png',
                      width: 80,
                    ),
                    onTap: () {
                      _imagePath = 'repo/images/bee.png';
                    },
                  ),
                  GestureDetector(
                    child: Image.asset(
                      'repo/images/cat.png',
                      width: 80,
                    ),
                    onTap: () {
                      _imagePath = 'repo/images/cat.png';
                    },
                  ),
                ],
              )
            ],

Row 위젯은 이미지가 화면을 벗어나면 다 표시하지 못한다. 그래서 지금은 이미지를 4개만 넣었다.
이를 리스트뷰를 사용하면 이미지를 더 넣을 수 있다.
리스트뷰의 scrollDirection을 사용하면 스크롤 방향을 정할 수 있다.

Row로 만든 이미지 선택 영역을 가로 리스트로 변경하고, 동물들을 추가하였다.

            children: <Widget>[
              TextField( ... ),
              Row( ... ),
              Row( ... ),
              
              Container(
                height: 100,
                child: ListView(
                  scrollDirection: Axis.horizontal,
                  children: <Widget>[
                    GestureDetector(
                      child: Image.asset(
                        'repo/images/cow.png',
                        width: 80,
                      ),
                      onTap: () {
                        _imagePath = 'repo/images/cow.png';
                      },
                    ),
                    GestureDetector(
                      child: Image.asset(
                        'repo/images/fox.png',
                        width: 80,
                      ),
                      onTap: () {
                        _imagePath = 'repo/images/fox.png';
                      },
                    ),
                    GestureDetector(
                      child: Image.asset(
                        'repo/images/bee.png',
                        width: 80,
                      ),
                      onTap: () {
                        _imagePath = 'repo/images/bee.png';
                      },
                    ),
                    GestureDetector(
                      child: Image.asset(
                        'repo/images/cat.png',
                        width: 80,
                      ),
                      onTap: () {
                        _imagePath = 'repo/images/cat.png';
                      },
                    ),
                    GestureDetector(
                      child: Image.asset(
                        'repo/images/pig.png',
                        width: 80,
                      ),
                      onTap: () {
                        _imagePath = 'repo/images/pig.png';
                      },
                    ),
                    GestureDetector(
                      child: Image.asset(
                        'repo/images/wolf.png',
                        width: 80,
                      ),
                      onTap: () {
                        _imagePath = 'repo/images/wolf.png';
                      },
                    ),
                  ],
                ),
              )
            ],

위젯들을 양쪽 여백 사이에 균일하게 배치하기 위해 각 Row마다 mainAxisAlignment: MainAxisAlignment.spaceAround를 추가하였다.

            children: <Widget>[
              TextField(
                controller: nameController,
                keyboardType: TextInputType.text,
                maxLines: 1,
              ),
              Row(
              	children: <Widget>[ ... ],
                mainAxisAlignment: MainAxisAlignment.spaceAround),
              Row(
              	children: <Widget>[ ... ],
              	mainAxisAlignment: MainAxisAlignment.spaceAround),
                
                Container( ... )
            ],

동물을 목록에 추가하는 버튼을 만들었다.
버튼을 누르면 추가하기 전에 알람 창을 띄우고 확인을 누르면 동물이 추가되도록 만들었다.

            children: <Widget>[
              TextField( ... ),
              Row( ... ),
              Row( ... ),
              Container( ... ),
              
              ElevatedButton(
                  onPressed: () {
                    var animal = Animal(
                        animalName: nameController.value.text,
                        kind: getKind(_radioValue),
                        imagePath: _imagePath,
                        flyExist: flyExist);
                    AlertDialog dialog = AlertDialog(
                      title: Text('동물 추가하기'),
                      content: Text(
                        '이 동물은 ${animal.animalName} 입니다. '
                        '동물의 종류는 ${animal.kind} 입니다. \n이 동물을 추가하시겠습니까?',
                        style: TextStyle(fontSize: 30.0),
                      ),
                      actions: [
                        ElevatedButton(
                            onPressed: () {
                              widget.list?.add(animal);
                              Navigator.of(context).pop();
                            },
                            child: Text('예')),
                        ElevatedButton(
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                            child: Text('아니요'))
                      ],
                    );
                    showDialog(
                        context: context,
                        builder: (BuildContext context) => dialog);
                  },
                  child: Text('동물 추가하기'))
            ],
  
  ...

  _radioChange(int? value) { ... }

  getKind(int? radioValue) {
    switch (radioValue) {
      case 0:
        return "양서류";
      case 1:
        return "파충류";
      case 2:
        return "포유류";
    }
  }

AlertDialog의 Title은 알림 창에 표시할 제목이고, content는 내용이다.
actions는 배열 형태로 위젯을 가져올 수 있다. 여기서는 버튼을 두 개 선언해 각각 "예", "아니오" 를 Text로 보이도록 하였고, "예"를 누르면 리스트에 추가하고 꺼지도록, "아니오"를 누르면 그냥 꺼지도록 하였다.


0개의 댓글