We will use Riverpod to fetch data from api or an end point. We will use http get request to accomplish this.
Along the way we will also explain some key properties of Riverpod provider(), futureProvider(), ref.watch() and ref.read(), consumerWidget().
Install the plugins
http: ^0.13.4
flutter_riverpod: ^1.0.3
Since, we are going to fetch data using api from the server, we will create a service class. Create a service class inside the lib folder and name it services.dart and then use the code below
class ApiServices{
String endpoint = 'https://reqres.in/api/users?page=2';
Future<List> getUsers() async {
Response response = await get(Uri.parse(endpoint));
if (response.statusCode == 200){
final List result = jsonDecode(response.body)['data'];
return result.map(((e) => UserModel.fromJson(e))).toList();
}else {
throw Exception(response.reasonPhrase);
}
}
}
final userProvider= Provider((ref)=>ApiServices());
Inside the dart file we created a class name ApiServices. Here we have the end point and a method name getUsers()
Most importantly see the last line. Here we used Provider (it's Riverpod provider) to create a shared state for the class and saved it in a variable name userProvider. Since we created it globally, our app could use this instance from anywhere.
Provider
Our Provider returns a function and the function itself takes a ref object. This ref object is like a context in Riverpod.
〈Simply speaking〉
Provider returns a sharable object by using the given class or object.
Code for UserModel
class UserModel {
final int id;
final String email;
final String firstname;
final String lastname;
final String avatar;
UserModel(
{required this.id,
required this.email,
required this.firstname,
required this.lastname,
required this.avatar});
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
id: json['id'],
email: json['email'],
firstname: json['first_name'] ?? 'First Name',
lastname: json['last_name'] ?? 'Last Name',
avatar: json['avatar'] ??
'https://img.freepik.com/free-vector/illustration-user-avatar-icon_53876-5907.jpg?w=740');
}
}
Since we are creating a network call which is asynchronous in type, we should wrap our userProvider object using FutureProvider.
We use FutureProvider() if we make a network call. It makes sure the data is updated to the UI even if there's a delay.
So create a new dart file name it data_provider.dart and put the code below
final userDataProvider = FutureProvider<List>((ref) async {
return ref.watch(userProvider).getUsers();
});
〈Simply speaking〉
FutureProvider could wrap another Provider and used for network calling
Inside the FutureProvider we used ref.watch() to listen to the changes. If you use ref.read() you won't get the updated value in the UI.
ref.read() only read the value once. I would say it's not reactive enough. If you change your data value it won't read the new value.
ref.watch() always watches on the data changes and let the UI know about the changes. It's very powerful and you can also listen to other providers like userProvider.
Warning: Even if you use ref.read(), you must not use it inside build method()
ProviderScope is the entry point for the Riverpod to be used in an app. So we put in main.dart file.
void main() {
runApp(ProviderScope(child: const MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
ProviderScope also stores other providers inside it, with this it's also possible for us to access the providers in the widget tree or in your UI.
We created my_home_page.dart file in it, we put the code below
class MyHomePage extends ConsumerWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, ref) {
final _data= ref.watch(userDataProvider);
return Scaffold(
appBar: AppBar(
title: const Text("Riverpod"),
),
body: _data.when(
data: (_data){
List userList = _data.map((e) => e).toList();
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
children: [
Expanded(child: ListView.builder(
itemCount: userList.length,
itemBuilder: (_,index){
return InkWell(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DetailScreen(
e: userList[index],
),
),
),
child: Card(
color: Colors.blue,
elevation: 4,
margin: const EdgeInsets.symmetric(vertical: 10),
child: ListTile(
title: Text(userList[index].firstname, style: const TextStyle(
color: Colors.white
),),
subtitle: Text(userList[index].lastname, style: const TextStyle(
color: Colors.white
)),
trailing: CircleAvatar(
backgroundImage: NetworkImage(userList[index].avatar),
),
),
),
);
}))
],
),
);
},
error: (err, s) => Text(err.toString()),
loading: () => const Center(
child: CircularProgressIndicator(),
)),
);
}
}
ConsumerWidget replaces statefulWidget. Since we are using Riverpod, we will use ConsumerWidget instead of statefulWidget.
Because previously we have used FutureProvider to access the resources from the provider, in home page we can access When() function of FutureProvider object. In this case _data is the FutureProvider object.
2024-10-23 14:23:47
well organized content