初心者と学ぶFlutter第5回:Todoアプリの設計と実装

初心者と学ぶFlutter

はじめに

前回は状態管理の方法について学びました。

初心者と学ぶFlutter第4回:State管理の基本
Flutterでの基本的なState管理を初心者向けに解説。StatefulWidgetとsetStateを使った動的なUI更新方法を丁寧に紹介します。

今回は、Flutterを使ったシンプルなTodoアプリの設計と実装を行います。Todoアプリは、アプリ開発の基本的な部分を学ぶのに最適なプロジェクトであり、一覧表示やデータの追加、削除といった操作が含まれるため、アプリケーションの基本的な構造を理解するのに役立ちます。

このプロジェクトでは、Flutterの代表的なウィジェットを使いながら、実際のアプリを作成し、CRUD機能(作成、読み込み、更新、削除)の実装を学びます。初めてのFlutterアプリとして、ぜひ一緒に作っていきましょう。

Todoアプリの画面設計

Todoアプリの基本機能には、タスクの一覧表示タスクの追加タスクの削除があります。これらを実現するために、Flutterの以下のUI要素を使います。

使うUI要素

  • Scaffold: アプリの基盤となるレイアウト
  • AppBar: 上部のバー(タイトルなどを表示)
  • FloatingActionButton: 画面の下部に配置されるアクションボタン
  • ListTile: タスクの各項目を表示
  • TextField: タスクを入力するテキスト入力欄

完成イメージ

  • AppBarにはアプリのタイトルを表示し、FloatingActionButtonを押すと、新しいTodoを追加できるようにします。
  • ListTileを使って、Todoリストを表示し、更新、削除操作を簡単に行えるようにします。

アプリの設計

最初に、アプリ全体の設計を行います。
main() 関数では、Flutterアプリを起動するために runApp() を呼び出しています。この関数によって、アプリ全体が実行されます。TodoListScreen というウィジェットがアプリのメイン画面(ホーム画面)として表示されます。

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

void main() {
  runApp(TodoApp());
}

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

Todoリストの基本画面

次に、Todoリストの基本的な画面を設計します。リストにタスクを表示するためにListViewListTileを使い、ユーザーがタスクを追加できるようにします。

タスクリストの状態を管理するためにStatefulWidgetを利用します。

TodoリストのUI

Dart
class TodoListScreen extends StatefulWidget {
  @override
  _TodoListScreenState createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {
  List<String> _todoItems = [];  // Todoアイテムを保持するリスト

  // 新しいタスクを入力するダイアログを表示する
  void _promptAddTodoItem() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        TextEditingController _textFieldController = TextEditingController();

        return AlertDialog(
          title: Text('Add a new task'),
          content: TextField(
            controller: _textFieldController,
            decoration: InputDecoration(hintText: 'Enter task here'),
          ),
          actions: <Widget>[
            TextButton(
              child: Text('Cancel'),
              onPressed: () {
                Navigator.of(context).pop();  // ダイアログを閉じる
              },
            ),
            TextButton(
              child: Text('Add'),
              onPressed: () {
                _addTodoItem(_textFieldController.text); // アイテムを追加(後で実装)
                Navigator.of(context).pop();  // ダイアログを閉じる
              },
            ),
          ],
        );
      },
    );
  }

  // 既存のタスクを編集するダイアログを表示する
  void _promptEditTodoItem(int index) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        TextEditingController _textFieldController = TextEditingController();
        _textFieldController.text = _todoItems[index];  // 現在のタスクをテキストフィールドに表示

        return AlertDialog(
          title: Text('Edit task'),
          content: TextField(
            controller: _textFieldController,
            decoration: InputDecoration(hintText: 'Edit task here'),
          ),
          actions: <Widget>[
            TextButton(
              child: Text('Cancel'),
              onPressed: () {
                Navigator.of(context).pop();  // ダイアログを閉じる
              },
            ),
            TextButton(
              child: Text('Save'),
              onPressed: () {
                _editTodoItem(index, _textFieldController.text);// アイテムを更新(後で実装)
                Navigator.of(context).pop();  // ダイアログを閉じる
              },
            ),
          ],
        );
      },
    );
  }

  // Todoリストを表示するメソッド
  Widget _buildTodoList() {
    return ListView.builder(
      itemCount: _todoItems.length,
      itemBuilder: (context, index) {
        return _buildTodoItem(_todoItems[index], index);
      },
    );
  }

  // 各Todoアイテムを表示するメソッド
  Widget _buildTodoItem(String todoText, int index) {
    return ListTile(
      title: Text(todoText),
      trailing: IconButton(
        icon: Icon(Icons.delete),
        onPressed: () => _removeTodoItem(index),  // アイテムを削除(後で実装)
      ),
      onTap: () => _promptEditTodoItem(index),  // アイテムをタップして編集
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todo List'),
      ),
      body: _buildTodoList(),  // Todoリストを表示
      floatingActionButton: FloatingActionButton(
        onPressed: _promptAddTodoItem,  // 新しいタスクを追加するダイアログを表示
        child: Icon(Icons.add),
      ),
    );
  }
}

