본문 바로가기
Development/Flutter

Flutter - 9. TodoList App (2) Todo Item 추가하기 (AlertDialog, FloatingActionButton)

by du.it.ddu 2021. 1. 25.
반응형

오늘 만들 화면은 위와 같다.

위 화면을 구성하기 위해 할일은 다음과 같다.

  • FloatingActionButton 생성
  • Todo Item 추가를 위한 AlertDialog 생성
  • 생성된 Todo Item을 추가하고 리스트를 갱신

어려운 것은 없으니 천천히 시작해본다.


1. TodoModel 작성

Todo Item을 표현하기 위한 TodoModel 클래스를 작성한다.

// todo_model.dart
class TodoModel {
  String _title;
  TodoState _state;

  final DateTime _createdTime;
  
  TodoModel(this._title, this._createdTime) {
    _state = TodoState.todo;
  }

  String getTitle() => _title;
  DateTime getCreatedTime() => _createdTime;
  TodoState getTodoState() => _state;

  String setTitle(String title) => _title = title;
  TodoState setTodoState(TodoState state) => _state = state;
}

enum TodoState {
  todo, inProgress, done
}

TodoModel은 아래 항목들을 가지고 있다.

  • String _title : TodoItem의 제목을 의미한다.
  • TodoState _state : TodoItem의 상태다. 할일인지, 하고 있는지, 완료되었는지를 나타낸다.
  • DateTime _createdTime : TodoItem의 생성 날짜를 나타낸다.

그리고 위 속성들에 접근할 수 있는 Getter, Setter를 작성한다.


2. 날짜를 다루기 위한 패키지 추가

Flutter에서 DateTime을 우리가 원하는 포맷(yyyy-MM-dd와 같은)으로 변경하기 위해 패키지가 필요하다.


pub.dev/packages/intl

 

intl | Dart Package

Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.

pub.dev

위 패키지를 추가 할 것이다.
pubspec.yaml에 추가하자.

name: ...
description: ...

publish_to: ...

version: ...

environment:
  ...

dependencies:
  flutter:
    sdk: flutter
  ...
  intl: ^0.16.0 # 이 부분을 추가한다.

...

완료한 후 Pub get으로 패키지를 갱신해주는 것을 잊지말자!


3. TodoListPage에 FloatingActionButton 추가

class _TodoListPageState extends State<TodoListPage> {
  final TextEditingController _todoTitleController = TextEditingController();
  List<TodoModel> _todoList = [];
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: _createFloatingActionButton(),
      floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
      body: _createTodoList(),
    );
  }
  ...
  
  Widget _createFloatingActionButton() {
    return FloatingActionButton(
      child: Icon(Icons.add, color: Colors.white),
      onPressed: () => {
        _openAddTodoDialog()
      },
    );
  }
  
  void _openAddTodoDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10.0)
          ),
          title: Text(
            "할일을 입력해주세요.",
            style: TextStyle(
              fontWeight: FontWeight.bold,
              fontSize: 24.0,
              color: Colors.blue
            ),
          ),
          content: TextField(
            controller: _todoTitleController,
          ),
          actions: [
            FlatButton(
              child: new Text(
                "취소",
                style: TextStyle(
                  fontSize: 16.0,
                  color: Colors.red
                ),
              ),
              onPressed: () {
                _todoTitleController.text = "";
                Navigator.pop(context);
              },
            ),
            FlatButton(
              child: new Text(
                "추가",
                style: TextStyle(
                  fontSize: 16.0,
                  color: Colors.blue
                ),
              ),
              onPressed: () {
                _addNewTodo(_todoTitleController.text);
                _todoTitleController.text = "";
                Navigator.pop(context);
              },
            ),
          ],
        );
      }
    );
  }
  
  void _addNewTodo(String title) {
    TodoModel newTodo = TodoModel(title, DateTime.now());
    setState(() {
      _todoList.add(newTodo);
    });
  }
  ...
}

