How to close 1 dialog box after another appears in flutter? - flutter

https://imgur.com/a/MZrqkJy
In the above video I am redeeeming the offer and then I am getting a confirmation dialog box that its successful. After 2 seconds the confirmation DB pops and then screen is refreshed. But I want the initial DB to pop as well after Confir. DB disappears. I am not able to do this. This is the code snippet:
showDialog(
context: context,
builder: (BuildContext context) {
_timer = Timer(Duration(seconds: 2), () {
Navigator.of(context).pop();
});
return AlertDialog(
content: Text(redemeResponseBody),
);
}).then((value) {
if (_timer.isActive) {
_timer.cancel();
}
Future.delayed(Duration(seconds: 2), () {
setState(() {
didRedeem = true;
});
Navigator.of(context).pop();
Get.to(() => OffersScreenPage(getIndex: 1));
});
});
Any idea? what's wrong

I'd suggest reading the Navigator API documentation.. The popUntil() method seems to be what you're after.

Related

How distinguish inner context?

I wrote the code like data: (data){Navigator.of(context).pop();},//here to pop showDialog but actually other Navigator.of(context) was popped and figured out that I should distinguish inner context in builder: (context) { //here2 from outer context but could not reach how to do that. Would somebody please give me some advise to solve this ?
final authC = ref.watch(authFutureProvider);
authC.when(
data: (data){Navigator.of(context).pop();},//here
error: (error, stackTrace) {},
loading: () {
final loading =
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) { //here2
return const Center(
child: CircularProgressIndicator(),
);
}
);
});
},
);
Well, you can easily define different BuildContexts by giving them different names, like this:
#override
Widget build(BuildContext parentContext) {
final authC = ref.watch(authFutureProvider);
authC.when(
data: (data){Navigator.of(parentContext).pop();},//here
error: (error, stackTrace) {},
loading: () {
final loading =
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
barrierDismissible: false,
context: parentContext,
builder: (context) { //here2
return const Center(
child: CircularProgressIndicator(),
);
}
);
});
},
);
return Widget();
}
However, that won't change the fact that the context I've named "parentContext" is the parent context! This is the context sent TO the Dialog, not the context of the Dialog itself.
If you want to pop whichever context is on top at the moment, you can define a GlobalKey variable like this:
//global.dart
import 'package:flutter/material.dart';
class GlobalVariable {
static final GlobalKey<NavigatorState> navState = GlobalKey<NavigatorState>();
}
Then, you can pop the current context like this, from anywhere:
import 'global.dart';
void func(){
Navigator.pop(GlobalVariable.navState.currentContext!);
}
That should pop the Dialog IF the Dialog is currently on top...
But of course, if some other BuildContext is on top, then that will be popped, instead. 😏 So make sure you write the proper conditions for that.
Edit:
You could also use popUntil(). If this happens to be the first screen, you can just write:
#override
Widget build(BuildContext parentContext) {
final authC = ref.watch(authFutureProvider);
authC.when(
data: (data){Navigator.popUntil(context, (route) => route.isFirst);},//here
error: (error, stackTrace) {},
loading: () {
final loading =
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
barrierDismissible: false,
context: parentContext,
builder: (context) { //here2
return const Center(
child: CircularProgressIndicator(),
);
}
);
});
},
);
return Widget();
}
If not, then it's a bit more complicated to make it pop back to this particular screen... But you could use routeSettings, for example.
I strongly not recommend WidgetsBinding.instance.addPostFrameCallback to show loading because it takes time to render, so your app will load a loading function. It will make End-user can do multiple clicks on the button because the loading has not rendered yet.
this package is verylight and easy to use, the dismiss and show even different from regular widget, so you will not accidentally close your screen because wrong logic or back-end handler failure.
class LoadingExample {
showLoadingExample(){
EasyLoading.instance
..displayDuration = const Duration(milliseconds: 2000)
..indicatorType = EasyLoadingIndicatorType.fadingCircle
..loadingStyle = EasyLoadingStyle.dark
..indicatorSize = 45.0
..radius = 10.0
..progressColor = Colors.yellow
..backgroundColor = Colors.green
..indicatorColor = Colors.yellow
..textColor = Colors.yellow
..maskColor = Colors.blue.withOpacity(0.5)
..userInteractions = true
..dismissOnTap = false
..customAnimation = CustomAnimation();
}
dismissExampleLoading(){
EasyLoading.dismiss();
}
}
to call it simply do: *I recommend OOP so you can modify it in 1 place when you need.
LoadingExample().showLoadingExample();
or
LoadingExample().dismissExampleLoading();
even you accidentally call show or dissmiss multiple time, the loading will not stack, and it will just recall or reset the animation. So your app will not crash
Flutter_easyloading

Snackbar won't show when included in .then

