Open8
毎回忘れるflutter/firestoreのTIPS
DateTime ⇔ Timestampの変換はWithConverterでこう使う
なんだかあまりうまくいかなかったのでモデルクラスにtoFirestore / fromFirestoreを作って対応した。
私なりのベストプラクティス
firestoreでの時刻のクエリ
QuerySnapshot ss = collectionRef.orderBy().startAt().endAt();
モデルクラスのサンプルコード(Enum,fromFirestore, toFirestore)
reservation.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'reservation.freezed.dart';
part 'reservation.g.dart';
enum BathName {
NIJI("虹"),
SORA("空"),
KUMO("雲");
const BathName(this.displayName);
final String displayName;
}
class Reservation with _$Reservation {
const Reservation._();
factory Reservation({
required String uid,
required DateTime date,
required DateTime fromTime,
required DateTime endTime,
required BathName bathName,
String? guestName,
String? phoneNumber,
(false) isOccupied,
}) = _Reservation;
factory Reservation.fromJson(Map<String, dynamic> json) =>
_$ReservationFromJson(json);
factory Reservation.fromFireStore(
DocumentSnapshot<Map<String, dynamic>> snapshot,
SnapshotOptions? options,
) {
DateTime toDateTime(Timestamp value) {
return value.toDate();
}
final data = snapshot.data();
return Reservation(
uid: data?["uid"],
date: toDateTime(data?["date"] as Timestamp),
fromTime: toDateTime(data?["fromTime"] as Timestamp),
endTime: toDateTime(data?["endTime"] as Timestamp),
bathName:
BathName.values.byName(data?["bathName"]), // data?["bathName"],
guestName: data?["guestName"],
phoneNumber: data?["phoneNumber"],
isOccupied: data?["isOccupied"]);
}
Map<String, dynamic> toFireStore() {
return {
"uid": uid,
"date": date,
"fromTime": fromTime,
"endTime": endTime,
"bathName": bathName.name,
"guestName": guestName,
"phoneNumber": phoneNumber,
"isOccupied": isOccupied
};
}
}
こちらを扱うcollectionreferenceのサンプルコード
sample.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../models/reservation.dart';
import 'user_provider.dart';
part 'business_days_provider.g.dart';
//まずはUserを使ってCollectionReference作る
//withConverterメソッド使ってタイプセーフに受け取る。
final reservationsRef = Provider<CollectionReference<Reservation>>(((ref) {
final db = FirebaseFirestore.instance;
final _user = ref.watch(currentUser);
final reservations = db
.collection("user/${_user.uid}/reservations")
.withConverter(
fromFirestore: Reservation.fromFireStore,
toFirestore: ((Reservation reservation, options) =>
reservation.toFireStore()));
return reservations;
}), dependencies: [currentUser, targetDayProvider]);
//クエリを使う。この場合はtimestampでクエリを作ってそれをStreamProviderを使って描画している。
final targetReservationsProvider =
StreamProvider.autoDispose<List<Reservation>>(((ref) async* {
final _targetDate = ref.watch(targetDayProvider);
final startAt =
DateTime(_targetDate.year, _targetDate.month, _targetDate.day, 0, 0);
final endAt =
DateTime(_targetDate.year, _targetDate.month, _targetDate.day, 23, 59);
print('target date : $_targetDate and startAt $startAt , endAt $endAt');
final _reservationsRef = ref.watch(reservationsRef);
final ss = await _reservationsRef
.orderBy("fromTime", descending: false)
.startAt([Timestamp.fromDate(startAt)]).endAt(
[Timestamp.fromDate(endAt)]).snapshots();
await for (final snapshot in ss) {
final data = snapshot.docs.map((s) => s.data()).toList();
print("data : $data");
yield data;
}
}), dependencies: [currentUser, targetDayProvider, reservationsRef]);
//targetDayprovider , code generationを使ってみたかっただけ。
class TargetDay extends _$TargetDay {
DateTime build() {
return DateTime.now();
}
void setDate({required DateTime date}) {
state = date;
}
}
//CRUD用のクラスを作る。描画と分けることで簡素に作れる。
final reservationHandlerProvider = Provider(
(ref) => ReservationHandler(ref: ref),
dependencies: [currentUser, reservationsRef]);
class ReservationHandler {
ReservationHandler({required this.ref});
Ref ref;
void update(Reservation reservation) {
final _reservationsRef = ref.watch(reservationsRef);
final docRef = _reservationsRef.doc(reservation.uid);
docRef.set(reservation);
}
void delete(Reservation reservation) {
final _reservationsRef = ref.watch(reservationsRef);
final docRef = _reservationsRef.doc(reservation.uid);
docRef.delete();
}
}
code generation使ってみたかったから使ってみたけど、今のところ使いづらいと思った。
where
とorderBy
はどうも一緒には使えない?エラーが出た。もしかしたら自分のクエリがおかしいせいかもしれない。
FirebaseQueryBuilderを使って場当たり的にUIを作っていったほうが使いやすい。
ProviderもQueryか、DocRefのプロバイダのみで良い。
DocRefの値を使うときはFutureProviderを使うと楽に取得できる。