코드가 꽤 길지만, 단순하다.

  • Scaffold에 FloatingActionButton을 생성하고 누를경우(onPressed) _openAddTodoDialog를 호출하여 AlertDialog를 보여준다.
  • AlertDialog를 생성하는 코드는 위를 참고 바라며, AlertDialog를 보여주기 위해서 "showDialog" 함수에 AlertDialog를 넘겨주면 된다.
  • Flutter에서 TextField로 텍스트를 입력하고 값을 받아오기 위해서 "TextEditingController" 객체를 생성하여 TextField의 controller 파라미터에 전달해주어야 하는 것을 주의한다.
  • 다이얼로그의 "추가" 버튼을 누르면 _addNewTodo가 호출되며, "setState" 함수 내부에서 _todoList에 새로 생성된 TodoModel 객체를 추가한다.

4. TodoListPage의 _createTodoList 함수 변경

class _TodoListPageState extends State<TodoListPage> {
  static const String TODO_DATE_FORMAT = "yyy-MM-dd";
  ...

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: _createFloatingActionButton(),
      floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
      body: _createTodoList(),
    );
  }

  Widget _createTodoList() {
    // 이 부분을 _todoList를 사용하도록 변경한다.
    return ListView.separated(
      itemCount: _todoList.length,
      itemBuilder: (BuildContext context, int index) {
        return _createTodoCard(_todoList[index]);
      },
      separatorBuilder: (BuildContext context, int index) {
        return Divider(
          thickness: 8.0,
          height: 8.0,
          color: Colors.transparent,
        );
      },
    );
  }

  Widget _createTodoCard(TodoModel todoModel) {
    return Card(
      elevation: 4.0,
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(8.0))),
      child:
      Container(padding: EdgeInsets.all(16.0), child: _createTodoItemRow(todoModel)),
    );
  }

  Widget _createTodoItemRow(TodoModel todoModel) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        _createTodoItemContentWidget(todoModel),
        Icon(Icons.keyboard_arrow_right, color: Colors.blue)
      ],
    );
  }

  Widget _createTodoItemContentWidget(TodoModel todoModel) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          todoModel.getTitle(),
          style: TextStyle(
            fontSize: 24.0, 
            color: Colors.blue
          )
        ),
        Divider(
          thickness: 8.0,
          height: 8.0,
          color: Colors.transparent,
        ),
        Text(
          DateFormat(TODO_DATE_FORMAT).format(todoModel.getCreatedTime()),
          style: TextStyle(
            fontSize: 18.0, 
            color: Colors.blueGrey
          )
        )
      ],
    );
  }
  ...
}

크게 달라진 부분은 없다. 데이터를 참조하는 방법만 변경되었다.

  • _createTodoList에서 ListView의 itemCount, itemBuilder를 _todoList를 사용하는 방법으로 변경한다.
  • TodoItem의 UI를 그리는 Widget들의 파라미터에 TodoModel을 전달한다.
    • _createTodoCard(TodoModel todoModel), _createTodoItemRow(TodoModel todoModel) 등이 그러하다.
  • TodoModel의 _createdTime을 yyyy-MM-dd 로 변경하기 위해 DateFormat을 사용한다.
    • DateFormat(TODO_DATE_FORMAT).format(todoModel.getCreatedTime())
    • DateFormat은 intl 패키지 안에 포함되어 있다.

현재까지의 코드는 아래 Github에 저장되어 있다.
Github : 
github.com/DuItDDu/Flutter-Codelabs/tree/master/Flutter-TodoList/flutter_todolist

아주 간단하게 TodoItem을 추가하고 리스트를 변경할 수 있게 되었다.
하지만, 앱을 종료하고 실행하면 모두 사라진다. 이 문제를 해결하기 위해 데이터베이스 작업이 필요하다.
그러므로, 다음 포스팅은 Flutter에 SQLite를 연동하는 작업을 진행 할 것이다.

반응형