To-Do List 앱 Step 2-3. 다이얼로그 > 추가 텍스트 버튼 추가

2024. 11. 26. 14:20같이 공부합시다 - Flutter/Flutter로 To-Do 앱 만들기

728x90
반응형

 
할 일 목록입니다. 😆
 

1 . 화면 오른쪽 아래에 “+ (할 일 추가)” 플로팅 버튼 만들기.
2-1. 플로팅 버튼을 눌렀을 때 다이얼로그가 열리고, 사용자가 할 일을 입력할 수 있음.
2-2. ‘취소’ 텍스트 버튼 (다이얼로그만 닫음)
2-3. ‘추가’ 텍스트 버튼 (할 일 리스트에 추가하면서 다이얼로그 닫음)
3 . 화면에 할 일 리스트 표시.

 
이번에는 다이얼로그에 ‘추가’ 버튼을 추가할 겁니다.
‘취소’ 버튼과 같이 구조는 같을 거예요.
 
하지만, 중요한 것은 다이얼로그에 입력한 텍스트가
화면에 뿌려지도록 해야 한다는 것이죠.
 
생각으로는 쉬운데 막상 만드려면,
“음. 어떻게 하는 거지?” 가 되죠. 😅
 
같이 하나씩 해 봅시다.

 



1) ‘추가’ 텍스트 버튼 만들기


일단, ‘취소’ 버튼과 껍데기는 같겠죠?
 

  // 다이얼로그를 표시한 메서드
  void _showSimpleDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text("할 일 추가"),
          content: const TextField(
            decoration: InputDecoration(
              hintText: "할 일을 입력하세요",
            ),
          ),
          actions: [
            // 취소 버튼
            TextButton(
              onPressed: () {
                Navigator.of(context).pop(); // 다이얼로그 닫기
              },
              child: const Text("취소"),
            ), // TextButton
            TextButton(
              onPressed: () {
                // 할 일 리스트 추가하기
                print("할 일을 추가했어요!");
              },
              child: const Text("추가"),
            ), // TextButton
          ], 
        ); // AlertDialog
      },
    );
  } // __showSimpleDialog

 


여기까진 쉽네요.

‘추가’ 버튼을 눌렀을 때,
추가가 되면서 다이얼로그는 닫혀야 되겠죠.

print() 아래 ‘취소’ 버튼 때와 같이,
Navigator.of(context).pop(); 을 추가해 줍시다.


TextButton(
              onPressed: () {
                // 할 일 리스트 추가하기
                print("할 일을 추가했어요!");
                Navigator.of(context).pop(); // 다이얼로그 닫기
              },
              child: const Text("추가"),
            ),

 
테스트하면 이제 콘솔에 찍히면서
다이얼로그는 닫히는 것을 확인할 수 있습니다. 굿!







다음은 다이얼로그에서 입력한 값이
‘할 일 목록’ 에 진짜로 추가되도록 하는 것이네요.

흐음… 어떻게 하는 걸까요.


출처 : ChatGPT


일단 생각하기로는 ‘할 일 목록’ 에 추가해야 하니까,
목록을 저장할 공간이 필요하겠고…

그리고 ‘추가’ 버튼을 눌렀을 때,
입력한 값이 저장되어야 하니까…

이건 함수로 만들면 어떨까요?

그럼 리스트 변수를 만들고,
이 리스트에 저장할 수 있도록 함수를 만들면 !

굿 ! 바로 진행해 봅시다.







2) ‘할 일 목록’ 리스트 선언하기



일단 리스트를 추가 시도했습니다.


 
헛.. 바로 경고에 오류.. 그래 내가 잘못했다 !


오류 메시지를 보니까 const 생성자를 사용했으면,
final 이 아닌 필드는 사용할 수 없는 것이었네요.
 


 
아하, 좋아요. 그럼 final 을 넣어봅시다.


 
허허, final 필드가 되니 const 생성자랑 부딪히네요. 흥.
이제 어떻게 할까요. 끝을 봅시다. (ㅎ)
 
 


 
 
사용자로부터 값이 입력되어서 화면에 뿌려지도록 하려면
StatefulWidget { } 을 사용해야 하는 것 같아요.
 
