resource_network_fetcher 1.0.0
resource_network_fetcher: ^1.0.0 copied to clipboard

Uses the NetworkBoundResources to control the offline-first or only to centralize the errors from fetching from the network or any other function.

Resource Network Fetcher! 🧰 #

Pub License: BSD-3-Clause

🧰 A package that provides you a way to track your request and avoid any errors. #

We use the NetworkBoundResources to make a request, process the response using then and centralize all errors of the application to a unique function that you inject and provide a friendly message to your users.


📄 Table of Contents #

❓ Why should I use #

Resource Network Fetcher standardize your flutter project, making all your functions that can occur errors return a same value, that is the Resource<T>. This object helps you to pass the error for the view, making a default way to know what message to display to the user. Other thing that it does is centralize the errors with the function called AppException errorMapper(e), that receives all errors of the application and map how you want to make the error readable.

Other thing that it provides is the Status, that can be: Status.success<span/>, Status.loading<span/> and Status.error<span/>

📦 Examples #

Examples of to-do apps that used resource_network_fetcher as base:

🔧 Install #

Follow this tutorial: Installation tab

Add resource_network_fetcher to your pubspec.yaml file:

dependencies:
  resource_network_fetcher:

Import get in files that it will be used:

import 'package:resource_network_fetcher/resource_network_fetcher.dart';

🎉 Usage #

Setup #

main.dart file #

import 'package:flutter/material.dart';
import 'error_mapper.dart';

void main() {
  Resource.setErrorMapper(ErrorMapper.from);
  runApp(MyApp());
}

error_mapper.dart #

This is an example of minimum configuration to use, you can add other parameters to map better.

import 'package:dio/dio.dart';
import 'package:resource_network_fetcher/resource_network_fetcher.dart';

abstract class ErrorMapper {
  static AppException from(dynamic e) {
    switch (e.runtimeType) {
      case AppException:
        return e;
      case DioError:
        return AppException(
          exception: e,
          message: _dioError(e),
        );
      default:
        return AppException(
          exception: e,
          message: e.toString(),
        );
    }
  }

  static String _dioError(DioError error) {
    switch (error.type) {
      case DioErrorType.sendTimeout:
      case DioErrorType.connectTimeout:
      case DioErrorType.receiveTimeout:
        return "Connection failure, verify your internet";
      case DioErrorType.cancel:
        return "Canceled request";
      case DioErrorType.response:
      case DioErrorType.other:
      default:
    }
    if (error.response?.statusCode != null) {
      switch (error.response!.statusCode) {
        case 401:
          return "Authorization denied, check your login";
        case 403:
          return "There was an error in your request, check the data and try again";
        case 404:
          return "Not found";
        case 500:
          return "Internal server error";
        case 503:
          return "The server is currently unavailable, please try again later";
        default:
      }
    }
    return "Request error, please try again later";
  }
}

❌ AppException #

We use this exception to standardize the exceptions to the app

throw AppException(
    message: "Error message",
    exception: Exception("Error message"),
    data: null,
);

↕️ Network Bound Resources #

Is a conjunction of rules that run with the Resource.asFuture and transforms the response of the fetch to the model specified in the processResponse param. Another use for this is using for offline-first or only to cache your requests. For that you need to use the other params of the method.

We have other options of methods, that returns a stream or a Future. Here is an example of how to use in a simple way:

asFuture #

Future<Resource<UserEntity>> getUser() {
  return NetworkBoundResources.asFuture<UserEntity, Map<String, dynamic>>(
    createCall: _getUserFromNetwork,
    processResponse: UserEntity.fromMap,
  );
}

Future<Map<String, dynamic>> _getUserFromNetwork() async {
  return {}; /// Fetch the api
}

asSimpleStream #

Stream<Resource<UserEntity>> streamUser() {
  return NetworkBoundResources.asSimpleStream<UserEntity, Map<String, dynamic>>(
    createCall: _streamUserFromNetwork,
    processResponse: UserEntity.fromMap,
  );
}

Stream<Map<String, dynamic>> _streamUserFromNetwork() async* {
  yield {}; /// Fetch the api
}

asResourceStream #

The difference of this and the asStream is that with this you can return your own Resource in createCall param.

