I have a screen that has to be reloaded (fetch new data from API) every time user navigates to in (even when the user pressed the back button).
I'm working with AsyncValue
and GoRouter
among riverpod
here's the screen code
class BatchScreen extends ConsumerWidget {
static const String screenPath = "/BatchScreen";
const BatchScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<BatchModel?> btachController =
ref.watch(batchControllerProvider);
final AuthRepository authRepository = ref.watch(authRepositoryProvider);
ref.listen<AsyncValue<void>>(
batchControllerProvider,
(_, state) => state.whenOrNull(
error: (error, stackTrace) {
// show snackbar if an error occurred
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
error.toString(),
),
),
);
},
),
);
Size size = MediaQuery.of(context).size;
bool isBegin = false;
return SafeArea(
child: Stack(
children: [
Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(250),
child: CustomeAppBar(
dynamicWidget: Expanded(
flex: 6,
child: UserInfo(
userId: authRepository.userModel.id,
fullName: authRepository.userModel.name,
warehouse: 'casablanca',
),
),
),
),
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: (isBegin)
? MainAxisAlignment.spaceAround
: MainAxisAlignment.center,
children: [
Column(
children: [
(isBegin)
? CustomButton(
ontap: () {
GoRouter.of(context).pushNamed(
APP_PAGE.palettQrCode.toName,
);
},
width: size.width / 1.2,
height: size.height / 12,
text: context.loc.continuee,
colorText: Colors.white,
backgroundColor: colorPrimary,
)
: StartButton(
ontap: () {
bool test = false;
QuickAlert.show(
context: context,
type: QuickAlertType.confirm,
text: 'Do you want to logout',
confirmBtnText: 'Yes',
cancelBtnText: 'No',
confirmBtnColor: Colors.green,
onConfirmBtnTap: () async {
// on error
if (test) {
await QuickAlert.show(
context: context,
type: QuickAlertType.error,
text: 'Please input something',
);
return;
}
Navigator.pop(context);
QuickAlert.show(
context: context,
type: QuickAlertType.loading,
title: 'Loading',
text: 'Fetching your data',
barrierDismissible: false,
);
await Future.delayed(
Duration(milliseconds: 3000),
() async {
if (context.mounted) {
Navigator.pop(context);
GoRouter.of(context).pushNamed(
APP_PAGE.palett.toName,
);
}
},
);
},
);
},
),
const SizedBox(height: 20),
if (isBegin)
CustomButton(
ontap: () {
GoRouter.of(context).pop();
},
width: size.width / 1.2,
height: size.height / 12,
text: context.loc.close,
colorText: Colors.white,
backgroundColor: Colors.black,
borderColor: Colors.black,
),
],
),
if (isBegin)
CustomButton(
ontap: () {
GoRouter.of(context).pop();
},
width: size.width / 1.2,
height: size.height / 12,
text: "Abondon",
colorText: Colors.red,
backgroundColor: Colors.white,
borderColor: Colors.red,
),
],
),
),
),
btachController.when(
data: (data) {
logDebug(data);
return const Positioned(
left: 23,
top: 200,
child: BatchProduct(
countProduct: 29,
),
);
},
error: (error, stackTrace) {
return Positioned(
left: 23,
top: 200,
child: Container(
height: 100,
width: size.width / 1.12,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.4),
spreadRadius: 4,
blurRadius: 5,
),
],
),
child: const Center(
child: Text(
'Uh oh. Something went wrong!',
style: TextStyle(
fontSize: 20,
color: Colors.black,
decoration: TextDecoration.none,
fontFamily: 'almarai',
),
),
),
),
);
},
loading: () {
return Positioned(
left: 23,
top: 200,
child: Container(
height: 100,
width: size.width / 1.12,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.4),
spreadRadius: 4,
blurRadius: 5,
),
],
),
child: const Center(
child: CircularProgressIndicator(),
),
),
);
},
),
],
),
);
}
}
I'm using a controller to handle all the functional work
class BatchController extends StateNotifier<AsyncValue<BatchModel?>> {
final BatchRepository batchRepository;
BatchController({required this.batchRepository})
: super(const AsyncValue.data(null)) {
getBatch();
}
Future<void> getBatch() async {
try {
state = const AsyncValue.loading();
await Future.delayed(Duration(milliseconds: 2000));
final List<BatchModel> bacth = await batchRepository.getBatch();
state = AsyncValue.data(bacth.first);
logDebug('hello');
logDebug(state);
} on DioError catch (e) {
state = AsyncValue.error(
"Uh oh. Something went wrong!",
StackTrace.current,
);
}
}
}
final batchControllerProvider =
StateNotifierProvider<BatchController, AsyncValue<BatchModel?>>(
(ref) {
final batchRepository = ref.read(batchRepositoryProvider);
return BatchController(batchRepository: batchRepository);
},
);
the problem is when the user land on the screen for the first time the API calls and get the data but if the user navigates back or goes to a new screen and comes back to this screen the user gets the same previous data (without recalling API).
the point is I want to make an API call every time the user land on this screen.
The GoRouter
is this:
Inheritance Object > ChangeNotifier > GoRouter
which means it is possible to add the required action via the addListener
method (and remove it via ref.onDispose
when the BatchController
class is no longer in use).
The point is to get an instance of GoRouter
and in the initialization of the class BatchController
add a method in which based on the route will be called to update the state (/data).
For some more information you can go to this repository, it gives you a new look at the Riverpod + GoRouter bundle.