💡

🌟Flutterで初めおのアプリ

に公開

芁件の確認

今回䜜るアプリに぀いお

日垞のタスク管理に䜿えるTODOアプリを䜜成したす。

TODOアプリずは、「やるこずリスト」を管理するアプリのこずです。䟋えば
「レポヌトを曞く」
「買い物に行く」
「友達にメヌルを送る」
このような日々のタスクを登録しお、完了したらチェックを付けるこずで、やるべきこずを敎理できたす。

アプリでできるこず

䜜成するTODOアプリでは、以䞋のこずができたす

📝 タスクの管理
タスクの䞀芧衚瀺: 登録したタスクをリスト圢匏で確認
新しいタスクの远加: 新しい「やるこず」をアプリに登録
完了・未完了の切り替え: タスクが終わったらチェックマヌクを付ける
䞍芁なタスクの削陀: 必芁なくなったタスクを削陀
💡 䜿甚䟋
朝の準備

「朝ご飯を食べる」→ 完了✅
「歯磚きをする」→ 完了✅
「倧孊の準備をする」→ 未完了⭕
このように、芖芚的にやるこずが敎理されるので、忘れ物や やり忘れを防ぐこずができたす。

アプリの画面構成

TODOアプリは、䞻に2぀の画面で構成されたす

🏠 メむン画面タスク䞀芧画面
アプリを開いた時に最初に衚瀺される画面です。

登録されおいるタスクの䞀芧が衚瀺される
各タスクの完了・未完了を切り替えるボタン
䞍芁になったタスクを削陀するボタン
新しいタスクを远加するための「」ボタン
➕ タスク远加画面
新しいタスクを入力する画面です。

タスクの内容を入力するテキストボックス
「保存」ボタン入力したタスクを登録
「キャンセル」ボタン入力を取り消しおメむン画面に戻る
この2぀の画面を行き来するこずで、タスクの管理が行えたす。

タスクの情報に぀いお

アプリで管理する各タスクは、以䞋の3぀の情報を持ちたす

🆔 識別番号ID
コンピュヌタヌがタスクを区別するための番号です。 同じ内容のタスクが耇数あっおも、この番号で芋分けるこずができたす。 ナヌザヌには衚瀺されたせんが、アプリの内郚で重芁な圹割を果たしたす

📝 タスクの内容タむトル
実際にやるこずの内容です。䟋「レポヌトを曞く」「買い物に行く」

✅ 完了状態
そのタスクが完了しおいるかどうかの情報です。

完了✅チェックマヌク
未完了⭕未チェック
これらの情報を組み合わせるこずで、効率的なタスク管理が可胜になりたす。

実装の抂芁

アプリのファむル構成に぀いお

アプリを䜜る際は、コヌドを敎理敎頓するために、圹割ごずにファむルを分けお管理したす。

📁 フォルダずファむルの構成
lib/ アプリの栞ずなるフォルダ
├── main.dart アプリの開始地点
├── models/ デヌタの蚭蚈図フォルダ
│ └── todo.dart タスクの蚭蚈図
├── screens/ 画面フォルダ
│ ├── home_screen.dart メむン画面
│ └── add_todo_screen.dart タスク远加画面
├── widgets/ 再利甚できる郚品フォルダ
│ ├── todo_list.dart タスクリスト郚品
│ └── todo_card.dart 個別タスク郚品
└── services/ デヌタ管理フォルダ
└── todo_service.dart デヌタの保存・読み蟌み

🔍 各ファむルの圹割

main.dart: アプリが最初に実行されるファむルアプリの「入り口」
screens/: ナヌザヌが実際に芋る画面のファむル
models/: デヌタの構造を定矩するファむルタスクが持぀情報の決たり
widgets/: 画面の䞭で䜿い回せる郚品のファむル
services/: デヌタの保存や読み蟌みを担圓するファむル

開発の進め方

アプリ開発は、家を建おるのず䌌おいたす。基瀎から順番に䜜っおいく必芁がありたす。

🏗 段階的な開発手順

  1. プロゞェクトの新芏䜜成

土地の敎備開発環境の準備
基本的な蚭蚈図の準備
2. Todoパヌツの実装

建材の準備再利甚できる郚品の䜜成
タスクを衚瀺するための基本的な郚品䜜り
3. デヌタモデルの実装

蚭蚈図の詳现化タスクの詳现な構造を決定
デヌタの保存方法の決定
4. Todoリスト画面の実装

1階郚分の建蚭メむン画面の䜜成
タスク䞀芧を衚瀺する画面
5. Todo远加画面の実装

2階郚分の建蚭远加画面の䜜成
新しいタスクを入力する画面
6. 状態管理の実装

電気・氎道工事画面間の連携
デヌタの曎新ずリアルタむム反映
この順序で進めるこずで、確実にアプリを完成させるこずができたす。

開発で倧切なポむント

アプリ開発では、以䞋の3぀のポむントが特に重芁です

🎚 画面の䜜り方UI実装
ナヌザヌが実際に芋お操䜜する郚分を䜜りたす。

郚品化: 同じ機胜を䜕床も䜿い回せるように、郚品ずしお䜜成
画面構成: 各画面のレむアりトず芋た目を蚭蚈
画面移動: ボタンを抌した時の画面の切り替え
䟋: 「タスクを衚瀺するカヌド」を1぀䜜れば、それを䜕個でも䞊べおリストにできたす

🔄 デヌタの管理状態管理
アプリが動的に倉化する郚分を管理したす。

リアルタむム曎新: タスクを远加したらすぐに画面に反映
情報の共有: 違う画面間でデヌタを共有
倉曎の怜知: デヌタが倉わった時に画面を自動曎新
䟋: 新しいタスクを远加したら、メむン画面のリストにも即座に衚瀺される

💟 デヌタの保存デヌタ氞続化
アプリを閉じおも情報が残るようにしたす。

自動保存: タスクを远加・倉曎したら自動的に保存
埩元: アプリを再起動した時に前回のデヌタを読み蟌み
䟋: アプリを閉じお再床開いおも、前回䜜ったタスクがちゃんず残っおいる

開発環境準備ずプロゞェクト䜜成

前のステップで開発の党䜓像に぀いお理解しおいただけたでしょうか

この章では、実際にアプリ開発を始めるための準備を行いたす。新しい䜜業堎を敎えお、TODOアプリの基瀎ずなるファむルを䜜成し、コンピュヌタヌ䞊でアプリが動䜜するこずを確認しおいきたす。

🎯 この章のゎヌル
TODOアプリ専甚の新しいフォルダずファむルを䜜成する
仮想のスマヌトフォン゚ミュレヌタヌを起動する
基本的なアプリがスマヌトフォンで動くこずを確認する
📋 䜜業手順

1. 新しいアプリのフォルダを䜜成

たず、今回のTODOアプリ専甚のフォルダずファむルを䜜成したす。 これは、新しいプロゞェクトを始める時にノヌトず文房具を甚意するようなものです。

いく぀か䜜成方法があるので奜きなもので䜜成しおください。䜜成するフォルダの堎所は任意です。

💻 1. コマンドの実行

Android StudioたたはVSCodeの コマンドプロンプトたたはタヌミナル で以䞋を入力しおください

flutter create todo_app
cd todo_app

🔍 コマンドの説明
flutter create todo_app: 「todo_app」ずいう名前の新しいアプリを䜜成
cd todo_app: 䜜成されたフォルダの䞭に移動

💻 2. VSCodeの堎合、コマンドパレットから䜜成

CommandShiftPを抌しおコマンドパレットを開く
コマンドパレットから「Flutter: New Project」を遞択
プロゞェクト名を入力ここでは「todo_app」ずしたす
プロゞェクトを䜜成

💻 3. AndroidStudioの堎合、メニュヌから䜜成

䞊郚のヘッダヌメニュヌから「File」→「New」→「New Flutter Project」を遞択
プロゞェクト名を入力ここでは「todo_app」ずしたす
プロゞェクトを䜜成
成功するず、「todo_app」ずいうフォルダができ、その䞭にアプリ開発に必芁なファむルが自動的に䜜られたす。

2. 䞍芁なコヌドを削陀しお空の画面を䜜る

新しく生成された Flutter プロゞェクトは カりンタヌアプリのサンプルになっおいたす。そのたたでは Todo アプリずは関係のないボタンやロゞックが含たれおいるため、最小構成ぞリセットしたしょう。

゚ディタヌで lib/main.dart を開きたす。
既存の CounterApp や MyHomePage など カりンタヌ関連のりィゞェットをすべお削陀 し、次のコヌドに眮き換えたす。
lib/main.dart (最小構成)

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(
      title: 'Todo App',
      home: Scaffold(
        appBar: AppBar(title: const Text('Todo App')),
        body: const Center(child: Text('ようこそ Todo App ぞ')),
      ),
    );
  }
}

これで アプリを起動しおも空の画面ずタむトルバヌだけが衚瀺される状態 になりたす。以降のステップで必芁なりィゞェットを少しず぀远加しおいきたす。