説明

  • _todoItems:Todoリストを格納するリストです。setState()を使ってリストの内容を変更し、UIに反映させます。
  • _addTodoItem():新しいタスクをリストに追加するメソッドです。入力したタスクが空でない場合のみ追加します。
  • _removeTodoItem():タスクを削除するメソッドです。リストの指定されたインデックスにあるタスクを削除します。
  • _promptAddTodoItem():タスクを入力するためのダイアログを表示します。このダイアログでタスクを入力し、「Add」ボタンを押すとリストにタスクが追加されます。
  • _promptEditTodoItem():タスクを更新するためのダイアログを表示します。テキストフィールドには登録しているタスクが表示されます。このダイアログでタスクを入力し、「save」ボタンを押すとリストの対象タスクが更新されます。
  • _buildTodoList()ListView.builderを使ってリスト内のアイテムを動的に生成し、表示します。
  • _buildTodoItem():各タスクをListTileとして表示し、右側に削除ボタンを付けています。

CRUD機能の実装

CRUDとは、Create(作成)、Read(読み込み)、Update(更新)、Delete(削除)の略で、データを扱う基本的な操作のことです。このTodoアプリでは、CRUD機能をシンプルに実装していきます。

作成(Create)

新しいTodoを作成するには、FloatingActionButtonを押してダイアログを表示し、タスクを入力します。_addTodoItem()メソッドでタスクをリストに追加します。

Dart
void _addTodoItem(String task) {
  if (task.isNotEmpty) {
    setState(() {
      _todoItems.add(task);
    });
  }
}

読み込み(Read)

作成されたタスクは_buildTodoList()で読み込み、ListView.builderを使ってリスト表示しています。これにより、複数のTodoアイテムがリストに表示されます。

Dart
Widget _buildTodoList() {
  return ListView.builder(
    itemCount: _todoItems.length,
    itemBuilder: (context, index) {
      return _buildTodoItem(_todoItems[index], index);
    },
  );
}

更新(Update)

ListTileをタップすると編集用のダイアログを表示し、ユーザーがタスクを編集することで、更新機能を追加できます。

Dart
void _editTodoItem(int index, String newTask) {
  if (newTask.isNotEmpty) {
    setState(() {
      _todoItems[index] = newTask;
    });
  }
}

4. 削除(Delete)

リスト内のタスクを削除するためには、ListTileに削除ボタン(IconButton)を追加し、ユーザーがそれを押すことでタスクが削除されます。

Dart
void _removeTodoItem(int index) {
  setState(() {
    _todoItems.removeAt(index);
  });
}

完成コード

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

void main() {
  runApp(TodoApp());
}

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

