Flutter:如何删除等待以利用 Firebase 离线持久性?

Posted

技术标签:

【中文标题】Flutter:如何删除等待以利用 Firebase 离线持久性?【英文标题】:Flutter: How to remove awaits to take advantage of Firebase Offline Persistence? 【发布时间】:2020-11-22 02:39:19 【问题描述】:

我正在使用一个下拉列表(DropDown),它的元素是从 Firebase 获取的。该表单可以正常工作,但是当 Internet 连接丢失时,Firebase Offline Persistence 属性不起作用并且 CircularProgressIndicator 保持活动状态。阅读Using Offline Persistence in Firestore in a Flutter App等一些回复,表明不应该处理等待,但是我不清楚如何实现它:

class EstanqueAlimentarPage extends StatefulWidget 

  @override
  _EstanqueAlimentarPageState createState() => _EstanqueAlimentarPageState();


class _EstanqueAlimentarPageState extends State<EstanqueAlimentarPage> 
  final formKey = GlobalKey<FormState>();
  AlimentoBloc alimentoBloc = new AlimentoBloc();
  AlimentoModel _alimento = new AlimentoModel();
  AlimentarModel alimentar = new AlimentarModel();
  List<AlimentoModel> _alimentoList;
  bool _alimentoDisponible = true;

@override
  void dispose() 
    alimentoBloc.dispose();
    super.dispose();
  

@override
void initState() 
  _obtenerListaAlimentoUnaVez();
  super.initState();  



Future<void> _obtenerListaAlimentoUnaVez() async 
  
  _alimentoList = await alimentoBloc.cargarAlimento(idEmpresa); // Await that I want to eliminate

  if (_alimentoList.length > 0)  // Here appears a BAD STATE error when the internet connection goes from off to on
    _alimento = _alimentoList[0];
    _alimentoDisponible = true;
   else 
    _alimentoDisponible = false;
  
  _cargando = false;
  setState(() );


  @override
  Widget build(BuildContext context) 
    
    return Form(
      key: formKey, 
      child: Column(
        children: <Widget> [
          _crearTipoAlimento(_alimentoList),
          SizedBox(height: 8.0),
          _crearComentarios(),
        ]
      )
    ),
    _crearBoton('Guardar'),

  

  Widget _crearTipoAlimento(List<AlimentoModel> lista) 

    return Container(
      decoration: _cajaBlanca,
      child: 
      !_cargando // If it isn't loading, Dropdown must be displayed
      ? DropdownButtonFormField<AlimentoModel>(
        decoration: InputDecoration(
          labelText: 'Nombre del Alimento',
          contentPadding: EdgeInsets.only(top:5.0),
          prefixIcon: Icon(FontAwesomeIcons.boxOpen, color: Theme.of(context).primaryColor,),
          border: InputBorder.none,
        ),
        value: _alimento,
        items: lista.map((AlimentoModel value) 
          return DropdownMenuItem<AlimentoModel>(
            child: Text(value.nombre),
            value: value,
          );
        ).toList(),
        onChanged: (_alimentoDisponible) ? (AlimentoModel _alimentoSeleccionado) 
          print(_alimentoSeleccionado.nombre);
          _alimento = _alimentoSeleccionado;
          setState(() );
         : null,
        disabledHint: Text('No hay Alimento en Bodega'),
        onSaved: (value) 
          alimentar.idAlimento = _alimento.idAlimento;
          alimentar.nombreAlimento = _alimento.nombreRef; 
        
      )
      : Center (child: CircularProgressIndicator(strokeWidth: 1.0,))

    );
  

  Widget _crearComentarios() 
    return TextFormField(
      // -- DESIGN OTHER FIELDS -- //
      onSaved: (value) 
        alimentar.comentarios = value;
      
    ),
    );
  

  Widget _crearBoton(String texto) 
    return RaisedButton(
        // -- DESIGN -- //
        onPressed: (_guardando) ? null : _submit,
      ),
    );
  

  void _submit() 

    // CODE TO WRITE FORM IN FIREBASE
  

我的 BLOC 中的功能代码是:

  Future<List<AlimentoModel>> cargarAlimento(String idEmpresa, [String filtro]) async 
    final alimento = await _alimentoProvider.cargarAlimento(idEmpresa, filtro); //It's one await more
    _alimentoController.sink.add(alimento);
    return alimento;
  

来自 PROVIDER 的查询是:

Future<List<AlimentoModel>> cargarAlimento(String idEmpresa, [String filtro]) async 

    Query resp;
    final List<AlimentoModel> alimento = new List(); 
    resp = db.child('empresas').child(idEmpresa).child('bodega/1').child('alimento')
            .orderByChild('cantidad').startAt(0.000001);
 
  return resp.once().then((snapshot) 

      if (snapshot.value == null) return [];
      if (snapshot.value['error'] != null) return []; 

      snapshot.value.forEach((id, alim)
        final temp = AlimentoModel.fromJson(Map<String,dynamic>.from(alim));
        temp.idAlimento = id;

        alimento.add(temp);
      );
      return alimento;
  ); 

【问题讨论】:

您提供的链接谈论事务,当您未连接到服务器时,该链接将不起作用。您的代码不使用事务,因此据我所见,不受链接中所说内容的影响。当你运行它时,你的代码有什么问题? 谢谢弗兰克!!谢谢弗兰克。连接离线时,我无法使下拉菜单工作。除了这个链接,Medium link 还提到应该避免事务和等待。 这些链接中没有足够的上下文来理解它们在此处的应用方式。离线时,代码中的哪一行不起作用? 这一行... _alimentoList = await alimentoBloc.cargarAlimento(idEmpresa); 它有什么作用?你期望它做什么?如果您 print 代码中的某些内容显示了您没有预料到的内容,通常最容易提供帮助。 【参考方案1】:

在离线使用 Firebase 时,您仅在更改服务器的操作(例如,创建或更新记录)上省略 await。所以你不会等待服务器说“是的,我写了它”,你假设它已经写好了。

但是,在您的情况下,您不是在写入数据,而是在读取数据。您必须在示例中保留await。您加载数据的方式有orderByChildstartAt,也许它们会阻止离线加载。通常,如果它已经在缓存中,你会得到它:https://firebase.google.com/docs/firestore/manage-data/enable-offline#get_offline_data

您提到了BAD STATE error,也许如果您提供,我们也许可以更好地查明问题。

【讨论】:

以上是关于Flutter:如何删除等待以利用 Firebase 离线持久性?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:Google Maps 如何从 Firestore 设置图标

如何将 Flutter Bloc 与 Firebase 电话身份验证一起使用

如何在 Flutter 中等待文件上传到 Firebase 存储?

如何在 Flutter 中删除键盘工具栏?

Flutter驱动程序如何等待特定元素具有特定文本

如何在 Flutter 手机身份验证中删除验证码