簡體   English   中英

Flutter:在應用初始化時將 Firestore 數據加載到 Provider

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM