An http request client, which supports Manageable Requests Cancellation, Request Policy (Timeout and Retry), Easier Multipart Requests, etc.

Related tags

Utilities postor
Overview

style: lint

A wrapper around Dart's http package, which supports Manageable Requests Cancellation, Request Policy (Timeout and Retry), Easier Multipart Requests, Error Handling, etc.

Using

Using Postor is actually almost the same as using the http package. But, instead of defining the URL on every request, Postor gives us the ability to set the default base URL, so that we just need to define the endpoint(s).

And one of the best ways to using it is to initialize its instance somewhere, whether as a singleton or as an instance in a class or something like that.

import 'dart:convert';

import 'package:postor/postor.dart';

class MyApi {
  // note: I'm not endorsing this url, so use this at your own risk!
  static const String baseUrl = 'https://fakestoreapi.com';
  static const String usersEndpoint = '/users';

  // this creates a new instance of Postor with the default
  // Request Policies of 10 seconds Timeout and 3 times Retry Attempts
  final Postor postor = Postor(baseUrl);
  // we can also initiate Postor with our own Request Policies
  // final Postor postor = Postor(
  //    baseUrl,
  //    defaultTimeout: const Duration(seconds: 1),
  //    retryPolicy: const RetryOptions(maxAttempts: 10),
  // );

  Future<List<User>> getUsers() async {
    final response = await postor.get(usersEndpoint);
    if (response.statusCode != 200) {
      throw transformStatusCodeToException(
        statusCode: response.statusCode,
        responseBody: response.body,
      );
    }
    final rawUsersList = json.decode(response.body) as List;

    return rawUsersList.map((user) {
      return User.fromMap(user as Map<String, dynamic>);
    }).toList();
  }
}

class User {
  const User({
    required this.id,
    required this.username,
  });

  final int id;
  final String username;

  factory User.fromMap(Map<String, dynamic> map) {
    return User(
      id: map['id'],
      username: map['username'],
    );
  }
}

The method body of getUsers can actually be simplified to