class TodoListScreen extends StatefulWidget {
  @override
  _TodoListScreenState createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {
  List<String> _todoItems = [];  // Todoアイテムを保持するリスト

  // Todoアイテムを追加するメソッド
  void _addTodoItem(String task) {
    if (task.isNotEmpty) {
      setState(() {
        _todoItems.add(task);  // 新しいタスクをリストに追加
      });
    }
  }

  // Todoアイテムを編集するメソッド
  void _editTodoItem(int index, String newTask) {
    if (newTask.isNotEmpty) {
      setState(() {
        _todoItems[index] = newTask;  // タスクを更新
      });
    }
  }

  // Todoアイテムを削除するメソッド
  void _removeTodoItem(int index) {
    setState(() {
      _todoItems.removeAt(index);  // 指定されたインデックスのアイテムを削除
    });
  }

  // 新しいタスクを入力するダイアログを表示する
  void _promptAddTodoItem() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        TextEditingController _textFieldController = TextEditingController();

        return AlertDialog(
          title: Text('Add a new task'),
          content: TextField(
            controller: _textFieldController,
            decoration: InputDecoration(hintText: 'Enter task here'),
          ),
          actions: <Widget>[
            TextButton(
              child: Text('Cancel'),
              onPressed: () {
                Navigator.of(context).pop();  // ダイアログを閉じる
              },
            ),
            TextButton(
              child: Text('Add'),
              onPressed: () {
                _addTodoItem(_textFieldController.text);
                Navigator.of(context).pop();  // ダイアログを閉じる
              },
            ),
          ],
        );
      },
    );
  }

  // 既存のタスクを編集するダイアログを表示する
  void _promptEditTodoItem(int index) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        TextEditingController _textFieldController = TextEditingController();
        _textFieldController.text = _todoItems[index];  // 現在のタスクをテキストフィールドに表示

        return AlertDialog(
          title: Text('Edit task'),
          content: TextField(
            controller: _textFieldController,
            decoration: InputDecoration(hintText: 'Edit task here'),
          ),
          actions: <Widget>[
            TextButton(
              child: Text('Cancel'),
              onPressed: () {
                Navigator.of(context).pop();  // ダイアログを閉じる
              },
            ),
            TextButton(
              child: Text('Save'),
              onPressed: () {
                _editTodoItem(index, _textFieldController.text);
                Navigator.of(context).pop();  // ダイアログを閉じる
              },
            ),
          ],
        );
      },
    );
  }

  // Todoリストを表示するメソッド
  Widget _buildTodoList() {
    return ListView.builder(
      itemCount: _todoItems.length,
      itemBuilder: (context, index) {
        return _buildTodoItem(_todoItems[index], index);
      },
    );
  }

  // 各Todoアイテムを表示するメソッド
  Widget _buildTodoItem(String todoText, int index) {
    return ListTile(
      title: Text(todoText),
      trailing: IconButton(
        icon: Icon(Icons.delete),
        onPressed: () => _removeTodoItem(index),  // アイテムを削除
      ),
      onTap: () => _promptEditTodoItem(index),  // アイテムをタップして編集
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todo List'),
      ),
      body: _buildTodoList(),  // Todoリストを表示
      floatingActionButton: FloatingActionButton(
        onPressed: _promptAddTodoItem,  // 新しいタスクを追加するダイアログを表示
        child: Icon(Icons.add),
      ),
    );
  }
}

まとめ

今回のチュートリアルでは、Flutterを使ってシンプルなTodoアプリを作成しました。このアプリを通して、状態管理やCRUD機能の実装、リスト表示といった基本的な概念を学びました。今回のTodoアプリをベースに、機能を追加したり、デザインをカスタマイズすることで、より複雑なアプリの開発に挑戦してみてください。

この記事のポイント

  • StatefulWidgetを使ってTodoアプリの状態管理を実装
  • ListViewListTileを使ったタスクの一覧表示
  • FloatingActionButtonで新しいタスクの追加を実現
  • CRUD操作(作成、読み込み、更新、削除)をシンプルに実装

次回は、このアプリにさらに機能を追加して、例えばローカルデータベースへのデータ保存の方法について学んでいきます。

第6回はこちら

初心者と学ぶFlutter第6回:ローカルデータの保存
shared_preferencesを使ってFlutterアプリのデータをローカルに保存し、アプリ再起動時にもTodoリストを保持する方法を詳しく解説します。

コメント

タイトルとURLをコピーしました