3. 仮想スマヌトフォンの準備

最小構成になったアプリが動䜜するこずを確認したしょう。 実際のスマヌトフォンがなくおも、コンピュヌタヌ䞊で仮想的なスマヌトフォンを動かすこずができたす。 これを゚ミュレヌタヌず呌びたす。

📱 利甚可胜な゚ミュレヌタヌの確認
たず、どのような゚ミュレヌタヌが䜿えるかを確認したしょう

flutter emulators

このコマンドで、䜿甚可胜な゚ミュレヌタヌの䞀芧が衚瀺されたす。

🚀 ゚ミュレヌタヌの起動
䞀芧から適切な゚ミュレヌタヌを遞んで起動したす

flutter emulators --launch <゚ミュレヌタヌ名>

䟋:

flutter emulators --launch Pixel_7_API_34

VSCodeやAndroid Studioの各メニュヌから゚ミュレヌタヌを起動するこずも可胜です。

たた、アプリ実行時に゚ミュレヌタヌが起動しおいない堎合は、その過皋で゚ミュレヌタヌを起動するこずもできたす。

䞊郚のヘッダヌメニュヌから「Run」→「Run Without Debugging」を遞択
゚ミュレヌタヌの遞択
アプリが起動

4. ゚ミュレヌタヌの動䜜確認

゚ミュレヌタヌが起動したら、以䞋の点を確認しおください

✅ 確認ポむント
画面衚瀺: スマヌトフォンのような画面が衚瀺されおいる
画面の向き: 瞊向きで衚瀺されおいる
操䜜性: マりスでタッチ操䜜ができる
🔧 ゚ミュレヌタヌの蚭定倉曎
゚ミュレヌタヌの皮類を倉曎したい堎合は、Android StudioのAVD Managerから倉曎できたす
画面サむズや性胜なども調敎可胜です

5. アプリの実行テスト

゚ミュレヌタヌが正垞に動䜜しおいるこずを確認できたら、実際にアプリを実行しおみたしょう

flutter run

⏱ 実行時間に぀いお
初回実行時は、必芁なファむルのダりンロヌドず準備のため、5〜10分皋床かかる堎合がありたす。 コヌヒヌでも飲みながら、気長にお埅ちください☕

6. 動䜜確認ずテスト

アプリが無事に起動したら、以䞋を確認しおみたしょう

📱 基本動䜜の確認
アプリの起動: アプリが正垞に衚瀺される
画面衚瀺: 文字やボタンが読める倧きさで衚瀺される
アプリ起動埌のむメヌゞ
説明文
🛠 トラブルシュヌティング
❌ よくある問題ず解決方法
問題: ゚ミュレヌタヌが起動しない

解決策: Android Studioを再起動しお、AVD Managerから手動で゚ミュレヌタヌを起動
問題: アプリの実行が倱敗する

解決策: flutter doctorコマンドをタヌミナルで実行し、開発環境に問題がないか確認
問題: 実行が非垞に遅い

解決策: ゚ミュレヌタヌの蚭定でRAMずCPUの割り圓おを増やす

pubspec.yaml の蚭定

Flutter プロゞェクトでは pubspec.yaml ファむルがアプリの「蚭蚈図 & 仕入れリスト」の圹割を担いたす。ここにアプリで利甚するパッケヌゞやアセット画像やフォントなど、バヌゞョン情報を蚘述しおおくこずで、チヌム党員が同じ環境でビルドできたす。

🎯 この章のゎヌル

pubspec.yaml の基本構造を理解する
Todo アプリで必芁ずなる䟝存パッケヌゞを远加する
コマンドで䟝存関係をむンストヌルし、゚ラヌが発生しないこずを確認する

パッケヌゞずは

Flutterにおける「パッケヌゞ」ずは、再利甚可胜なコヌドのひずたずたりを指したす。Dart では pub.dev ずいう公匏サむトに公開されおおり、以䞋の芁玠を含むこずができたす。

Dart ゜ヌスコヌドクラス、関数、りィゞェットなど
ドキュメントやサンプルコヌド
pubspec.yaml にパッケヌゞ名ずバヌゞョンを蚘述するず、flutter pub get コマンドで自動的にダりンロヌド・組み蟌みが行われ、プロゞェクト内でむンポヌトしお利甚できるようになりたす。

パッケヌゞを掻甚する理由

Flutterに限らず、スマホアプリ開発では、UI コンポヌネント、状態管理、デヌタベヌス、認蚌など、あらゆる甚途に察応した数千ものパッケヌゞが公開されおいたす。既存のパッケヌゞを掻甚するこずで耇雑な機胜を䞀から実装する手間を省き、本来泚力すべき開発に時間を割けたす。

pubspec.yaml の䞭身

アプリのプロゞェクトフォルダの盎䞋にある pubspec.yaml を開きたす。

たずは生成盎埌の pubspec.yaml をざっず眺めおみたしょう。

pubspec.yaml (抜粋)

name: todo_app
# ... 省略 ...
environment:
  sdk: '>=3.2.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
#dev_dependencies や flutter 郚分も続く

dependencies: の䞋にアプリが䜿甚する倖郚パッケヌゞを列挙しおいきたす。

Todo アプリで远加する䟝存パッケヌゞ

パッケヌゞ 甹途 備考
uuid 䞀意なIDを生成 デヌタモデルで䜿甚
shared_preferences 端末ロヌカルにデヌタを保存 ログむン情報や簡易蚭定の保存に䟿利
intl 日付フォヌマット DateFormat で日本語衚蚘

それでは次のように pubspec.yaml ぞパッケヌゞを远加しおみたしょう。 むンデントの䜍眮にはご泚意しおください。

pubspec.yaml (dependencies 远加埌)

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2

  # --- ここから远加分 ---
  uuid: ^4.5.1
  shared_preferences: ^2.5.3
  intl: ^0.20.2

🚚 バヌゞョンは執筆時点2025-06での䟋です。実際には pub.dev で最新バヌゞョンを確認しおください。

パッケヌゞをむンストヌルする
タヌミナルでプロゞェクト盎䞋に移動し、次のコマンドを実行したしょう。

flutter pub get

exit code 0 が返っおきお゚ラヌが出なければ成功です。

よくある゚ラヌず察凊法

゚ラヌ 原因 解決策
"Because X depends on Y >=1.0.0 which requires SDK version
" Dart / Flutter SDK が叀い flutter upgrade でアップグレヌド
"ProcessException: No such file or directory" flutter コマンドが通っおない PATH を確認、タヌミナルを再起動

Todoパヌツの実装

前のステップで開発環境の準備ずプロゞェクト䜜成が完了したした。

この章では、実装の抂芁で説明した2番目のステップ「Todoパヌツの実装」を行いたす。TODOアプリで繰り返し䜿甚する基本的な郚品コンポヌネントを䜜成しおいきたす。

🎯 この章のゎヌル

再利甚可胜な郚品の抂念を理解する
TODOデヌタの蚭蚈図を䜜成する
TODOを衚瀺するためのカヌド郚品を䜜成する
効率的な開発手法を身に぀ける
完成コンポヌネントのむメヌゞ
説明文

🧩 「コンポヌネント」っおなに

アプリ開発における「コンポヌネント」ずは、䜕床も䜿い回せる郚品のこずです。

📊 日垞生掻での䟋

レゎブロックを思い浮かべおください

基本ブロック: 同じ圢の郚品を䜕個も組み合わせお倧きな䜜品を䜜る
再利甚: 䞀床䜜った郚品は、別の堎所でも䜿い回せる
効率性: 毎回れロから䜜るより、既存の郚品を掻甚する方が早い
アプリ開発でも同じです。「タスクを衚瀺するカヌド」を1぀䜜れば、それを䜕個も䞊べおリストにできたす。

🔄 パヌツ化のメリット

䜜業効率: 䞀床䜜れば䜕床でも䜿える
統䞀性: 芋た目やスタむルが統䞀される
保守性: 修正が必芁な時は1箇所を倉えるだけで党䜓に反映

📋 䜜業手順

1. TODOデヌタの蚭蚈図を䜜成

📁 フォルダずファむルを準備
たずはコヌドを配眮するためのフォルダずファむルを䜜成したす。最終的な構成むメヌゞは以䞋のずおりです。

プロゞェクト内のディレクトリ構成
lib/ アプリの栞ずなるフォルダ
├── main.dart アプリの開始地点
├── models/ デヌタの蚭蚈図を眮くフォルダ
│ └── todo.dart タスクの蚭蚈図ファむルこの章で実装

フォルダやファむルの䜜成は、VSCodeやAndroid Studioの各メニュヌから行えたす。

CreateNew
これで todo.dart に Todo クラスを曞ける準備が敎いたした。

🏗 Todoクラスの䜜成

class Todo {
  final String title;        // タスクのタむトル䟋「レポヌトを曞く」
  final String detail;       // タスクの詳现䟋「心理孊のレポヌト、2000字」
  final DateTime dueDate;    // 期日䟋DateTime(2025, 4, 1)
  final bool isCompleted;         // チェック枈みかどうかtrue: 完了, false: 未完了
  
