【第十五天 - Flutter 官方 CodeLab Get-To-Know 活动报名教学(下)】

前言

我很喜欢这篇 CodeLab,我自己认为,如果这篇的内容看得懂那 Provider 基本上都会了。

如果想要看这篇的成品什麽样子,欢迎去 官方 CodeLab 看。

ApplicationProvider

接续 【第十四天 - Flutter 官方 CodeLab Get-To-Know 活动报名教学(上)】,直接进入主题介绍方法。

初始化

在使用 Firebase 的时候都需要初始化。

await Firebase.initializeApp();

FireStore 监听资料

这边是用来查看有几个人参加报名的,不需要判断有无登入,都需要有参与者的人数。
可以看到这里,他去找寻 collection == attendees 并找寻底下的 attending 栏位 == true,并进行即时监听资料。可以看到这里,他去找寻 collection == attendees 并找寻底下的 attending 栏位 == true,并进行即时监听资料。

https://ithelp.ithome.com.tw/upload/images/20210908/20134548WunVMoJAf1.png

    FirebaseFirestore.instance
        .collection('attendees')
        .where('attending', isEqualTo: true)
        .snapshots()
        .listen((snapshot) {
      _attendees = snapshot.docs.length;
      notifyListeners();
    });

这边是先判断使用者是否有登入
未登入

  • Subscription
  • 留言暂存的 list 清空
    已登入
  • 监听聊天内容( collection == guestbook,并且照着 timestamp 由大到小排序,并读取每一个 document 的资料
  • 取得现在登入者的参与状况
    FirebaseFirestore.instance.collection('attendees').doc(user.uid),是表示他要去取得 attendees 的资料,并且 doc 的 id == user.uid(现在使用者 的 uid)
    https://ithelp.ithome.com.tw/upload/images/20210908/20134548LyMAqf7KOJ.pnghttps://ithelp.ithome.com.tw/upload/images/20210908/20134548CdK0o7YxVz.png
    // 监听使用者使否有登入的资讯
    FirebaseAuth.instance.userChanges().listen((user) {
      // 更改後,使用者从无登入 -> 已经登入的状态
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        // StreamSubscription 监听资讯
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
              // 先清空资料
          _guestBookMessages = [];
          snapshot.docs.forEach((document) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'].toString(),
                message: document.data()['text'].toString(),
              ),
            );
          });
          // 资料变动完後, rebuild 画面
          notifyListeners();
        });
        // 监听 attending 参加人数的资料
        _attendingSubscription = FirebaseFirestore.instance
            .collection('attendees')
            .doc(user.uid)
            .snapshots()
            .listen((snapshot) {
          if (snapshot.data() != null) {
            if (snapshot['attending']==true) {
              _attending = Attending.yes;
            } else {
              _attending = Attending.no;
            }
          } else {
            _attending = Attending.unknown;
          }
          notifyListeners();
        });
      } else {
        // 关闭监听器
        _loginState = ApplicationLoginState.loggedOut;
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        _attendingSubscription?.cancel(); // new
      }
      notifyListeners();
    });
    ```
    # 更新参与者状况
    这边会去存入 `collection == attendees` 且 `document id == currentUser.uid`。
    存入资料都必须像这样,是一个 `map` 的型态。
    ```dart
      /// 更新 firebase user 的参加状态。
  /// 这边更新後会触发 firebase 的 userChanges().listen,然後信行 attending 的更新
  set attending(Attending attending) {
    final userDoc = FirebaseFirestore.instance
        .collection('attendees')
        .doc(FirebaseAuth.instance.currentUser!.uid);
    if (attending == Attending.yes) {
      var map = {'attending': true};
      userDoc.set(map);
    } else {
      var map = {'attending': false};
      userDoc.set(map);
    }
  }

更新留言资讯

  这边会去存入 `collection == guestbook`,这边没有指定 `document` 的名称,因此 `firebase` 会随机产生。
存入资料都必须像这样,是一个 map 的型态。
  /// 更新 firebase message 的留言状态。
