Riverpod:摄取 REST 的简单方法 API

Riverpod: Simple way to ingest REST API

我目前正在构建一个应用程序,它正在摄取 WordPress REST API 来显示博客文章。在初始应用程序加载时,我想让它使用一种方法提取初始数据,然后通过用户交互提取更多帖子。我目前有这个工作,但是,我的实现似乎很复杂,可能有一种更简洁的方法来实现这个工作。我的实现基于 this GitHub issue response。这是我的代码的样子:

main.dart中:

final blogRepository = FutureProvider((ref) async {
  final posts = await getPosts();
  return BlogService(posts);
      },
);

final blogProvider = StateNotifierProvider<BlogService, List<BlogPost>>((ref) => throw UnimplementedError(
    "Access to a [BlogService] should be provided through a [ProviderScope]."));

blogservice.dart中:

class BlogService extends StateNotifier<List<BlogPost>> {
  final List<BlogPost> _posts;
  BlogService(this._posts) : super(_posts);
  List<BlogPost> get posts => _posts;

  Future<void> morePosts(int length) async {
    Response response;
    var dio = Dio();
    response = await dio.get('https://wordpress-site.com/wp-json/wp/v2/posts/?offset=' + length.toString());
    var posts = (response.data as List);

    state = [...state, ...posts.map((post) => BlogPost.fromJson(post)).toList()];
  }
}

Future<List<BlogPost>> getPosts() async {
  Response response;
  var dio = Dio();
  response = await dio.get('https://wordpress-site.com/wp-json/wp/v2/posts/');
  var posts = (response.data as List);

  return posts.map((post) => BlogPost.fromJson(post)).toList();

}

我认为更好的方法是使用 AsyncValue,但是,我找不到任何使用 API 的好的参考实现。

请最好创建另一个 class 以提供来自 Api 的数据并使用漂移包 查看我的屏幕我如何从数据库提供数据(漂移是 sqlite DB) 这是我的数据库 class 基于 sqlite(drift)

import 'dart:io';
import 'package:drift/native.dart';
import 'package:mobile_nsk/models/policy/driver.dart';
import 'package:mobile_nsk/models/policy/driver_converter.dart';
import 'package:mobile_nsk/models/policy/holders.dart';
import 'package:mobile_nsk/models/policy/holders_converter.dart';
import 'package:mobile_nsk/models/policy/vehicle.dart';
import 'package:mobile_nsk/models/policy/vehicle_converter.dart';
import 'package:path/path.dart' as p;
import 'package:drift/drift.dart';
import 'package:path_provider/path_provider.dart';
part 'database.g.dart';
LazyDatabase _openConnection()=> LazyDatabase(()async{
  final dbFolder = await getApplicationDocumentsDirectory();
  final file = File(p.join(dbFolder.path, 'db.sqlite'));
  return NativeDatabase(file,logStatements: true);
});

@DriftDatabase(tables: [Holders, Drivers, Vehicles])
class MyDatabase extends _$MyDatabase {
  MyDatabase() : super(_openConnection());

  // Для страхователя
  Future<List<Holder>> getAllHolders() => select(holders).get();
  Stream<List<Holder>> watchAllHolder() => select(holders).watch();
  Future insertNewHolder(Holder holder) => into(holders).insert(holder, mode:  InsertMode.insertOrReplace);
  Future deleteHolder(Holder holder) => delete(holders).delete(holder);
  Future<Holder> getHolderById(Holder holder)=> select(holders).getSingle();

  Future insetData(Holder holder) => into(holders).insert(holder, mode:  InsertMode.insertOrReplace);
  Future updateHolder(Holder holder, int id, String phone) async
  {
    await update(holders)..where((tbl) => tbl.id.equals(id))..write(HoldersCompanion(phone: Value(phone)));
    await into(holders).insert(holder, mode: InsertMode.insertOrReplace);
  }
  // Для Водителя
  Future<List<Driver>> getAllDriver() => select(drivers).get();
  Stream<List<Driver>> watchAllDriver() => select(drivers).watch();
  Future insertNewDriver(Driver driver) => into(drivers).insert(driver, mode: InsertMode.insertOrReplace);
  Future deleteDriver(Driver driver) => delete(drivers).delete(driver);