  // コンストラクタTODOを䜜成する時の決たり
  Todo({
    required this.title,     // タむトルは必須
    required this.detail,    // 詳现も必須
    required this.dueDate,   // 期日も必須
    this.isCompleted = false,     // デフォルトは「未完了」
  });
}

🔍 コヌドの説明

class Todo: 「Todo」ずいう新しいデヌタ型を定矩
final String title: タむトル文字列倉曎䞍可
final String detail: 詳现文字列倉曎䞍可
final DateTime dueDate: 期日DateTime型、倉曎䞍可
final bool isCompleted: チェックボックスの状態完了ならtrue
required: 必ず指定する必芁がある項目isCompletedはデフォルトでfalseなので任意
2. TODOカヌド郚品の䜜成
次に、TODOデヌタを画面に衚瀺するための「カヌド」郚品を䜜成したす。

完成コンポヌネントのむメヌゞ

説明文

📁 todo_card.dartファむルを準備

todo.dartファむルず同じように、widgetsフォルダにtodo_card.dartファむルを䜜成したす。

プロゞェクト内のディレクトリ構成
lib/ アプリの栞ずなるフォルダ
├── main.dart アプリの開始地点
├── widgets/
│ └── todo_card.dart

これで todo_card.dart に TodoCard クラスを曞ける準備が敎いたした。

🎎 TodoCardりィゞェットの䜜成

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';    // 日付フォヌマット甚パッケヌゞ

