[英]Flutter: loading Firestore data into Provider at app initialization
我正在開發一個帶有 Firestore 數據庫后端的應用程序,因為我不需要從應用程序更新任何內容,而且數據非常 static,我想在應用程序初始化時將所有 Firestore 數據反序列化為 Dart 數據結構(理想情況下為Provider) 以避免在我的 Widget 類中使用 Future/StreamBuilders。
Firestore 中的數據結構如下
Route
|->Room
|->Point of Interest
這是提供者 class
class AppData with ChangeNotifier {
static final FirebaseRepo _repo = new FirebaseRepo();
List<model.Route> _routes;
List<model.Route> get routes => _routes;
set routes(List<model.Route> routes) {
_routes = routes;
notifyListeners();
}
AppData() {
_loadData();
}
Future<void> _loadData() async {
routes = await _repo.findAllRoutes();
}
// some get methods that operate on _routes
}
這是存儲庫 class 的摘錄,我在其中加載了routes
class FirebaseRepo {
FirebaseFirestore _db = FirebaseFirestore.instance;
Source _dbSource = Source.cache;
Future<List<model.Route>> findAllRoutes() async {
List<model.Route> routes = [];
var routeContent = await _db.collection('content')
.where("type", isEqualTo: "route")
.orderBy("order", descending: false)
.get(GetOptions(source: _dbSource));
if (routeContent.docs.length > 0) {
await Future.forEach(routeContent.docs, (snapshot) async {
model.Route route = await loadRouteFromSnapshot(snapshot);
route.rooms = await findRouteRooms(route.id);
routes.add(route);
});
}
return routes;
}
Future<List<Room>> findRouteRooms(String routeId) async {
List<Room> rooms = [];
var roomContent = await _db.collection('content')
.doc(routeId)
.collection("rooms")
.orderBy("order", descending: false)
.get(GetOptions(source: _dbSource));
if (roomContent.docs.length > 0) {
await Future.forEach(roomContent.docs, (snapshot) async {
Room room = await loadRoomFromSnapshot(snapshot, routeId);
room.pois = await findRoomPois(room.parentRouteId, room.id);
rooms.add(room);
});
}
return rooms;
}
Future<List<Poi>> findRoomPois(String routeId, String roomId) async {
List<Poi> pois = [];
var poiContent = await _db.collection('content')
.doc(routeId)
.collection("rooms")
.doc(roomId)
.collection("poi")
.orderBy("order", descending: false)
.get(GetOptions(source: _dbSource));
if (poiContent.docs.length > 0) {
await Future.forEach(poiContent.docs, (snapshot) async {
Poi poi = await loadPoiFromSnapshot(snapshot, routeId, roomId);
pois.add(poi);
});
}
return pois;
}
................
}
這是主要的 class
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(CatedralApp());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.white,
fontFamily: 'Alata',
),
routes: {
RouteDetail.routeName: (context) => RouteDetail(),
RoomDetail.routeName: (context) => RoomDetail(),
PoiDetail.routeName: (context) => PoiDetail(),
},
home: LoadingScreen()
);
}
}
class LoadingScreen extends StatelessWidget{
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: AppData(),
child: Consumer<AppData> (
builder: (BuildContext context, AppData provider, Widget child) {
if (provider.routes != null) {
return Home();
} else {
return Stack(
children: <Widget>[
Container(
alignment: Alignment.center,
color: Colors.white,
child: Image.asset('assets/img/splash_background.png'),
),
Container(
alignment: Alignment(0.0, 0.0),
child: Image.asset(
'assets/img/icon.png',
height: 200,
width: 200,
),
)
],
);
}
}
)
);
}
}
這個想法是在數據加載時顯示啟動畫面,然后 go 到 Home 視圖,我嘗試了上述代碼的一些變體,但它似乎不起作用,Provider 中的數據始終是一個空列表,我真的不'甚至不知道我想要做的對於 Flutter 應用程序的工作方式是否“正確”,但我試圖盡可能多地理解......
據我測試,從緩存讀取不是問題,因為它嘗試緩存然后移動到Source.server
(但我嘗試使用Source.server
並且它無論如何都不起作用),當已經緩存數據時應用程序工作正常,並且通過在不存在緩存數據時對其進行調試,存儲庫方法實際上從 Firestore 檢索數據,因此與數據庫的連接也很好。
好的,事實證明我從緩存中讀取是錯誤的,正確的方法是(顯然,如果你總是想強制從緩存中讀取)是
Future<List<model.Route>> findAllRoutes() async {
List<model.Route> routes = [];
var routeContent = await _db.collection('content')
.where("type", isEqualTo: "route")
.orderBy("order", descending: false)
.get(GetOptions(source: Source.Cache));
if (routeContent.docs.length > 0) {
...
} else {
routeContent = await _db.collection('content')
.where("type", isEqualTo: "route")
.orderBy("order", descending: false)
.get(GetOptions(source: Source.Server));
...
}
return routes;
}
此方法嘗試從緩存中讀取,然后從服務器中讀取,因此在第一次應用程序運行時它將從服務器中讀取,然后在隨后的運行中它將始終從緩存中讀取,我希望這可以幫助某人:) 無論如何,如果你有對整個解決方案進行一些觀察,將不勝感激!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.