  // Для транспорта
  Future<List<Vehicle>> getAllVehicles() => select(vehicles).get();
  Stream<List<Vehicle>> watchAllVehicle() => select(vehicles).watch();
  Future insertNewVehicle(Vehicle vehicle) => into(vehicles).insert(vehicle, mode:  InsertMode.insertOrReplace);
  Future deleteVehicle(Vehicle vehicle) => delete(vehicles).delete(vehicle);

  // всегда инкрементить версию схему если что то добавил/убрал/изменил/нахуевертил
  @override
  int get schemaVersion => 1;
}

这是我的支架模型(由 build_runner 生成)

import 'package:drift/drift.dart';
import 'package:json_annotation/json_annotation.dart' as j;
import 'package:mobile_nsk/database/database.dart';
import 'package:mobile_nsk/models/policy/holders_converter.dart';
@UseRowClass(Holder)
class Holders extends Table{
  IntColumn get id => integer()();
  TextColumn get firstName =>text()();
  TextColumn get middleName  => text()();
  TextColumn get lastName  => text()();
  TextColumn get legalName  => text()();
  IntColumn get  isIndividual  => integer()();
  TextColumn get iin  => text()();
  IntColumn get isResident  => integer()();
  TextColumn get phone  => text()();
  TextColumn get email  => text()();
  TextColumn get address  => text()();
  IntColumn get documentTypeId  => integer()();
  TextColumn get documentNumber => text()();
  TextColumn get documentIssueDate => text()();
  TextColumn get classType => text().named("class")();
  TextColumn get birthDate => text()();
  BoolColumn get isExperienced => boolean()();
  BoolColumn get isPrivileged  => boolean()();

  TextColumn get holders =>
      text().map(const HolderConverter()).nullable()();
  @override
  Set<Column> get primaryKey=> {id};

}
@j.JsonSerializable()
class Holder implements Insertable<Holder>{
  final int  id;
  final String firstName;
  final String middleName;
  final String lastName;
  final String legalName;
  final int  isIndividual;
  final String iin;
  final int isResident;
  final String phone;
  final String email;
  final String address;
  final int documentTypeId;
  final String documentNumber;
  final String documentIssueDate;
  @j.JsonKey(name: "class")
  final String classType;
  final String birthDate;
  final bool isExperienced;
  final bool isPrivileged;

const  Holder(this.id, this.firstName, this.middleName, this.lastName, this.legalName, this.isIndividual, this.iin, this.isResident, this.phone, this.email, this.address, this.documentTypeId, this.documentNumber, this.documentIssueDate, this.classType, this.birthDate, this.isExperienced, this.isPrivileged);
factory Holder.fromJson(Map<String, dynamic>json)=> Holder(
  json['id'] as int,
  json['firstName'] as String? ?? "",
  json['middleName'] as String? ?? "",
  json['lastName'] as String? ?? "",
  json['legalName'] as String? ?? "",
  json['isIndividual'] as int,
  json['iin'] as String? ?? "",
  json['isResident'] as int? ?? 0,
  json['phone'] as String? ?? "",
  json['email'] as String? ?? "",
  json['address'] as String? ?? "",
  json['documentTypeId'] as int? ?? 0,
  json['documentNumber'] as String? ?? "",
  json['documentIssueDate'] as String? ?? "",
  json['class'] as String? ?? "",
  json['birthDate'] as String? ?? "",
  json['isExperienced'] as bool? ?? false,
  json['isPrivileged'] as bool? ?? false,
);
Map<String, dynamic>toJson()=>{
  'id': id,
  'firstName':firstName,
  'middleName': middleName,
  'lastName': lastName,
  'legalName': legalName,
  'isIndividual': isIndividual,
  'iin': iin,
  'isResident': isResident,
  'phone': phone,
  'email': email,
  'address': address,
  'documentTypeId': documentTypeId,
  'documentNumber': documentNumber,
  'documentIssueDate': documentIssueDate,
  'class': classType,
  'birthDate': birthDate,
  'isExperienced': isExperienced,
  'isPrivileged': isPrivileged,
};
@override
  Map<String, Expression> toColumns(bool nullToAbsent) {
    return HoldersCompanion(
        id: Value(id),
        firstName: Value(firstName),
        middleName: Value(middleName),
        lastName: Value(lastName),
        legalName: Value(legalName),
        isIndividual: Value(isIndividual),
        iin: Value(iin),
        isResident: Value(isResident),
        phone: Value(phone),
        email: Value(email),
        address: Value(address),
        documentTypeId: Value(documentTypeId),
        documentNumber: Value(documentNumber),
        documentIssueDate: Value(documentIssueDate),
        classType: Value(classType),
        birthDate: Value(birthDate),
        isExperienced: Value(isExperienced),
        isPrivileged: Value(isPrivileged)
    ).toColumns(nullToAbsent);
  }

}