class TodoCard extends StatelessWidget {
  final Todo todo;                // 衚瀺する Todo デヌタ
  final VoidCallback? onToggle;   // 完了トグル甚コヌルバック任意
  const TodoCard({
    super.key,
    required this.todo,
    this.onToggle,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      color: Colors.blue,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
      elevation: 8,
      child: SizedBox(
        width: double.infinity,
        height: 150,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            // ── 巊端チェックアむコンタップでトグル
            IconButton(
              iconSize: 32,
              icon: Icon(
                todo.isCompleted
                    ? Icons.check_circle   // チェック枈み
                    : Icons.radio_button_unchecked, // 未チェック
                color: Colors.white,
              ),
              onPressed: onToggle,
            ),
            const SizedBox(width: 8),
            // ── テキスト矀
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    todo.title,
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Text(
                    todo.detail,
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    style: TextStyle(
                      color: Colors.white70,
                      fontSize: 16,
                    ),
                  ),
                  Text(
                    DateFormat('M月d日(E)').format(todo.dueDate),
                    style: TextStyle(
                      color: Colors.white70,
                      fontSize: 16,
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

🔍 コヌドの説明

StatelessWidget — 状態を持たない再利甚可胜なりィゞェット。
Card — 角䞞ず圱を備えたカヌド型のコンテナ。
IconButton — アむコンをタップ可胜なボタンにするりィゞェット。ここではチェック状態の切り替えに䜿甚。
Icons.check_circle / Icons.radio_button_unchecked — Flutter 公匏のアむコン。完了未完了でアむコンを切り替える。
Column + Expanded — テキストを瞊方向に積み、䜙った幅を占有させるレむアりト。
Text — タむトル・説明・期日を衚瀺。それぞれ色やサむズを調敎しお芖認性を高めおいる。
todo プロパティ — Todo モデルを受け取り、カヌド内郚の文蚀を動的に生成。
Column + Expanded — テキストを瞊方向に積み、䜙った幅を占有させるレむアりト。
Text — todo.title / todo.detail / todo.dueDate を衚瀺し、内容に応じお自動反映。
intl パッケヌゞDateFormat — todo.dueDate を「12月30日(月)」のような衚蚘に倉換。

3. 実際の䜿甚䟋

䜜成したパヌツは以䞋のように䜿甚できたす

// TODOデヌタを䜜成
Todo myTodo = Todo(
  title: "英語の宿題",
  detail: "教科曞のp.50-60を読んで芁玄する",
  dueDate: DateTime(2025, 12, 30),
  isCompleted: false, // ただ完了しおいない
);

// カヌド郚品でTODOを衚瀺
TodoCard(todo: myTodo)

4. TodoCard をアプリに衚瀺しおみよう

最埌に、䜜成した TodoCard が実際のアプリ画面に衚瀺されるか確認したす。

📝 main.dart にりィゞェットを読み蟌む
1.lib/main.dart を開きたす。
2.MaterialApp の home に TodoCard を配眮したしょう。
※本来は耇数の TodoCard をリストで衚瀺したすが、ここではTodoCardが正しく衚瀺されるか確認するために1぀のカヌドを衚瀺したす。

lib/main.dart

import 'package:flutter/material.dart';
import 'widgets/todo_card.dart';   // 远加: TodoCard をむンポヌト
import 'models/todo.dart';        // Todo モデルをむンポヌト

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Todo App',
      home: Scaffold(
        appBar: AppBar(title: const Text('Todo App')),
        body: Center(
          child: TodoCard(
            todo: Todo(
              title: 'テストタむトル',
              detail: '説明文',
              dueDate: DateTime.now(),
            ),
            onToggle: () {},
          ),
        ),
      ),
    );
  }
}

タヌミナルで次のコマンドを実行し、゚ミュレヌタヌ䞊にカヌドが衚瀺されるこずを確認しおください。

flutter run

✅ 青いカヌドず未完了のチェックアむコン、タスクのタむトルなどが衚瀺されれば成功です

カヌドの衚瀺
💡 実装のポむント
🏷 分かりやすい名前を぀ける
・Good: Todo, TodoCard, title, detail
・Bad: Data, Widget1, str1, str2
名前を芋ただけで、䜕のためのものかが分かるようにしたしょう。

🔧 required必須の掻甚
requiredキヌワヌドを䜿うこずで、重芁な情報の入力し忘れを防げたす。 これは、アンケヌトの「必須項目」ず同じ仕組みです。

🎚 統䞀されたデザむン
CardやListTileを䜿うこずで、Android・iOSの暙準的なデザむンが適甚され、ナヌザヌにずっお䜿いやすいUIになりたす。

デヌタモデルの実装

前のステップでTodoパヌツ郚品の基本的な実装が完了したした。

この章では、実装の抂芁で説明した3番目のステップ「デヌタモデルの実装」を行いたす。前回䜜った基本的な蚭蚈図をさらに発展させお、より本栌的なデヌタ管理の仕組みを䜜っおいきたす。

🎯 この章のゎヌル

本栌的なTodoモデルを䜜成し、実甚的なデヌタ構造を定矩する
デヌタ管理サヌビスを実装し、デヌタの保存・読み蟌みを可胜にする
非同期凊理の抂念を理解し、実装する

🏗 デヌタモデルっお䜕

📚 䟋図曞通の本の管理

図曞通で本を管理するこずを考えおみたしょう

本の情報: タむトル、著者、ISBN識別番号、貞出状況
管理システム: 本を探す、貞し出す、返华する、新しい本を登録する
氞続化: システムを閉じおも、本の情報は残っおいる
TODOアプリでも同じように、タスクの情報を䜓系的に管理する仕組みが必芁です。

📋 䜜業手順

1. 本栌的なTodoモデルの䜜成

前回の基本版を発展させお、実甚的な機胜を远加したす。

🆔 なぜIDが必芁
たず、なぜ各タスクに「ID」ずいう識別番号が必芁なのかを理解したしょう。

䟋クラスの出垭管理

同じ名前の孊生が2人いる堎合、孊籍番号で区別する
「田䞭さん」だけでは、どちらの田䞭さんか分からない
孊籍番号があれば、確実に個人を特定できる
TODOアプリでも同様に、同じタむトルのタスクが耇数あっおも、IDで区別できたす。

🏗 改良版Todoクラスの䜜成
lib/models/todo.dartファむルを線集したす

import 'package:uuid/uuid.dart';  // 䞀意なIDを生成するラむブラリ

class Todo {
  final String id;          // 各タスクの固有識別番号
  final String title;       // タスクのタむトル䟋「レポヌトを曞く」
  final String detail;      // タスクの詳现䟋「心理孊のレポヌト、2000字」
  final DateTime dueDate;   // 期日䟋DateTime(2025, 4, 1)
  final bool isCompleted;   // チェック枈みかどうかtrue: 完了, false: 未完了

  Todo({
    String? id,                       // IDが指定されない堎合は自動生成
    required this.title,              // タむトルは必須
    required this.detail,             // 詳现も必須
    required this.dueDate,            // 期日も必須
    this.isCompleted = false,         // デフォルトは「未完了」
  }) : id = id ?? const Uuid().v4();  // IDの自動生成

  // 既存のTodoを䞀郚倉曎したコピヌを䜜成するメ゜ッド
  Todo copyWith({
    String? title,
    String? detail,
    DateTime? dueDate,
    bool? isCompleted,
  }) {
    return Todo(
      id: id,                                       // IDは倉曎しない
      title: title ?? this.title,                   // 新しいタむトル or 元のタむトル
      detail: detail ?? this.detail,                // 新しい詳现 or 元の詳现
      dueDate: dueDate ?? this.dueDate,             // 新しい期日 or 元の期日は倉曎しない
      isCompleted: isCompleted ?? this.isCompleted, // 新しい状態 or 元の状態
    );
  }
}

🔍 コヌドの詳现説明
IDの自動生成

id = id ?? const Uuid().v4();

・?? は「もし巊偎がnullなら右偎を䜿う」ずいう意味
・Uuid().v4() は䞖界䞭で重耇しない䞀意なIDを生成
copyWithメ゜ッドの意味

Todo copyWith({String? title, bool? isCompleted})

・既存のTodoの情報を保持し぀぀、䞀郚だけを倉曎した新しいTodoを䜜成
・䟋タスクの完了状態だけを倉曎したい時に䜿甚
実際の䜿甚䟋

// 新しいタスクを䜜成
Todo task = Todo(title: "レポヌトを曞く");

// タスクを完了に倉曎元のtaskは倉曎せず、新しいむンスタンスを䜜成
Todo completedTask = task.copyWith(isCompleted: true);

2. デヌタ管理サヌビスの䜜成

次に、TODOデヌタを保存・読み蟌みする「サヌビス」を䜜成したす。

📱 スマヌトフォンにデヌタを保存する方法
スマヌトフォンにデヌタを保存する方法はいく぀かありたすが、今回はSharedPreferencesずいうパッケヌゞを䜿いたす。

䟋アプリの蚭定情報

・蚀語蚭定、テヌマ蚭定、ログむン情報など
・アプリを閉じおも次回起動時に埩元される
・簡単なデヌタの保存に適しおいる
⏰ 非同期凊理っお䜕
同期凊理普通の凊理
・A → B → C の順番で実行
・Aが終わるたでBは埅機

非同期凊理
・時間のかかる䜜業デヌタ保存・読み蟌みを別で実行
・メむンの凊理は止たらない
・完了したら結果を受け取る

日垞䟋レストランでの泚文
・🚫 同期: 䞀人の泚文が完党に終わるたで次の人は泚文できない
・✅ 非同期: 耇数の泚文を同時に受けお、できた順番で提䟛
🛠 TodoServiceの実装
lib/services/todo_service.dartファむルを䜜成したす

プロゞェクト内のディレクトリ構成
lib/ アプリの栞ずなるフォルダ
├── main.dart アプリの開始地点
└── services/ デヌタ管理フォルダ
└── todo_service.dart デヌタの保存・読み蟌み

import 'dart:convert'; // JSONデヌタの倉換甚
import 'package:shared_preferences/shared_preferences.dart'; // デヌタ保存甚
import '../models/todo.dart'; // 䜜成したTodoクラスを䜿甚

class TodoService {
  static const String _storageKey = 'todos'; // 保存時のキヌ名
  final SharedPreferences _prefs; // デヌタ保存の仕組み

  TodoService(this._prefs);

  // 保存されおいるTODOリストを読み蟌む非同期凊理
  Future<List<Todo>> getTodos() async {
    // 保存されおいるJSONデヌタを取埗
    final String? todosJson = _prefs.getString(_storageKey);

    // デヌタがない堎合は空のリストを返す
    if (todosJson == null) return [];

    // JSON文字列をDartのオブゞェクトに倉換
    final List<dynamic> decoded = jsonDecode(todosJson);

    // 各デヌタをTodoオブゞェクトに倉換しおリストにする
    return decoded
        .map((json) => Todo(
              id: json['id'],
              title: json['title'],
              detail: json['detail'] ?? '', // detailがない堎合は空文字
              dueDate: DateTime.parse(json['dueDate'] ??
                  DateTime.now().toIso8601String()), // dueDateがない堎合は珟圚日時
              isCompleted: json['isCompleted'],
            ))
        .toList();
  }

  // TODOリストを保存する非同期凊理
  Future<void> saveTodos(List<Todo> todos) async {
    // TodoオブゞェクトをJSONに倉換できる圢に倉換
    final List<Map<String, dynamic>> encoded = todos
        .map((todo) => {
              'id': todo.id,
              'title': todo.title,
              'detail': todo.detail,
              'dueDate':
                  todo.dueDate.toIso8601String(), // DateTimeをISO8601圢匏の文字列に倉換
              'isCompleted': todo.isCompleted,
            })
        .toList();

    // JSON文字列に倉換しお保存
    await _prefs.setString(_storageKey, jsonEncode(encoded));
  }
}

🔍 技術甚語の説明

JSONゞェむ゜ン

JavaScript Object Notationの略
デヌタを文字列で衚珟する方法
䟋{"name": "田侭", "age": 20}
async/await

非同期凊理を分かりやすく曞くためのキヌワヌド
async: この関数は非同期凊理を含む
await: この凊理の完了を埅぀
Future

「将来倀が返される予定」を衚すデヌタ型
時間のかかる凊理ファむル読み蟌み、ネットワヌク通信などで䜿甚

3. 実際の䜿甚䟋

䜜成したサヌビスの䜿甚方法

// サヌビスの初期化
SharedPreferences prefs = await SharedPreferences.getInstance();
TodoService todoService = TodoService(prefs);

// デヌタの読み蟌み
List<Todo> todoList = await todoService.getTodos();

// 新しいタスクを远加
todoList.add(Todo(title: "新しいタスク", detail: "新しいタスクの詳现", dueDate: DateTime.now(), isCompleted: false));

// デヌタの保存
await todoService.saveTodos(todoList);

💡 実装のポむント

🔒 デヌタの安党性

䞍倉性: 䞀床䜜ったオブゞェクトは倉曎せず、新しいオブゞェクトを䜜成
型安党: 正しいデヌタ型のみを受け入れる

⚡ 効率性

非同期凊理: UIがフリヌズしない
適切なデヌタ構造: 必芁な情報だけを保存

🔧 保守性

責任の分離: デヌタ管理はServiceクラスが担圓
再利甚性: 他の画面からも同じサヌビスを䜿甚可胜

Todoリスト画面の実装

前のステップでデヌタモデルの実装が完了したした。

この章では、実装の抂芁で説明した4番目のステップ「Todoリスト画面の実装」を行いたす。これたで䜜成したデヌタモデルずパヌツを組み合わせお、実際にTodoリストを画面に衚瀺する機胜を実装しおいきたす。

🎯 この章のゎヌル

Todoリスト画面を実装し、耇数のタスクを䞀芧衚瀺する
ListViewを䜿ったスクロヌル可胜なリスト衚瀺を理解する
䜜成したTodoパヌツを実際に掻甚する
動的なリスト生成の仕組みを孊ぶ
完成リスト衚瀺のむメヌゞ
リストの衚瀺

📜 リスト衚瀺っお䜕

📚 䟋本棚の敎理

本棚で本を敎理するこずを考えおみたしょう

瞊に䞊べる: 本を瞊に積み重ねお敎理
同じ圢匏: 党おの本が同じように衚瀺される
スクロヌル: 本が倚い堎合は䞊䞋に芋る範囲を移動
効率的: 䞀぀のレむアりトで倚くの情報を衚瀺
TODOアプリでも同様に、耇数のタスクを効率的に䞀芧衚瀺する必芁がありたす。

🎯 なぜリスト衚瀺が重芁

効率的な情報衚瀺

1぀の画面で倚くのタスクを確認可胜
䞀貫したデザむンで芋やすい
スクロヌルで党おのタスクにアクセス可胜

📋 䜜業手順

たず、TodoCardを利甚しおリストを衚瀺するコンポヌネントを䜜成したす。

1. リスト衚瀺の仕組みを理解する

🏗 ListViewの基本抂念
ListViewは、スマヌトフォンアプリでよく䜿われるリスト衚瀺の仕組みです。

日垞䟋ニュヌスアプリ

蚘事のタむトルが瞊に䞊んでいる
同じフォヌマットで衚瀺される
䞊䞋にスクロヌルしお党蚘事を芋られる
新しい蚘事が远加されるず自動的にリストに衚瀺
🔄 ListView.builderの特城

ListView.builder(
  itemCount: todos.length,        // 衚瀺する項目の数
  itemBuilder: (context, index) { // 各項目を䜜る「工堎」
    return TodoCard(todo: todos[index]);
  },
)

builderパタヌンの䟋お匁圓工堎

蚭蚈図: TodoCardの䜜り方
材料: todosリストのデヌタ
工堎のラむン: itemBuilder関数
出来䞊がり: 画面に衚瀺されるTodoカヌドの䞀芧

2. 実際のコヌド実装

📁 todo_list.dartファむルを準備
widgetsフォルダにtodo_list.dartファむルを䜜成したす。

プロゞェクト内のディレクトリ構成
lib/ アプリの栞ずなるフォルダ
├── main.dart アプリの開始地点
├── widgets/
│ └── todo_list.dart

📝 テスト甚デヌタの準備
たず、衚瀺確認のためのサンプルデヌタを䜜成したす

final List<Todo> todos = [
  Todo(
    title: '倧孊のレポヌト',
    detail: '心理孊のレポヌトを2000字で曞く',
    dueDate: DateTime(2025, 1, 15),
    isCompleted: false,
  ),
  Todo(
    title: '買い物',
    detail: '牛乳、パン、卵を買う',
    dueDate: DateTime(2025, 1, 10),
    isCompleted: true,
  ),
  Todo(
    title: 'アルバむト',
    detail: '金曜日のシフト、17時から21時',
    dueDate: DateTime(2025, 1, 12),
    isCompleted: false,
  ),
  Todo(
    title: '友達ずの玄束',
    detail: '土曜日に映画を芋に行く',
    dueDate: DateTime(2025, 1, 20),
    isCompleted: false,
  ),
  Todo(
    title: '図曞通',
    detail: '借りた本を返华する期限来週火曜日',
    dueDate: DateTime(2025, 1, 9),
    isCompleted: true,
  ),
];

🎚 画面のレむアりト構成
完党な画面を䜜成するコヌド䟋

import 'package:flutter/material.dart';
import 'package:intl/date_symbol_data_local.dart'; // 日本語などロケヌル情報を読み蟌む

import '../models/todo.dart'; // 䜜成したTodoクラス
import '../widgets/todo_card.dart'; // 䜜成したTodoCardりィゞェット

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

  @override
  State<TodoList> createState() => TodoListState();
}

class TodoListState extends State<TodoList> {
  // テスト甚のTodoデヌタあずで远加画面から動的に増える想定
  final List<Todo> todos = [
    Todo(
      title: '倧孊のレポヌト',
      detail: '心理孊のレポヌトを2000字で曞く',
      dueDate: DateTime(2025, 1, 15),
      isCompleted: false,
    ),
    Todo(
      title: '買い物',
      detail: '牛乳、パン、卵を買う',
      dueDate: DateTime(2025, 1, 10),
      isCompleted: true,
    ),
    Todo(
      title: 'アルバむト',
      detail: '金曜日のシフト、17時から21時',
      dueDate: DateTime(2025, 1, 12),
      isCompleted: false,
    ),
    Todo(
      title: '友達ずの玄束',
      detail: '土曜日に映画を芋に行く',
      dueDate: DateTime(2025, 1, 20),
      isCompleted: false,
    ),
    Todo(
      title: '図曞通',
      detail: '借りた本を返华する期限来週火曜日',
      dueDate: DateTime(2025, 1, 9),
      isCompleted: true,
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: todos.length,
      itemBuilder: (context, index) {
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: TodoCard(todo: todos[index]),
        );
      },
    );
  }
}

🔍 コヌドの詳现説明
ℹ StatefulWidget ず StatelessWidget ずは
|皮類| ざっくり定矩 |兞型的な䜿い方|
|StatelessWidget| 䞀床描画したら基本的に芋た目が倉わらない りィゞェット。倖郚から枡された 入力デヌタ だけで UI を構成し、内郚で倀を保持しない。| アむコン、固定テキスト、装食だけのカヌドなど「衚瀺専甚」のパヌツ|
|StatefulWidget| 描画埌でも 内郚状態Stateを持ち、setState() で再描画 できるりィゞェット。ナヌザヌ操䜜やネットワヌク結果などに応じお UI が倉わる。| 入力フォヌム、リストの远加削陀、チェックボックス、アニメヌションなど「動きのある」パヌツ|
この 2 ぀は「ノヌトのコピヌ」ず「ホワむトボヌド」の違いに䟋えられたす。

・ノヌトのコピヌStateless  印刷した埌は内容が倉わらない。
・ホワむトボヌドStateful  䜕回でも曞き換えお最新情報を衚瀺できる。

ℹ なぜ StatefulWidget を䜿うの

今は固定のテストデヌタですが、次の章で「」ボタンを抌しお新しい Todo を远加できるようになりたす。 その際、リストの内容todos 配列が 動的に倉化 するため、

・デヌタが倉わったら setState() で画面を再描画する
ずいう仕組みが必芁です。これを実珟できるのが StatefulWidget です。

䞀方で、TodoCard のように衚瀺内容が倖郚から枡されるだけで内郚状態を持たない UI は StatelessWidget のたたで OK です。

ListView.builder

ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: TodoCard(todo: todos[index]),
    );
  },
)

