Flutterを用いたシンプルなTODOアプリの作成チュートリアル

flutter

はじめに

まず始めに、本記事では、ダート言語とFlutterフレームワークを使ってシンプルなTODOアプリケーションを作成する方法について解説します。この記事は初心者向けに作られており、基本的なFlutterの概念やウィジェットの操作について理解していることを前提としています。

この記事で紹介しているアプリは下記のブランチにアップロードしていますのでこちらも参照してください

tkwest3143/flutter-sample
flutter study repository. Contribute to tkwest3143/flutter-sample development by creating an account on GitHub.

初めてのFlutter/Dartマルチプラットフォームアプリ開発チュートリアル

初めてのFlutter/Dartマルチプラットフォームアプリ開発チュートリアル
はじめに FlutterはGoogleが開発した、マルチプラットフォームのモバイルアプリ開発のフレームワークです。Flutterを使うと、AndroidとiOSの両方のプラットフォームで同時にアプリを開発することができます。 また、ブラウザ...

Flutter環境のセットアップ

まずはじめに、開発環境を整える必要があります。Flutterのインストールは公式ドキュメントを参照してください。Flutterを利用することで、iOSとAndroidの両方向けに一度にアプリを作成することが可能となります。

新規プロジェクトの作成

Flutterの環境設定が終わったら、新規プロジェクトを作成します。以下のコマンドをターミナルに入力します。

flutter create todo_app
cd todo_app

このコマンドを実行すると、todo_appという名前の新しいFlutterプロジェクトが生成されます。ここで、cd todo_appコマンドで作成したプロジェクトのディレクトリに移動します。

必要なパッケージのインストール

このTODOアプリでは、状態管理のためにproviderパッケージを使用します。これをプロジェクトに追加するためには、providerをインストールしましょう

flutter pub add provider

pubspec.yamlファイルに以下のように追加されます

dependencies: 
  flutter: 
    sdk: flutter 
  provider: ^6.0.5

Todoリストのモデル作成

まずはじめに、Todoリストのモデルを作成します。以下のようにlib/modelsディレクトリを作成し、その中にtodo.dartというファイルを作成します。

class Todo {
  String title;
  bool isDone;

  Todo({required this.title, this.isDone = false});
}

このTodoクラスは、タイトルとそのタスクが完了しているかどうかのフラグを保持します。

続いては、Todoモデルのリストを管理するTodoListクラスを作成します。このクラスはChangeNotifierを継承します。ChangeNotifierはFlutterのproviderパッケージに含まれるクラスで、状態の変更をリスナーに通知する役割を果たします。以下のようにlib/modelsディレクトリ内にtodo_list.dartファイルを作成します。

import 'package:flutter/foundation.dart';
import 'todo.dart';

class TodoList extends ChangeNotifier {
  List _todos = [];

  List get todos => _todos;

  void addTodo(String title) {
    _todos.add(Todo(title: title));
    notifyListeners();
  }

  void toggleTodo(int index) {
    _todos[index].isDone = !_todos[index].isDone;
    notifyListeners();
  }
}

TodoListクラスには以下の2つのメソッドがあります。

  • addTodo:新しいTodoインスタンスをリストに追加します。追加後、notifyListenersメソッドを呼び出してリスナーに状態の変更を通知します。
  • toggleTodo:指定されたインデックスのTodoの完了状態を切り替えます。切り替え後、notifyListenersメソッドを呼び出してリスナーに状態の変更を通知します。

ここで重要な点は、notifyListenersメソッドを呼び出すことで、状態の変更をUIに反映することができます。具体的には、ConsumerウィジェットやProvider.ofメソッドを通じて、状態の変更を購読しているウィジェットがリビルドされます。

これで、Todoリストを管理するモデルの作成が完了しました。次に、このモデルをアプリケーションの状態として提供する方法を見てみましょう。

UIの作成

