Flutter Modal Bottom Sheet with Navigator does not pop as expected - flutter

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.

Related

Searching for the name of a specific menu - Flutter

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(),
],
),
),
),
),
),
),
);
}
}

How retrieve two classes Flutter

i am green in flutter and i have quick question, would like to make retrieve two classes SearchBar() & HomeView(), but it only allows to retrieve one class. I understand that something is missing, i would like to hear your suggestions in order to learn it better.
return Scaffold(
appBar: AppBar(
title: Text(title),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.add_box,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AddBookPage(uid: this.widget.uid)));
})
],
),
body: SearchBar(),
body: HomeView(),
drawer: NavigateDrawer(uid: this.widget.uid));
}
Here is the picture how i would like make it look like
I get Search Bar overflowed by Infinity pixels on the bottom
Use Column:
Scaffold(
body: Column(
children: [
SearchBar(),
SizedBox(height: 20),
Expanded(
child: HomeView(),
)
],
),
// ... your other code
)
Using a Column may be the best way.
The overflow error is probably caused by the SearchBar() widget not having a defined height. A Column widget can't have a child with infinite height.

Dialog state does not change after restarting (closing and opening again) in flutter

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.

How to transition to a new screen instantly

I want to create a transition immediately from one screen to another (no animation). However isInitialRoute generates an error due to an update to the Router widget. The suggestion is to use onGenerateInitialRoutes but I am not sure how to implement it.
The flutter design doc recommends this:
Routes & RouteSettings
Currently, the only way to add a Route to the history stack without playing its entrance animation is to mark it as an initial route in its RouteSettings. The declarative API requires that routes can be added without animation at any time. This is useful when the route is covered by another route and playing the animation simply doesn't make sense. To support this, a didAdd method is added to the Route interface. This method is called by the Navigator instead of didPush when the Route should just be added without its regular entrance animation. To simplify things, this new method will also be used to bring the initial route on screen. This makes the RouteSettings.initialRoute parameter useless and it will be removed from RouteSettings. This is a minor breaking change.
This is the code that is generating the error on isInitialRoute:
import 'package:flutter/cupertino.dart'
show
CupertinoApp,
CupertinoButton,
CupertinoPageRoute,
CupertinoPageScaffold;
import 'package:flutter/widgets.dart'
show
BuildContext,
Center,
Column,
Navigator,
Route,
RouteSettings,
SafeArea,
Spacer,
Text,
runApp,
Widget;
Widget makeButton(BuildContext context, String routeName) =>
new CupertinoButton(
onPressed: () => Navigator.pushReplacementNamed(context, routeName),
child: Text('Go to \'$routeName\''),
);
Route generateRoute(RouteSettings settings) {
switch (settings.name) {
case 'not-animated':
return new CupertinoPageRoute(
settings: RouteSettings(name: settings.name, isInitialRoute: true),
builder: (context) => CupertinoPageScaffold(
child: SafeArea(
child: Center(
child: Column(
children: [
Spacer(),
Text('This is \'not-animated\''),
makeButton(context, 'animated'),
Spacer(),
],
),
),
),
),
);
default:
return null;
}
}
void main() {
runApp(
CupertinoApp(
onGenerateRoute: generateRoute,
initialRoute: 'animated',
routes: {
'animated': (context) => CupertinoPageScaffold(
child: SafeArea(
child: Center(
child: Column(
children: [
Spacer(),
Text('This is \'animated\''),
makeButton(context, 'not-animated'),
Spacer(),
],
),
),
),
),
},
),
);
}
If You Want to just navigate your screen add this to your code
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => yoursecondscreenname()),
);
},

Navigator gets the wrong context

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.