Cloud Firestore保持重新下载文档-Flutter

时间:2019-03-06 01:03:09

标签: firebase flutter google-cloud-firestore

这发生在我的主应用程序中 和 我用给定的代码实验室复制它: https://codelabs.developers.google.com/codelabs/flutter-firebase/index.html?index=..%2F..index#10

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'Baby Names',
     home: MyHomePage(),
   );
 }
}

class MyHomePage extends StatefulWidget {
 @override
 _MyHomePageState createState() {
   return _MyHomePageState();
 }
}

class _MyHomePageState extends State<MyHomePage> {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(title: Text('Baby Name Votes')),
     body: _buildBody(context),
   );
 }

Widget _buildBody(BuildContext context) {
 return StreamBuilder<QuerySnapshot>(
   stream: Firestore.instance.collection('baby').snapshots(),
   builder: (context, snapshot) {
     if (!snapshot.hasData) return LinearProgressIndicator();

     return _buildList(context, snapshot.data.documents);
   },
 );
}

Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot){
   return ListView(
     padding: const EdgeInsets.only(top: 20.0),
     children: snapshot.map((data) => _buildListItem(context, data)).toList(),
   );
 }

 Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
  final record = Record.fromSnapshot(data);

   return Padding(
     key: ValueKey(record.name),
     padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
     child: Container(
       decoration: BoxDecoration(
         border: Border.all(color: Colors.grey),
         borderRadius: BorderRadius.circular(5.0),
       ),
       child: ListTile(
         title: Text(record.name),
         trailing: Text(record.votes.toString()),
         onTap: () => print(record),
       ),
     ),
   );
 }
}

class Record {
 final String name;
 final int votes;
 final DocumentReference reference;

 Record.fromMap(Map<String, dynamic> map, {this.reference})
     : assert(map['name'] != null),
       assert(map['votes'] != null),
       name = map['name'],
       votes = map['votes'];

 Record.fromSnapshot(DocumentSnapshot snapshot)
     : this.fromMap(snapshot.data, reference: snapshot.reference);

 @override
 String toString() => "Record<$name:$votes>";
}

我唯一使用的插件是 cloud_firestore 0.9.5 + 2 。请耐心等待此测试过程。您不会立即看到该问题。首先运行应用程序,设置该项目。您可以按照给定的代码实验室中的说明进行操作。一旦一切都设置在前端和后端(在firstore上创建文档)。去吃午饭,晚餐,玩视频游戏或和朋友一起出去玩。 1小时后再回来。运行该应用,您将被收取新的阅读费用。再做一次,回来1个小时,它将再次发生

如何在现实生活中复制它: 通过代码实验室中的给定代码启动此应用。运行它,如果您将4个文档存储到Firestore中,则会导致读取4个文档。

再次启动。不收取任何费用。太棒了!但是不,真的没有。

我第二天醒来,打开应用程序,读取4次。好吧,也许有些魔术发生了。我立即重新启动它,没有任何费用产生(很棒!)。 稍后,我会在1小时内启动该应用程序,并收取4次读取费用,以显示完全相同的4个文档。

问题是,在应用程序启动时。似乎是从查询快照下载文档。没有对文档进行任何更改。这个流生成器以前已经运行了很多次。

离线模式(飞行模式),显示的缓存数据没有问题。

例如,在我的主应用程序中,

我有一个photoUrl,并且在重新启动App时,您可以看到它是从firestore中加载的(意味着将其下载为新文件,从而产生READ费用)。我重新启动主应用程序,不收费,照片也不刷新(太好了!)。 1小时后,我启动了该应用程序,并对检索到的每个文档收取费用(未更改)。

这应该是Cloud Firestore的行为方式吗? 根据我的阅读,它不应该表现为:(

2 个答案:

答案 0 :(得分:0)

除了构建窗口小部件树之外,您不应在build()中进行实际工作

代替构建中的此类代码

stream: Firestore.instance.collection('baby').snapshots(),

您应该使用

Stream<Snapshot> babyStream;
@override
void initState() {
  super.initState();
  babyStream = Firestore.instance.collection('baby').snapshots();
}

Widget _buildBody(BuildContext context) {
 return StreamBuilder<QuerySnapshot>(
   stream: babyStream,
   builder: (context, snapshot) {
     if (!snapshot.hasData) return LinearProgressIndicator();

     return _buildList(context, snapshot.data.documents);
   },
 );
}

FutureBuilder文档没有明确提及,但完全相同

https://docs.flutter.io/flutter/widgets/FutureBuilder-class.html

  

将来一定要早一些,例如中   State.initState,State.didUpdateConfig或   State.didChangeDependencies。不得在   构造状态时调用State.build或StatelessWidget.build方法   FutureBuilder。如果未来与   FutureBuilder,那么每次重新构建FutureBuilder的父项时,   异步任务将重新启动。

答案 1 :(得分:0)

Also, if the listener is disconnected for more than 30 minutes (for example, if the user goes offline), you will be charged for reads as if you had issued a brand-new query.

https://firebase.google.com/docs/firestore/pricing

我想我正在面对这个。 30分钟(我已经对其进行了多次测试),我可以启动我的应用,并且不计入读取次数。等待(关闭应用程序>)30〜35分钟后,我再次启动应用程序,并对查询中的每个文档收费。

现在,如果我保持监听器处于打开状态(而不是关闭其订阅),则应该避免这种情况,而是下载经过修改的文档。

我尝试过:

  List<DocumentSnapshot> nameDocs = [];
  StreamSubscription nameSub;
  @override
  void initState() {
    super.initState();
    nameSub = Firestore.instance.collection('baby').snapshots().listen((data) {
      print('listener Fired at line 39 main.dart');
      data.documentChanges.forEach((x) {
        print('${x.type} this is the type line 40');
        if (x.type == DocumentChangeType.modified) {
          print('modified');
          nameDocs.removeAt(x.oldIndex);
          setState(() {
            nameDocs.add(x.document);
          });
        }
        if (x.type == DocumentChangeType.added) {
          print('added');
          setState(() {
            nameDocs.add(x.document);
          });
        }
        if (x.type == DocumentChangeType.removed) {
          print('removed');
          setState(() {
            nameDocs.removeAt(x.oldIndex);
          });
        }
        print(x.document.data['name']);
      });
    });
  }

我不取消订阅,因为我不想取消订阅。在31分钟后重新访问该应用程序后,发生相同的问题

有没有办法让听众保持活力,我知道有些缺点可能是电池寿命,而有些则不行。我想在应用程序的某些部分保留实时监听器(例如,在Firestore中不经常更改的文档,我希望避免在 REAL 应用程序中重新下载此类文档)和其他部分将照常离开(在处理方法上侦听并取消)