Flutter

[Practice] Todolist 만들기2

모리선생 2023. 4. 13. 07:04
728x90

목표

Todolist에서 삭제버튼을 만들어 관리를 할 수 있는 방법을 찾는다. 앱의 재 실행에도 내용이 삭제되지 않도록 SharePreference를 적용한다.


import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

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

///5. 클래스를 만들어준다. 클래스의 생성자를 만든다는 것은 해당 내용을 받을 수 있는 그릇을 만드는 것이라고 해석하면 된다.
///즉, Textformfield에서 입력받은 글자들을 저장하기 위한 저장 그릇이다.
class Todo {
  final String title;
  final String description;

  Todo({required this.title, required this.description});
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

///1. todoApp의 상태는 변한다. 그러므로 Statefulwidget을 쓴다.
class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Todo List',

      ///2. debugshow배너를 삭제한다.
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.purple,
      ),
      home: const todoApp(),
    );
  }
}

///3. StatefulWidget을 상속받는 todoApp 클래스를 만든다.
class todoApp extends StatefulWidget {
  const todoApp({super.key});

  @override
  State<todoApp> createState() => _todoAppState();
}

class _todoAppState extends State<todoApp> {
  ///6. 변수를 선언한다. 변수를 선언한다는 것은 앞으로 이 내용을 사용할 것을 미리 알려주는 것이다.
  ///List 형식을 추가함으로써 어떠한 형태로 저장 될 것인지도 알려준다.
  ///빈문자열이 만들어졌다.

  String title = "";
  String description = "";

  List<Todo> todos = [];

