I have an app where on a view I allow users to perform a booking.
The page first load and I use a Riverpod provider to get the future booking dates:
@riverpod
Future<BookDatesState> futureBookingDates(Ref ref) async ...
Then I defined a set of states:
sealed class BookDatesState {
const BookDatesState();
}
class BookDatesLoadingState extends BookDatesState {
const BookDatesLoadingState();
}
class BookDatesErrorState extends BookDatesState {
final String message;
const BookDatesErrorState({required this.message});
}
class BookDatesNoInternetState extends BookDatesState {
final String message;
const BookDatesNoInternetState({required this.message});
}
class BookDatesLoadedState extends BookDatesState {
final List<DateTime> dates;
const BookDatesLoadedState({required this.dates});
}
Which I then use in my view to observe and display views:
final bookDatesUi = switch (bookDatesState) {
BookDatesLoadingState() => const Center(
child: Padding(
padding: EdgeInsets.all(21.0),
child: LoadingView(),
),
),
BookDatesErrorState() => ErrorView(
message: bookDatesState.message,
showErrorImage: true,
),
BookDatesNoInternetState() => ErrorView(
message: bookDatesState.message,
showNoInternetImage: true,
),
BookDatesLoadedState() => BookingDatesView(
bookDates: bookDatesState.dates,
selectedDate: chosenDate,
onDateSelected: (date) {
// Reset the time when date is selected
ref.read(chosenTimeProvider.notifier).set(null);
// Set the date selected
ref.read(chosenDateProvider.notifier).set(date);
// Load the dates
ref.read(availableTimeSlotsProvider.notifier).load(
service.merchantId,
date,
);
},
),
};
final bookDatesState = ref.watch(futureBookingDatesProvider).when(
data: (state) => state,
error: (error, stack) =>
BookDatesErrorState(message: error.toString()),
loading: () => const BookDatesLoadingState(),
);
Now a list of dates is shown on screen. When the user selects a date, I then use a Notifier riverpod class to get the available list of time slots:
@riverpod
class AvailableTimeSlots extends _$AvailableTimeSlots ...
I then make use of another set of states for the slots:
sealed class SlotsState {
const SlotsState();
}
class SlotsInitialState extends SlotsState {
const SlotsInitialState();
}
class SlotsLoadingState extends SlotsState {
const SlotsLoadingState();
}
class SlotsErrorState extends SlotsState {
final String message;
const SlotsErrorState({required this.message});
}
class SlotsEmptyState extends SlotsState {
const SlotsEmptyState();
}
class SlotsNoInternetState extends SlotsState {
final String message;
const SlotsNoInternetState({required this.message});
}
class SlotsLoadedState extends SlotsState {
final DateTime date;
final List<TimeOfDay> slots;
const SlotsLoadedState({required this.slots, required this.date});
}
And display the view on my screen:
final slotsState = ref.watch(availableTimeSlotsProvider).when(
data: (state) => state,
error: (error, stack) => SlotsErrorState(message: error.toString()),
loading: () => const SlotsLoadingState(),
);
// Get the slots ui
final slotsUi = switch (slotsState) {
SlotsInitialState() => const SlotsViewInitial(),
SlotsLoadingState() => const Center(
child: Padding(
padding: EdgeInsets.all(21.0),
child: LoadingView(),
),
),
SlotsEmptyState() => const SlotsViewEmpty(),
SlotsErrorState() => ErrorView(
message: slotsState.message,
showErrorImage: true,
),
SlotsNoInternetState() => ErrorView(
message: slotsState.message,
showNoInternetImage: true,
),
SlotsLoadedState() => SlotsViewLoaded(
slots: slotsState.slots,
chosenTime: chosenTime,
onTimeSelected: (TimeOfDay time) {
ref.read(chosenTimeProvider.notifier).set(time);
},
),
};
I make use of different views because I don't want the whole screen to reload when the user selects a date, I want just the time slots view to reload.
Now I have other Riverpod providers just for this specific page which is based on the user input:
@riverpod
class ChosenDate extends _$ChosenDate {
@override
DateTime? build() => null;
void set(DateTime? date) {
state = date;
}
}
@riverpod
class ChosenTime extends _$ChosenTime {
@override
TimeOfDay? build() => null;
void set(TimeOfDay? time) {
state = time;
}
}
@riverpod
class ChosenFromHome extends _$ChosenFromHome {
@override
bool build() => false;
void update(bool selected) {
state = selected;
}
}
Instead of having different Riverpod notifiers and providers, I want to have a single main Notifier class and then have different methods in it which follows more the MVVM design structure while still keeping the same flow of my app (when a user selects a date, only the time part should reload and so on).
Does anyone have any idea on how I can achieve this ?
I have an app where on a view I allow users to perform a booking.
The page first load and I use a Riverpod provider to get the future booking dates:
@riverpod
Future<BookDatesState> futureBookingDates(Ref ref) async ...
Then I defined a set of states:
sealed class BookDatesState {
const BookDatesState();
}
class BookDatesLoadingState extends BookDatesState {
const BookDatesLoadingState();
}
class BookDatesErrorState extends BookDatesState {
final String message;
const BookDatesErrorState({required this.message});
}
class BookDatesNoInternetState extends BookDatesState {
final String message;
const BookDatesNoInternetState({required this.message});
}
class BookDatesLoadedState extends BookDatesState {
final List<DateTime> dates;
const BookDatesLoadedState({required this.dates});
}
Which I then use in my view to observe and display views:
final bookDatesUi = switch (bookDatesState) {
BookDatesLoadingState() => const Center(
child: Padding(
padding: EdgeInsets.all(21.0),
child: LoadingView(),
),
),
BookDatesErrorState() => ErrorView(
message: bookDatesState.message,
showErrorImage: true,
),
BookDatesNoInternetState() => ErrorView(
message: bookDatesState.message,
showNoInternetImage: true,
),
BookDatesLoadedState() => BookingDatesView(
bookDates: bookDatesState.dates,
selectedDate: chosenDate,
onDateSelected: (date) {
// Reset the time when date is selected
ref.read(chosenTimeProvider.notifier).set(null);
// Set the date selected
ref.read(chosenDateProvider.notifier).set(date);
// Load the dates
ref.read(availableTimeSlotsProvider.notifier).load(
service.merchantId,
date,
);
},
),
};
final bookDatesState = ref.watch(futureBookingDatesProvider).when(
data: (state) => state,
error: (error, stack) =>
BookDatesErrorState(message: error.toString()),
loading: () => const BookDatesLoadingState(),
);
Now a list of dates is shown on screen. When the user selects a date, I then use a Notifier riverpod class to get the available list of time slots:
@riverpod
class AvailableTimeSlots extends _$AvailableTimeSlots ...
I then make use of another set of states for the slots:
sealed class SlotsState {
const SlotsState();
}
class SlotsInitialState extends SlotsState {
const SlotsInitialState();
}
class SlotsLoadingState extends SlotsState {
const SlotsLoadingState();
}
class SlotsErrorState extends SlotsState {
final String message;
const SlotsErrorState({required this.message});
}
class SlotsEmptyState extends SlotsState {
const SlotsEmptyState();
}
class SlotsNoInternetState extends SlotsState {
final String message;
const SlotsNoInternetState({required this.message});
}
class SlotsLoadedState extends SlotsState {
final DateTime date;
final List<TimeOfDay> slots;
const SlotsLoadedState({required this.slots, required this.date});
}
And display the view on my screen:
final slotsState = ref.watch(availableTimeSlotsProvider).when(
data: (state) => state,
error: (error, stack) => SlotsErrorState(message: error.toString()),
loading: () => const SlotsLoadingState(),
);
// Get the slots ui
final slotsUi = switch (slotsState) {
SlotsInitialState() => const SlotsViewInitial(),
SlotsLoadingState() => const Center(
child: Padding(
padding: EdgeInsets.all(21.0),
child: LoadingView(),
),
),
SlotsEmptyState() => const SlotsViewEmpty(),
SlotsErrorState() => ErrorView(
message: slotsState.message,
showErrorImage: true,
),
SlotsNoInternetState() => ErrorView(
message: slotsState.message,
showNoInternetImage: true,
),
SlotsLoadedState() => SlotsViewLoaded(
slots: slotsState.slots,
chosenTime: chosenTime,
onTimeSelected: (TimeOfDay time) {
ref.read(chosenTimeProvider.notifier).set(time);
},
),
};
I make use of different views because I don't want the whole screen to reload when the user selects a date, I want just the time slots view to reload.
Now I have other Riverpod providers just for this specific page which is based on the user input:
@riverpod
class ChosenDate extends _$ChosenDate {
@override
DateTime? build() => null;
void set(DateTime? date) {
state = date;
}
}
@riverpod
class ChosenTime extends _$ChosenTime {
@override
TimeOfDay? build() => null;
void set(TimeOfDay? time) {
state = time;
}
}
@riverpod
class ChosenFromHome extends _$ChosenFromHome {
@override
bool build() => false;
void update(bool selected) {
state = selected;
}
}
Instead of having different Riverpod notifiers and providers, I want to have a single main Notifier class and then have different methods in it which follows more the MVVM design structure while still keeping the same flow of my app (when a user selects a date, only the time part should reload and so on).
Does anyone have any idea on how I can achieve this ?
Share Improve this question asked yesterday Mervin HemarajuMervin Hemaraju 2,1273 gold badges34 silver badges87 bronze badges1 Answer
Reset to default 0Can you define one common ChosenDate
model with DateTime? date
, TimeOfDay? time
and bool fromHomeselected
fields, in which be sure to make the correct hashCode/==
. Place this state in the ChosenDateNotifier
.
Then, in each particular Consumer
widget, use select
to listen for only the desired change:
TimeOfDay? time = ref.watch(chosenDateNotifierProvider.select((model) => model.time));
Read more:
- Optimizing performance | Riverpod