2023年1月11日 星期三

Flutter學習-firebase Cloud Firestore說明(1)

 翻譯自:https://firebase.flutter.dev/docs/firestore/usage/

Cloud Firestore
注意

此頁已存檔,可能無法反映 FlutterFire 插件的最新版本。您可以在 firebase.google.com 上找到最新資訊。

要開始在您的專案中使用Cloud Firestore套件,請在專案檔案的頂部導入下列程式:

import 'package:cloud_firestore/cloud_firestore.dart';


在使用 Firestore 之前,您必須先確保已經初始化 FlutterFire。要建立一個新的 Firestore 實例,請宣告一個FirebaseFirestore的實例:


FirebaseFirestore firestore = FirebaseFirestore.instance;


預設,上面的作法可以讓你的裝置和Firestore進行連接。 但是,如果您想使用宣告一個次要的Firebase App的Firestore,請使用instanceFormethod:


FirebaseApp secondaryApp = Firebase.app('SecondaryApp');
FirebaseFirestore firestore = FirebaseFirestore.instanceFor(app: secondaryApp);


Collections & Documents#

Firestore 會將資料儲存於「文件 "documents"」內,而文件則包含於「集合"collections"」中。文件也可以包含巢狀集合。舉例來說,我們的每個使用者都會有自己的「文件」儲存於「使用者」的集合內。集合方法可以讓我們在程式碼內參照集合。 在以下範例中,我們可以參照使用者的集合"collections",並在按下按鈕時建立新的使用者文件"documents":


import 'package:flutter/material.dart';
// import入方法庫
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
//創建一個增加使用者的類別方法
class AddUser extends StatelessWidget {
final String fullName;
final String company;
final int age;
AddUser(this.fullName, this.company, this.age);
Widget build(BuildContext context) {
// 創建一個名為users的CollectionReference,它引用Firestore集合叫做users。

CollectionReference users = FirebaseFirestore.instance.collection('users');
//異步的增加一個使用者,所以用Future,不需要回傳值
Future<void> addUser() {
// 呼叫使用者的CollectionReference來新增一個新的使用者
return users
.add({
'full_name': fullName, // John Doe
'company': company, // Stokes and Sons
'age': age // 42
})
.then((value) => print("User Added"))
.catchError((error) => print("Failed to add user: $error"));
}
return TextButton(
onPressed: addUser,
child: Text(
"Add User",
),
);
}
}


Read Data

Cloud Firestore可以讓你讀取集合或文件的值。這可以是一次性讀取,或是在查詢內的資料改變時提供即時的更新。


One-time Read#(單次讀取)

若要單次讀取收集或文件,請調用Query.get或DocumentReference.get方法。 下面的示例使用FutureBuilder來幫助管理請求的狀態:


class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>;
return Text("Full Name: ${data['full_name']} ${data['last_name']}");
}
return Text("loading");
},
);
}
}


想要瞭解更多關於離線讀取資料的知識,請參閱離線存取資料的文件說明。


Realtime changes#(即時,持續性的讀取)

FlutterFire提供支持來處理對集合和文檔的即時變化。在初始請求時提供了一個新的事件,並且在集合/文檔發生變化時(修改,刪除或添加)會收到任何後續變化。 CollectionReference和DocumentReference都提供一個snapshots()方法,它返回一個Stream:


Stream collectionStream = FirebaseFirestore.instance.collection('users').snapshots();
Stream documentStream = FirebaseFirestore.instance.collection('users').doc('ABC123').snapshots();


一旦返回,您可以通過listen()方法訂閱更新。以下示例使用StreamBuilder,它可以幫助你管理傳入資料的狀態,並在應用程序中不再使用流時自動結束資料讀取:


class UserInformation extends StatefulWidget {
_UserInformationState createState() => _UserInformationState();
}
class _UserInformationState extends State<UserInformation> {
final Stream<QuerySnapshot> _usersStream = FirebaseFirestore.instance.collection('users').snapshots();
Widget build(BuildContext context) {
//以下為使用StreamBuilder讀取資料的方法
return StreamBuilder<QuerySnapshot>(
//要讀取的方法,已在前調用
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['full_name']),
subtitle: Text(data['company']),
);
}).toList(),
);
},
);
}
}


預設情況下,如果只有變更了自身的資料,監聽就不會更新。如果您想要在文件或查詢自身的資料更改時接收事件,您可以將includeMetadataChanges傳遞給snapshots方法:


