一、Firestore結構認識
下面介紹幾種常用的取值方法:
不論用什麼方法取值,一開始都需要先取得實體,下面將FirebaseFirestore實體化,並以db為變數名稱
FirebaseFirestore db = FirebaseFirestore.instance;
向firebaes取值,受限於網路傳輸需要時間,寫函式時,需記得於函式後加上async採用異步的操作,而在需要異步回傳的程式前加入await 如下:
void getData() async{
var data= await .....
}
一、如果需要一次性的取得資料,可以使用get()方法。官方作法參考
(一)透過集合名稱,取得集合下的全部資料
取得的格式如下:
FirebaseFirestore.instance.collection("集合名").get();以上面quiz_questions集合的資料為例:
QuerySnapshot<Map<String, dynamic>> data =FirebaseFirestore.instance.collection("quiz_questions").get();
abstract class QuerySnapshot<T extends Object?> {
/// Gets a list of all the documents included in this snapshot.
List<QueryDocumentSnapshot<T>> get docs;
/// An array of the documents that changed since the last snapshot. If this
/// is the first snapshot, all documents will be in the list as Added changes.
List<DocumentChange<T>> get docChanges;
/// Returns the [SnapshotMetadata] for this snapshot.
SnapshotMetadata get metadata;
/// Returns the size (number of documents) of this snapshot.
int get size;
}
QuerySnapshot裡面有四個方法,可以取得不同的資料。
可取得這個集合下面的文件數量
(3)docChanges
取得這個集合下改變的資料,而如果是第一次新增,則會顯示新增的資料
(4)metadata
取得這個集合下原始的資料
//取得quiz_questions集合裡的資料
QuerySnapshot<Map<dynamic, dynamic>> data =
await db.collection("quiz_questions").get();
//取得集合下第一個文件名
print(data.docs[0].id);
//取得集合下第一個文件下的所有欄位
print(data.docs[0].data());
//取得集合下第一個文件名下欄位名為options的值
print(data.docs[0].data()!['options']);
abstract class CollectionReference<T extends Object?> implements Query<T> {
/// Returns the ID of the referenced collection.
String get id;
/// Returns the parent [DocumentReference] of this collection or `null`.
///
/// If this collection is a root collection, `null` is returned.
// This always returns a DocumentReference even when using withConverter
// because we do not know what is the correct type for the parent doc. @override
DocumentReference<Map<String, dynamic>>? get parent;
/// A string containing the slash-separated path to this CollectionReference
/// (relative to the root of the database).
String get path;
/// Returns a `DocumentReference` with an auto-generated ID, after
/// populating it with provided [data].
///
/// The unique key generated is prefixed with a client-generated timestamp
/// so that the resulting list will be chronologically-sorted.
Future<DocumentReference<T>> add(T data);
/// {@template cloud_firestore.collection_reference.doc}
/// Returns a `DocumentReference` with the provided path.
///
/// If no [path] is provided, an auto-generated ID is used.
///
/// The unique key generated is prefixed with a client-generated timestamp
/// so that the resulting list will be chronologically-sorted.
/// {@endtemplate}
DocumentReference<T> doc([String? path]);
/// Transforms a [CollectionReference] to manipulate a custom object instead
/// of a `Map<String, dynamic>`.
///
/// This makes both read and write operations type-safe.
///
/// ```dart
/// final modelsRef = FirebaseFirestore
/// .instance
/// .collection('models')
/// .withConverter<Model>(
/// fromFirestore: (snapshot, _) => Model.fromJson(snapshot.data()!),
/// toFirestore: (model, _) => model.toJson(),
/// );
///
/// Future<void> main() async {
/// // Writes now take a Model as parameter instead of a Map
/// await modelsRef.add(Model());
///
/// // Reads now return a Model instead of a Map
/// final Model model = await modelsRef.doc('123').get().then((s) => s.data());
/// }
/// ```
// `extends Object?` so that type inference defaults to `Object?` instead of `dynamic`
@override
CollectionReference<R> withConverter<R extends Object?>({
required FromFirestore<R> fromFirestore,
required ToFirestore<R> toFirestore,
});
}
建續上面的範例,CollectionRef為所設的變數,寫法如下:
CollectionRef.id
CollectionRef.parent
CollectionRef.path
(二)透過集合名稱及文件名稱,取得文件下的資料
使用格式如下:
DocumentSnapshot<Map<String, dynamic>> data =await db.collection("集合名").doc('文件名').get();
DocumentSnapshot<Map<String, dynamic>> data2 =
await db.collection("quiz_questions").doc('index2').get();
//取得quiz_questions集合裡文件名為index2的資料
DocumentSnapshot<Map<String, dynamic>> data2 =
await db.collection("quiz_questions").doc('index2').get();
//印出index2文件下所有的欄位
print(data2.data());
//印出index2文件下optinos這個欄位的值,data()有可能空,因此要加上不為空的判斷
print(data2.data()!['options']);
abstract class DocumentSnapshot<T extends Object?> {(1)id
/// This document's given ID for this snapshot.
String get id;
/// Returns the reference of this snapshot.
DocumentReference<T> get reference;
/// Metadata about this document concerning its source and if it has local
/// modifications.
SnapshotMetadata get metadata;
/// Returns `true` if the document exists.
bool get exists;
/// Contains all the data of this document snapshot.
T? data();
/// {@template firestore.documentsnapshot.get}
/// Gets a nested field by [String] or [FieldPath] from this [DocumentSnapshot].
///
/// Data can be accessed by providing a dot-notated path or [FieldPath]
/// which recursively finds the specified data. If no data could be found
/// at the specified path, a [StateError] will be thrown.
/// {@endtemplate}
dynamic get(Object field);
/// {@macro firestore.documentsnapshot.get}
dynamic operator [](Object field);
}
此文件的名稱
(2)reference
取得指向的目標
(3)exists
文件是否存在
(三) 透過集合名稱,並透過where()這個方法,有條件的取得集合下的資料
//取得quiz_questions集合裡文件名為index2的資料,並透過where()選擇 answer為2的值這裡可以使用where這個方法是因為CollectionReference 類implements 了Query這個類,因此可以採用Query 下所屬的where的方法。比較的方法如下,可參考上面的那個範例進行修改變
final data3 = await db
.collection("quiz_questions")
.where("answer", isEqualTo: "2")
.get();
//取得集合的數目
print(data3.size);
//取得集合下第一個文件名
print(data3.docs[0].id);
//取得集合下第一個文件名下欄位名為options的值
print(data3.docs[0].data()!['options']);
Query<T> where(
Object field, {
Object? isEqualTo, //相等於就回傳
Object? isNotEqualTo, //不相等於就回傳
Object? isLessThan, //少於就回傳
Object? isLessThanOrEqualTo, //少於或是相等於就回傳
Object? isGreaterThan, //大於就回傳
Object? isGreaterThanOrEqualTo, //大於或是相等於就回傳
Object? arrayContains, //包含某個值就回傳
List<Object?>? arrayContainsAny, //包含這個陣列中的某個值就回傳
List<Object?>? whereIn, //
List<Object?>? whereNotIn, //不包含在此陣列的值就回傳
bool? isNull,
});
使用arrayContains
Query query = Firestore.instance.collection('myCollection').where('myArrayField', arrayContains: 'myValue'); // 假設 myArrayField 包含 ['value1', 'value2', 'value3', 'myValue'] // 結果會返回所有 myArrayField 包含 'myValue' 的文件
使用arrayContainsAny
範例: Query query = Firestore.instance.collection('myCollection').where('myArrayField', arrayContainsAny: ['value1', 'value2', 'myValue']); // 假設 myArrayField 包含 ['value1', 'value2', 'value3', 'myValue'] // 結果會返回所有 myArrayField 包含 'value1' 或 'value2' 或 'myValue' 的文件
await db.collection("集合名稱").orderBy("欄位名稱", descending: true).get();
var data3 =
await db.collection("quiz_questions").orderBy("id", descending: true).get();
for(int i=0;i<data3.docs.length;i++){
print(data3.docs[i].data());
}
import 'package:cloud_firestore/cloud_firestore.dart';
class QuizModel2 {
//變數宣告
int? id;
String? unitName;
String? quizName;
String? options;
String? answer;
String? answer_notes;
// 該類的建構子
QuizModel2({
this.id,
this.unitName,
this.quizName,
this.options,
this.answer,
this.answer_notes,
});
//透過fromJson這個方法,將取得的json資料轉換成QuizModel
//讓QuizModel.id 等於 json["id"] ,那在使用這個類別的時候就不用背id,
// 打QuizModel加.,就會自動出現 id unitName等屬性名字可以選擇,減少出錯的機會
factory QuizModel2.fromJson(DocumentSnapshot<Map<String, dynamic>> snapshot,SnapshotOptions? options,){
final json = snapshot.data();
return QuizModel2(
id: json?["id"]== null ? null : json?["id"],
unitName: json?["unitName"]== null ? null : json?["unitName"],
quizName: json?["quizName"] == null ? null : json?["quizName"],
options: json?["options"] == null ? null : json?["options"],
answer: json?["answer"] == null ? null : json?["answer"],
answer_notes: json?["answer_notes"] == null ? null : json?["answer_notes"],
);
}
//這裡是進行反序列化,在存回資料庫时,要將資料變為{"id":值,"unitName":值}的形式,才能符合json的格式
Map<String, dynamic> toJson() => {
"id": id == null ? null : id,
"unitName": unitName == null ? null : unitName,
"quizName": quizName == null ? null : quizName,
"options": options == null ? null : options,
"answer": answer == null ? null : answer,
"answer_notes": answer_notes == null ? null : answer_notes,
};
}
//quiz_questions為你的欄位名稱,index1為你的文件名稱
//fromFirestore後面放的是你的Model轉成序列化的方法
//toFirestore後面放的是你轉回序列化的方法
var data4 = await db
.collection('quiz_questions').doc('index1')
.withConverter(fromFirestore: QuizModel2.fromJson, toFirestore: (QuizModel2 quizModel2, _) => quizModel2.toJson())
.get();
// 透過上面的方法就可以開始使用 data4.data()?.id;
StreamBuilder getFirebaseData() {
return StreamBuilder (
//資料流來源為FirebaseFirestore.instance.collection("quiz_questions").snapshots()
stream:
FirebaseFirestore.instance.collection("quiz_questions").snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
//沒有資料時,回傳CircularProgressIndicator(),有資料才進行更下面動作
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator(), //顥示圈圈的進度條
);
// 利用data將快照資料流裡的資料取出,此時的collectionData為quiz_questions這個集合下面的全部資料
var collectionData=snapshot.data;
//加上.docs,此時的documentData為quiz_questions下的文件集合,為list[index1,index2.....]
//一般會透過for迴圈,或是listview、gridview才能將個別的資料一個一個取出。
//documentref[0]為第一筆,documentref[1]為第二筆....依此類推
var documentRef=collectionData.docs;
//若要取得文件長度,可用陣列的方法.length
final int documentCount = documentRef.length;
//假如有讀出資料時,就進行資料呈現
if (documentCount > 0) {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
//itemCount為listView的總數目,這裡設為與文件的數量等長
itemCount: documentCount,
//利用listView的index依序遍歷文件
itemBuilder: (_, int index) {
//index為0時,為取得quiz_questions下第一個文件
var documentData=documentRef[index];
//如果需要序列化與反序列化可以在這裡進行
var documentData_fromjson=QuizModel.fromJson(documentData.data() as Map<String, dynamic>);
//序列化後可使用.的方式使用變數 如:documentData_fromjson.answer;
//如果不需要序列化可以用map鍵值對的方法取出欄位中的值 documentData['欄位名']
return Text(documentData['options'],style: TextStyle(fontSize: 20));
},
);
//沒有資料的話做以下動作
} else {
return Container(
padding: EdgeInsets.symmetric(vertical: 10.0),
alignment: Alignment.center,
child: Text(
'未取得資料',
style: TextStyle(fontSize: 20),
),
);
}
},
);
}
class QuizModel {
//變數宣告
int? id;
String? unitName;
String? quizName;
String? options;
String? answer;
String? answer_notes;
// 該類的建構子
QuizModel({
this.id,
this.unitName,
this.quizName,
this.options,
this.answer,
this.answer_notes,
});
//透過fromJson這個方法,將取得的json資料轉換成QuizModel
//讓QuizModel.id 等於 json["id"] ,那在使用這個類別的時候就不用背id,
// 打QuizModel加.,就會自動出現 id unitName等屬性名字可以選擇,減少出錯的機會
factory QuizModel.fromJson(Map<String, dynamic> json){
return QuizModel(
id: json["id"]== null ? null : json["id"],
unitName: json["unitName"]== null ? null : json["unitName"],
quizName: json["quizName"] == null ? null : json["quizName"],
options: json["options"] == null ? null : json["options"],
answer: json["answer"] == null ? null : json["answer"],
answer_notes: json["answer_notes"] == null ? null : json["answer_notes"],
);
}
//這裡是進行反序列化,在存回資料庫时,要將資料變為{"id":值,"unitName":值}的形式,才能符合json的格式
Map<String, dynamic> toJson() => {
"id": id == null ? null : id,
"unitName": unitName == null ? null : unitName,
"quizName": quizName == null ? null : quizName,
"options": options == null ? null : options,
"answer": answer == null ? null : answer,
"answer_notes": answer_notes == null ? null : answer_notes,
};
}
沒有留言:
張貼留言