class TodoListScreen extends StatelessWidget { }
class TodoListScreen  extends StatefulWidget { }
 
우선 class TodoListScreen 의 extends 가
StatelessWidget  에서 StatefulWidget  으로 바뀌었습니다.
 
바뀐 이유는,
입력되는 값에 따라 화면(UI)이 바뀌어야 하기 때문이예요.
 
즉, 할 일 리스트가 사용자에 의해 추가되면
상태(State)가 변경됨에 따라
화면 UI 가 변경되어야 하기 때문이죠.
 
https://flutter.devState management | Flutter
경로에 문서로 나와 있는데...
전 한글로 번역해도 외국어처럼 보였습니다.
 
더 쉽게 !
StatelessWidget 은 화면이 바뀔 필요가 없을 때,
StatefulWidget 은 화면이 바뀔 수 있을 때 사용합시다.
 
 


3) StatelessWidget 에서 StatefulWidget 으로 교체하기


그래서 어떻게 하면 되는가.
우선 TodoListScreen 클래스의 확장을
StatefulWidget 으로 바꿉시다.


바로 오류가 나는데,
StatefulWidget 은 반드시
오버라이드 해야 하는 메서드가 있어서예요.
 
Quick Fix 를 사용하셔도 되고,
직접 타이핑 해 보셔도 좋습니다.
 
여기서 중요한 것은,
클래스를 하나 더 만들어야 한다는 것이죠. 두둥!
 
StatelessWidget 에서는 같은 TodoListScreen 클래스에서
버튼을 만들거나 다이얼로그를 만들었어요.
 
그런데 StatefulWidget 으로 바꾸면,
StatefulWidget 의 클래스 말고
State 클래스를 하나 더 만들어서 관리해야 합니다.
 
내용은 이렇습니다.


Flutter 는 [ StatefulWidget ] 과 [ State ] 를 분리하여 관리합니다:

- StatefulWidget:
    - 위젯의 구조와 외형(UI)을 정의하는 클래스.
    - 변경되지 않는 부분(불변)을 정의합니다.
    
- State:
    - 위젯의 상태(State)를 관리하고, 변경 가능한 데이터와 로직을 포함합니다.
    - 상태가 변경되면 UI를 재구성하는 역할을 합니다.

이런 구조를 꼭 지켜야 하는 이유는 이렇습니다.

- 명확한 역할 분리
    - StatefulWidget은 UI를 정의하고, State는 상태 변경 및 로직을 처리합니다.
    - 역할을 분리하면 유지보수와 코드 가독성이 좋아집니다.

- 상태 관리의 일관성
    - 모든 상태는 State 객체에서 관리됩니다.
    - Flutter의 위젯 트리는 불변(immutable) 구조를 기반으로 동작하므로,
    상태 변경은 State를 통해서만 가능합니다.


 


 
 
어쨌든 이런 이유에서 클래스를 하나 더 만들어야 합니다.
 

기존 :
- class TodoListScreen extends StatelessWidget { }

변경 :
- class TodoListScreen extends StatefulWidget { }
- class _TodoListScreen extends State<TodoListScreen> { }


좋습니다.
 
100% 이해가 됐다고는 하기 어렵지만,
클래스를 분리해야 한다는 것은 알았네요.
 
그럼 _TodoListScreenState 에 기존에 있었던
코드를 몽땅 넣으면 될까요 ? 맞습니다. Good guess !
 
전체 코드를 한 번 정리하고 가야겠네요.
 

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false, // 디버그 배너 숨기기
      title: 'To-Do List',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const TodoListScreen(),
    );
  }
}

class TodoListScreen extends StatefulWidget {
  const TodoListScreen({super.key});

  @override
  _TodoListScreenState createState() => _TodoListScreenState();
}
  
   // build() {}
class _TodoListScreenState extends State<TodoListScreen> {
  final List<String> _todoList = []; // 할 일 목록

