🍃

【Flutter】TODOアプリを作ってみた

に公開

はじめに

DroidKaigi 2025(オフライン)に参加したばかりのHaruです。アプリを作りたい欲が出てきたため、ほぼ初心者同然であるFlutterを用いて、TODOアプリを作ってみました。

やったこと

環境構築関係

  • Flutterの環境構築
    私がまとめたスクラップである、Flutter環境構築備忘録を参考にしていただければと思います。
  • flutter doctorを実行(flutter doctor --android-licenses実行後の結果)
    Doctor summary (to see all details, run flutter doctor -v):
    [] Flutter (Channel stable, 3.27.1, on macOS 15.6.1 24G90 darwin-x64, locale
        ja-JP)
    [] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
    [] Xcode - develop for iOS and macOS (Xcode 16.4)
    [] Chrome - develop for the web
    [] Android Studio (version 2025.1)
    [] VS Code (version 1.103.2)
    [] Connected device (2 available)
    [] Network resources
    
    • No issues found!
    
  • flutter --version
    Flutter 3.27.1 • channel stable • https://github.com/flutter/flutter.git
    Framework • revision 17025dd882 (9 months ago)2024-12-17 03:23:09 +0900
    Engine • revision cb4b5fff73
    Tools • Dart 3.6.0 • DevTools 2.40.2
    
  • flutter upgradeで最新版にしておく。(2025/9/15現在の最新版)
    Flutter 3.35.3 • channel stable • https://github.com/flutter/flutter.git
    Framework • revision a402d9a437 (12 days ago)2025-09-03 14:54:31 -0700
    Engine • hash 672c59cfa87c8070c20ba2cd1a6c2a1baf5cf08b (revision ddf47dd3ff) (11
    days ago)2025-09-03 20:02:13.000Z
    Tools • Dart 3.9.2 • DevTools 2.48.0
    
  • flutter docterを実行(flutter upgrade実行後の結果)
    Doctor summary (to see all details, run flutter doctor -v):
    [] Flutter (Channel stable, 3.35.3, on macOS 15.6.1 24G90 darwin-x64, locale
      ja-JP)
    [] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
    [] Xcode - develop for iOS and macOS (Xcode 16.4)
    [] Chrome - develop for the web
    [] Android Studio (version 2025.1)
    [] VS Code (version 1.103.2)
    [] Connected device (2 available)
    [] Network resources
    
    • No issues found!
    

TODOアプリ(雛形)を作成

  1. flutter create mytodoappでアプリ作成
  2. git init
  3. git add .
  4. git commit -m "Initial Commit
  5. リスト一覧画面(トップ画面)作成
  6. リスト追加画面作成
    ※5.6のコードは、Todoアプリ概要 | Flutterで始めるアプリ開発そのままです。
  7. AppBarを表示する
  8. リスト一覧を表示する
  9. リスト一覧のデザインを整える
    ※7~9のコードは、Todoリスト一覧画面 | Flutterで始めるアプリ開発そのままのコードです。
  10. リスト追加フォームを表示する
    color: Colors.blue,Todoリスト追加画面 | Flutterで始めるアプリ開発そのままだとエラーになるため、以下の様にする必要があった。
    style: ElevatedButton.styleFrom(backgroundColor: Colors.blue),
    
  11. 入力されたテキストを扱う
  12. リスト一覧画面にデータを渡す
  13. リスト追加画面からのデータを受け取る
  14. データを元にリスト一覧を表示する
    ※11~15はTodoリスト追加画面 | Flutterで始めるアプリ開発そのままのコードです。

動作動画

以下のような動きのアプリが作成できます。

TODOアプリ(雛形にUpdateとDeleteを追加)

このアプリは、CRUDの、CreateとReadしかできていません。
以下で、Update(更新)とDelete(削除)を追加していきます。

