Networking plays a crucial role in modern app development, allowing us to interact with APIs and fetch data.
In this comprehensive guide, we’ll explore the Dio package, a powerful HTTP client for Dart and Flutter, to perform complete CRUD (Create, Read, Update, Delete) operations. We’ll cover everything you need to know, from installation to advanced usage.
Table of Contents
1. Introduction to Dio
2. Setting Up Dio
3. Creating a Model and Performing CRUD Operations
– Create User
– Read Users
– Update User
– Delete User
4. Best Practices
Below, I provided the complete implementation examples for all CRUD operations using Dio in Flutter.
1. Create User:
import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; class User { final int id; final String name; final String email; User({ required this.id, required this.name, required this.email, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } Map<String, dynamic> toJson() => { 'id': id, 'name': name, 'email': email, }; } class DioClient { final Dio _dio = Dio(); final _baseUrl = 'https://jsonplaceholder.typicode.com'; Future<User> createUser(User user) async { final response = await _dio.post('$_baseUrl/users', data: user.toJson()); return User.fromJson(response.data); } } class CreateUserPage extends StatefulWidget { @override _CreateUserPageState createState() => _CreateUserPageState(); } class _CreateUserPageState extends State<CreateUserPage> { final DioClient _dioClient = DioClient(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _emailController = TextEditingController(); void _createUser() async { final newUser = User( id: 0, // This will be assigned by the API name: _nameController.text, email: _emailController.text, ); try { final createdUser = await _dioClient.createUser(newUser); print('New User Created: ${createdUser.toJson()}'); } catch (e) { print('Error Creating User: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Create User')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _nameController, decoration: InputDecoration(labelText: 'Name'), ), TextField( controller: _emailController, decoration: InputDecoration(labelText: 'Email'), ), ElevatedButton( onPressed: _createUser, child: Text('Create User'), ), ], ), ), ); } }
2. Read Users:
import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; class User { final int id; final String name; final String email; User({ required this.id, required this.name, required this.email, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } } class DioClient { final Dio _dio = Dio(); final _baseUrl = 'https://jsonplaceholder.typicode.com'; Future<List<User>> fetchUsers() async { final response = await _dio.get('$_baseUrl/users'); final userList = (response.data as List) .map((userData) => User.fromJson(userData)) .toList(); return userList; } } class ReadUsersPage extends StatelessWidget { final DioClient _dioClient = DioClient(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Users')), body: FutureBuilder<List<User>>( future: _dioClient.fetchUsers(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return CircularProgressIndicator(); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else if (snapshot.hasData) { final userList = snapshot.data!; return ListView.builder( itemCount: userList.length, itemBuilder: (context, index) { final user = userList[index]; return ListTile( title: Text(user.name), subtitle: Text(user.email), ); }, ); } return Text('No data available'); }, ), ); } }
3. Update User:
import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; class User { final int id; final String name; final String email; User({ required this.id, required this.name, required this.email, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } Map<String, dynamic> toJson() => { 'id': id, 'name': name, 'email': email, }; } class DioClient { final Dio _dio = Dio(); final _baseUrl = 'https://jsonplaceholder.typicode.com'; Future<User> updateUser(User user) async { final response = await _dio.put('$_baseUrl/users/${user.id}', data: user.toJson()); return User.fromJson(response.data); } } class UpdateUserPage extends StatefulWidget { final User user; UpdateUserPage({required this.user}); @override _UpdateUserPageState createState() => _UpdateUserPageState(); } class _UpdateUserPageState extends State<UpdateUserPage> { final DioClient _dioClient = DioClient(); final TextEditingController _nameController = TextEditingController(text: widget.user.name); final TextEditingController _emailController = TextEditingController(text: widget.user.email); void _updateUser() async { final updatedUser = User( id: widget.user.id, name: _nameController.text, email: _emailController.text, ); try { final newUser = await _dioClient.updateUser(updatedUser); print('Updated User: ${newUser.toJson()}'); } catch (e) { print('Error Updating User: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Update User')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _nameController, decoration: InputDecoration(labelText: 'Name'), ), TextField( controller: _emailController, decoration: InputDecoration(labelText: 'Email'), ), ElevatedButton( onPressed: _updateUser, child: Text('Update User'), ), ], ), ), ); } }
4. Delete User:
import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; class User { final int id; final String name; final String email; User({ required this.id, required this.name, required this.email, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } } class DioClient { final Dio _dio = Dio(); final _ baseUrl = 'https://jsonplaceholder.typicode.com'; Future<void> deleteUser(int userId) async { await _dio.delete('$_baseUrl/users/$userId'); } } class DeleteUserPage extends StatelessWidget { final DioClient _dioClient = DioClient(); final User user; DeleteUserPage({required this.user}); void _deleteUser() async { try { await _dioClient.deleteUser(user.id); print('User Deleted'); } catch (e) { print('Error Deleting User: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Delete User')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Are you sure you want to delete ${user.name}?'), SizedBox(height: 20), ElevatedButton( onPressed: _deleteUser, style: ElevatedButton.styleFrom(primary: Colors.red), child: Text('Delete User'), ), ], ), ), ); } }
These are the complete implementation examples for all CRUD operations using Dio in Flutter. You can integrate these examples into your Flutter project and modify them as needed. Remember to customize the API endpoints, UI components, and error handling based on your project’s requirements.
Conclusion
 1. Introduction to Dio
Dio is a versatile HTTP client that simplifies network operations in Flutter. It supports advanced features such as interceptors, request cancellation, timeout handling, and file uploads. Dio’s user-friendly interface and extensive capabilities make it an excellent choice for managing network requests.
2. Setting Up Dio
To get started with Dio, create a new Flutter project and add the Dio package to your `pubspec.yaml` file:
yaml dependencies: dio: ^4.0.0
 3. Creating a Model
Below, I provided you with the complete implementation examples for all CRUD operations using Dio in Flutter.
1. Create User:
import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; class User { final int id; final String name; final String email; User({ required this.id, required this.name, required this.email, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } Map<String, dynamic> toJson() => { 'id': id, 'name': name, 'email': email, }; } class DioClient { final Dio _dio = Dio(); final _baseUrl = 'https://jsonplaceholder.typicode.com'; Future<User> createUser(User user) async { final response = await _dio.post('$_baseUrl/users', data: user.toJson()); return User.fromJson(response.data); } } class CreateUserPage extends StatefulWidget { @override _CreateUserPageState createState() => _CreateUserPageState(); } class _CreateUserPageState extends State<CreateUserPage> { final DioClient _dioClient = DioClient(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _emailController = TextEditingController(); void _createUser() async { final newUser = User( id: 0, // This will be assigned by the API name: _nameController.text, email: _emailController.text, ); try { final createdUser = await _dioClient.createUser(newUser); print('New User Created: ${createdUser.toJson()}'); } catch (e) { print('Error Creating User: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Create User')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _nameController, decoration: InputDecoration(labelText: 'Name'), ), TextField( controller: _emailController, decoration: InputDecoration(labelText: 'Email'), ), ElevatedButton( onPressed: _createUser, child: Text('Create User'), ), ], ), ), ); } }
2. Read Users:
import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; class User { final int id; final String name; final String email; User({ required this.id, required this.name, required this.email, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } } class DioClient { final Dio _dio = Dio(); final _baseUrl = 'https://jsonplaceholder.typicode.com'; Future<List<User>> fetchUsers() async { final response = await _dio.get('$_baseUrl/users'); final userList = (response.data as List) .map((userData) => User.fromJson(userData)) .toList(); return userList; } } class ReadUsersPage extends StatelessWidget { final DioClient _dioClient = DioClient(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Users')), body: FutureBuilder<List<User>>( future: _dioClient.fetchUsers(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return CircularProgressIndicator(); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else if (snapshot.hasData) { final userList = snapshot.data!; return ListView.builder( itemCount: userList.length, itemBuilder: (context, index) { final user = userList[index]; return ListTile( title: Text(user.name), subtitle: Text(user.email), ); }, ); } return Text('No data available'); }, ), ); } }
3. Update User:
import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; class User { final int id; final String name; final String email; User({ required this.id, required this.name, required this.email, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } Map<String, dynamic> toJson() => { 'id': id, 'name': name, 'email': email, }; } class DioClient { final Dio _dio = Dio(); final _baseUrl = 'https://jsonplaceholder.typicode.com'; Future<User> updateUser(User user) async { final response = await _dio.put('$_baseUrl/users/${user.id}', data: user.toJson()); return User.fromJson(response.data); } } class UpdateUserPage extends StatefulWidget { final User user; UpdateUserPage({required this.user}); @override _UpdateUserPageState createState() => _UpdateUserPageState(); } class _UpdateUserPageState extends State<UpdateUserPage> { final DioClient _dioClient = DioClient(); final TextEditingController _nameController = TextEditingController(text: widget.user.name); final TextEditingController _emailController = TextEditingController(text: widget.user.email); void _updateUser() async { final updatedUser = User( id: widget.user.id, name: _nameController.text, email: _emailController.text, ); try { final newUser = await _dioClient.updateUser(updatedUser); print('Updated User: ${newUser.toJson()}'); } catch (e) { print('Error Updating User: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Update User')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _nameController, decoration: InputDecoration(labelText: 'Name'), ), TextField( controller: _emailController, decoration: InputDecoration(labelText: 'Email'), ), ElevatedButton( onPressed: _updateUser, child: Text('Update User'), ), ], ), ), ); } }
4. Delete User:
import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; class User { final int id; final String name; final String email; User({ required this.id, required this.name, required this.email, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } } class DioClient { final Dio _dio = Dio(); final _ baseUrl = 'https://jsonplaceholder.typicode.com'; Future<void> deleteUser(int userId) async { await _dio.delete('$_baseUrl/users/$userId'); } } class DeleteUserPage extends StatelessWidget { final DioClient _dioClient = DioClient(); final User user; DeleteUserPage({required this.user}); void _deleteUser() async { try { await _dioClient.deleteUser(user.id); print('User Deleted'); } catch (e) { print('Error Deleting User: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Delete User')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Are you sure you want to delete ${user.name}?'), SizedBox(height: 20), ElevatedButton( onPressed: _deleteUser, style: ElevatedButton.styleFrom(primary: Colors.red), child: Text('Delete User'), ), ], ), ), ); } }
These are the complete implementation examples for all CRUD operations using Dio in Flutter. You can integrate these examples into your Flutter project and modify them as needed. Remember to customize the API endpoints, UI components, and error handling based on your project’s requirements.
6. Best Practices
– Utilize Dio’s interceptors for logging, error handling, and more.
– Organize network requests using separate methods in the `DioClient` class.
– Handle errors gracefully using `try-catch` blocks.
– Leverage Dio’s features like multipart requests for file uploads.
 7. Conclusion
Dio is a powerful tool that empowers Flutter developers to efficiently manage network operations. By mastering Dio’s capabilities, you can create responsive and reliable apps that interact seamlessly with APIs. This guide has provided a comprehensive overview of using Dio for CRUD operations, enabling you to build robust networking layers for your Flutter projects.
This detailed guide has walked you through the process of using Dio to perform complete CRUD operations in Flutter. By following these steps and best practices, you can confidently manage network requests and create feature-rich applications. If you have any questions or need further assistance, feel free to reach out. Happy coding!