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

Viewed 686

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;
  }); 
1 Answers

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.

Related