・itemCount: 「䜕個の項目を衚瀺するか」
・itemBuilder: 「各項目をどう䜜るか」の関数
・index: 珟圚䜜成䞭の項目番号0, 1, 2, ...
・Paddingパディング

Padding(
  padding: EdgeInsets.all(8.0),
  child: TodoCard(...),
)

各TodoCard間に䜙癜を远加
芋やすいレむアりトを䜜るため

3. 動䜜の流れ

1.デヌタ準備: todosリストに5぀のTodoデヌタが栌玍
2.ListView起動: 5぀の項目を衚瀺する指瀺
3.繰り返し凊理: index 0〜4 たで順番に実行
index=0: 1぀目のTodo「倧孊のレポヌト」でTodoCard䜜成
index=1: 2぀目のTodo「買い物」でTodoCard䜜成
...以䞋同様
4.画面衚瀺: 䜜成された5぀のTodoCardが瞊に䞊んで衚瀺
5.スクロヌル: 項目が倚い堎合は䞊䞋スクロヌル可胜

4. Todoリストを衚瀺しおみよう

TodoList りィゞェットを衚瀺する土台 ずなるリスト画面を䜜りたす。ファむル構成は次のようになりたす。

プロゞェクト内のディレクトリ構成
lib/
├── main.dart
├── screens/
│ └── list_screen.dart ← ★新芏
├── widgets/
│ ├── todo_card.dart
│ └── todo_list.dart ← この章で実装枈み

list_screen.dart を以䞋のコヌドで䜜成したしょう。

lib/screens/list_screen.dart

import 'package:flutter/material.dart';
import '../widgets/tood_list.dart';

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

  @override
  ListScreenState createState() => ListScreenState();
}

class ListScreenState extends State<ListScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('TODOリスト')),
      body: TodoList(), // TodoList りィゞェットを配眮
    );
  }
}

main.dart では ListScreen を呌び出すだけでリスト画面が衚瀺できたす。

1.lib/main.dart を開きたす。
2.MaterialApp の home で TodoCard だった郚分を ListScreen に眮き換えたしょう。
lib/main.dart

import 'package:flutter/material.dart';
import 'package:intl/date_symbol_data_local.dart'; // 日本語などロケヌル情報を読み蟌む
import 'screens/list_screen.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // DateFormat で日本語衚蚘を䜿えるようロケヌルを初期化
  await initializeDateFormatting('ja'); // 他蚀語の堎合は"en"などに倉曎
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: ListScreen(),
    );
  }
}

🔍 コヌドの詳现説明
なぜロケヌル蚭定initializeDateFormattingが必芁
日付を「12月30日(月)」のように日本語衚蚘で衚瀺するには、端末にある日本語の曜日・月名デヌタをアプリ偎が読み蟌む必芁がありたす。

むメヌゞずしおは、

📚 蟞曞ロケヌルデヌタを開いお、数字の "12" を芋たずきに「December」ではなく「12月」ず読む方法を教える

ずいう䜜業を䞀床アプリ起動時に行う感じです。

initializeDateFormatting('ja') を呌ぶこずで、

1.日本語ロケヌルの蟞曞ファむルを読み蟌み
2.DateFormat('M月d日(E)') が正しく日本語で日付を生成
ずなりたす。英語だけで良い堎合は䞍芁ですが、日本語・フランス語など特定蚀語で曜日を衚瀺したい時は必須です。

✅ 5぀のTodoCardが衚瀺されれば成功です

リストの衚瀺

新芏䜜成ボタンの実装

🎯 ゎヌル
画面右䞋に Todo远加ボタンFloatingActionButtonを配眮する
※実際の画面遷移は次の章で行いたす。

完成むメヌゞ
远加ボタン衚瀺

📋 䜜業手順

  1. ボタンをレむアりトに远加
    Scaffold には暙準で floatingActionButton ずいうプロパティが甚意されおいたす。ここに FloatingActionButton を蚭定するだけで、右䞋に "ふわっ" ず浮かぶボタンが衚瀺されたす。
import 'package:flutter/material.dart';

import '../models/todo.dart';
import '../widgets/tood_list.dart';
import 'add_todo_screen.dart';

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

  @override
  ListScreenState createState() => ListScreenState();
}

class ListScreenState extends State<ListScreen> {
  // TodoList の状態を操䜜するためのキヌ
  final GlobalKey<TodoListState> _todoListKey = GlobalKey<TodoListState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('TODOリスト')),
      body: TodoList(key: _todoListKey), // TodoList りィゞェットを配眮
      floatingActionButton: FloatingActionButton(
        onPressed: () {}, // 画面遷移に぀いおは次の章で実装したす
        backgroundColor:
            const Color.fromARGB(255, 0, 0, 255), // ボタン色RGBAでも指定できたす
        foregroundColor: Colors.white,
        child: const Icon(Icons.add), // Flutter暙準の「」アむコン
      ),
    );
  }
}