So I have reusable snackbar on my code and I wanted to show my snackbar when user already pop from certain page. I tried to put my snackbar inside .then but It wont show anything. Any idea how to fix this ?
Here's my code:
void openChangeClientPage(BuildContext context, user){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddClientPage(
clientId: user!.uid,
clientNickname: Provider.of<UserDataProvider>(context, listen: false).userNickname,
clientProfileLink: Provider.of<UserDataProvider>(context, listen: false).userProfilePicture ?? '',
ticketData: ticketData,
ticketId: ticketId,
source: 'ticketDetail',
),
),
).then((value) {
if (value) {
ReusableSnackBar.buildSnackBar(context, 'New Client has been assigned');
Navigator.pop(context);
} else {
changeInvolvedState(true);
}
});
}
I think you can wrap the ReusableSnackBar into the Build method because snackbar tries to find the BuildContext closet but it is now in the Then method. You can read more in this
I think the code will be this:
void openChangeClientPage(BuildContext context, user){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddClientPage(
clientId: user!.uid,
clientNickname: Provider.of<UserDataProvider>(context, listen: false).userNickname,
clientProfileLink: Provider.of<UserDataProvider>(context, listen: false).userProfilePicture ?? '',
ticketData: ticketData,
ticketId: ticketId,
source: 'ticketDetail',
),
),
).then((value) {
if (value) {
Builder(builder: (context)=>ReusableSnackBar.buildSnackBar(context, 'New Client has been assigned'));
Navigator.pop(context);
} else {
changeInvolvedState(true);
}
});}
The issue is that you are calling Navigator.pop immediately after calling ReusableSnackBar. This will cause the SnackBar to pop too because you can assume the snackbar is like a pop-up/dialog and is immediately dismissed.
You can call Navigator.pop before displaying the snackbar.
You can also check out this answer if you want to show snackbar without context.
Firstly, make sure condition ("value" variable) is true.
Remove this line in your code:
Navigator.pop(context);
This code will close current screen, not dismiss snack bar.
If you want to dismiss snack bar, use:
ScaffoldMessenger.of(context).hideCurrentSnackBar();

Flutter - How to pause qr scan reading

I'm developing a qr scan app. And using qr_code_scanner 0.5.2.
When i scan a qr code, my app jump to another page with the scanned code, but the app still scanning the qr codes and new pages appears until you d not focus on any qr code.
How can i scan codes one by one and pause scanning while on the another page and resume scanning when the new page is closed?
Thanks
code:
void _onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
});
controller.scannedDataStream.listen((scanData) {
setState(() {
controller.pauseCamera();
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return ReadData(data: scanData.code);
}),
);
controller.resumeCamera();
});
});
}
edit:
solution code:
void _onQRViewCreated(QRViewController controller) {
this.controller = controller;
controller.scannedDataStream.listen((scanData) async {
controller.pauseCamera();
await Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ReadData(data: scanData.code);
}),
);
controller.resumeCamera();
});
}
qr_code_scanner controller got a method called pauseCamera use that to pause camera whenever a valid barcode is detected.
edit: if pauseCamera is not working then you can add boolean variable and switch it to true whenever you get valid QR code. And, navigate to new page only if this variable is false.
I've updated the code, Please take a look.
...
bool gotValidQR = false; //toggle this value when you got a new qr code
...
controller.scannedDataStream.listen((scanData)async {
if(gotValidQR) {
return;
}
gotValidQR = true;
dynamic pop = await Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ScannedData(data: scanData.code);
}),
);
gotValidQR = false;
});
...

refresh previous page on Navigator.pop function

Is there any way to refresh the previous page/ stack when Navigator.pop(context) is called? I need to do the API calling of the previous page to refresh the state of the page. The navigator.pop will sometimes be an alert dialog or maybe a back button. Is there a way to do the API calling? ThankYou.
use the then function after you push another route.
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => MyHomePage(),
),
).then(
(value) {
if (value) {
//refresh here
}
},
);
and when you return to previous screen, provide the pop function a value that determines whether to perform some action.
Navigator.of(context).pop(true);
Previous page
void goToNextPage()async {
var refresh = await Navigator.push(_context, new MaterialPageRoute(
builder: (BuildContext context) => new nextPage(context))
);
if(refresh ) setState((){});
}
NextPage
Navigator.pop(context, true);

Flutter exception: 'Context is not a subtype of BuildContext' error using Navigator

I am working on a flutter app and I am running into the following error: "The argument type 'Context' can't be assigned to the parameter type 'BuildContext'." However, when I try to pass in context to my functions as it says to do on the internet, I am unable to get it to work. Can someone please help me with this? The app is a camera app, and I know the video is successfully being recorded. here is the code:
This is the stop button that is pressed.
IconButton(
icon: const Icon(Icons.stop),
color: Colors.red,
onPressed: () {controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo
? onStopButtonPressed. //this is the function that is being called
: null;},
) //icons.stop
This is where the app isn't working with my Navigator.push call. It works fine when I take it out.
void onStopButtonPressed() {
stopVideoRecording().then((_) {
if (mounted) setState(() {});
print('Video recorded to: $videoPath');
print('Navigator is hit');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PreviewImageScreen(videoPath: videoPath),
), //MaterialpageRoute
); //Navigator
});
}
And here is my stopVideoRecording function
Future<void> stopVideoRecording() async {
if (!controller.value.isRecordingVideo) {
return null;
}
try {
await controller.stopVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
//await _startVideoPlayer();
}
Thanks!
change it to
Navigator.push(
this.context, //add this so it uses the context of the class
MaterialPageRoute(
builder: (context) => PreviewImageScreen(videoPath: videoPath),
), //MaterialpageRoute
); //Navigator
Maybe you're importing a library or something that has a class named context and is interfering with the name context