使用 API REST Firebase 和 Flutter 的离线持久化

Offline Persistence using API REST Firebase and Flutter

我正在开发一个 Agro 应用程序,当我观看 Firebase 宣传视频时,我非常喜欢他们描述应用程序即使在没有互联网(离线持久性)时也能工作的方式。

为了开发我的应用程序,我使用了:

我已经基本完成了应用程序,但我注意到当没有互联网时会出现错误:

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.

这个说法正确吗?如果是这样,我有以下疑惑:

  1. 是否有文档或提示可以让我轻松地将 http REST Firebase API 请求(我主要在 Provider 中)迁移到 Flutter 中的 Firebase SDK?

  2. 我可以保留我正在使用的 BLOC 和 PROVIDER 的架构吗?

  3. 在进行更改之前还有其他提示吗?

供您参考,我在我的提供商上的示例代码:

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,您就可以继续使用它们。