1
4

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×FirebaseのAuthとCRUD処理まとめ

Last updated at Posted at 2021-06-28
Firebase_auth_crud

M1 MacBook、Android Studio、Flutter 2.0
2021年1月末からプログラミング学習始めた初心者です。自分用メモ。

https://qiita.com/igakeso/items/2c567176326f783801d6
前回、Flutter×FirebaseのCRUD処理をまとめたのですが、Authのuidと紐付いていないことに気付いたため、FrebaseAuthのuidと共通でFirestoreにusersのcollectionを作り、そのサブコレクションにtodoのcollectionを作り、サブコレクションに対してCRUDを行う処理をまとめました。

1、FlutterとFIrebaseのセットアップ
https://firebase.google.com/docs/flutter/setup
2、セットアップ後、main.dartだけで動くコードです。

  firebase_core: ^1.3.0
  firebase_auth: ^1.4.1
  cloud_firestore: ^2.2.2
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Auth CRUD',
      theme: ThemeData(
        backgroundColor: Colors.blue,
      ),
      home: AuthPage(),
    );
  }
}

class AuthPage extends StatefulWidget {
  @override
  _AuthPageState createState() => _AuthPageState();
}

class _AuthPageState extends State<AuthPage> {
  late TextEditingController nameInputController = TextEditingController();
  late TextEditingController emailInputController = TextEditingController();
  late TextEditingController passwordInputController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ログイン画面'),
      ),
      body: Center(
        child: Container(
          padding: const EdgeInsets.all(20),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Image.network(
                  'https://1.bp.blogspot.com/-UG7cj_zxYes/X4aVsGG-tgI/AAAAAAABbys/BpJ8aaXO6vALEcBTUJtPzo-0sBQaOT69wCNcBGAsYHQ/s862/ofuro_sauna_door.png',
                  height: 200.0),
              TextFormField(
                controller: nameInputController,
                decoration: const InputDecoration(labelText: 'お名前(初回登録時のみ入力)'),
              ),
              TextFormField(
                controller: emailInputController,
                decoration: const InputDecoration(labelText: 'メールアドレス'),
                keyboardType: TextInputType.emailAddress,
              ),
              TextFormField(
                controller: passwordInputController,
                decoration: const InputDecoration(labelText: 'パスワード(6文字以上)'),
                obscureText: true,
              ),
              const SizedBox(height: 20),
              Container(
                width: double.infinity,
                child: ElevatedButton(
                  child: const Text('ユーザー登録'),
                  onPressed: () async {
                    try {
                      await FirebaseAuth.instance.createUserWithEmailAndPassword(
                              email: emailInputController.text,
                              password: passwordInputController.text);
                      await FirebaseFirestore.instance.collection("users").doc(FirebaseAuth.instance.currentUser!.uid).set({
                        'name': nameInputController.text,
                        'email': emailInputController.text,
                      });
                    } catch (e) {
                      print(e);
                    }
                  },
                ),
              ),
              const SizedBox(height: 10),
              Container(
                width: double.infinity,
                child: ElevatedButton(
                  child: const Text('ログイン'),
                  onPressed: () async {
                    try {
                      await FirebaseAuth.instance.signInWithEmailAndPassword(
                          email: emailInputController.text,
                          password: passwordInputController.text);
                      final loginUser = FirebaseAuth.instance.currentUser;
                      await FirebaseFirestore.instance.collection("users").doc(loginUser!.uid).get();
                      Navigator.of(context).pushReplacement(MaterialPageRoute(
                          builder: (context) => HomePage(loginUser)));
                    } catch (e) {
                      print(e);
                    }
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  HomePage(this.loginUser);
  late User loginUser;

  @override
  _HomePageState createState() => _HomePageState(loginUser);
}

class _HomePageState extends State<HomePage> {
  _HomePageState(this.loginUser);
  User loginUser;
  TextEditingController createTodoController = TextEditingController();
  TextEditingController updateTodoController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ログイン後画面'),
      ),
      body: SafeArea(
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(10.0),
              child: Text('Login User uid: ' + loginUser.uid),
            ),
            Image.network('https://1.bp.blogspot.com/-f3RpEHd25R4/X79cj7SEo_I/AAAAAAABce8/tPhWRoxKbOAvUNRcwqxK1YmVyKrKCNapwCNcBGAsYHQ/s762/ofuro_sauna_gaikiyoku.png',
                height: 200.0),
            Container(
              child: Expanded(
                child: StreamBuilder<QuerySnapshot>(
                    stream: FirebaseFirestore.instance.collection('users').doc(loginUser.uid).collection('todo').snapshots(),
                    builder: (context, snapshot) {
                      if (snapshot.hasData) {
                        return ListView.builder(
                          itemBuilder: (BuildContext context, int index) {
                            return CheckboxListTile(
                              controlAffinity: ListTileControlAffinity.leading,
                              title: Text(snapshot.data!.docs[index]['title']),
                              value: snapshot.data!.docs[index]['isDone'],
                              onChanged: (value) {
                                snapshot.data!.docs[index].reference.update({
                                  'isDone': value,
                                  'updatedTime': Timestamp.now(),
                                });
                              },
                              secondary: IconButton(
                                icon: const Icon(Icons.more_horiz),
                                onPressed: () {
                                  showModalBottomSheet(
                                      context: context,
                                      builder: (context) {
                                        return SafeArea(
                                          child: Column(
                                            mainAxisSize: MainAxisSize.min,
                                            children: [
                                              ListTile(
                                                title: const Text('編集'),
                                                leading: const Icon(Icons.edit),
                                                onTap: () {
                                                  Navigator.pop(context);
                                                  showDialog(
                                                      context: context,
                                                      builder: (context) {
                                                        return SimpleDialog(
                                                          title: Container(
                                                            child: Column(
                                                              children: [
                                                                const Text('編集'),
                                                                Container(
                                                                  child: TextField(
                                                                    controller: updateTodoController,
                                                                    decoration: const InputDecoration(
                                                                        border: OutlineInputBorder()
                                                                    ),
                                                                  ),
                                                                ),
                                                                ElevatedButton(
                                                                  onPressed:
                                                                      () async {
                                                                    await snapshot.data!.docs[index].reference.update({
                                                                      'title': updateTodoController.text,
                                                                      'updatedTime': Timestamp.now(),
                                                                    });
                                                                    Navigator.pop(context);
                                                                  },
                                                                  child: const Text('編集'),
                                                                )
                                                              ],
                                                            ),
                                                          ),
                                                        );
                                                      });
                                                },
                                              ),
                                              ListTile(
                                                title: const Text('削除'),
                                                leading: const Icon(Icons.delete),
                                                onTap: () {
                                                  Navigator.pop(context);
                                                  showDialog(
                                                      context: context,
                                                      builder: (context) {
                                                        return AlertDialog(
                                                          title: Text(
                                                              '${snapshot.data!.docs[index]['title']}を削除実行しますか?'
                                                          ),
                                                          actions: [
                                                            TextButton(
                                                              onPressed:
                                                                  () async {
                                                                await snapshot.data!.docs[index].reference.delete();
                                                                Navigator.pop(context);
                                                              },
                                                              child: const Text('削除実行'),
                                                            ),
                                                            TextButton(
                                                              onPressed: () {
                                                                Navigator.pop(context);
                                                              },
                                                              child: const Text('キャンセル'),
                                                            ),
                                                          ],
                                                        );
                                                      });
                                                },
                                              )
                                            ],
                                          ),
                                        );
                                      });
                                },
                              ),
                            );
                          },
                          itemCount: snapshot.data!.docs.length,
                        );
                      } else {
                        return Container();
                      }
                    }),
              ),
            ),
            Container(
              padding: const EdgeInsets.only(left: 20, right:20),
              width: double.infinity,
              child: ElevatedButton(
                  child: const Text('ログアウト'),
                  onPressed: () async {
                    await FirebaseAuth.instance.signOut();
                    Navigator.of(context).pop();
                  }),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
          child: const Icon(Icons.add),
          onPressed: () {
            showModalBottomSheet(
                context: context,
                builder: (context) {
                  return SafeArea(
                    child: Column(mainAxisSize: MainAxisSize.min, children: [
                      const Padding(
                        padding: EdgeInsets.all(10.0),
                        child: Text('ToDo新規登録'),
                      ),
                      Padding(
                        padding: const EdgeInsets.all(10.0),
                        child: Text('Login User uid: ' + loginUser.uid),
                      ),
                      Padding(
                        padding: const EdgeInsets.all(20.0),
                        child: Container(
                          child: TextField(
                            controller: createTodoController,
                            decoration: const InputDecoration(
                                border: OutlineInputBorder()
                            ),
                          ),
                        ),
                      ),
                      Container(
                        padding: const EdgeInsets.only(left:20, right:20),
                        width: double.infinity,
                        child: ElevatedButton(
                          child: const Text('新規登録'),
                          onPressed: () async {
                            await FirebaseFirestore.instance.collection("users").doc(loginUser.uid).collection('todo').add({
                              'title': createTodoController.text,
                              'isDone': false,
                              'createdTime': Timestamp.now()
                            });
                            Navigator.pop(context);
                          },
                        ),
                      ),
                      Container(
                        width: double.infinity,
                        padding: const EdgeInsets.only(left:20, right:20),
                        child: ElevatedButton(
                          child: const Text('戻る'),
                          onPressed: () {
                            Navigator.of(context).pop();
                          },
                        ),
                      ),
                    ]
                    ),
                  );
                });
          }),
    );
  }
}
1
4
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
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?