FirebaseFirestore.instance
.collection('users')
.snapshots(includeMetadataChanges: true)


Document & Query Snapshots#

執行查詢時,Firestore會傳回QuerySnapshot或DocumentSnapshot其中之一。

QuerySnapshot:

從集合查詢傳回的QuerySnapshot,可以檢查集合,例如集合中存在多少文件,提供存取集合中的文件的存取權,查看自上次查詢以來的任何變更等等。要存取QuerySnapshot中的文件,請呼叫docs屬性,該屬性會傳回一個包含DocumentSnapshot類別的清單。


FirebaseFirestore.instance
.collection('users')
.get()
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
print(doc["first_name"]);
});
});


DocumentSnapshot# 從查詢或直接訪問文檔時返回的DocumentSnapshot。即使在數據庫中不存在文檔,快照也總是會返回。要確定文檔是否存在,請使用exists屬性:


FirebaseFirestore.instance
.collection('users')
.doc(userId)
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('Document exists on the database');
}
});


如果文件存在,可以透過調用data方法讀取它的數據,它返回一個Map<String, dynamic>,如果文件不存在則返回null:


FirebaseFirestore.instance
.collection('users')
.doc(userId)
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('Document data: ${documentSnapshot.data()}');
} else {
print('Document does not exist on the database');
}
});


DocumentSnapshot還提供了一種訪問深層嵌套數據的能力,而不需要通過get方法手動迭代返回的Map。該方法接受一個用點分隔的路徑或FieldPath實例。如果在嵌套路徑上不存在數據,則拋出StateError:


try {
dynamic nested = snapshot.get(FieldPath(['address', 'postcode']));
} on StateError catch(e) {
print('No nested field exists!');
}


Querying#(查詢)

Cloud Firestore 提供了用於查詢集合的高級功能。查詢可以用於一次性讀取或訂閱更改。 篩選# 要篩選集合中的文件,可以將where方法鏈接到集合引用上。篩選支持equality checks和“in”查詢。例如,篩選其年齡大於20歲的用戶:


FirebaseFirestore.instance
.collection('users')
.where('age', isGreaterThan: 20)
.get()
.then(...);


Firestore也支援陣列查詢。例如,要過濾說英語(en)或義大利語(it)的使用者,請使用arrayContainsAny過濾器:


FirebaseFirestore.instance
.collection('users')
.where('language', arrayContainsAny: ['en', 'it'])
.get()
.then(...);


要更多了解 Cloud Firestore 所提供的查詢功能,請參閱 Firebase 文件。 限制:若要限制查詢返回的文件數量,請在集合參考上使用 limit 方法:


FirebaseFirestore.instance
.collection('users')
.limit(2)
.get()
.then(...);


您也可以透過使用 limitToLast 來限制集合查詢中的最後一份文件:


FirebaseFirestore.instance
.collection('users')
.orderBy('age')
.limitToLast(2)
.get()
.then(...);


Ordering#(排序)

要按照特定值排序文件,請使用 orderBy 方法:


FirebaseFirestore.instance
.collection('users')
.orderBy('age', descending: true)
.get()
.then(...);


Start & End Cursors#

要在集合中的特定點開始和/或結束查詢,您可以將值傳遞給startAt,endAt,startAfter或endBefore方法。 您必須指定要使用游標查詢的順序,例如:

FirebaseFirestore.instance
.collection('users')
.orderBy('age')
.orderBy('company')
.startAt([4, 'Alphabet Inc.'])
.endAt([21, 'Google LLC'])
.get()
.then(...);

你可以將 DocumentSnapshot 物件傳到 startAfterDocument、startAtDocument、endAtDocument 或 endBeforeDocument 方法,以變更指定查詢的結果。例如:

FirebaseFirestore.instance
.collection('users')
.orderBy('age')
.startAfterDocument(documentSnapshot)
.get()
.then(...);

Query Limitations#

Cloud Firestore 不支援以下類型的查詢: 不同欄位的範圍篩選查詢,如前一節所述。 邏輯 OR 查詢。在這種情況下,您應該為每個 OR 條件創建一個單獨的查詢,並在應用程式中合併查詢結果。 查詢具有 != 條款。在這種情況下,您應該將查詢分為大於查詢和小於查詢。例如,不支援具有 where("age", isNotEqualTo: 30) 條款的查詢,但您可以通過結合兩個查詢,一個具有條款 where("age", isLessThan: 30),另一個具有條款 where("age", isGreaterThan: 30),得到相同的結果集。

沒有留言:

張貼留言