web-dev-qa-db-ja.com

FlutterにFirebaseデータをキャッシュする方法は?

私のアプリでは、Firebaseからのデータを使用してオブジェクトのリストを作成します。 StreamBuilder内で、スナップショットにデータがあるかどうかを確認します。表示されない場合は、「Loading ...」を含む単純なテキストウィジェットを返します。私の問題は、アプリの別のページに移動してから戻ってくると、画面の真ん中に「読み込み中...」と一瞬表示され、少しイライラすることです。 Firebaseからデータをダウンロードし、そのページに戻るたびにウィジェットを構築していると確信しています。また、データのチェックを行わないと、nullからデータにアクセスしようとしているデータが得られます。

すでにダウンロードされたデータをキャッシュする方法はありますか?Firebaseからのデータに変更がない場合は、キャッシュされたデータを使用しますか?

ここに私のコードの編集されたバージョンがあります:

class Schedule extends StatefulWidget implements AppPage {
  final Color color = Colors.green;
  @override
  _ScheduleState createState() => _ScheduleState();
}

class _ScheduleState extends State<Schedule> {
  List<Event> events;
  List<Event> dayEvents;
  int currentDay;
  Widget itemBuilder(BuildContext context, int index) {
    // Some Code
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: StreamBuilder(
        stream: Firestore.instance.collection('events').snapshots(),
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Text("Loading...");
          }
          events = new List(snapshot.data.documents.length);
          for (int i = 0; i < snapshot.data.documents.length; i++) {
            DocumentSnapshot doc = snapshot.data.documents.elementAt(i);

            events[i] = Event(
              name: doc["name"],
              start: DateTime(
                doc["startTime"].year,
                doc["startTime"].month,
                doc["startTime"].day,
                doc["startTime"].hour,
                doc["startTime"].minute,
              ),
              end: DateTime(
                doc["endTime"].year,
                doc["endTime"].month,
                doc["endTime"].day,
                doc["endTime"].hour,
                doc["endTime"].minute,
              ),
              buildingDoc: doc["location"],
              type: doc["type"],
            );
          }
          events.sort((a, b) => a.start.compareTo(b.start));
          dayEvents = events.where((Event e) {
            return e.start.day == currentDay;
          }).toList();
          return ListView.builder(
            itemBuilder: itemBuilder,
            itemCount: dayEvents.length,
          );
        },
      ),
    );
  }
}
4
Marco

データがFirestoreのローカルキャッシュからのものか、ネットワークからのものかを確認するには、次のようにします。

_          for (int i = 0; i < snapshot.data.documents.length; i++) {
            DocumentSnapshot doc = snapshot.data.documents.elementAt(i);
            print(doc.metadata.isFromCache ? "NOT FROM NETWORK" : "FROM NETWORK");
_

説明したケースでは、「NOT FROM NETWORK」のときにロード画面が表示される可能性があります。これは、ローカルキャッシュから取得するのに時間がかかるためです。すぐに、結果が空の場合に クエリのメタデータ を要求できるようになります。

他の人が提案したように、結果をキャッシュすることができ、これは表示されません。最初に、次のようなものを使用してウィジェットにキャッシュすることができます。

_  QuerySnapshot cache; //**

  @override
  Widget build(BuildContext context) {
    return Center(
      child: StreamBuilder(
        initialData: cache, //**
        stream: Firestore.instance.collection('events').snapshots(),
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Text("Loading...");
          }
          cache = snapshot.data; //**
_

これにより、ウィジェットにデータが記憶されます。ただし、これで問題が解決しない場合は、このウィジェットではなく別の場所に保存する必要があります。 1つのオプションは、Providerウィジェットを使用して、この特定のウィジェットのスコープを超えて存在する変数にそれを格納することです。

おそらく関連していませんが、Firestore.instance.collection('events').snapshots()initState()に移動し、ストリームへの参照をプライベートフィールドに保存して、それをStreamBuilderとして使用することもお勧めします。それ以外の場合は、すべてのbuild()で新しいストリームを作成している可能性があります。理由にかかわらず、毎秒何回も発生するbuild()呼び出しの準備ができている必要があります。

1
Gazihan Alankus