  /// 10. SharedPreferences에 todo 데이터를 불러오는 함수를 만든다.
  Future<void> _loadData() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String>? savedTitles = prefs.getStringList('titles');
    List<String>? savedDescriptions = prefs.getStringList('descriptions');
    if (savedTitles != null && savedDescriptions != null) {
      for (int i = 0; i < savedTitles.length; i++) {
        todos.add(
            Todo(title: savedTitles[i], description: savedDescriptions[i]));
      }
    }
  }

  /// 11. 추가한 Todo 데이터를 SharedPreferences에 저장하는 메소드를 추가합니다.
  Future<void> _saveData() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String> titleList = [];
    List<String> descriptionList = [];

    for (int i = 0; i < todos.length; i++) {
      titleList.add(todos[i].title);
      descriptionList.add(todos[i].description);
    }

    prefs.setStringList('titles', titleList);
    prefs.setStringList('descriptions', descriptionList);
  }

  /// 12. Todo 데이터를 리스트에 저장하기
  Future<void> _addTodo() async {
    // Add a new todo to the list
    setState(() {
      todos.add(Todo(
        title: title,
        description: description,
      ));
    });

    // Save the updated list to SharedPreferences
    await _saveData();
  }

  @override
  void initState() {
    super.initState();
    _loadData();
    _saveData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("todolist"),
        actions: [
          IconButton(

              ///4. 할일을 추가할 수 있는 버튼을 만들어보자.
              onPressed: () {
                showDialog(
                    context: context,
                    barrierDismissible: true,
                    builder: (BuildContext ctx) {
                      return AlertDialog(
                        content: Text("Todo"),
                        actions: [
                          TextFormField(
                            ///7. 변화를 감지할 수 있는 onchanged를 사용한다
                            ///상태를 변화하여 UI에 표현하기 위한 것이 setState이다.
                            onChanged: (value) {
                              setState(() {
                                title = value;
                              });
                            },
                            decoration: InputDecoration(hintText: "title"),
                          ),
                          TextFormField(
                            onChanged: (value) {
                              setState(() {
                                description = value;
                              });
                            },
                            decoration: InputDecoration(hintText: "content"),
                          ),
                          Center(
                            child: ElevatedButton(
                                onPressed: () {
                                  ///9. Add버튼 입력 후 팝업창을 없애보자.
                                  Navigator.of(context).pop();

                                  ///7-1. elevatedbutton을 누를 시 todo list에 값을 추가할 수 있도록 한다.
                                  _addTodo();
                                },
                                child: Text("Add")),
                          )
                        ],
                      );
                    });
              },
              icon: Icon(Icons.add))
        ],
      ),

      ///8. 이제 바디 부분을 만들어서 우리가 입력한 값들이 나타날 수 있도록 설정한다.
      ///Inkwell은 제스쳐 기능을 제공하지 않는 위젯을 이 위젯으로 감싸면 onTap()의 기능을 이용할 수 있다.
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (BuildContext context, int index) {
          final todo = todos[index];
          return InkWell(
            child: ListTile(
              shape: StadiumBorder(
                side: BorderSide(width: 2),
              ),
              title: Text(todo.title),
              subtitle: Text(todo.description),

              /// 9. 삭제 버튼을 삽입하기
              trailing: OutlinedButton(
                onPressed: () {
                  setState(() {
                    todos.removeAt(index);
                  });
                },
                child: Icon(
                  Icons.delete,
                  color: Colors.black,
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

 

추가된 부분 설명

 

9. 내용을 삭제한다. todos.removeAt(index)를 사용하여 삭제를 진행한다.

/// 9. 삭제 버튼을 삽입하기
              trailing: OutlinedButton(
                onPressed: () {
                  setState(() {
                    todos.removeAt(index);
                  });
                },
                child: Icon(
                  Icons.delete,
                  color: Colors.black,
                ),

 

10. SharedPreference의 사용 앱이 종료되어도 유지되도록 하는 todo 데이터를 불러오는 함수이다. getStringList() 메소드를 호출하여, titles와 description이라는 Key값을 가진 데이터를 불러옵니다. 

불러온 데이터가 null이 아니라면 for 루프를 사용하여 todos 리스트에 Todo 클래스 인스턴스를 추가합니다. Todo 클래스의 인스턴스는 불러온 titles와 descriptions의 데이터를 사용하여 생성합니다.

 

즉, ShredPreferences에서 데이터를 불러와서 todos 리스트에 추가하여 이전에 추가한 todo 항목을 유지합니다.

 /// 10. SharedPreferences에 todo 데이터를 불러오는 함수를 만든다.
  Future<void> _loadData() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String>? savedTitles = prefs.getStringList('titles');
    List<String>? savedDescriptions = prefs.getStringList('descriptions');
    if (savedTitles != null && savedDescriptions != null) {
      for (int i = 0; i < savedTitles.length; i++) {
        todos.add(
            Todo(title: savedTitles[i], description: savedDescriptions[i]));
      }
    }
  }

 

11. SharedPreferences.getInstance()를 호출하여 SharedPreferences 인스턴스를 가져오고 있으며 setStringList() 메소드를 호출하여 titles와 descriptions 라는 Key 값을 가진 데이터를 저장한다. title과 description을 각각 titleList와 descriptionList 리스트에 추가하고, prefs.setStringList() 메소드를 이용하여 titles와 descriptions 라는 key 값을 가진 데이터에  titleList와 descriptionList 리스트를 저장한다.

/// 11. 추가한 Todo 데이터를 SharedPreferences에 저장하는 메소드를 추가합니다.
  Future<void> _saveData() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    List<String> titleList = [];
    List<String> descriptionList = [];

    for (int i = 0; i < todos.length; i++) {
      titleList.add(todos[i].title);
      descriptionList.add(todos[i].description);
    }

    prefs.setStringList('titles', titleList);
    prefs.setStringList('descriptions', descriptionList);
  }

 

728x90

'Flutter' 카테고리의 다른 글

5-3. Riverpod (WeatherApp)  (0) 2023.04.17
5-2. Riverpod으로 Todo 앱 만들기  (0) 2023.04.14
11-2. BLoC과 freezed  (0) 2023.04.11
11-1. BLoC의 구조 (상세)  (0) 2023.04.10
11. BLoC  (0) 2023.04.09