Networking in Flutter using Dio
Handling network requests is a critical aspect of application development, and it is essential to handle unexpected results gracefully to ensure a good user experience.
In this article, we will explore how to use the Dio package to handle REST API requests in Flutter.
Dio is a powerful HTTP client for Dart that provides an intuitive API for performing advanced network tasks with ease.
It offers support for interceptors, global configuration, FormData, request cancellation, file downloading, and timeout, among other features.
While Flutter's built-in http package is suitable for performing basic network tasks, it can be challenging to use when handling more advanced features.
Therefore, Dio is an excellent choice for developers who want to perform advanced network tasks with ease. With its extensive documentation and active community support, developers can handle network requests in Flutter without difficulty.
In summary, Dio is a valuable package that simplifies the process of handling network requests in Flutter. By using its intuitive API, developers can easily handle complex network tasks and ensure that their applications provide an excellent user experience.
Here's a step-by-step tutorial on how to use Dio, MVVM architecture, Provider with Consumer, and ListView to fetch and display data from the JSONPlaceholder API in Flutter:
Step 1: Add dependencies
First, you need to add the Dio and Provider packages to your Flutter project by adding the following lines to your pubspec.yaml file:
dio: ^5.0.3
provider: ^6.0.5
logger: ^1.3.0
Then, run flutter pub get in your terminal to install these packages.
..
Step 2: Define the User model
Next, define a User class that will represent the data that you will fetch from the JSONPlaceholder API. You can create a new file called model / user_model.dart and add the following code:
// Step 1: Create a data model for the user object
class User {
final int id; // User ID
final String name; // User name
final String email; // User email address
User({required this.id, required this.name, required this.email});
// Factory method to create a User object from a JSON map
factory User.fromJson(Map json) {
return User(
id: json['id'], // Extract the ID from the JSON map
name: json['name'], // Extract the name from the JSON map
email: json['email'], // Extract the email address from the JSON map
);
}
}
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import '../model/user.dart';
import 'logger_interceptor.dart';
// Step 2: Create a repository that handles fetching the data from the API
class UserRepository {
// Create a Dio instance with base options and logger interceptor
final Dio _dio = Dio(BaseOptions(
baseUrl: 'https://jsonplaceholder.typicode.com',
connectTimeout: const Duration(seconds:5),
receiveTimeout: const Duration(seconds: 3),
responseType: ResponseType.json,
))..interceptors.addAll([
//LoggerInterceptor()
]);
// This method makes a GET request to the '/users' endpoint of the API
// and returns a list of User objects parsed from the response
Future> getUsers() async {
try {
final response = await _dio.get('/users');
final data = response.data as List;
final users = data.map((json) => User.fromJson(json)).toList();
return users;
} catch (e) {
// If the request fails, throw an exception with a message
throw Exception('Failed to load users');
}
}
}
// Step 3: Create a view model that uses the repository to get the data
import 'package:flutter/cupertino.dart';
import '../controller/api_service.dart';
import '../model/user.dart';
class UserViewModel extends ChangeNotifier {
final UserRepository userRepository; // Declare a final UserRepository object
UserViewModel({required this.userRepository}); // Constructor to initialize the UserRepository object
List _users = []; // List of User objects
bool _loading = false; // Boolean flag to track if data is loading or not
String _errorMessage = ''; // String to hold error message if any
List get users => _users; // Getter method to get the list of users
bool get loading => _loading; // Getter method to get the loading flag
String get errorMessage => _errorMessage; // Getter method to get the error message
Future fetchUsers() async { // Async method to fetch users from repository
_loading = true; // Set loading flag to true
try {
_users = await userRepository.getUsers(); // Call getUsers() method from UserRepository and assign the returned value to _users list
} catch (e) { // Catch any exceptions
_errorMessage = e.toString(); // Set the error message string to the exception string
} finally {
_loading = false; // Set loading flag to false
notifyListeners(); // Notify listeners that the data has changed
}
}
}
// Step 5: Create the view
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../view_model.dart/user_view_model.dart';
class UserList extends StatefulWidget {
const UserList({super.key}); // A stateful widget to display the users
@override
State createState() => _UserListState(); // Returns the state of the widget
}
class _UserListState extends State {
@override
void initState() { // Called when the state object is inserted into the tree.
super.initState();
final userViewModel = Provider.of(context, listen: false); // Fetches userViewModel object
userViewModel.fetchUsers(); // Calls the method to fetch the users
}
@override
Widget build(BuildContext context) { // Build method which returns the UI
return Scaffold(
appBar: AppBar(
title: const Text('Users'),
),
body: Consumer( // Consumer widget to listen for changes in UserViewModel
builder: (context, userViewModel, child) {
if (userViewModel.loading) { // If data is still loading, show a progress indicator
return const Center(
child: CircularProgressIndicator(),
);
} else if (userViewModel.errorMessage.isNotEmpty) { // If there is an error, show the error message
return Center(
child: Text(userViewModel.errorMessage),
);
} else { // Otherwise, show the list of users
return ListView.builder(
itemCount: userViewModel.users.length,
itemBuilder: (context, index) {
final user = userViewModel.users[index];
return ListTile(
title: Text(user.name),
subtitle: Text(user.email),
leading: CircleAvatar(
child: Text(user.id.toString()),
),
);
},
);
}
},
),
);
}
}
import 'package:dio_api/view/user_list.dart'; import 'package:dio_api/view_model.dart/user_view_model.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'controller/api_service.dart'; // Main function to run the app F
uturemain() async {
// Create a multi-provider for the app and provide UserViewModel
runApp(
MultiProvider(
providers: [
// Pass in the UserRepository instance as an argument
ChangeNotifierProvider(create: (_) => UserViewModel(userRepository: UserRepository())),
],
child: const MyApp(),
));
}
// MyApp widget to build the app
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State createState() => _MyAppState();
}
// State for MyApp widget
class _MyAppState extends State {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'DIO api',
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.red,
),
// Set the home screen to the UserList screen
home:
const Scaffold(
body: UserList(),
),
);
}
}
Comments
Post a Comment