I want to close a specified dialog.
My case:
Open 2 dialogs (1) and (2), and 2 dialogs are showing at the same time. (2) is overriding (1) and I want to close (1) first.
With Android: I can assign each dialog to a variable and use dialog.dismiss().
I came across this example, it works but it doesn't seem to be the best way.
How to close a specific Flutter AlertDialog?
Thanks for all the answers!
As far as I know, you can't do that with Dialog because Dialog use Navigator, which use Stack only allow you to push and pop the top Route
Another option you can use to implement this is OverLayEntry, like this:
class _MyHomePageState extends State<MyHomePage> {
OverlayEntry? _overlayEntry1;
OverlayEntry? _overlayEntry2;
#override
void initState() {
super.initState();
_overlayEntry1 = OverlayEntry(
builder: (context) {
return Material(
color: Colors.transparent,
child: Center(
child: Container(
height: 300,
width: 300,
color: Colors.green,
),
),
);
},
);
_overlayEntry2 = OverlayEntry(
builder: (context) {
return Material(
color: Colors.transparent,
child: Center(
child: InkWell(
onTap: () {
_overlayEntry1?.remove();
},
child: Container(
height: 150,
width: 150,
color: Colors.red,
),
),
),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: InkWell(
onTap: () {
Overlay.of(context)!.insert(_overlayEntry1!);
Overlay.of(context)!.insert(_overlayEntry2!);
},
child: Text('Show overlay'),
),
),
);
}
}
You can close the first dialogue before the second dialogue pops up by using Navigator.pop(context) or if you use GetX then you can use Get.back() before calling the second dialogue opening function.
If you want the first dialogue opens up after the second dialogue pops then you can try this:
When you close the second dialogue you will call the first dialogue up by using .then((e)=> 'previous dialogue open function goes here').
So at the same time, no two dialogues are opened. Just use them one by one.
Related
I have sigIn function that get data from Api and move to another screen if request is successful and if request is not successful then show alertDialog
I change state and before fetching data I show CircularProgressIndicator to make user know that data is fetching.
But when alertDialog window pops up and I close it then CircularProgressIndicator doesn't disappear. How to remove WaitingSignInAuth and show me Scaffold with inputs
When error comes then I emit ErrorSignInAuth but why there is WaitingSignInAuth to.. Why ErrorSignInAuth doesn't replace WaitingSignInAuth or it should work differently?
This is the code:
#override
Widget build(BuildContext context) {
return BlocConsumer<AuthCubit, AuthState>(
listener: (context, state) {
if (state is WaitingSignInAuth) {
showDialog(
context: context,
builder: (context) => Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.black.withOpacity(0.6),
child: const Center(
child: CircularProgressIndicator(
strokeWidth: 1,
color: Colors.black,
backgroundColor: Colors.white,
),
),
));
}
if (state is ErrorSignInAuth) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Bad request"),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text("Error")
],
),
),
actions: <Widget>[
TextButton(
child: const Text("Close"),
onPressed: () {
Navigator.pop(context); // If i press it then alertDialog diappears but not the loading circle
},
)
],
);
}
);
}
},
);
}
This is how it looks like:
The issue is, that you showDialog() twice. First is shown when WaitingSignInAuth state is fired, and the second one when you receive ErrorSignInAuth.
So Navigator.pop(context); in the "error" alert dialog closes only the showDialog that you've triggered in if (state is ErrorSignInAuth), the second dialog, which contains progress indicator is still visible.
To easy fix it, you can change your code to:
TextButton(
child: const Text("Close"),
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
)
But in my opinion thats not the best fix to your issue. IMO you don't need to show ProgressIndicator for WaitingSignInAuth in the dialog. Adjust your UI in the way, that the container with progress indicator will be displayed over it, but this doesn't need to be a dialog.
So I'm searching for a Menu often found in a Settings Screen. It's used to select a Setting.
It opens when you press on a ListTile and then it fills the mayority of the Screen, while the borders are transparent. In the menu you have to select one of the options.
An example usecase of that would be to select a Language(onTap of the ListTile opens a Menu where you can select between all the avaliable languages)
It is similar to that of a PopupMenuButton, however as already mentioned it fills most of the screen, and the individual selectable items have a radiobutton in front of the text(probably RadioListTiles)
I hope someone gets what I'm talking about, because for the past 3 hours I'm searching for this widget but just find articles about the PopupMenuButton over and over.
Edit: I finally found an app that uses the feature I'm looking for
: https://imgur.com/a/A9k71io
By clicking on the first ListTile in the first Picture, the dialog in the second picture opens up.
Hopefully this custom widget is something roughly what you want, try to copy and paste it in your project and play with it.
this is made using the Dialog widget, to exit the Dialog you can tap out of it/ press the device back arrow and I added a small back icon on the top left corner.
tell me if it helped :)
class TestPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;// the device size
return Scaffold(
body: Center(
child: ListTile(
tileColor: Colors.grey[300],
title: Text(
'Language',
style: TextStyle(fontSize: 25),
),
onTap: () => showDialog(
context: context,
builder: (context) => Dialog(
insetPadding: EdgeInsets.all(20),
child: Container(
color: Colors.white,
height: deviceSize.height,
width: deviceSize.width,
child: Column(
children: [
Row(
children: [
IconButton(
color: Colors.red,
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
],
),
Spacer(),
Text('Pick A Language'),
Spacer(),
],
),
),
),
),
),
),
);
}
}
Im developing a Shopping cart using the BLoC pattern and I got stuck trying to learn the subset Cubit. My main question is how can I display the state of a previously updated Cubit? My flow is the next...
On the Product Screen I increase/decrease the items I want to use.
To push to change the state, I click a button and send the items as a parameter to the Cubit function.
The item list gets updated and I want to get it into another widget that is outside of the Product Screen.
Here is the code:
main.dart
void main() {HttpOverrides.global = new MyHttpOverrides();
runApp(
RepositoryProvider<AuthenticationService>(
create: (context) {
return AuthService();
},
child: MultiBlocProvider(
providers: [
BlocProvider<AuthenticationBloc>(
create: (context) {
final authService = RepositoryProvider.of<AuthenticationService>(context);
return AuthenticationBloc(authService)..add(AppLoaded());
}
),
BlocProvider<CartCubit>(create: (context) => CartCubit())
],
child: MyApp(),
),
)
);
}
product_screen.dart
BlocBuilder<CartCubit, List<Item>>(
builder: (context, state) {
return Row(
children: [
_shoppingItem(0),
SizedBox(
width: SizeConfig.blockSizeHorizontal * 2,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
color: Color(0xFF48AD71),
),
child: IconButton(
onPressed: () {
for (int i = 1; i <= counter; i++) {
items.add(widget.item);
print('something');
}
context.read<CartCubit>().addToList(items);
},
icon: Icon(
Icons.shopping_bag),
color: Colors.white,
),
),
],
);
},
)
cart_cubit.dart
class CartCubit extends Cubit<List<Item>> {
CartCubit() : super([]);
void addToList(List<Item> items) {
state.addAll(items);
emit(state);
print(state);
}
}
What I should add on my Cart Screen so I can get the value of the Cubit State? Also, do this should be better handled by using a bloc instead of cubit?
Edit: Based on the comment of Loren.A I removed the BlocBuilder of my ProductScreen and I added it to my CartScreen.
class _CartScreenState extends State<CartScreen> {
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return SingleChildScrollView(
child: BlocBuilder<CartCubit, List<Item>>(
builder: (context, state) {
return Column()
...
...
Align(
alignment: Alignment.topRight,
child: Text(
state.length.toString(), // this is not updating
textAlign: TextAlign.end,
style: TextStyle(
fontFamily: 'SinkinSans',
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: Color(0xFFC9C9C9)),
),
),
Cubit is fine for this. You can update widgets anywhere in your app based on that list just by adding another BlocBuilder<CartCubit, List<Item>>. So just add another one to your Cart page and do what ya gotta do inside of it. It will reflect the previous changes.
Edit: Just noticed that it doesn't appear that you're actually rebuilding any widgets in your product screen so that BlocBuilder you have is not really doing anything. You can remove that and just use it where you need to reflect that value of the list. You can still fire that addToList method without being inside a BlocBuilder.
I have defined one button inside one StatelessWidget (This will have the bloc creation logic and injecting using bloc provider, ), on click of the button i am showing a dialog and passing the bloc instance to it, as shown in the code.
//EsignBloc is defined here in parent statelessWidget. Defined i.e. creating the bloc instance and passing through the BlocProvider. Removed the code for simplicity
//This listener will be called when Button defined inside statelessWidget will be clicked. this is responsible for showing the dialog.
void _onClickHere(
BuildContext context,
) {
final dialog = Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
),
elevation: 0.0,
backgroundColor: Colors.transparent,
child: _GetSignUsingOtpView(),
);
showDialog(
context: context,
builder: (_) => BlocProvider<EsignBloc>(
create: (_) => BlocProvider.of<EsignBloc>(context), // passing already created bloc to dialog
child: WillPopScope(
onWillPop: () => Future.value(false),
child: dialog,
),
),
barrierDismissible: false,
);
}
Pasting some code of _GetSignUsingOtpView()
class _GetSignUsingOtpView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<EsignBloc, EsignState>(builder: (context, state) {
return Container(
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(
AppConstants.borderRadius,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () => _closeDialog(context),
child: Padding(
padding: const EdgeInsets.only(top: 8.0, right: 8),
child: Icon(
Icons.cancel,
color: AppColor.primaryDark,
size: SizeConfig.safeBlockVertical * 2,
),
),
),
),
Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
PrimaryText(text: state.otp), // data does not change after closing and opeing dialog again
PrimaryText(text: state.remainingTime), // data does not change after closing and opeing dialog again
],
),
),
],
),
);
});
}
void _closeDialog(BuildContext context) {
Navigator.pop(context);
}
}
The problem that I am facing is whenever the dialog opens again after closing, it doesn't show the latest data from the bloc. The dialog just shows whatever previous data is in the bloc. Can someone point it out, where i am making the mistake?
The reason that your dialog is not showing new data is because you are creating a new instance of the bloc. Even though you say that you're not.
BlocProvider<EsignBloc>(
create: (_) => BlocProvider.of<EsignBloc>(context), // passing already created bloc to dialog
child: ...
In some cases, BlocProvider can be used to provide an existing cubit to a new portion of the widget tree. This will be most commonly used when an existing cubit needs to be made available to a new route. In this case, BlocProvider will not automatically close the cubit since it did not create it.
To not create a new instance, you need to use BlocProvider.value. You can read more about it here
BlocProvider.value(
value: BlocProvider.of<EsignBloc>(context),
child: ...,
);
Instead of creating a new instance, this will use the previous instance and provide it to the child widget and will NOT close the bloc when its done using it. This is handy for dialogs and navigation.
I have a flutter application which shows a camera, then scans a qr-code using git://github.com/arashbi/flutter_qrcode_reader and on the call back, I want to navigate to another screen to show the information I get from some query. The problem is, it seems Navigator get a wrong context, so instead of full page navigation, I see the second page pushed inside my first page as a widget.
The build of first page is as follow :
#override
Widget build(BuildContext context) {
Widget main =
new Scaffold(
appBar: _buildAppBar(),
body: Column(
children: <Widget>[
_eventButton ?? new Text(""),
new Center(
child: new Text("Hi"))
],
),
floatingActionButton: new FloatingActionButton(
onPressed:
() {
new QRCodeReader()
.setAutoFocusIntervalInMs(200)
.setForceAutoFocus(true)
.setTorchEnabled(true)
.setHandlePermissions(true)
.setExecuteAfterPermissionGranted(true)
.scan().then((barcode) => _showTicketPage(barcode));
}
,
child: new Icon(Icons.check),
),
);
if (!_loading) {
return main;
} else {
return new Stack(
children: [main,
new Container(
decoration: new BoxDecoration(
color: Color.fromRGBO(220, 220, 220,
0.3) // Specifies the background color and the opacity
),
child: new Center(child: new CircularProgressIndicator(),))
]
);
}
}
The 'Hi' Widget is for debugging, and the navigate method is
void _showTicketPage(var barcode) {
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) => TicketScreen(barcode)));
}
After getting the barcode, the second screen - TicketScreen - is pushed under 'Hi' Widget. I didn't even know this was possible to do.
I tried to get the context of the screen, save it to a field var, but that didn't help either.
How can I fix it? I ran out of ideas.