Effortless API Integration in Flutter with DIO: Everything You Need to Know

Hello Devs, Today, I want to chat with you about Dio, a super powerful networking package used in Flutter for fetching APIs from the server. You might already know that there are other packages available for network calls, but what makes Dio stand out is its extra features like interceptors, the ability to abort and cancel requests, custom adapters, transformers, and more. If you're someone who's shifted from Android to Flutter, you'll find Dio to be quite similar to Retrofit. Let's dive in and learn more about this awesome tool together!

Great, let's work on installing the Dio package on your project together!

Package Installation

There are two to-install packages in Flutter:

  • Using Command

  • Directly add the package to your Pubspec.yaml file

Using Command

Run this command on your vs code terminal:

For Dart:

$ dart pub add dio

For Flutter:

$ flutter pub add dio

Direct add package on your Pubspec.yamp file

dependencies:
  dio: ^5.4.1

Remember to always run this dart pub get command after adding a package to your project. This step is crucial to ensure the smooth and error-free functioning of your project and should not be overlooked.

Awesome! Your Dio package installation is all done now. Let's take the next step and create an init class for Dio where you can easily configure your Dio code.

Dio config class

class DioConfig {
  Dio init() {
     Dio dio = Dio();
     dio.options.baseUrl = 'https://jsonplaceholder.typicode.com'; // Replace with your base URL
     dio.interceptors.add(LogInterceptor(responseBody: true));
     dio.transformer = MyTransformer();
     dio.httpClientAdapter = MyHttpClientAdapter();
    return dio;
  }
}

// Custom transformer
class MyTransformer extends DefaultTransformer {
  @override
  Future<String> transformResponse(RequestOptions options, ResponseBody response) async {
    return response.toString(); // Custom transformation, in this case, just convert response to string
  }
}

// Custom HttpClientAdapter
class MyHttpClientAdapter extends HttpClientAdapter {
  @override
  Future<ResponseBody> fetch(RequestOptions options, Stream<List<int>> requestStream, Future cancelFuture) async {
    final uri = options.uri;
    final client = HttpClient();
    final request = await client.getUrl(uri);

    // Optionally, you can modify the request headers, timeouts, etc. here

    final response = await request.close();
    final responseBody = await response.transform(Utf8Decoder()).join();

    return ResponseBody.fromString(responseBody, 200);
  }

  @override
  void close({bool force = false}) {}
}

The following is a brief overview of the DioConfig class that has been implemented in the code.

  • It contains a method init() that initializes and configures the Dio instance.

  • Inside the method, a new Dio instance is created.

  • The base URL for requests is set using dio.options.baseUrl.

  • An interceptor is added using dio.interceptors.add(LogInterceptor()) to log HTTP request and response data.

  • Custom transformer and HTTP client adapter are set using dio.transformer and dio.httpClientAdapter respectively.

As you see I created one MyTransformer class. The purpose of this class is to allow customization of the response transformation process. Here is a short description of this class

  • Extends DefaultTransformer class provided by Dio.

  • Overrides the transformResponse() method to customize how responses are transformed.

  • In this example, it simply converts the response body to a string.

There is one more class that I created on this code called MyHttpClientAdapter. It helps with handling HTTP requests and responses. Here is a short description of this class

  • Extends HttpClientAdapter provided by Dio.

  • Overrides the fetch() method that is responsible for making HTTP requests.

  • Inside fetch(), it creates a new HttpClient instance, sends the request, and retrieves the response.

  • Optionally, it allows for modification of request headers, timeouts, etc.

  • The response body is decoded into a string using UTF-8 decoding and returned as an ResponseBody object.

Overall, this code demonstrates how to configure Dio with custom behaviour such as logging, transforming responses, and customizing the basic HTTP client adapter.

Let us now proceed to the main class where we can invoke the DioConfig class to initiate API requests. We will be utilizing the DioConfig class in this section to facilitate communication with the API.

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Dio Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late Dio _dio;

  @override
  void initState() {
    super.initState();
    _dio = DioConfig().init();
  }

  @override
  void dispose() {
    _dio.close();
    super.dispose();
  }

  Future<void> fetchData() async {
    try {
      final response = await _dio.get('/posts/1');
      print(response.data);
    } catch (e) {
      print(e.toString());
    }
  }

  void cancelRequest() {
    _dio.clear();
    print('Request canceled.');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Dio Advanced Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: fetchData,
              child: Text('Fetch Data'),
            ),
            ElevatedButton(
              onPressed: cancelRequest,
              child: Text('Cancel Request'),
            ),
          ],
        ),
      ),
    );
  }
}

It's time to provide a concise explanation of this code block. Let me give you a brief description of what it does.

  • Application Setup: The main() function initializes the Flutter application by running MyApp().

  • MyApp Widget: MyApp is a Stateless widget that sets up the basic structure of the application. It uses MaterialApp to define the app's title, theme, and home page.

  • MyHomePage Widget: MyHomePage is a Stateful widget that represents the home page of the application. It contains two main functionalities:

    • Initialization: In the initState() method, Dio is initialized using DioConfig().init(). This configures Dio with base URLs and interceptors.

    • Request Handling: The fetchData() method makes a GET request to '/posts/1' using Dio and prints the response data. The cancelRequest() method cancels any ongoing requests.

  • DioConfig: This class provides a convenient way to initialize Dio with custom configurations such as base URLs, interceptors, etc. It is used in initState() of MyHomePage to set up the Dio instance.

We've wrapped up the blog post now. Just wanted to remind you to aim for clean and organized code when you're working on your projects. Remember, writing good code is key to success!


Connect with Me:

Hey there! If you enjoyed reading this blog and found it informative, why not connect with me on LinkedIn? ๐Ÿ˜Š You can also follow my Instagram page for more mobile development-related content. ๐Ÿ“ฒ๐Ÿ‘จโ€๐Ÿ’ป Letโ€™s stay connected, share knowledge and have some fun in the exciting world of app development! ๐ŸŒŸ

Check out my Instagram page

Check out my LinkedIn

0
Subscribe to my newsletter

Read articles from Mayursinh Parmar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mayursinh Parmar
Mayursinh Parmar

๐Ÿ“ฑMobile App Developer | Android & Flutter ๐ŸŒŸ๐Ÿ’ก Passionate about creating intuitive and engaging apps ๐Ÿ’ญโœจ Letโ€™s shape the future of mobile technology!