Update(更新)処理を追加

  1. body: ListView.builderreturnを、以下のように変更して、onTapを追加する
    return Card(
             child: ListTile(
               title: Text(todoList[index]),
               onTap: () {
                 print(todoList[index]);
               },
             ),
           );
    
  2. flutter pub add shared_preferencesを実行し、shared_preferencesをインストールする
  3. _showEditTaskDialogを作成
    【Flutter】TODO / タスク管理アプリを作成する方法 #iOS - Qiitaを参考にした。
      Future<void> _showEditTaskDialog(int index) async {
        // 選択したn番目のタスクの名前を取得
        final nameController = TextEditingController(text: todoList[index]);
    
        await showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text('タスクを編集'),
              content: Column(
                //Columnの長さを最小化する
                mainAxisSize: MainAxisSize.min,
                children: [
                  // タスク名を設定
                  TextField(
                    controller: nameController,
                    decoration: InputDecoration(labelText: 'タスク名'),
                  ),
                  SizedBox(height: 20),
                ],
              ),
              actions: [
                // タスク編集をキャンセルするボタン
                TextButton(
                  child: Text('キャンセル'),
                  onPressed: () => Navigator.pop(context),
                ),
                // タスク編集を完了するボタン
                TextButton(
                  child: Text('保存'),
                  onPressed: () {
                    // タスク名が空でないか確認して実行
                    if (nameController.text.isNotEmpty) {
                      todoList[index] = nameController.text;
                      // タスクの変更を保存
                      _saveTasks();
                      Navigator.pop(context);
                    }
                  },
                ),
              ],
            );
          },
        );
        setState(() {});
      }
    
  4. _saveTasksを作成
    【Flutter】TODO / タスク管理アプリを作成する方法 #iOS - Qiitaを参考にした。
      // タスクを保存する関数
      Future<void> _saveTasks() async {
        SharedPreferences prefs = await SharedPreferences.getInstance();
        await prefs.setStringList('tasks', todoList); // リストを保存
      }
    

Delete(削除)処理を追加

  1. _deleteTaskを作成
    【Flutter】TODO / タスク管理アプリを作成する方法 #iOS - Qiitaを参考にした。
      // タスクを削除する関数
      void _deleteTask(int index) {
        setState(() {
          todoList.removeAt(index);
          // タスクの削除を保存
          _saveTasks();
        });
      }
    
  2. ListTileに以下を追加する
     trailing: Wrap(
                    spacing: 8, // アイコンの間の幅を調整
                    children: [
                      IconButton(
                        icon: Icon(Icons.delete),
                        onPressed: () {
                          _deleteTask(index);
                        },
                      ),
                    ],
                  ),
    
    参考: [Flutter]ListTileのtrailingに複数のアイコンを表示する方法
    参考: 【Flutter】 IconButton 使ってみた!【Material 3】 | 週刊Flutter大学
  3. [任意] 削除する前に、ダイアログを出すようにする
    _deleteTask(index); _confirmDeleteTask(index);にするように変更し、以下のコードを追加します。
      void _confirmDeleteTask(int index) {
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text("削除確認"), // ダイアログのタイトル
              content: Text(
                "このアイテムを削除してもよろしいですか?",
              ), // ダイアログのメッセージ
              actions: <Widget>[
                TextButton(
                  onPressed: () =>
                      Navigator.of(context).pop(false), // キャンセルボタンを押したときの処理
                  child: Text("キャンセル"),
                ),
                TextButton(
                  onPressed: () => {
                    // 削除ボタンを押したときの処理
                    Navigator.of(context).pop(true),
                    _deleteTask(index),
                  },
                  child: Text("削除"),
                ),
              ],
            );
          },
        );
      }
    
    参考: FlutterでDismissible(スワイプして消す)とshowDialog(アラート確認)を組み合わせて実装する方法 #Flutter - Qiita

動作動画

以下のような動きのアプリが作成できます。

TODOアプリ(今までの変更に永続処理を追加)

  1. _loadTasksを追加
      
      void initState() {
        super.initState();
        // アプリ起動時に保存しているタスクを読み込む
        _loadTasks();
      }
    
      // タスクを読み込む関数
      Future<void> _loadTasks() async {
        SharedPreferences prefs = await SharedPreferences.getInstance();
        List<String>? tasksJson = prefs.getStringList('tasks');
        if (tasksJson != null) {
          setState(() {
            todoList = tasksJson;
          });
        }
      }
    
  2. 保存のときに、_saveTasksを呼ぶように変更+nullのときには保存されないように変更
              if (newListText != null && newListText.toString().trim().isNotEmpty) {
             // キャンセルした場合は newListText が null となるので注意
             setState(() {
               // リスト追加
               todoList.add(newListText);
               _saveTasks();
    

参考: 【Flutter】TODO / タスク管理アプリを作成する方法 #iOS - Qiita

終わりに

TODOアプリは基本中の基本がさらえると思っています。
これを元に、何かしらのアプリを公開まで行きたいなと思っています。

Discussion