🤣

[Flutter×Firebase]世界一簡単なToDoアプリ

2024/06/10に公開

記事を書いたきっかけ

登竜門ハッカソンのチームメンターをしていて、実際にFlutterを使った簡単なToDoアプリをまずは作ってもらおうということでこの記事を書きました。

時間がない人に向けた全てのコード

main.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:testproj/SecondPage.dart';
import 'firebase_options.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    final TextEditingController _nameController = TextEditingController();
    final db = FirebaseFirestore.instance;
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
          child: Column(
        children: [
          Container(
            height: 120,
          ),
          Container(
            width: 200,
            child: TextField(
              controller: _nameController,
              decoration: const InputDecoration(
                border: OutlineInputBorder(),
                labelText: 'Name',
              ),
            ),
          ),
          Container(
            height: 20,
          ),
          ElevatedButton(
            onPressed: () async {
              await db.collection('users').doc().set({
                'name': _nameController.text,
              });
              _nameController.clear();
            },
            child: const Text("submit"),
          ),
          ElevatedButton(
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => SecondPage(),
                ),
              );
            },
            child: const Text("Next Page"),
          ),
        ],
      )),
    );
  }
}

解説

基幹部分を解説していきます。
まず、_nameControllerでtextfieldに入力されたStringを取得してそれを

ElevatedButton(
            onPressed: () async {
              await db.collection('users').doc().set({
                'name': _nameController.text,
              });
              _nameController.clear();
            },
            child: const Text("submit"),
          ),

ここでfirestoreに保管しています。
そしてその下のボタンで次のページに移動します。

SecondPage.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

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

  
  Widget build(BuildContext context) {
    final db = FirebaseFirestore.instance;
    final Stream<QuerySnapshot> _usersStream =
        db.collection('users').snapshots();
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second Page'),
      ),
      body: StreamBuilder(
        stream: _usersStream,
        builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
          if (snapshot.hasError) {
            return Center(child: Text('Something went wrong'));
          }

          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          }

          return ListView(
            children: snapshot.data!.docs.map((DocumentSnapshot document) {
              Map<String, dynamic> data =
                  document.data()! as Map<String, dynamic>;
              return Container(
                padding: const EdgeInsets.all(8),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(data['name']),
                    TextButton(
                      onPressed: () {
                        db.collection('users').doc(document.id).delete();
                      },
                      child: const Text("delete"),
                    )
                  ],
                ),
              );
            }).toList(),
          );
        },
      ),
    );
  }
}

ここのページでは「Streamを使うことでfirestoreとViewを繋ぐ際の状態管理をうまくしてくれているんだなぁ」程度の認識で大丈夫です!

return ListView(
            children: snapshot.data!.docs.map((DocumentSnapshot document) {
              Map<String, dynamic> data =
                  document.data()! as Map<String, dynamic>;
              return Container(
                padding: const EdgeInsets.all(8),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(data['name']),
                    TextButton(
                      onPressed: () {
                        db.collection('users').doc(document.id).delete();
                      },
                      child: const Text("delete"),
                    )
                  ],
                ),
              );
            }).toList(),
          );

この部分でsnapshotの中のそれぞれのdataに対してnameを取り出してlist形式で表示しています。
という形で世界一簡単なToDoアプリでした!
チームの皆さん、頑張りましょう!

Discussion