- We will use the StateNotifierProvider to manage the state of our application.
- Fetch data from API using Dio.
- Create Our Model
- Create a
modelsfile and create aactivity_model.dartfile. - Create an immutable class
ActivityModelthat 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
repositoriesfile and create aactivity_repository.dartfile. - Create a class named
ActivityRepositorythat must contain- A
Dioinstance - A
Futuremethod namedgetActivity()that must return aActivityModelobject that must return eitherActivityModelorFailure.
- A
- Create a Simple Provider named
activityRepositoryProviderthat must return aActivityRepositoryobject.
- Create a
- Create Our Controller
- Create a
controllersfile and create aactivity_controller.dartfile. - Create a class named
ActivityControllerthat must contain- A
StateNotifiernamedactivityStateNotifierthat must return aActivityModelobject. - A
Futuremethod namedgetActivity()that return nothing or void.
- A
- Create a State Notifier Provider named
activityControllerSNProviderthat must return aActivityModelandActivityControllerobject.
- Create a
- Create Our View
- Create a
viewsfile and create ahome_screen.dartfile. - Create a Consumer State Full Widget named
HomeScreenthat must contain- A
initState()function with bindings.
- A
- Display your data
- Create a
- Create a
modelsfile and create aactivity_model.dartfile. - Create an immutable class
ActivityModelthat 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,
);
}
}@immutableis used to make our class immutable.LoadingStateis an enum that we will already created.LoadingState.progressis the default value of ourloadingStatevariable.- All the variables are optional and have a default value.
factory ModelName.fromJson(Map<String, dynamic> json)is a factory method that will return aModelNameobject.ModelName copyWith({})is a factory method that will return aModelNameobject.factory ModelName.initialState()is a factory method that will return aModelNameobject with the initial values.
- Create a
repositoriesfile and create aactivity_repository.dartfile. - Create a class named
ActivityRepositorythat 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
activityRepositoryProviderthat must return aActivityRepositoryobject.
final activityRepositoryProvider = Provider<ActivityRepository>((ref) => ActivityRepository());NameRepositoryis a class that will contain aDioinstance.NameRepositoryis a class that will contain aFuturemethod nameddoSomething()that will return aNameModelobject or aFailureobject.nameRepositoryProvideris a simple provider that will return aNameRepositoryobject.
- Create a
controllersfile and create aactivity_controller.dartfile. - Create a class named
ActivityControllerthat must contain two things theRefand the_repowhile constructing .
class ActivityController extends StateNotifier<ActivityModel> {
ActivityController(Ref ref)
: _repo = ref.read(activityRepositoryProvider),
super(const ActivityModel());
final ActivityRepository _repo;
}- Create a
Futuremethod 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
activityControllerSNProviderthat must return aActivityModelandActivityControllerobject.
final activityControllerSNProvider = StateNotifierProvider<ActivityController, ActivityModel>(
(ref) => ActivityController(ref),
);NameControlleris a class that will contain aRefand aNameRepositoryinstance.NameControlleris a class that will contain aFuturemethod nameddoSomething()that will return nothing or void.nameControllerSNProvideris a State Notifier Provider that will return aNameModelandNameControllerobject. Example:StateNotifierProvider<NameController, NameModel>ref.read(nameRepositoryProvider)is used to read theNameRepositoryinstance.state = state.copyWith()is used to change the state.state.copyWith()is a method that will return aNameModelobject with the new values.
- Create a
viewsfolder and create ahome_screen.dartfile. - Create a Consumer State Full Widget named
HomeScreenthat 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
),
],
),
),
);
}
}HomeScreenis a Consumer State Full Widget that will contain aRefinstance.initStateis a method that will be called when the widget is created.WidgetsBinding.instance.addPostFrameCallbackis 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.