0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FlutterでTodoアプリを作成した その1

Posted at

はじめに

Flutter で よく見かけるようなTodoアプリを作成したので、備忘録としていくつかの記事に分けて残します。
今回の記事では全体の構成とタスク一覧画面について書いていきます。

開発環境

  • Windows10
  • Flutter 3.0.1
  • Dart 2.17.1
  • Android Studio Chipmunk|2021.2.1

アプリの画面構成

アプリ起動時にログイン画面が表示され、「Firebase Authentication」を使用した認証を行います。
認証が通ったらタスクの一覧画面に遷移します。
登録したタスクは、「Firestore」上にデータが保存されます。
Todoアプリ全体図.png

タスク一覧画面

ハンバーガーメニュー

スマートフォンアプリでよく見かける以下のような、ハンバーガーメニューのやり方です。

Drawer

ハンバーガーメニューを実装するには「Drawer」を使います。
「ListTile」を増やすことによって、メニューを追加することができます。

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: <Widget>[
            const DrawerHeader(
              decoration: BoxDecoration(
                color: Colors.amber,
              ),
              child: Text(
                'Todoアプリ',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 24,
                ),
              ),
            ),
            ListTile(
              leading: const Icon(Icons.flag_rounded, color: Colors.grey),
              title: const Text('フラグ'),
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) {
                    return TodoListFlagPage(userId: widget.userId);
                  }),
                );
              },
            ),
            ListTile(
              leading: const Icon(Icons.circle_outlined, color: Colors.grey),
              title: const Text('未完了'),
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) {
                    return TodoListNoDonePage(userId: widget.userId);
                  }),
                );
              },
            ),
            ListTile(
              leading: const Icon(Icons.logout, color: Colors.grey),
              title: const Text('ログアウト'),
              onTap: () async {
                FirebaseAuth.instance.signOut();
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) {
                    return const Login();
                  }),
                );
              },
            ),
          ],
        ),
      ),
      body: Column(
      //以下省略

タスク一覧

タスク一覧画面では「ListTile」を使って実装しています。。
無題.png

body: Column(
        children: [
          Expanded(
            // FutureBuilder
            child: StreamBuilder<QuerySnapshot>(
              stream: FirebaseFirestore.instance
                  .collection('User')
                  .doc(widget.userId)
                  .collection('Todo')
                  .orderBy('date')
                  .snapshots(),
              builder: (context, snapshot) {
                // データが取得できた場合
                if (snapshot.hasData) {
                  final List<DocumentSnapshot> documents = snapshot.data!.docs;
                  // 取得した投稿メッセージ一覧を元にリスト表示
                  return ListView(
                    children: documents.map((document) {
                      return Card(
                        child: Slidable(
                          key: ObjectKey(FirebaseFirestore.instance
                              .collection('User')
                              .doc(widget.userId)
                              .collection('Todo')
                              .doc(document.id)),
                          startActionPane: ActionPane(
                            motion: const ScrollMotion(),
                            dismissible: DismissiblePane(onDismissed: () {
                              setState(() async {
                                await FirebaseFirestore.instance
                                    .collection('User')
                                    .doc(widget.userId)
                                    .collection('Todo')
                                    .doc(document.id)
                                    .delete();
                              });
                            }),
                            children: [
                              SlidableAction(
                                onPressed: (context) async {
                                  setState(() async {
                                    await FirebaseFirestore.instance
                                        .collection('User') // コレクションID
                                        .doc(widget.userId)
                                        .collection('Todo')
                                        .doc(document.id)
                                        .delete();
                                  });
                                },
                                backgroundColor: const Color(0xFFFE4A49),
                                foregroundColor: Colors.white,
                                icon: Icons.delete,
                                label: '削除',
                              ),
                              SlidableAction(
                                onPressed: (context) async {
                                  await Navigator.of(context).push(
                                    // タスク編集画面に遷移
                                    MaterialPageRoute(builder: (context) {
                                      return TodoEditPage(
                                          userId: widget.userId,
                                          documentId: document.id,
                                          title: document['title'],
                                          memo: document['memo'],
                                          startDay: document['startDay'].toDate(),
                                          endDay: document['endDay'].toDate());
                                    }),
                                  );
                                },
                                backgroundColor: const Color(0xFF21B7CA),
                                foregroundColor: Colors.white,
                                icon: Icons.edit,
                                label: '編集',
                              ),
                            ],
                          ),
                          child: ListTile(
                            title: Text(document['title'],
                                // チェックありの時に、タスクに取り消し線をつける
                                style: document['done']
                                    ? const TextStyle(
                                        decoration: TextDecoration.lineThrough)
                                    : const TextStyle(
                                        decoration: TextDecoration.none)),
                            leading: Checkbox(
                              activeColor: Colors.blue,
                              value: document['done'],
                              shape: const CircleBorder(),
                              onChanged: (value) async {
                                await FirebaseFirestore.instance
                                    .collection('User')
                                    .doc(widget.userId)
                                    .collection('Todo')
                                    .doc(document.id)
                                    .update({'done': !document['done']}); // データ
                              },
                            ),
                            // お気に入りの状態で、アイコンを変化させる
                            trailing: document['flag']
                                ? IconButton(
                                    icon: const Icon(Icons.flag_rounded,
                                        color: Colors.redAccent),
                                    tooltip: 'フラグあり',
                                    onPressed: () async {
                                      await FirebaseFirestore.instance
                                          .collection('User')
                                          .doc(widget.userId)
                                          .collection('Todo')
                                          .doc(document.id)
                                          .update({
                                        'flag': !document['flag']
                                      }); // データ
                                    },
                                  )
                                : IconButton(
                                    icon: const Icon(Icons.flag_rounded,
                                        color: null),
                                    tooltip: 'フラグなし',
                                    onPressed: () async {
                                      await FirebaseFirestore.instance
                                          .collection('User')
                                          .doc(widget.userId)
                                          .collection('Todo')
                                          .doc(document.id)
                                          .update({
                                        'flag': !document['flag']
                                      }); // データ
                                    },
                                  ),
                            onTap: () async {
                              await Navigator.of(context).push(
                                // タスク詳細画面に遷移
                                MaterialPageRoute(builder: (context) {
                                  return TodoRecordView(
                                      documentId: document.id,
                                      userId: widget.userId,
                                      title: document['title'],
                                      memo: document['memo'],
                                      startDay: document['startDay'].toDate(),
                                      endDay: document['endDay'].toDate());
                                }),
                              );
                            },
                          ),
                        ),
                      );
                    }).toList(),
                  );
                }
                // データが読込中の場合
                return const Center(
                  child: Text('読込中...'),
                );
              },
            ),
          ),
        ],
      ),

おわり

実装は手を動かしながらやるのでさくさく進めることができましたが、記事書くのはつまらないので書くスピードが落ちてしまっているので、次回以降はスムーズに書けるように工夫したいと思ってます。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?