Stream<Resource<UserEntity>> streamUser() {
  return NetworkBoundResources.asResourceStream<UserEntity, Map<String, dynamic>>(
    createCall: _streamUserFromNetwork,
    processResponse: UserEntity.fromMap,
  );
}

Stream<Resource<Map<String, dynamic>>> _streamUserFromNetwork() async* {
  yield Resource.loading();
  yield Resource.success(data: {}); /// Fetch the api
}

asStream #

Stream<Resource<UserEntity>> streamUser() {
  return NetworkBoundResources.asStream<UserEntity, Map<String, dynamic>>(
    createCall: _streamUserFromNetwork,
    processResponse: UserEntity.fromMap,
  );
}

Stream<Map<String, dynamic>> _streamUserFromNetwork() async* {
  yield {}; /// Fetch the api
}

📜 Resource #

⏯‍️ Executing a function with resource #

Here is an example of how to run a function with the Resource, because with that, any error that occur inside will be mapped with the ErrorMapper setted.

final result = await Resource.asFuture(() async {
  /// Here you execute wherever you want and returns the result.
  return ["one"];
});
print(result.isSuccess); /// Prints true
print(result.isFailed); /// Prints false
print(result.isLoading); /// Prints false
print(result.data); /// Prints the result: ["one"]

States #

We have 3 basic states, the success, loading and failed state. But each state can storage an error so, in the total can have 6 states, that includes having or not the data.

Loading state #

final resource = Resource.loading<T>({T data});

Loading without data state

final resource = Resource.loading<List<String>>();

Loading with data state

final resource = Resource.loading<List<String>>(data: ["one"]);

Success state #

final resource = Resource.success<T>({T data});

Success without data state

final resource = Resource.success<List<String>>();

Success with data state

final resource = Resource.success<List<String>>(data: ["one"]);

Failed state #

final resource = Resource.failed<T>({dynamic error, T data});

Failed without data state

final resource = Resource.failed<List<String>>(error: AppException());

Failed with data state

final resource = Resource.failed<List<String>>(error: AppException(), data: ["one"]);

📑 Resource MetaData #

Is an information about the last returns of the Resource, can be very useful in streams, when you need to know the last result that was returned. If you use the NetworkBoundResources.asSimpleStream(), NetworkBoundResources.asResourceStream() or NetworkBoundResources.asStream(), the add to the Resource.metaData is automatic!

Here is an example that show how you can get the MetaData of a Resource.

var resource = Resource.success(data: <String>[]);
resource = resource.addData(Status.success, ["newData"]);

// Prints the metadata of the resource
print(resource.metaData);

// Prints the last data returned by this resource
print(resource.metaData.data); /// ["newData"]

// Prints the list of the lasts 2 returns of the resource
print(resource.metaData.results); /// [ ["newData"], [] ]

❇️️ Widgets #

We created widgets that helps you to summarize your code and work better with Resource<T> object, treating all the states at your way!

🔹 ListViewResourceWidget #

Widget build(BuildContext) {
  return ListViewResourceWidget(
    resource: Resource.success(data: []),
    loadingTile: ListTile(),
    tileMapper: (data) => ListTile(),
    loadingTileQuantity: 2,
    refresh: () async {},
    emptyWidget: Container(),
  );
}

🔸 ResourceWidget #

Widget build(BuildContext) {
  return ResourceWidget(
    resource: Resource.success(),
    loadingWidget: CircularProgressIndicator(),
    doneWidget: (data) => Container(),
    refresh: () async {},
    errorWithDataWidget: (e, data) => Container(),
    loadingWithDataWidget: (data) => Container(),
  );
}

👤 Author #

Lucas Henrique Polazzo #

🤝 Contributing #

Contributions, issues and feature requests are welcome!
Feel free to check issues page. You can also take a look at the contributing guide.

💚 Show your support #

Give a ⭐ and a like in Pub.dev️ if this project helped you!

📝 License #

Copyright © 2021 Lucas Henrique Polazzo.
This project is BSD-3 Clause licensed.

3
likes
100
pub points
73%
popularity

Uses the NetworkBoundResources to control the offline-first or only to centralize the errors from fetching from the network or any other function.

Homepage
Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD (LICENSE)

Dependencies

flutter

More

Packages that depend on resource_network_fetcher