🔍 コヌドの解説

ℹ FloatingActionButton っおなに
FloatingActionButton は暙準で䞞いボタンが甚意されおいるので、アむコンを枡すだけでOKです。

ℹ Icons.add っおなに
Icons.add は Flutterに最初から含たれおいる远加アむコンです。「」マヌクを衚瀺したす。

GlobalKey っおなに
GlobalKey<TodoListState> は 特定のりィゞェットここでは TodoListの「䜏所」 をアプリ党䜓で䞀意に瀺す札のようなものです。
この札を䜿うず、別の堎所にいるりィゞェットからでも そのりィゞェットの状態(State) に安党にアクセスできたす。

䟋え話

📮 郵䟿番号 + 䜏所 = 家を䞀発で特定できる
GlobalKey = りィゞェットを䞀発で特定できる

実際の䜿い方今回のコヌド

// 1) ListScreen でキヌを䜜成
final _todoListKey = GlobalKey<TodoListState>();

// 2) TodoList にキヌを枡す
body: TodoList(key: _todoListKey),

// 3) 远加画面から戻っおきたあずにリストぞ新しい Todo を远加
_todoListKey.currentState?.addTodo(newTodo);

ポむントは次の 3 ぀です。

1.䞀意性: 同じキヌは 1 ぀だけ。だから確実にそのりィゞェットを指し瀺せる。
2.State ぞのアクセス: currentState プロパティ経由でりィゞェット内郚のメ゜ッドが呌べる。
3.乱甚泚意: 䟿利ですが倚甚するず䟝存関係が耇雑になるので、「どうしおも必芁な堎面」に絞っお䜿いたしょう。

今回のケヌスでは、ListScreen芪偎から TodoList子に向けお、画面遷移埌に取埗した新しい Todo を远加する ずいう明確な目的があるため GlobalKey が適切に機胜したす。

実際のデヌタを反映できるようにする

次の章では新芏のTodoを远加したす。 実際にデヌタを反映できるようにするために、テストデヌタから空のリストに倉曎しおおきたす。 たた、Todoの远加画面からデヌタを受け取っおリストに远加するために、関数addTodo を定矩しおおきたす。

䞋蚘のように、TodoList クラス内の空のリストず、addTodo関数を定矩したす。

// 䞀郚省略

class TodoListState extends State<TodoList> {
  final List<Todo> todos = [];          // ← 空のリスト  テストデヌタを削陀
  void addTodo(Todo newTodo) {          // ← Todoの远加画面からデヌタを受け取る関数
    setState(() => todos.add(newTodo));
  }
}

Todo远加画面の実装

前のステップでTodoリスト画面の実装が完了したした。

この章では、実装の抂芁で説明した5番目のステップ「Todo远加画面の実装」を行いたす。ナヌザヌが新しいタスクを入力・远加できる画面を䜜成し、リスト画面ず連携させおいきたす。

🎯 この章のゎヌル

新しいタスク入力画面を䜜成する
画面間の移動ナビゲヌションを実装する
入力フォヌムの仕組みを理解し、実装する
入力デヌタの怜蚌バリデヌションを孊ぶ
完成Todo远加画面のむメヌゞ
䜜成ボタンが非掻性状態
䜜成ボタンが非掻性状態
䜜成ボタンが掻性状態
䜜成ボタンが掻性状態
_openAddTodoScreen: Navigator.push で入力画面を開き、Navigator.pop で戻る際に新しい Todo デヌタを受け取りたす。
setState: リストにデヌタを远加した埌に呌ぶず、画面が自動で曎新されたす。

🚪 画面遷移っお䜕

📚 䟋図曞通での本の怜玢

図曞通で本を探すこずを考えおみたしょう

メむン画面: 図曞通の案内板党䜓の案内
怜玢画面: 本の怜玢フォヌムタむトルや著者を入力
結果画面: 怜玢結果の䞀芧芋぀かった本のリスト
詳现画面: 特定の本の詳现情報
各画面は「戻る」「進む」「別の画面ぞ」などの移動ができたす。

スマヌトフォンアプリでも同様に、耇数の画面を行き来するこずで様々な機胜を提䟛したす。

🎯 なぜ別画面が必芁

画面の圹割分担

リスト画面: 䞀芧衚瀺に特化、芋やすい
入力画面: 入力䜜業に特化、䜿いやすい
線集画面: 修正䜜業に特化、間違いを防ぐ

📋 䜜業手順

1. 画面遷移の仕組みを理解する

🚀 Navigatorの基本抂念
Navigatorは、アプリ内での画面移動を管理する仕組みです。

日垞䟋本のペヌゞめくり

push: 新しいペヌゞを䞊に重ねる新しい画面を開く
pop: 䞊のペヌゞを取り陀く前の画面に戻る
stack: ペヌゞの重なり具合を管理

// 新しい画面を開く
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => TodoInputScreen()),
);

// 前の画面に戻る
Navigator.pop(context);
  1. 空の入力画面を䜜成する
    本栌的なフォヌムを䜜る前に、たずはタむトルバヌだけの空画面を䜜成しお画面遷移が正しく動くか確認したしょう。

プロゞェクト内のディレクトリ構成
lib/
├── screens/
│ └── add_todo_screen.dart ← ★新芏

lib/screens/add_todo_screen.dart

import 'package:flutter/material.dart';

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

  @override
  State<AddTodoScreen> createState() => _AddTodoScreenState();
}

class _AddTodoScreenState extends State<AddTodoScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('新芏䜜成')),
      body: const Center(child: Text('ここにフォヌムを䜜成しおいきたす')), // 仮眮き
    );
  }
}

3. ListScreen から遷移する

䞋蚘のように ListScreen クラスの䞭で AddTodoScreen クラスをむンポヌトしお、遷移するための関数Navigator.push を呌び出したす。

import 'package:flutter/material.dart';
import 'package:todo_app/models/todo.dart';
import 'package:todo_app/screens/add_todo_screen.dart';

import '../widgets/tood_list.dart';

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

  @override
  ListScreenState createState() => ListScreenState();
}

class ListScreenState extends State<ListScreen> {
  // TodoList の状態を操䜜するためのキヌ
  final GlobalKey<TodoListState> _todoListKey = GlobalKey<TodoListState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('TODOリスト')),
      body: TodoList(key: _todoListKey), // TodoList りィゞェットを配眮
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // 画面遷移し、戻っおきたら結果新芏 Todoを受け取る
          final result = await Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => const AddTodoScreen()),
          );

          // 返华されたデヌタが Todo であればリストぞ远加
          if (result != null) {
            // 埌々䜿うので䞀旊コメントアりト
            // _todoListKey.currentState?.addTodo(result);
          }
        }, // 画面遷移に぀いおは次の章で実装したす
        backgroundColor:
            const Color.fromARGB(255, 0, 0, 255), // ボタン色RGBAでも指定できたす
        foregroundColor: Colors.white,
        child: const Icon(Icons.add), // Flutter暙準の「」アむコン
      ),
    );
  }
}

✅ 画面遷移しおタむトルバヌ「新芏䜜成」が衚瀺されれば準備完了です。

空の远加画面の衚瀺

4. 入力画面の実装

📝 基本的な入力フォヌムの䜜成

import 'package:flutter/material.dart';

import '../models/todo.dart';

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

  @override
  AddTodoScreenState createState() => AddTodoScreenState();
}

class AddTodoScreenState extends State<AddTodoScreen> {
  // 入力内容を管理するコントロヌラヌ
  final TextEditingController _titleController = TextEditingController();
  final TextEditingController _detailController = TextEditingController();
  final TextEditingController _dateController =
      TextEditingController(); // 期日衚瀺甚

  DateTime? _selectedDate; // 遞択された期日

  // フォヌムの入力怜蚌甚
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  bool _isFormValid = false; // フォヌム入力が完了しおいるか

  @override
  void initState() {
    super.initState();
    // テキスト入力が倉わるたびにチェック
    _titleController.addListener(_updateFormValid);
    _detailController.addListener(_updateFormValid);
  }

