- We will use the StateNotifierProvider to manage the state of our application.
- Fetch data from API using Dio.
- Create Our Model
- Create a
models
file and create aactivity_model.dart
file. - Create an immutable class
ActivityModel
that must contain- All variables that we need to fetch from API.
- Constructor Without Parameters
.fromJson()
factory method.copyWith()
factory method.initialState()
factory method Not much needed
- Create a
- Create Our Repository
- Create a
repositories
file and create aactivity_repository.dart
file. - Create a class named
ActivityRepository
that must contain- A
Dio
instance - A
Future
method namedgetActivity()
that must return aActivityModel
object that must return eitherActivityModel
orFailure
.
- A
- Create a Simple Provider named
activityRepositoryProvider
that must return aActivityRepository
object.
- Create a
- Create Our Controller
- Create a
controllers
file and create aactivity_controller.dart
file. - Create a class named
ActivityController
that must contain- A
StateNotifier
namedactivityStateNotifier
that must return aActivityModel
object. - A
Future
method namedgetActivity()
that return nothing or void.
- A
- Create a State Notifier Provider named
activityControllerSNProvider
that must return aActivityModel
andActivityController
object.
- Create a
- Create Our View
- Create a
views
file and create ahome_screen.dart
file. - Create a Consumer State Full Widget named
HomeScreen
that must contain- A
initState()
function with bindings.
- A
- Display your data
- Create a
- Create a
models
file and create aactivity_model.dart
file. - Create an immutable class
ActivityModel
that must contain.
@immutable
class ActivityModel {
final String activity;
final String type;
final int participants;
final LoadingState loadingState;
const ActivityModel({
this.activity = '',
this.type = '',
this.participants = 0,
this.loadingState = LoadingState.progress,
});
factory ActivityModel.fromJson(Map<String, dynamic> json) {
return ActivityModel(
activity: json['activity'],
type: json['type'],
participants: json['participants'],
);
}
ActivityModel copyWith({
String? activity,
String? type,
int? participants,
LoadingState? loadingState,
}) {
return ActivityModel(
activity: activity ?? this.activity,
type: type ?? this.type,
participants: participants ?? this.participants,
loadingState: loadingState ?? this.loadingState,
);
}
factory ActivityModel.initialState() {
return const ActivityModel(
activity: '',
type: '',
participants: 0,
loadingState: LoadingState.progress,
);
}
}
@immutable
is used to make our class immutable.LoadingState
is an enum that we will already created.LoadingState.progress
is the default value of ourloadingState
variable.- All the variables are optional and have a default value.
factory ModelName.fromJson(Map<String, dynamic> json)
is a factory method that will return aModelName
object.ModelName copyWith({})
is a factory method that will return aModelName
object.factory ModelName.initialState()
is a factory method that will return aModelName
object with the initial values.
- Create a
repositories
file and create aactivity_repository.dart
file. - Create a class named
ActivityRepository
that must contain.
class ActivityRepository {
final Dio _dio;
ActivityRepository(this._dio);
Future<Either<Failure, ActivityModel>> getActivity() async {
try {
final response = await _dio.get('http://www.boredapi.com/api/activity');
final activity = ActivityModel.fromJson(response.data);
return Right(activity);
} on DioError catch (e) {
return Left(Failure(e.message));
}
}
}
- Create a Simple Provider named
activityRepositoryProvider
that must return aActivityRepository
object.
final activityRepositoryProvider = Provider<ActivityRepository>((ref) => ActivityRepository());
NameRepository
is a class that will contain aDio
instance.NameRepository
is a class that will contain aFuture
method nameddoSomething()
that will return aNameModel
object or aFailure
object.nameRepositoryProvider
is a simple provider that will return aNameRepository
object.
- Create a
controllers
file and create aactivity_controller.dart
file. - Create a class named
ActivityController
that must contain two things theRef
and the_repo
while constructing .
class ActivityController extends StateNotifier<ActivityModel> {
ActivityController(Ref ref)
: _repo = ref.read(activityRepositoryProvider),
super(const ActivityModel());
final ActivityRepository _repo;
}
- Create a
Future
method namedgetActivity()
that return nothing or void.
Future<void> getActivity() async {
state = state.copyWith(
loadingState: LoadingState.progress,
);
final result = await _repo.getActivity();
result.fold(
(failure) => state = state.copyWith(
loadingState: LoadingState.error,
activity: failure.message,
),
(response) => state = state.copyWith(
loadingState: LoadingState.success,
activity: response.activity,
type: response.type,
participants: response.participants,
),
);
}
All you need to do to change the state is to use the state
variable and the copyWith()
method.
- Create a State Notifier Provider named
activityControllerSNProvider
that must return aActivityModel
andActivityController
object.
final activityControllerSNProvider = StateNotifierProvider<ActivityController, ActivityModel>(
(ref) => ActivityController(ref),
);
NameController
is a class that will contain aRef
and aNameRepository
instance.NameController
is a class that will contain aFuture
method nameddoSomething()
that will return nothing or void.nameControllerSNProvider
is a State Notifier Provider that will return aNameModel
andNameController
object. Example:StateNotifierProvider<NameController, NameModel>
ref.read(nameRepositoryProvider)
is used to read theNameRepository
instance.state = state.copyWith()
is used to change the state.state.copyWith()
is a method that will return aNameModel
object with the new values.
- Create a
views
folder and create ahome_screen.dart
file. - Create a Consumer State Full Widget named
HomeScreen
that must contain.
class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
@override
ConsumerState<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends ConsumerState<HomeScreen> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(activityControllerSNProvider.notifier).getActivity();
});
}
@override
Widget build(BuildContext context) {
final response = ref.watch(activityControllerSNProvider);
return Scaffold(
body: response.loadingState == LoadingState.progress
? const Center(
child: CircularProgressIndicator(),
)
: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
response.activity, // There will be the activity
),
],
),
),
);
}
}
HomeScreen
is a Consumer State Full Widget that will contain aRef
instance.initState
is a method that will be called when the widget is created.WidgetsBinding.instance.addPostFrameCallback
is a method that will be called after the widget is created. will ensure that there is no problems in the widget treeref.read(nameControllerSNProvider.notifier).doSomething()
is the way to access our methods in the controller.ref.watch(activityControllerSNProvider)
is the way to access our state (values) in the controller.