/// 这边更新後会触发 firebase 的 userChanges().listen,然後信行 attending 的更新
Future<DocumentReference> addMessageToGuestBook(String message) {
  if (_loginState != ApplicationLoginState.loggedIn) {
    throw Exception('Must be logged in');
  }
  var map = {
    'text': message,
    'timestamp': DateTime.now().millisecondsSinceEpoch,
    'name': FirebaseAuth.instance.currentUser!.displayName,
    'userId': FirebaseAuth.instance.currentUser!.uid,
  };
  return FirebaseFirestore.instance.collection('guestbook').add(map);
}

介绍 Firebase 的一些方法

他会返回可用於登录给定的登录方法列表,用户(由其主要电子邮件地址标识)。当您支持多种身份验证机制时,此方法很有用。如果找不到用户,则返回一个空的 List。如果电子邮件地址无效则抛出 FirebaseAuthException不合规电邮

FirebaseAuth.instance.fetchSignInMethodsForEmail(email)

提供 eamil 的登入方式

FirebaseAuth.instance.signInWithEmailAndPassword

注册

FirebaseAuth.instance
        .createUserWithEmailAndPassword(email: email, password: password);
FirebaseAuth.instance.signOut();

Consumer

Cosumer,他类似一个 View 的控制器,他有 Cosumer2Cosumer3 等等...,builder 里面第一个参数是 context,第二个则是 model(ApplicationProvider 的 model),第三个则是 child

Consumer4 的范例

Consumer4<changeNotifier1, changeNotifier2, changeNotifier3, changeNotifier4>(
builder: (context, changeNotifier1, changeNotifier2, changeNotifier3, changeNotifier4,   child) {
   // return your widget
}
)

这边的话,累是一个 View 的中转站,从 ApplicationProvider 取得现在登入的状态,和方法,在 Consumer 中转站传递给自定义的 Authentication 元件,让逻辑和画面分离。并使用 callBack 的方式,让 ApplicationProvider,Firebase 里面的 exception 可以显示在画面上面。

          Consumer<ApplicationProvider>(
          builder: (context, appState, _) {
            return Authentication(
              email: appState.email,
              loginState: appState.loginState,
              startLoginFlow: appState.startLoginFlow,
              // 把逻辑写在 provider 里面。然後当 Widget 里面触发资料後
              verifyEmail: appState.verifyEmail,
              signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
              cancelRegistration: appState.cancelRegistration,
              registerAccount: appState.registerAccount,
              signOut: appState.signOut,
            )
            ;
          }
        ),

References

  • Get to know Firebase for Flutter

  • Flutter Codelab

    总结

    这篇虽然很长,但是只要看懂 providerAuthentication 元件、CallBack,整篇大约就会一目了然。
    初接触 Firebase 的朋友,可能就要了解一下要怎麽连接 Firebase,还有 FireStore 怎麽使用,collectiondocumentuserID、还有一些即时更新的 listener,需要去熟悉他,希望大家可以在 Flutter 上有所成长~~


<<:  [第14天]理财达人Mx. Ada-盘中零股交易(Intraday Odd Order)

>>:  Day14 Gin and Go Mod

D04 / 可不可以用 ConstraintLayout - ConstraintLayout

今天大概会聊到的范围 Constraint Layout in Compose 上一篇提到,有 R...

[Day20] swift & kotlin 游戏篇!(2) 小鸡BB-游戏制作-小鸡排版

游戏示意 swift 版本 kotlin 版本 swift - 改写小鸡动画 原本画面是这样 下一步...

Day 12 Kafka 超简单安装!!

今天要介绍如何安装 Kafka 方法一. 利用 Docker 安装 Kafka 安装 Docker-...

[Day25] Flutter - Application Authentication (part9)

前言 Hi, 我是鱼板伯爵接着就是来验证登入状态,如果已经登入就跳转到首页否则就在登入画面,看完我这...

[Part 7 ] Vue.js 的精随-元件生命周期 (续)

摧毁阶段 这个阶段负责元件的移除,适合用来移除所有的事件监听以及任何会造成记忆体泄漏(memory ...