Networking In Flutter : Dio
What is Dio? Why choose Dio over HTTP? How to Perform CRUD operation using Dio? How to Handle Dio Errors easily? - A Complete Beginners Guide

Search for a command to run...
What is Dio? Why choose Dio over HTTP? How to Perform CRUD operation using Dio? How to Handle Dio Errors easily? - A Complete Beginners Guide

Thanks admin,
Bhai Thank you... Great Article.
Nice article! How can I write a Unit test for this kind of architecture,? Thanks
I was looking for something like that!
Very useful!
Thanks a lot!
Thanks a lot. This is very useful. Kindly let us know how we can use provider instead of getit.
Lessons from Code, Content, and Consistency

Reliability basics: Outbox pattern + why it matters

Introducing Kafka/Redpanda + move to event-driven workflow

Connect Orders ↔ Inventory (first working service-to-service flow)

Inventory service + gRPC + proto contracts

dependencies:
dio: ^4.0.6
get_it: ^7.2.0
intl: ^0.17.0
flutter pub get and you are ready to go.

lib > data > network and create a dio_client.dart file in it.Create a Dio instance and assign different parameters inside the constructor as below:
class DioClient {
// dio instance
final Dio _dio;
DioClient(this._dio) {
_dio
..options.baseUrl = Endpoints.baseUrl
..options.connectTimeout = Endpoints.connectionTimeout
..options.receiveTimeout = Endpoints.receiveTimeout
..options.responseType = ResponseType.json
}
}
lib > data > network > api > constant and create a file named endpoints.dartclass Endpoints {
Endpoints._();
// base url
static const String baseUrl = "https://reqres.in/api";
// receiveTimeout
static const int receiveTimeout = 15000;
// connectTimeout
static const int connectionTimeout = 15000;
static const String users = '/users';
}
// Get:-----------------------------------------------------------------------
Future<Response> get(
String url, {
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onReceiveProgress,
}) async {
try {
final Response response = await _dio.get(
url,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onReceiveProgress: onReceiveProgress,
);
return response;
} catch (e) {
rethrow;
}
}
https://abcapi.com/user?name=xyz. Here name is the query parameter and you can pass this parameter in queryParameter in Map format: { 'name' : 'xyz' }We are also catching the error on SocketException, FormatException. We are going to handle different errors further in this article.
Now let's define the remaining 3 methods:
// Post:----------------------------------------------------------------------
Future<Response> post(
String url, {
data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) async {
try {
final Response response = await _dio.post(
url,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
return response;
} catch (e) {
rethrow;
}
}
// Put:-----------------------------------------------------------------------
Future<Response> put(
String url, {
data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) async {
try {
final Response response = await _dio.put(
url,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
return response;
} catch (e) {
rethrow;
}
}
// Delete:--------------------------------------------------------------------
Future<dynamic> delete(
String url, {
data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) async {
try {
final Response response = await _dio.delete(
url,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
return response.data;
} catch (e) {
rethrow;
}
}
class UserModel {
int? id;
String? email;
String? firstName;
String? lastName;
String? avatar;
UserModel({this.id, this.email, this.firstName, this.lastName, this.avatar});
UserModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
email = json['email'];
firstName = json['first_name'];
lastName = json['last_name'];
avatar = json['avatar'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['email'] = email;
data['first_name'] = firstName;
data['last_name'] = lastName;
data['avatar'] = avatar;
return data;
}
}
class NewUser {
String? name;
String? job;
String? id;
String? createdAt;
String? updatedAt;
NewUser({this.name, this.job, this.id, this.createdAt, this.updatedAt});
NewUser.fromJson(Map<String, dynamic> json) {
name = json['name'];
job = json['job'];
id = json['id'];
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['name'] = name;
data['job'] = job;
data['id'] = id;
data['createdAt'] = createdAt;
data['updatedAt'] = updatedAt;
return data;
}
}
lib > data > network > api and create a user folder and inside it create a user_api.dart file.Consider the below code:
class UserApi {
final DioClient dioClient;
UserApi({required this.dioClient});
Future<Response> addUserApi(String name, String job) async {
try {
final Response response = await dioClient.post(
Endpoints.users,
data: {
'name': name,
'job': job,
},
);
return response;
} catch (e) {
rethrow;
}
}
Future<Response> getUsersApi() async {
try {
final Response response = await dioClient.get(Endpoints.users);
return response;
} catch (e) {
rethrow;
}
}
Future<Response> updateUserApi(int id, String name, String job) async {
try {
final Response response = await dioClient.put(
Endpoints.users + '/$id',
data: {
'name': name,
'job': job,
},
);
return response;
} catch (e) {
rethrow;
}
}
Future<void> deleteUserApi(int id) async {
try {
await dioClient.delete(Endpoints.users + '/$id');
} catch (e) {
rethrow;
}
}
}
class UserRepository {
final UserApi userApi;
UserRepository(this.userApi);
Future<List<UserModel>> getUsersRequested() async {
try {
final response = await userApi.getUsersApi();
final users = (response.data['data'] as List)
.map((e) => UserModel.fromJson(e))
.toList();
return users;
} on DioError catch (e) {
final errorMessage = DioExceptions.fromDioError(e).toString();
throw errorMessage;
}
}
Future<NewUser> addNewUserRequested(String name, String job) async {
try {
final response = await userApi.addUserApi(name, job);
return NewUser.fromJson(response.data);
} on DioError catch (e) {
final errorMessage = DioExceptions.fromDioError(e).toString();
throw errorMessage;
}
}
Future<NewUser> updateUserRequested(int id, String name, String job) async {
try {
final response = await userApi.updateUserApi(id, name, job);
return NewUser.fromJson(response.data);
} on DioError catch (e) {
final errorMessage = DioExceptions.fromDioError(e).toString();
throw errorMessage;
}
}
Future<void> deleteNewUserRequested(int id) async {
try {
await userApi.deleteUserApi(id);
} on DioError catch (e) {
final errorMessage = DioExceptions.fromDioError(e).toString();
throw errorMessage;
}
}
}
lib > data > network and create dio_exception.dart class.Paste the below code inside this class
class DioExceptions implements Exception {
late String message;
DioExceptions.fromDioError(DioError dioError) {
switch (dioError.type) {
case DioErrorType.cancel:
message = "Request to API server was cancelled";
break;
case DioErrorType.connectTimeout:
message = "Connection timeout with API server";
break;
case DioErrorType.receiveTimeout:
message = "Receive timeout in connection with API server";
break;
case DioErrorType.response:
message = _handleError(
dioError.response?.statusCode,
dioError.response?.data,
);
break;
case DioErrorType.sendTimeout:
message = "Send timeout in connection with API server";
break;
case DioErrorType.other:
if (dioError.message.contains("SocketException")) {
message = 'No Internet';
break;
}
message = "Unexpected error occurred";
break;
default:
message = "Something went wrong";
break;
}
}
String _handleError(int? statusCode, dynamic error) {
switch (statusCode) {
case 400:
return 'Bad request';
case 401:
return 'Unauthorized';
case 403:
return 'Forbidden';
case 404:
return error['message'];
case 500:
return 'Internal server error';
case 502:
return 'Bad gateway';
default:
return 'Oops something went wrong';
}
}
@override
String toString() => message;
}
final errorMessage = DioExceptions.fromDioError(e).toString();
lib > di > service_locator.dart filefinal getIt = GetIt.instance;
Future<void> setup() async {
getIt.registerSingleton(Dio());
getIt.registerSingleton(DioClient(getIt<Dio>()));
getIt.registerSingleton(UserApi(dioClient: getIt<DioClient>()));
getIt.registerSingleton(UserRepository(getIt.get<UserApi>()));
}

Create a home_page.dart file inside lib > ui > home and paste the below code.
class HomePage extends StatelessWidget {
HomePage({Key? key}) : super(key: key);
final homeController = getIt<HomeController>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const BaseAppBar(),
floatingActionButton: AddUserBtn(),
body: FutureBuilder<List<UserModel>>(
future: homeController.getUsers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
final error = snapshot.error;
return Center(
child: Text(
"Error: " + error.toString(),
),
);
} else if (snapshot.hasData) {
if (snapshot.data!.isEmpty) {
return const Center(
child: Text('No data'),
);
}
return ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: (context, index) {
final user = snapshot.data![index];
return ListTile(
leading: user.avatar != null
? ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.network(
user.avatar!,
width: 50,
height: 50,
),
)
: null,
title: Text(user.email ?? ''),
subtitle: Text(user.firstName ?? ''),
);
},
);
}
return Container();
},
),
);
}
}
controller.dart file inside the same directory and paste the below code.class HomeController {
// --------------- Repository -------------
final userRepository = getIt.get<UserRepository>();
// -------------- Textfield Controller ---------------
final nameController = TextEditingController();
final jobController = TextEditingController();
// -------------- Local Variables ---------------
final List<NewUser> newUsers = [];
// -------------- Methods ---------------
Future<List<UserModel>> getUsers() async {
final users = await userRepository.getUsersRequested();
return users;
}
Future<NewUser> addNewUser() async {
final newlyAddedUser = await userRepository.addNewUserRequested(
nameController.text,
jobController.text,
);
newUsers.add(newlyAddedUser);
return newlyAddedUser;
}
Future<NewUser> updateUser(int id, String name, String job) async {
final updatedUser = await userRepository.updateUserRequested(
id,
name,
job,
);
newUsers[id] = updatedUser;
return updatedUser;
}
Future<void> deleteNewUser(int id) async {
await userRepository.deleteNewUserRequested(id);
newUsers.removeAt(id);
}
}

