# Generated Code in Flutter ”Retrofit”

## Uploading Images to an API Using Retrofit in Flutter

![Generated Code in Flutter ”Retrofit”](https://miro.medium.com/v2/resize:fit:700/1*Xuvevet2bzbpaVC0Ktu9XQ.jpeg)

Peace upon you,

In this article, we’ve covered how to set up and use the Retrofit package in Flutter to upload an image to an API. With Retrofit’s type-safe HTTP client and Dio’s efficient networking capabilities, uploading images becomes simple and maintainable. The process involves adding dependencies, generating code, and writing the necessary logic to handle image selection and upload.

**retrofit**.**dart** is a type conversion [**dio**](https://github.com/flutterchina/dio/)client generator using [**source\_gen**](https://github.com/dart-lang/source_gen) and inspired by [**Chopper**](https://github.com/lejard-h/chopper)and [**Retrofit**](https://github.com/square/retrofit).

## What is Generated Code in Flutter?

In Flutter (and Dart), **generated code** refers to code that is automatically created by a tool or package based on predefined rules, templates, or annotations. Instead of manually writing all the boilerplate code yourself, certain tools (like `build_runner`, `json_serializable`, and `retrofit_generator`) can generate large portions of your code based on the information you provide through annotations or configuration files.

Generated code can include:

- API clients (like with the `retrofit` package).
- Data models (like with the `json_serializable` package).
- Dependency injection setup.
- Code for state management.

These tools help automate repetitive tasks, allowing developers to focus more on core business logic.

## How Generated Code Works in Flutter

In Flutter, tools like `build_runner` are often used to generate code. For instance, if you use a package like `json_serializable` or `retrofit`, you will annotate your classes or methods, and when you run the code generation command, it will create a file (often ending in `.g.dart`) that contains all the code needed to implement those annotations.

Here’s a simple process for generated code:

1. **Annotation**: You annotate your Dart code with special annotations provided by the package you’re using.
2. **Code Generation**: You run a command like `flutter pub run build_runner build` to generate the necessary code.
3. **Generated Code**: A new file is generated (e.g., `api_client.g.dart`), which contains the implementation based on the annotations and configuration you provided.

## Benefits of Generated Code in Flutter

1. **Reduction of Boilerplate Code**: Generated code helps eliminate the need to write repetitive code, especially for common tasks like network requests, data parsing, and state management.
2. **Improved Productivity**: Since generated code automates routine tasks, developers can spend more time focusing on business logic rather than implementing low-level details like serializing JSON or setting up HTTP clients.
3. **Consistency**: Generated code ensures a consistent structure across your application. This can help in avoiding human errors and maintaining uniformity in your codebase.
4. **Simplified Maintenance**: When your app evolves, and you need to update parts of your code (e.g., adding new API endpoints or modifying models), it’s easy to make changes in your annotations, re-run the generator, and have the required code updated automatically.
5. **Type Safety**: By using generated code with Dart’s strong type system, you can avoid runtime errors and improve the reliability of your code. For example, if the generated code doesn’t match the model or method signatures, you’ll get compile-time errors, helping catch bugs early.
6. **Separation of Concerns**: Code generation allows developers to separate the concern of data modeling and networking logic. Instead of manually handling these concerns in your code, you describe them, and the generator does the rest.

Let do our Example …

## Step 1: Add Dependencies

Add the following dependencies to your `pubspec.yaml` file:

```plaintext
dependencies:
  dio: ^5.0.10
  retrofit: ^4.4.1
  json_annotation: ^4.7.0
  logger: ^1.1.0 # Optional for debugging and logging requests

dev_dependencies:
  retrofit_generator: ^4.4.1
  build_runner: ^2.1.7
```

Run the following command to install the packages:

```plaintext
flutter pub get
```

## Step 2: Define the API Client

We will define the API client using Retrofit annotations. Create a new file named `api_client.dart` and define the interface:

```plaintext
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';

part 'api_client.g.dart';

@RestApi(baseUrl: "https://yourapi.com/")
abstract class ApiClient {
  factory ApiClient(Dio dio, {String baseUrl}) = _ApiClient;

  @POST("/upload")
  @MultiPart()
  Future<void> uploadImage(
    @Part(name: "file") File file,
    @Part(name: "description") String description,
  );
}
```

In this code:

- `@MultiPart()` is used to indicate that this is a multipart request.
- `@Part()` specifies the parts of the request, such as the image file and any other data (e.g., description).

## Step 3: Generate API Client Code

Once you define the API client, use `build_runner` to generate the code that Retrofit uses to interact with the API. Run the following command in your terminal:

```plaintext
flutter pub run build_runner build
```

## Step 4: Initialize Dio and ApiClient

You need to create a `Dio` instance and initialize the `ApiClient` to make API calls. You can do this in your `main.dart` or in a separate service file.

```plaintext
import 'package:dio/dio.dart';
import 'api_client.dart';

void main() {
  final dio = Dio(); // Dio instance with default options
  final apiClient = ApiClient(dio);

  runApp(MyApp());
}
```

## Step 5: Upload Image to API

To upload an image, you will typically get the image from the gallery or camera using the `image_picker` package. Here’s an example function that handles image selection and uploading:

```plaintext
import 'dart:io';
import 'package:image_picker/image_picker.dart';
import 'api_client.dart';

void uploadImageToApi(ApiClient apiClient) async {
  final picker = ImagePicker();
  final XFile? image = await picker.pickImage(source: ImageSource.gallery);

  if (image != null) {
    File file = File(image.path);

    try {
      await apiClient.uploadImage(file, "Sample Image Description");
      print("Image uploaded successfully!");
    } catch (e) {
      print("Error uploading image: $e");
    }
  } else {
    print("No image selected");
  }
}
```

In this example:

- We use the `ImagePicker` to allow the user to select an image from their gallery.
- The selected image is converted to a `File` and uploaded to the API using the `uploadImage()` method.
- The `description` is sent along with the file, but it can be replaced or removed if not neede

## Step 6: Handle API Response

You can handle responses or errors more effectively by wrapping the upload call in a `try-catch` block and handling different status codes from the API:

```plaintext
void uploadImageToApi(ApiClient apiClient) async {
  try {
    final picker = ImagePicker();
    final XFile? image = await picker.pickImage(source: ImageSource.gallery);

    if (image != null) {
      File file = File(image.path);

      // Attempt to upload the image
      await apiClient.uploadImage(file, "Image Upload");

      print("Image uploaded successfully!");
    } else {
      print("No image selected.");
    }
  } catch (DioError e) {
    if (e.response != null) {
      print("Error uploading image: ${e.response?.statusCode} ${e.response?.data}");
    } else {
      print("Error: ${e.message}");
    }
  }
}
```

In this case, `DioError` is caught if the upload fails, allowing you to check the response status and log any errors.

## Conclusion

Using Retrofit in Flutter makes networking and API interactions easier to manage, especially for multipart requests like image uploads. In this article, we’ve walked through how to set up Retrofit `^4.4.1` with Dio, define your API client, and upload images to an API.

With this setup, you can now extend the code to support more complex API operations, such as uploading additional fields, handling errors, and more. Happy coding!

## References

<https://www.youtube.com/watch?v=ViqtHHLfN94>

---
Originally published on [Medium](https://ms3byoussef.medium.com/generated-code-in-flutter-731df46352ae) on September 30, 2024.
