Flutter: How to remove awaits to take advantage of Firebase Offline Persistence?

534

When using Firebase offline, you omit the await only on things that change the server (e.g., creating or updating a record). So you won't wait for the server to say "yes I wrote it", you assume that it's written.

In your case, however, you are not writing data, you are reading data. You will have to keep await in your example. The way you load your data has orderByChild and startAt, maybe those are preventing offline loading. Normally, you get it if it's already in the cache: https://firebase.google.com/docs/firestore/manage-data/enable-offline#get_offline_data

You mention a BAD STATE error, maybe if you provide that, we may be able to pinpoint the issue a bit better.

Share:
534
David L
Author by

David L

Updated on December 23, 2022

Comments

  • David L
    David L 11 months

    I'm using a drop-down list (DropDown), whose elements are obtained from Firebase. The form works right, however when the internet connection is lost the Firebase Offline Persistence property doesn't work and the CircularProgressIndicator stays active. Reading some responses such as Using Offline Persistence in Firestore in a Flutter App, it is indicated that awaits should not be handled, however it is not clear to me how to achieve it:

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

    The function code from my BLOC is:

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

    And the Query from PROVIDER is:

    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;
      }); 
    
    • Frank van Puffelen
      Frank van Puffelen over 3 years
      The link you provide talks about transactions, which won't work when you're not connected to the server. You code doesn't use transactions, so is not affected by what is said in the link as far as I can see. What doesn't work about your code when you run it?
    • David L
      David L over 3 years
      Thank you Frank!! Thanks Frank. I can't make dropdown works when connection is offline. In addition to this link, a Medium link also mentions that transactions and awaits should be avoided.
    • Frank van Puffelen
      Frank van Puffelen over 3 years
      There's just not enough context in those links, to understand how they apply here. What specific line in your code doesn't work when you're offline?
    • David L
      David L over 3 years
      This line... _alimentoList = await alimentoBloc.cargarAlimento(idEmpresa);
    • Frank van Puffelen
      Frank van Puffelen over 3 years
      What does it do? What did you expect it to do? It's typically easiest to help if you print something in the code that shows what you didn't expect.