// use the `get
   
    (expectedStatusCode)` extension to parse the
   
// response body to T (e.g. List or Map).
// by using this, we also no longer need to check the status code for
// throwing exceptions based on the status code
// since the extension already handles it.
//
// note: by default the [expectedStatusCode] is set to 200.
// so if we expect other status code, we just need to set it.
// for example:
// final response = await postor.get(usersEndpoint).get
   
    (201);  
   
final response = await postor.get(usersEndpoint).get<List>();
return response.map((u) => User.fromMap(u as Map<String, dynamic>)).toList();

Now based on the above example, we can add another method to MyApi class that can cancel the request made by getUsers.

  ...
  void cancelGetUsers() {
    // postor will use the base URL + endpoint + parameters as the
    // cancellation token
    postor.cancel(baseUrl + usersEndpoint);
    // or if we're on strong analyzer mode, 
    // which will suggest us to use string interpolation instead:
    // postor.cancel('$baseUrl$usersEndpoint');
  }
  ...

One thing to remember that we can only request one specific URL per execution. So besure to check it first before requesting the same URL again, otherwise it will throw an AssertionError.

Now how do we check that getUsers has completed or not? There are many possible ways to achieve this, but we'll talk about 2 possible solutions.

example solution 1

final List<User> users = [];
final MyApi myApi = MyApi();

void main() {
  requestUsers();
  requestUsers();
}
// we can have a boolean variable that can be checked later by
// [requestUsers] method below
bool isGetUsersRunning = false;
Future<void> requestUsers() async {
  if(isGetUsersRunning) return;
  isGetUsersRunning = true;
  final usersList = await myApi.getUsers();
  users.addAll(usersList);
}

example solution 2

// we can utilize CTManager 
// read more about it here: https://pub.dev/packages/ctmanager
import 'package:ctmanager/ctmanager.dart';

final List<User> users = [];
final MyApi myApi = MyApi();

void main() {
  requestUsers();
  requestUsers();
}

// instead of creating another variable, we just need to check
// whether the get users URL exists in CTManager or not.
Future<void> requestUsers() async {
  const getUsersURL = MyApi.baseUrl + MyApi.usersEndpoint;
  // since we didn't specify anything in Postor's `ctManager` 
  // parameter, our Postor in MyApi will use CTManager.I instead.
  if(CTManager.I.hasTokenOf(getUsersURL)){
    return;
  }
  final usersList = await myApi.getUsers();
  users.addAll(usersList);
}

Next, to create a Multipart Request, e.g. image(s) uploading, there is Postor's multiPart method.

import 'package:postor/postor.dart';

final postor = Postor('https://my-api.com');

void main() {
  postImageAndFields();
}

Future<void> postImageAndFields() async {
  final fields = {
    'name': 'my name',
    'age': '99',
  };
  final files = {
    'avatar': PFileFromPath('path_to_avatar_image', filename: 'avatar.png'),
    'avatar_small': PFileFromPath('path_to_avatar_small_image'),
    // for file bytes
    // 'avatar_bytes': PFileFromBytes(avatar_bytes),
  };
  final response = await postor.multiPart(
    '/upload',
    // we don't need to specify the method if it's POST,
    // since it's the default value.
    // method: 'POST',
    fields: fields,
    files: files,
  );
  print(response.statusCode);
  print(response.body);
}

And to cancel it, just call

postor.cancel('https://my-api.com/upload');

or with CTManager

CTManager.I.cancel('https://my-api.com/upload');

Note: Postor handles files processing in an isolate, and both file processing and multipart request are cancelable.

Lastly, there's a new feature: catchIt. This might be useful for reducing line of codes in methods that have try-catch.

import 'package:postor/postor.dart';
import 'package:postor/error_handler.dart' as eh;

final Postor postor = Postor('my-api.com');

Future<void> getResponse() async {
  try {
    final response = await postor.get('/test').get<List>();
    throw SomeError();
    print('response: $response');
  } catch (error, stackTrace) {
    eh.catchIt(
      error: error,
      stackTrace: stackTrace,
      otherErrorMessage: 'Failed to get response from /test',
      onCatch: _onError,
    );
  }
}

void _onError(String errorMessage) {
  print(errorMessage);
}

Don't forget to initialize the error message handler before using catchIt, e.g. in main.dart

void main() {
  initErrorMessages((Object error, StackTrace? stackTrace, String? otherErrorMessage) {
    if(error is TimeoutException) {
      return 'Operation timeout'.
    }else{
      return otherErrorMessage ?? 'An unknown error occurred.'
    }
  });
}

Alternatively, there's [defaultErrorMessageHandler] that can be used.

void main() {
  eh.initErrorMessages(eh.defaultErrorMessageHandler);
}
You might also like...

Generate random data(string, integer, IPs etc...) using Dart.

Generate random data(string, integer, IPs etc...) using Dart.

Generate random data using Features API provides generation of: Integers in any range of numbers Strings with various characters and length Colors rep

Apr 17, 2022

A JMAP client library in Dart to make JMAP method calls and process the responses

JMAP Dart client A JMAP client library to make JMAP method calls and process the responses. We most notably use it to write the TMail Flutter applicat

Dec 19, 2022

An example todo list back end project that uses gRPC for client-server communication and Firestore for data storage

An example todo list back end project that uses gRPC for client-server communication and Firestore for data storage

Apr 18, 2022

simplified man-pages, a tldr.sh client

simplified man-pages, a tldr.sh client

Report bug ยท Request feature Table of contents Overview Screenshots CI Build and run Bugs and feature requests Contributing Creators Copyright and lic

Dec 27, 2022

A pure Dart implementation of the Pusher Channels Client

pusher_channels is a pure Dart pusher channels client. This client is work in progress and it is unstable. Usage A simple usage example: import 'packa

Nov 6, 2022

This is the client application for connecting to cptserver

This is the client application for connecting to cptserver. CPT stands for Course Participation Tracker.

Nov 2, 2022

AsyncCallQueue is a Dart class which provides a queuing mechanism to prevent concurrent access to asynchronous code.

async_call_queue AsyncCallQueue is a Dart class which provides a queuing mechanism to prevent concurrent access to asynchronous code. Getting Started

Jan 18, 2022

MB Contact Form is a basic flutter widget which helps you for your contact page.

mb_contact_form is a third party flutter package. This is a simple version of Contact Form. This can be easily added to your flutter projects. This make your works simpler and your code shorter. This is recently updated and has null safety too.

Oct 17, 2022
Owner
Iandi Santulus
a senior cs student who loves building apps using Flutter
Iandi Santulus
Flexible retry library for Dio package

Flexible retry library for Dio package. This is a next generation of an abandoned dio_retry package.

Rodion Mostovoy 43 Dec 12, 2022
http_plus - a drop-in replacement for http with HTTP/2 goodies

http_plus is a drop-in replacement for http with HTTP/2 goodies! Under the hood, it wraps http2 to make it compatible with APIs of http. Additionally, it fallbacks to HTTP/1.1 if H2 is not supported by the server.

Harsh Bhikadia 7 Apr 22, 2022
Future based HTTP client for the Dart and Flutter

Uno Future based HTTP client for the Dart and Flutter. Uno, inspired by Axios, bringing a simple and robust experience to the crossplatform apps in Fl

Flutterando 56 Dec 16, 2022
A simple, unofficial AWS Polly client in dart. Supports generating a URL given an input text and voice identifier.

Flutter AWS Polly Plugin This plugin is a Flutter wrapper for AWS Polly which is a cloud service that converts text into lifelike speech. Getting Star

null 4 Aug 20, 2022
A Dart package which supports checking if a current package is up-to-date.

pub_updater A Dart package which enables checking whether packages are up to date and supports updating them. Intended for use in CLIs for prompting u

Very Good Open Source 47 Oct 27, 2022
This is a simple class to package up and send requests to the Kroki.io web service.

Kroki.dart This is a simple class to package up and send requests to the Kroki.io web service. A live editor to editing diagrams that Kroki supports c

Tim Maffett 1 Jun 8, 2022
Contains utility functions and classes in the style of dart:collection to make working with collections easier

The collection package for Dart contains a number of separate libraries with utility functions and classes that makes working with collections easier.

Dart 273 Dec 27, 2022
Quiver is a set of utility libraries for Dart that makes using many Dart libraries easier and more convenient, or adds additional functionality.

Quiver is a set of utility libraries for Dart that makes using many Dart libraries easier and more convenient, or adds additional functionality.

Google 905 Jan 2, 2023
Utilities to make working with 'Duration's easier.

duration Utilities to make working with 'Duration's easier. NOTE: Use prettyDuration, prettySeconds, prettyMilliseconds instead of printDuration, prin

null 45 Sep 21, 2022
Flutter_socks_proxy is a dart package, HTTP/Socks4/Socks5 proxy

flutter_socks_proxy flutter_socks_proxy is a dart package, HTTP/Socks4/Socks5 proxy Usage Use global import 'dart:convert';

null 12 Oct 23, 2022