  // 다이얼로그를 표시한 메서드
  void _showSimpleDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text("할 일 추가"),
          content: const TextField(
            decoration: InputDecoration(
              hintText: "할 일을 입력하세요",
            ),
          ),
          actions: [
            // 취소 버튼
            TextButton(
              onPressed: () {
                Navigator.of(context).pop(); // 다이얼로그 닫기
              },
              child: const Text("취소"),
            ),
            TextButton(
              onPressed: () {
                // 할 일 리스트 추가하기
                print("할 일을 추가했어요!");
                Navigator.of(context).pop(); // 다이얼로그 닫기
              },
              child: const Text("추가"),
            ),
          ],
        );
      },
    );
  } // __showSimpleDialog

  @override
  Widget build(BuildContext context) {
    var floatingActionButton = FloatingActionButton(
      onPressed: () {
        _showSimpleDialog(context); // 다이얼로그 호출
      }, // 다이얼로그 호출
      child: const Icon(Icons.add),
    );
    return Scaffold(
      appBar: AppBar(
        title: const Text('To-Do List'),
      ),
      body: const Center(
        child: Text('할 일이 없습니다!'),
      ),
      floatingActionButton: floatingActionButton,
    );
  }
}

 
물론 앱에서는 달라진 점이 없습니다.
이제 리스트 변수를 쓸 수 있게 된 정도… 후…
쉬고 오고 싶지만 그래도 마무리는 해야죠.
 
자, 이제 할 일 목록을 담을 수 있게 되었으니까,
이제 넣기만 하면 되겠네요 ! 힘냅시다 !
 
 


 
 
‘추가’ 텍스트 버튼에서 print( ) 만 되어 있는 부분을
이제는 드디어 진짜 목록에 추가해 봅시다.
 
필요한 내용을 주욱 이어가 보면,
일단 다이얼로그에서 입력되는
‘할 일(텍스트)’을 저장할 변수를 만들고,
이 ‘할 일(텍스트)’을 받아서
리스트에 담는 함수를 만들면 되겠네요.
 
 

- 할 일(텍스트) 저장할 변수 만들기
- 할 일(텍스트) 를 받아서
   ‘할 일 목록(리스트)’ 에 담는 함수 만들기
- 함수 호출 해보기

 
 
 


 
 
 

4) 할 일(텍스트) 저장할 변수 만들기


다이얼로그 안에서 사용하는 거니까,
void _showSimpleDialog(BuildContext context) {
아래에 선언해 두면 되겠네요 : ) 굿.
 

변수만 만들었다고 끝난게 아니죠.

다이얼로그에 입력받은 ‘할 일’ 을 변수에 넣어야 합니다.
다이얼로그에서 입력받는 곳은 TextField 로 되어 있고,
입력받는 모든 값은 onChanged 속성으로
체크할 수 있습니다.
 


 
TextField에 입력되는 모든 값은 value 로 받아서,
newTask 할 일 변수로 담아 두는 거죠.
⚠️ TextField 의 const 는 제거합니다. 😀
 
 


 


 

5) 호출 결정하기

입력된 값은 newTask 로 담았었죠?
newTask 를 _addTodoItem 메서드로 보냅시다!
(아직은 메서드가 없죠 다음 단계에서 만들어 봅시다!)

 
 
 


 

6) 할 일(텍스트) 를 받아서 ‘할 일 목록(리스트)’ 에 담는 함수 만들기


메서드 호출하면서 받은 newTask 를
할 일 목록(리스트) 에 더해 봅시다.


좋아요. 깔끔하네요 !
 
바로 확인하고 싶지만, 앱에서는 변화가 없어요.
아직 화면에 뿌려주는 부분은 없으니까요… 험난하네요.
 
일단 ‘할 일 목록(리스트)’ 에 값이
잘 저장되는지 찍어봅시다.
콘솔에 찍는 print 기억하시죠? 구웃 ~
 

(콘솔에 찍기)


 
후아~~ 여기까지 정말 고생하셨습니다.
 
처음 보는 내용이 많아서 시간이 좀 오래 걸렸네요.
앞으로 점점 빨라질테니 걱정 안해도 될 것 같습니다.
 
다음 시간에는 대망의
‘화면에 할 일 리스트 보여주기’ 입니다.
 
오늘도 수고하셨습니다.
바람이나 쐬러 가야겠어요. 후~~
 
비도 오고 내일부터는 정말 추워진답니다.
옷 따뜻하게 입고 다니시고 감기 조심하세요 : )
 
끝!
 
 
 
 
 
 
 
 

728x90
반응형