这是我从 api 到数据库

的代码插入
    import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:mobile_nsk/database/database.dart';
import 'package:mobile_nsk/models/policy/holders.dart';

class HolderProvider {
  String baseUrl = 'http://172.16.3.49:9096/api/v1';
  Future<void> getHolder(
    String iin,MyDatabase database
  ) async {

    final response = await http.get(
        Uri.parse(baseUrl + '/Customer/get_full_client?iin=$iin'),
        headers: {'Content-Type': 'application/json'});
    if (jsonDecode(response.body)['isExperienced'] != false) {
      database.insertNewHolder(Holder.fromJson(jsonDecode(response.body)));
    } else {
      throw Exception();
    }
  }
}

这是一个使用 AsyncValue 的示例 - 它消除了 repository

让你的 service.dart 文件像这样:

final blogServiceProvider = Provider<BlogService>((ref) => BlogService());

class BlogService {
  Future<AsyncValue<List<BlogPost>>> getBlogPost() async {
    try {
      var dio = Dio();
      Response response = await dio.get('https://wordpress-site.com/wp-json/wp/v2/posts/');
      var posts = (response.data as List);
      List<BlogPost> list  = posts.map<BlogPost>((post) => BlogPost.fromJson(post)).toList();
      return AsyncData(list);
    } catch (e) {
      return AsyncError("Something went wrong");
    }
  }
}

你的供应商是这样的:

final blogNotifierProvider = StateNotifierProvider<BlogNotifier, AsyncValue<List<BlogPost>>>((ref){
  BlogService _service = ref.read(blogServiceProvider);
  return BlogNotifier(_service);
});

class BlogNotifier extends StateNotifier<AsyncValue<List<BlogPost>>> {
  BlogNotifier(this._service) : super(AsyncLoading()) {
    getPosts();
  }
  final BlogService _service;

  void getPosts() async {
    state = await _service.getBlogPost();
  }
}



编辑:要将现有帖子与新帖子合并,试试这个:

class BlogService {

  List<BlogPost> _posts = [];

  Future<AsyncValue<List<BlogPost>>> getBlogPost() async {
    try {
      var dio = Dio();
      Response response = await dio.get('https://wordpress-site.com/wp-json/wp/v2/posts/');
      var posts = (response.data as List);
      List<BlogPost> list  = posts.map<BlogPost>((post) => BlogPost.fromJson(post)).toList();
      _posts = list;
      return AsyncData(list);
    } catch (e) {
      return AsyncError("Something went wrong");
    }
  }

    Future<AsyncValue<List<BlogPost>>> morePosts() async {
    try {
      var dio = Dio();
      Response response = await dio.get('https://wordpress-site.com/wp-json/wp/v2/posts/?offset=' + length.toString());
      var posts = (response.data as List);
      List<BlogPost> list  = posts.map<BlogPost>((post) => BlogPost.fromJson(post)).toList();
      _posts.addAll(list);
      return AsyncData(_posts);
    } catch (e) {
      return AsyncError("Something went wrong");
    }
  }
}

通知者 class 将是:

class BlogNotifier extends StateNotifier<AsyncValue<List<BlogPost>>> {
  BlogNotifier(this._service) : super(AsyncLoading()) {
    getPosts();
  }
  final BlogService _service;

  void getPosts() async {
    state = await _service.getBlogPost();
  }

  void morePosts() async {
    state = await _service.morePosts();
  }
}