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.
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.
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.
I a working with ModalBottomSheet and I love the package. However I am encountering an issue when trying to navigate with in a ModalBottomSheet.
Here is a ScreenVideo for a better understanding.
As you can see the View is presented as a ModalBottomSheet. But when popping it, it is not simply dismissing to the bottom but instead it is popping to some empty screen with a MaterialPage Pop animation.
I changed my code so I can push with a MaterialPageRoute-Animation inside that ModalBottomSheet. Before that everything was working as expected.
Here is my Page:
class _AboutScreenState extends State<AboutScreen> {
#override
Widget build(BuildContext context) {
return Material(
color: AppColors.primary,
child: Navigator(
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context2) => Builder(
builder: (context) => CupertinoPageScaffold(
backgroundColor: AppColors.secondary,
resizeToAvoidBottomInset: false,
child: SafeArea(
child: Padding(
padding: EdgeInsets.all(sidePadding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButtonWithExpandedTouchTarget(
onTapped: () {
Navigator.pop(context);
},
svgPath: 'assets/icons/down.svg',
),
],
),
Text(
'About',
style: AppTextStyles.montserratH2SemiBold,
),
...
RoundedCornersTextButton(
title: 'Impressum',
onTap: () {
Navigator.pop(context);
},
textColor: AppColors.primary,
backgroundColor: AppColors.secondary,
borderColor: AppColors.primary,
),
],
),
),
)),
),
),
),
);
}
}
I was simply following this example. What am I missing here? With the code above I can push inside the ModalSheet as expected with a MaterialPageRoute Animation but popping the first screen brings up the issue...
Let me know if you need any more info! Any help is appreciated :)
I think you are popping with the wrong context. The example is popping with rootContext, which is from the top-most widget in the hierarchy. You are popping from with context, defined at your lowest builder in the hierarchy.
I believe you are using the incorrect context. rootContext, which is from the top-most widget in the hierarchy, is what makes the sample pop. You're popping from your lowest builder in the hierarchy, which is dictated by context.
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'm new to flutter and am trying to stack a searchView on top of a ListView. I would Look something like this:
I don't know how to achieve this layout on flutter, i tried using a column(which crashes the app), used the a stack (which overlaps the widgets) and i finally tried to add a search widget a the first item of the ListView but that didn't result am looking for.
Widget build(BuildContext context) {
return new Scaffold(
drawer: _makeDrawer(),
appBar: new AppBar(
title: new Center(
child: new Text("translate"),
),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.people_outline), onPressed: () {})
],
),
body: _phraseListFutureBuilder(), // this part of the code renders the listview
);
}
FutureBuilder _phraseListFutureBuilder() {
return new FutureBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
return mPhrases.isEmpty
? new CircularProgressIndicator()
: _buildPhrases();
},
future: _fetchData());
}
A Column was the right choice, but you will also need an Expanded widget to specify which widget should fill the remaining space:
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextField(
decoration: InputDecoration(
labelText: 'Search'
),
),
Expanded(
child: _phraseListFutureBuilder(),
)
],
);