Flutter: How to remove awaits to take advantage of Firebase Offline Persistence?
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.
David L
Updated on December 23, 2022Comments
-
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 over 3 yearsThe 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 over 3 yearsThank 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 over 3 yearsThere'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 over 3 yearsThis line... _alimentoList = await alimentoBloc.cargarAlimento(idEmpresa);
-
Frank van Puffelen over 3 yearsWhat 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.
-