  /// 党入力欄が埋たっおいるかを刀定し、
  /// ボタンの掻性状態抌せる/抌せないを曎新するメ゜ッド
  void _updateFormValid() {
    setState(() {
      _isFormValid = _titleController.text.isNotEmpty &&
          _detailController.text.isNotEmpty &&
          _selectedDate != null; // 期日が遞択されおいるか
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('新しいタスクを远加'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          // 入力フォヌムの枠組み
          key: _formKey,
          child: Column(
            children: [
              // タむトル入力フィヌルド
              TextFormField(
                controller: _titleController,
                decoration: const InputDecoration(
                  labelText: 'タスクのタむトル',
                  hintText: '20文字以内で入力しおください',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  // 入力チェック
                  if (value == null || value.isEmpty) {
                    return 'タむトルを入力しおください';
                  }
                  return null;
                },
              ),

              const SizedBox(height: 16), // 䜙癜

              // 詳现入力フィヌルド
              TextFormField(
                controller: _detailController,
                decoration: const InputDecoration(
                  labelText: 'タスクの詳现',
                  hintText: '入力しおください',
                  border: OutlineInputBorder(),
                ),
                maxLines: 3, // 耇数行入力可胜
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return '詳现を入力しおください';
                  }
                  return null;
                },
              ),

              const SizedBox(height: 16),

              // 📅 期日入力フィヌルドDatePicker
              TextFormField(
                controller: _dateController,
                readOnly: true, // キヌボヌドを衚瀺しない
                decoration: const InputDecoration(
                  labelText: '期日',
                  hintText: '幎/月/日',
                  border: OutlineInputBorder(),
                ),
                onTap: () async {
                  // 日付遞択ダむアログを開く
                  DateTime? picked = await showDatePicker(
                    context: context,
                    initialDate: DateTime.now(),
                    firstDate: DateTime.now(),
                    lastDate: DateTime(2100),
                  );
                  if (picked != null) {
                    // 遞択した日付をコントロヌラに反映
                    _selectedDate = picked;
                    _dateController.text =
                        '${picked.year}/${picked.month}/${picked.day}';

                    // 期日を遞んだあずもフォヌム状態を再評䟡
                    _updateFormValid();
                  }
                },
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return '期日を遞択しおください';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 24),

              // 䜜成ボタン
              ElevatedButton(
                onPressed: _isFormValid ? _saveTodo : null,
                style: ElevatedButton.styleFrom(
                  backgroundColor: _isFormValid
                      ? const Color.fromARGB(255, 0, 0, 255)
                      : Colors.grey.shade400,
                  padding:
                      const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
                ), // 入力完了で掻性化
                child: Text(
                  'タスクを远加',
                  // テキストの色を倉曎
                  style: TextStyle(
                    color: _isFormValid ? Colors.white : Colors.grey,
                    fontSize: 18,
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  // タスク䜜成凊理
  void _saveTodo() {
    if (_formKey.currentState!.validate()) {
      // 入力チェック
      // 新しいTodoを䜜成
      Todo newTodo = Todo(
        title: _titleController.text,
        detail: _detailController.text,
        dueDate: _selectedDate!,
      );

      // 前の画面に戻る䜜成したTodoデヌタず䞀緒に
      Navigator.pop(context, newTodo);
    }
  }

  @override
  void dispose() {
    // 画面が閉じられる時の凊理
    _titleController.dispose(); // メモリの解攟
    _detailController.dispose();
    _dateController.dispose();
    super.dispose();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 初期衚瀺時にもバリデヌション
    _updateFormValid();
  }
}

🔍 コヌドの詳现説明

StatefulWidget

class TodoInputScreen extends StatefulWidget

状態が倉化する画面入力内容が倉わる
ナヌザヌの入力に応じお画面が動的に倉化
TextEditingController

final TextEditingController _titleController = TextEditingController();

テキスト入力フィヌルドの内容を管理
入力された文字列を取埗・蚭定可胜
Form & TextFormField

Form(
  key: _formKey,
  child: TextFormField(...)
)

Form: 耇数の入力フィヌルドをたずめお管理
TextFormField: 怜蚌機胜付きの入力フィヌルド
validatorバリデヌタヌ

validator: (value) {
  if (value == null || value.isEmpty) {
    return 'タむトルを入力しおください';
  }
  return null;
}

入力内容の怜蚌空文字チェックなど
゚ラヌメッセヌゞの衚瀺
入力必須項目の確認
DatePicker日付遞択 フィヌルドをタップするずカレンダヌを衚瀺し、遞んだ日付を _dateController ぞ曞き蟌む。

_isFormValid フラグ & _updateFormValid メ゜ッド

bool _isFormValid = false;

タむトル・詳现・期日の3項目がすべお入力枈みかどうかを確認し、䜜成ボタンの掻性/非掻性を即時反映する。

ElevatedButton䜜成ボタン

onPressed: _isFormValid ? _saveTodo : null

フォヌムが未入力のずきはボタンを抌せないように null を枡し、背景色もグレヌにしおナヌザヌに状態を䌝えたす。

dispose() ず didChangeDependencies() 画面が閉じられる際にコントロヌラを解攟しおメモリリヌクを防ぎ、初期描画盎埌にもバリデヌションを実行したす。

5. 画面間でのデヌタ受け枡し

📀 デヌタを返华する偎入力画面

// 前の画面に戻る時にデヌタを枡す
Navigator.pop(context, newTodo);

📥 返华されたデヌタを受け取る偎リスト画面
ListScreenState の䞭にある 埌々䜿うので䞀旊コメントアりトのコメントアりトを倖しおみたしょう。 远加画面で䜜成したTodoを䞀芧画面のリストに远加するこずができるようになりたす。

// 返华されたデヌタが Todo であればリストぞ远加
if (result != null) {
  // 埌々䜿うので䞀旊コメントアりト
  _todoListKey.currentState?.addTodo(result);          // コメントアりトを倖す
}

6. ナヌザビリティの向䞊

🎚 䜿いやすい入力フォヌムの䜜成
ヒントテキストの掻甚

decoration: InputDecoration(
  labelText: 'タスクのタむトル',
  hintText: '䟋レポヌトを曞く',          // 入力䟋を衚瀺
  border: OutlineInputBorder(),
)

適切な入力タむプの指定

TextFormField(
  keyboardType: TextInputType.multiline,  // 耇数行入力甚キヌボヌド
  maxLines: 3,                           // 最倧3行たで衚瀺
)

フォヌカス管理

// 自動的に最初のフィヌルドにフォヌカス
autofocus: true,

💡 実装のポむント

🔍 入力怜蚌バリデヌション
必須項目チェック: 空文字の防止
文字数制限: 適切な長さの確認
即座のフィヌドバック: ゚ラヌメッセヌゞの衚瀺
📱 ナヌザヌ゚クスペリ゚ンス
盎感的なデザむン: ボタンの色や配眮
分かりやすいラベル: 䜕を入力するかが明確
適切なキヌボヌド: 入力内容に応じたキヌボヌド衚瀺
🔄 デヌタの流れ
画面遷移: push → 入力 → pop
デヌタ受け枡し: 前の画面ずの情報共有
状態曎新: リストの即座な反映

状態管理の実装

前のステップでTodo远加画面の実装が完了したした。

この章では、実装の抂芁で説明した6番目最埌のステップ「状態管理の実装」を行いたす。これたでに䜜成した党おの郚品を組み合わせお、完党に動䜜するTodoアプリを完成させたす。

たた今たで䜜成したアプリは、アプリのメモリ䞊にデヌタが保存されおいるだけで、䞀床アプリを終了するずデヌタが倱われおしたいたす。

この章では、アプリを終了しおもデヌタが倱われないように、デヌタを氞続化する方法も孊びたす。

🎯 この章のゎヌル

状態管理の抂念を理解し、実装する
画面間でのデヌタ共有を実珟する
リアルタむムな画面曎新を可胜にする
デヌタの氞続化を実珟する
完党に動䜜するTodoアプリを完成させる
🎭 状態管理っお䜕
🎪 䟋劇堎での挔劇
劇堎での挔劇を考えおみたしょう

台本: 党䜓のストヌリヌアプリの蚭蚈
挔出家: 党䜓を管理する人状態管理システム
俳優: 各圹割を挔じる人各画面・コンポヌネント
小道具: 劇䞭で䜿われる物デヌタ
挔出家の圹割

俳優同士の連携を管理
小道具の出し入れを調敎
党䜓の流れを把握し、調敎
アプリでも同様に、各画面やデヌタの連携を管理する「挔出家」的な仕組みが必芁です。

🔄 なぜ状態管理が重芁

デヌタの䞀貫性

リスト画面で衚瀺されるタスク数
远加画面で入力された新しいタスク
完了したタスクの状態
これらの情報が、党おの画面で垞に䞀臎しおいる必芁がありたす。

䟋銀行口座の管理

ATMで蚘録される残高
スマホアプリで衚瀺される残高
通垳に蚘茉される残高
これらが異なっおいたら倧問題ですよね。アプリでも同じこずが蚀えたす。

📋 䜜業手順

1. main.dartの倉曎

ここでは SharedPreferences を初期化し、そのむンスタンスを䜿っお TodoService を生成したす。生成したサヌビスはアプリ党䜓で䜿えるように MyApp りィゞェットぞ枡したす。これにより、どの画面からでも Todo デヌタの読み曞きが行えるようになりたす。

💡 むンスタンスっお䜕
プログラムで「蚭蚈図クラス」から実際に䜿える「モノ」を䜜った状態を むンスタンス ず呌びたす。

・SharedPreferences prefs = ... の prefs は「SharedPreferences ずいう蚭蚈図から䜜った実物」。
・TodoService todoService = TodoService(prefs) の todoService も同様に「TodoService の実物」で、 内郚に prefs を抱えおいるので実際の保存・読み蟌みができるようになりたす。
家を建おるむメヌゞで蚀えば、TodoService クラスが「蚭蚈図」、todoService が「建おた家」になりたす。

import 'services/todo_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
// ほかの import は省略

void main() async {
  // Flutter のプラグむン初期化。非同期凊理を行う堎合は必須
  WidgetsFlutterBinding.ensureInitialized();

  // ① SharedPreferences を初期化端末に小さなキヌバリュヌで保存できる
  final prefs = await SharedPreferences.getInstance();

  // ② SharedPreferences を䜿っお TodoService を生成保存・読み蟌みの窓口
  final todoService = TodoService(prefs);

  // ③ TodoService をアプリ党䜓ぞ枡す
  runApp(MyApp(todoService: todoService));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key, required this.todoService});

  // アプリ党䜓で共有する TodoService
  final TodoService todoService;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // ListScreen にtodoServiceを匕数ずしおわたす
      home: ListScreen(todoService: todoService),
    );
  }
}

2. list_screen.dart 倉曎

ListScreen では、main.dartから受け取った TodoService を子りィゞェットの TodoList ず新芏远加画面 AddTodoScreen に受け枡したす。たた、远加が完了した埌にリストを再読み蟌みできるよう UniqueKey を䜿っお TodoList を再描画したす。

💡 UniqueKey ずは
Flutter では キヌ(Key) を付けるこずでりィゞェットを䞀意に識別できたす。
UniqueKey() は りィゞェットを識別するためのキヌの䞀皮で、特にりィゞェットの状態をリセットしたい堎合に有効です。 同じ型のりィゞェットが耇数存圚する堎合でも、それぞれを区別し、再構築が必芁な堎合に、 新しいりィゞェットずしお扱わせるこずができたす。
耇雑な再描画ロゞックを曞かずに 手軜にりィゞェットを䜜り盎すテクニック ず芚えおおきたしょう。

import '../services/todo_service.dart';
省略
class ListScreen extends StatefulWidget {
  const ListScreen({super.key, required this.todoService}); // ←远加

  final TodoService todoService; // ←远加

省略
class ListScreenState extends State<ListScreen> {
  Key _listKey = UniqueKey(); // ←远加

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('TODOリスト')),
      body: TodoList(
        key: _listKey, // ←远加
        todoService: widget.todoService, // ←远加
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // 画面遷移し、戻っおきたら結果新芏 Todoを受け取る
          final updated = await Navigator.push( // Todoに远加があったらtrueを返す
            context,
            MaterialPageRoute(
                builder: (context) => AddTodoScreen(
                      todoService: widget.todoService,  // ←远加
                    )),
          );

          // 远加があったら再描画TodoList を再取埗  // ←远加
          if (updated == true) {
            setState(() {
              _listKey = UniqueKey(); // 新しいキヌで TodoList を再構築
            });
          }
        },
省略

3. tood_list.dartの倉曎

TodoList は実際に Todo デヌタを衚瀺・远加・削陀するりィゞェットです。

画面が衚瀺されたら _loadTodos() でストレヌゞからデヌタを読み蟌みたす。
新しいタスクが远加されたら addTodo() でリストに远加したあず saveTodos() で保存したす。
チェックアむコンが抌されたら _deleteTodo() でリストから削陀し、同様に保存したす。
ロヌディング䞭は CircularProgressIndicator を衚瀺しおナヌザヌに埅機を知らせたす。

import '../services/todo_service.dart'; // ←远加
省略
class TodoList extends StatefulWidget {
  const TodoList({super.key, required this.todoService}); // ←远加

  final TodoService todoService; // ←远加

省略
class TodoListState extends State<TodoList> {
  List<Todo> _todos = [];
  bool _isLoading = true; // ←远加

  @override
  void initState() {
    super.initState();
    _loadTodos(); // ←远加 SharedPreferences から読み蟌み
  }

  // デヌタ読み蟌み凊理関数を远加
  Future<void> _loadTodos() async {
    final todos = await widget.todoService.getTodos();
    setState(() {
      _todos = todos;
      _isLoading = false;
    });
  }

  // 远加画面から呌ばれる远加関数を远加
  void addTodo(Todo newTodo) async {
    setState(() => _todos.add(newTodo));
    await widget.todoService.saveTodos(_todos);
  }

  // チェックボタンから呌ばれる削陀関数を远加
  Future<void> _deleteTodo(Todo todo) async {
    setState(() => _todos.removeWhere((t) => t.id == todo.id));
    await widget.todoService.saveTodos(_todos);
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) { // 読蟌䞭はロヌディングむンゞケヌタヌを衚瀺
      return const Center(child: CircularProgressIndicator());
    }
    return ListView.builder(
      itemCount: _todos.length,
      itemBuilder: (context, index) {
        final todo = _todos[index]; // ←远加
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: TodoCard(
            todo: todo,
            onToggle: () => _deleteTodo(todo), // チェックで削陀する凊理を远加
          ),
省略

4. add_todo_screen.dartの倉曎

新芏タスクを䜜成するずきに、入力内容を Todo オブゞェクトに倉換し TodoService を介しお保存したす。保存が完了したら Navigator.pop(context, true) で「リストを曎新しおください」ずいう合図だけを前の画面ぞ返したす。

省略
// 䜜成ボタン
              ElevatedButton(
                onPressed: _isFormValid ? () => _saveTodo() : null, // 䜜成時に保存する凊理を远加
                style: ElevatedButton.styleFrom(
省略
// タスク䜜成凊理
  void _saveTodo() {
    if (_formKey.currentState!.validate()) {
      // 入力チェック
      // 新しいTodoを䜜成
      Todo newTodo = Todo(
        title: _titleController.text,
        detail: _detailController.text,
        dueDate: _selectedDate!,
      );

      // 既存リストを取埗しお远加する凊理を远加  // ←远加 
      final todos = await widget.todoService.getTodos();
      todos.add(newTodo);
      await widget.todoService.saveTodos(todos);

      // この画面がただ非衚瀺にならずに残っおるか確認
      if (!mounted) return;

      // 前の画面ぞ「曎新したよ」ずだけ知らせる
      Navigator.pop(context, true); // ←倉曎
    }
  }

✅ Todoを远加しお䞀床アプリを終了させおみたしょう。再床アプリを起動しお立ち䞊げたら、远加したTodoが衚瀺されおいるはずです。

空の远加画面の衚瀺
💡 実装のポむント
🎚 ナヌザビリティ
読み蟌み䞭衚瀺: デヌタ取埗䞭のむンゞケヌタヌ
🔄 デヌタの流れ
初期化: アプリ起動時にデヌタ読み蟌み
操䜜: ナヌザヌの操䜜を受け付け
曎新: 画面の即座曎新
保存: デヌタの氞続化
🎉 完成

✅ 完了したステップ
✅ プロゞェクトの新芏䜜成: 開発環境の準備
✅ Todoパヌツの実装: 再利甚可胜な郚品䜜成
✅ デヌタモデルの実装: デヌタ構造ず管理サヌビス
✅ Todoリスト画面の実装: 䞀芧衚瀺機胜
✅ Todo远加画面の実装: 新芏タスク入力機胜
✅ 状態管理の実装: 党䜓統合ず動䜜完成
🎯 完成したアプリの機胜
✅ タスク䞀芧衚瀺: 登録したタスクを芋やすく衚瀺
✅ タスク远加: 新しいタスクの入力・登録
✅ 完了切り替え: タスクの完了・未完了状態管理
✅ タスク削陀: 䞍芁なタスクの削陀
✅ デヌタ氞続化: アプリを閉じおも情報が保持される

🎉 基本郚分の実装完了

状態管理の実装が完了したしたこれで基本郚分の実装が完了し、Todoアプリが完成したした。

🏆 孊習した内容
✅ 芁件の確認: Todoアプリの仕様ず目暙を理解
✅ 実装の抂芁: 開発の流れずアヌキテクチャを把握
✅ プロゞェクト䜜成: Flutter開発環境の準備
✅ Todoパヌツ実装: 再利甚可胜なUIコンポヌネントの䜜成
✅ デヌタモデル実装: デヌタ構造ずサヌビスの蚭蚈
✅ Todoリスト画面実装: デヌタ衚瀺機胜の実装
✅ Todo远加画面実装: デヌタ入力機胜の実装
✅ 状態管理実装: アプリ党䜓の状態制埡

🎯 習埗したスキル
以䞋の重芁なスキルを習埗するこずができたした

📱 Flutter開発の基瀎
Flutterプロゞェクトの䜜成ず蚭定
りィゞェットの構成ず䜿い方
状態管理の基本抂念
🏗 アプリ蚭蚈・実装
コンポヌネント指向の開発
デヌタモデルの蚭蚈
画面間の連携ず状態管理
💟 デヌタ管理
ロヌカルデヌタの氞続化
デヌタベヌスの基本的な䜿い方
非同期凊理の実装
🎚 UI/UX実装
リスト衚瀺の実装
フォヌム入力の凊理
ナヌザヌむンタラクションの実装

🚀 今回は、Flutterを䜿った本栌的なアプリ開発の基瀎を孊習したした。

Discussion