使用 API REST Firebase 和 Flutter 的离线持久化
Offline Persistence using API REST Firebase and Flutter
我正在开发一个 Agro 应用程序,当我观看 Firebase 宣传视频时,我非常喜欢他们描述应用程序即使在没有互联网(离线持久性)时也能工作的方式。
为了开发我的应用程序,我使用了:
- 颤动
- Firebase API REST(http 插件)
- 提供商向 API
提出请求
- 集团模式
我已经基本完成了应用程序,但我注意到当没有互联网时会出现错误:
SocketException: Failed host lookup: ‘node1.bitcoiin.com’ (OS Error: No address associated with hostname, errno = 7)
@IshwarChandra 在 Medium 上澄清了我:
When you use offline persistence in Firebase, don't use Transactions
or await for response.
对吗?是否真的可以在不使用 await 的情况下从 Firebase 检索数据?我问这个是因为我在某些情况下实现了 BLOC 模式,从而从用户界面中删除了等待。然而,它背后总是调用一个等待加载数据的提供者。
另一方面,Whosebug 的一位开发人员向我表示,离线持久化管理只能通过 Flutter SDK 和
it's NOT possible (or would require too much programming) to implement
it with the REST API as I have been using it.
这个说法正确吗?如果是这样,我有以下疑惑:
是否有文档或提示可以让我轻松地将 http REST Firebase API 请求(我主要在 Provider 中)迁移到 Flutter 中的 Firebase SDK?
我可以保留我正在使用的 BLOC 和 PROVIDER 的架构吗?
在进行更改之前还有其他提示吗?
供您参考,我在我的提供商上的示例代码:
import 'dart:convert';
import 'package:aplians_fish/src/preferencias_usuario/preferencias_usuario.dart';
import 'package:http/http.dart' as http;
import 'package:aplians_fish/src/models/alimentar_model.dart';
class AlimentarProvider {
final String _url ='https://apliansdb.firebaseio.com';
final _prefs = new PreferenciasUsuario();
// ==========================================================
// ======================== CREATE ==========================
// ==========================================================
Future<bool> crearAlimentar(AlimentarModel alimentar, String idEmpresa, String idEstanque) async {
final url ='$_url/empresas/$idEmpresa/estanques/$idEstanque/loteActual/alimentacion.json?auth=${_prefs.token}';
final resp = await http.post(url, body: alimentarModelToJson(alimentar));
final decodedData = json.decode(resp.body); // Da la respuesta, sea un cargue o un error
print(decodedData);
return true;
}
// ==========================================================
// ================== READ =====================
// ==========================================================
Future<List<AlimentarModel>> cargarAlimentar(String idEmpresa, String idEstanque) async {
final url ='$_url/empresas/$idEmpresa/estanques/$idEstanque/loteActual/alimentacion.json?auth=${_prefs.token}';
final resp = await http.get(url);
final Map<String, dynamic> decodedData = json.decode(resp.body);
final List<AlimentarModel> alimentar = new List();
if (decodedData == null) return [];
if (decodedData['error'] != null) return [];
decodedData.forEach((id, alim){
final temp = AlimentarModel.fromJson(alim);
temp.idAlimentar = id;
alimentar.add(temp);
});
return alimentar;
}
// ==========================================================
// =========================== UPDATE =======================
// ==========================================================
Future<bool> editarAlimentar(String idEmpresa, String idEstanque, AlimentarModel alimentar) async {
final url = '$_url/empresas/$idEmpresa/estanques/$idEstanque/loteActual/alimentacion/${alimentar.idAlimentar}.json?auth=${_prefs.token}';
final resp = await http.put(url, body: alimentarModelToJson(alimentar));
final decodedData = json.decode(resp.body);
print(decodedData);
return true;
}
// ==========================================================
// ================== DELETE =======================
// ==========================================================
Future<bool> borrarAlimentar(String idEmpresa, String idEstanque, String id) async {
final url = '$_url/empresas/$idEmpresa/estanques/$idEstanque/loteActual/alimentacion/$id.json?auth=${_prefs.token}';
final resp = await http.delete(url);
print(json.decode(resp.body));
return true;
}
}
而且,这就是我使用 BLOC 的方式:
import 'package:rxdart/rxdart.dart';
import 'package:aplians_fish/src/models/alimentar_model.dart';
import 'package:aplians_fish/src/providers/alimentar_provider.dart';
class AlimentarBloc {
final _alimentarController = new BehaviorSubject<List<AlimentarModel>>();
final _cargandoController = new BehaviorSubject<bool>();
final _alimentarProvider = new AlimentarProvider();
Stream <List<AlimentarModel>> get alimentarStream => _alimentarController.stream;
Stream <bool> get cargando => _cargandoController.stream;
Future<List<AlimentarModel>> cargarAlimentar(String idEmpresa, String idEstanque) async {
final alimentar = await _alimentarProvider.cargarAlimentar(idEmpresa, idEstanque);
// ahora hay que insertar estos productos al stream...
_alimentarController.sink.add(alimentar);
return alimentar;
}
Future<bool> crearAlimentar(AlimentarModel alimentar, String idEmpresa, String idEstanque) async {
_cargandoController.sink.add(true);
await _alimentarProvider.crearAlimentar(alimentar, idEmpresa, idEstanque);
_cargandoController.sink.add(false); //este es útil para bloquear botones cuando se esté cargando
// esto se hace para notificar que está cargando el producto y de esa forma espere
return true;
}
void editarAlimentar(String idEmpresa, String idEstanque, AlimentarModel alimentar) async {
_cargandoController.sink.add(true);
await _alimentarProvider.editarAlimentar(idEmpresa, idEstanque, alimentar);
_cargandoController.sink.add(false);
// esto se hace para notificar que está cargando el producto y de esa forma espere
}
Future<bool> borrarAlimentar(String idEmpresa, String idEstanque, String id) async {
// _cargandoController.sink.add(true);
await _alimentarProvider.borrarAlimentar(idEmpresa, idEstanque, id);
// _cargandoController.sink.add(false);
// no se requieren los controllers porque para la persona al desplazar, siente que ya se borró
return true;
}
dispose() {
_alimentarController?.close();
_cargandoController?.close();
}
}
您似乎是通过 REST API 从 Firebase 实时数据库获取数据。 Firebase SDK 实现了客户端缓存,但由于您没有使用 SDK,因此您也没有使用 SDK 中的缓存。
要了解如何使用 SDK 从 Firebase 读取数据,我建议阅读(例如)Android, and the example in the Flutter library 的 Firebase 文档。在那些之间你应该能够找出一个映射。如果您的特定代码片段有问题,post 一个新问题就是那个问题。
使用 BLoC 与您是否使用 Firebase 无关,因此只要您将与数据库交互的代码从使用 REST API 映射到使用 Firebase SDK,您就可以继续使用它们。
我正在开发一个 Agro 应用程序,当我观看 Firebase 宣传视频时,我非常喜欢他们描述应用程序即使在没有互联网(离线持久性)时也能工作的方式。
为了开发我的应用程序,我使用了:
- 颤动
- Firebase API REST(http 插件)
- 提供商向 API 提出请求
- 集团模式
我已经基本完成了应用程序,但我注意到当没有互联网时会出现错误:
SocketException: Failed host lookup: ‘node1.bitcoiin.com’ (OS Error: No address associated with hostname, errno = 7)
@IshwarChandra 在 Medium 上澄清了我:
When you use offline persistence in Firebase, don't use Transactions or await for response.
对吗?是否真的可以在不使用 await 的情况下从 Firebase 检索数据?我问这个是因为我在某些情况下实现了 BLOC 模式,从而从用户界面中删除了等待。然而,它背后总是调用一个等待加载数据的提供者。
另一方面,Whosebug 的一位开发人员向我表示,离线持久化管理只能通过 Flutter SDK 和
it's NOT possible (or would require too much programming) to implement it with the REST API as I have been using it.
这个说法正确吗?如果是这样,我有以下疑惑:
是否有文档或提示可以让我轻松地将 http REST Firebase API 请求(我主要在 Provider 中)迁移到 Flutter 中的 Firebase SDK?
我可以保留我正在使用的 BLOC 和 PROVIDER 的架构吗?
在进行更改之前还有其他提示吗?
供您参考,我在我的提供商上的示例代码:
import 'dart:convert';
import 'package:aplians_fish/src/preferencias_usuario/preferencias_usuario.dart';
import 'package:http/http.dart' as http;
import 'package:aplians_fish/src/models/alimentar_model.dart';
class AlimentarProvider {
final String _url ='https://apliansdb.firebaseio.com';
final _prefs = new PreferenciasUsuario();
// ==========================================================
// ======================== CREATE ==========================
// ==========================================================
Future<bool> crearAlimentar(AlimentarModel alimentar, String idEmpresa, String idEstanque) async {
final url ='$_url/empresas/$idEmpresa/estanques/$idEstanque/loteActual/alimentacion.json?auth=${_prefs.token}';
final resp = await http.post(url, body: alimentarModelToJson(alimentar));
final decodedData = json.decode(resp.body); // Da la respuesta, sea un cargue o un error
print(decodedData);
return true;
}
// ==========================================================
// ================== READ =====================
// ==========================================================
Future<List<AlimentarModel>> cargarAlimentar(String idEmpresa, String idEstanque) async {
final url ='$_url/empresas/$idEmpresa/estanques/$idEstanque/loteActual/alimentacion.json?auth=${_prefs.token}';
final resp = await http.get(url);
final Map<String, dynamic> decodedData = json.decode(resp.body);
final List<AlimentarModel> alimentar = new List();
if (decodedData == null) return [];
if (decodedData['error'] != null) return [];
decodedData.forEach((id, alim){
final temp = AlimentarModel.fromJson(alim);
temp.idAlimentar = id;
alimentar.add(temp);
});
return alimentar;
}
// ==========================================================
// =========================== UPDATE =======================
// ==========================================================
Future<bool> editarAlimentar(String idEmpresa, String idEstanque, AlimentarModel alimentar) async {
final url = '$_url/empresas/$idEmpresa/estanques/$idEstanque/loteActual/alimentacion/${alimentar.idAlimentar}.json?auth=${_prefs.token}';
final resp = await http.put(url, body: alimentarModelToJson(alimentar));
final decodedData = json.decode(resp.body);
print(decodedData);
return true;
}
// ==========================================================
// ================== DELETE =======================
// ==========================================================
Future<bool> borrarAlimentar(String idEmpresa, String idEstanque, String id) async {
final url = '$_url/empresas/$idEmpresa/estanques/$idEstanque/loteActual/alimentacion/$id.json?auth=${_prefs.token}';
final resp = await http.delete(url);
print(json.decode(resp.body));
return true;
}
}
而且,这就是我使用 BLOC 的方式:
import 'package:rxdart/rxdart.dart';
import 'package:aplians_fish/src/models/alimentar_model.dart';
import 'package:aplians_fish/src/providers/alimentar_provider.dart';
class AlimentarBloc {
final _alimentarController = new BehaviorSubject<List<AlimentarModel>>();
final _cargandoController = new BehaviorSubject<bool>();
final _alimentarProvider = new AlimentarProvider();
Stream <List<AlimentarModel>> get alimentarStream => _alimentarController.stream;
Stream <bool> get cargando => _cargandoController.stream;
Future<List<AlimentarModel>> cargarAlimentar(String idEmpresa, String idEstanque) async {
final alimentar = await _alimentarProvider.cargarAlimentar(idEmpresa, idEstanque);
// ahora hay que insertar estos productos al stream...
_alimentarController.sink.add(alimentar);
return alimentar;
}
Future<bool> crearAlimentar(AlimentarModel alimentar, String idEmpresa, String idEstanque) async {
_cargandoController.sink.add(true);
await _alimentarProvider.crearAlimentar(alimentar, idEmpresa, idEstanque);
_cargandoController.sink.add(false); //este es útil para bloquear botones cuando se esté cargando
// esto se hace para notificar que está cargando el producto y de esa forma espere
return true;
}
void editarAlimentar(String idEmpresa, String idEstanque, AlimentarModel alimentar) async {
_cargandoController.sink.add(true);
await _alimentarProvider.editarAlimentar(idEmpresa, idEstanque, alimentar);
_cargandoController.sink.add(false);
// esto se hace para notificar que está cargando el producto y de esa forma espere
}
Future<bool> borrarAlimentar(String idEmpresa, String idEstanque, String id) async {
// _cargandoController.sink.add(true);
await _alimentarProvider.borrarAlimentar(idEmpresa, idEstanque, id);
// _cargandoController.sink.add(false);
// no se requieren los controllers porque para la persona al desplazar, siente que ya se borró
return true;
}
dispose() {
_alimentarController?.close();
_cargandoController?.close();
}
}
您似乎是通过 REST API 从 Firebase 实时数据库获取数据。 Firebase SDK 实现了客户端缓存,但由于您没有使用 SDK,因此您也没有使用 SDK 中的缓存。
要了解如何使用 SDK 从 Firebase 读取数据,我建议阅读(例如)Android, and the example in the Flutter library 的 Firebase 文档。在那些之间你应该能够找出一个映射。如果您的特定代码片段有问题,post 一个新问题就是那个问题。
使用 BLoC 与您是否使用 Firebase 无关,因此只要您将与数据库交互的代码从使用 REST API 映射到使用 Firebase SDK,您就可以继续使用它们。