次に、Todoアイテムを表示し、新しいTodoアイテムを追加するためのUIを作成します。screensディレクトリ内にhome_screen.dartという名前の新しいファイルを作成します。

providerを使った状態管理を行うため、Consumerを使っています

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo_app/models/todo_list.dart';

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Todo List'),
      ),
      body: Consumer(
        builder: (context, todoList, child) {
          return ListView.builder(
            itemCount: todoList.todos.length,
            itemBuilder: (context, index) {
              return CheckboxListTile(
                title: Text(todoList.todos[index].title),
                value: todoList.todos[index].isDone,
                onChanged: (bool? value) {
                  todoList.toggleTodo(index);
                },
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _showAddTodoDialog(context),
        child: const Icon(Icons.add),
      ),
    );
  }

  void _showAddTodoDialog(BuildContext context) {
    final todoTextController = TextEditingController();

    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Todoの入力'),
          content: TextField(
            controller: todoTextController,
            decoration: const InputDecoration(hintText: "Todoを入力してください..."),
          ),
          actions: [
            TextButton(
              child: const Text('Cancel'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
            TextButton(
              child: const Text('Add'),
              onPressed: () {
                Provider.of(context, listen: false)
                    .addTodo(todoTextController.text);
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }
}

このコードは、_showAddTodoDialogTextFieldを通じて新しいTodoアイテムを追加する機能と、Todoアイテムの完了状態をトグルする機能、Todoアイテムを並べる機能を提供します

ダイアログの部分の画面を別ファイルにしておくのもありではと思いました

メインファイルの更新

最後に、main.dart ファイルを以下のように更新します。これにより、TodoListクラスのインスタンスがアプリ全体でアクセス可能になります。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/todo_list.dart';
import 'screens/home_screen.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => TodoList(),
      child: TodoApp(),
    ),
  );
}

class TodoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Todo List',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomeScreen(),
    );
  }
}

以上でシンプルなTODOアプリケーションの作成は完了です。このアプリでは新しいタスクの追加と追加したタスクの参照が可能です。

タスク完了と削除の機能追加

次にタスクの完了と削除を実装します。今回はタスクを長押ししたときに削除する機能を追加します。

最初にTodoListのモデルに削除する処理を追加します。今回はremoveTodoという名前でメソッドを追加します

import 'package:flutter/foundation.dart';
import 'todo.dart';

class TodoList extends ChangeNotifier {
  final List _todos = [];

  List get todos => _todos;

  void addTodo(String title) {
    _todos.add(Todo(title: title));
    notifyListeners();
  }

  void toggleTodo(int index) {
    _todos[index].isDone = !_todos[index].isDone;
    notifyListeners();
  }

  void removeTodo(Todo item) {
    _todos.remove(item);
    notifyListeners();
  }
}

杖技はhome_screen.dartのTodoのリストを表示しているListView.builderの部分を下記のように編集してタスク完了と削除機能を追加します

長押しするとタスクを削除することができ、完了にするを押下するとタスクの左側に✅マークが表示され、完了にするボタンが未完了にするボタンへと変化します

ListView.builder(
  itemCount: todoList.todos.length,
  itemBuilder: (context, index) {
    final item = todoList.todos[index];
    return ListTile(
      title: Text(
        item.title,
      ),
      leading: item.isDone
        ? const Icon(Icons.done, color: Colors.green) : null,
      trailing: ElevatedButton(
        onPressed: () {
          todoList.toggleTodo(index);
        },
        child: item.isDone ? const Text("完了にする") : const Text("未完了にする"),
      ),
      onLongPress: () {
        // 長押しすることで削除します
        Provider.of(context, listen: false).removeTodo(item);
      },
    );
  },
);

以上がFlutterを用いたシンプルなTODOアプリケーションの作り方でした。Providerを用いて状態管理を行うことで、アプリケーションのコードが明快で読みやすくなります。この手法を自身のプロジェクトに適用してみてください。