【问题标题】:Flutter: loading Firestore data into Provider at app initializationFlutter:在应用初始化时将 Firestore 数据加载到 Provider
【发布时间】:2021-05-24 07:19:27
【问题描述】:

我正在开发一个带有 Firestore 数据库后端的应用程序,因为我不需要从应用程序更新任何内容并且数据是非常静态的,我想在应用程序初始化时将所有 Firestore 数据反序列化为 Dart 数据结构(理想情况下放入 Provider)以避免在我的 Widget 类中出现 Future/StreamBuilders。

Firestore 中的数据结构如下

Route
|->Room
   |->Point of Interest

这是提供者类

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
}

这是我加载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;
  }

................
}

这是主类


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,
                  ),
                )
              ],
            );
          }
        }
      )
    );
  }
}

这个想法是在数据加载时显示启动画面,然后转到 Home 视图,我尝试了上述代码的一些变体,但它似乎不起作用,Provider 中的数据始终是一个空列表,老实说,我什至不知道我想要做的对于 Flutter 应用程序的工作方式是否“正确”,但我正在尝试尽可能多地理解......

据我测试,从缓存读取不是问题,因为它会尝试缓存,然后移动到 Source.server(但我用 Source.server 尝试过,但它无论如何都不起作用),当已经有缓存数据应用程序工作正常,并且通过在没有缓存数据存在时调试它,存储库方法实际上从 Firestore 检索数据,因此与数据库的连接也很好。

【问题讨论】:

    标签: firebase flutter dart google-cloud-firestore deserialization


    【解决方案1】:

    好的,事实证明我从缓存中读取错误,正确的方法是(显然,如果您总是想强制从缓存中读取)是

    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;
      }
    

    此方法尝试从缓存中读取,然后从服务器中读取,因此在第一次运行应用程序时它将从服务器中读取,然后在后续运行中它将始终从缓存中读取,我希望这可以帮助某人:) 无论如何,如果您对整个解决方案有一些观察,我们将不胜感激!

    【讨论】:

      猜你喜欢
      • 2021-03-09
      • 2020-08-31
      • 2020-06-12
      • 1970-01-01
      • 2021-04-03
      • 2021-11-20
      • 2020-12-22
      • 2022-11-24
      • 2012-11-24
      相关资源
      最近更新 更多