Why Navigator.of(context).pop() back to previous page instead close EndDrawer? - flutter

I have 2 page, page1 and page2. page1 navigate to page2. page2 has endDrawer inside scaffold, but i must wrap scaffold with materialApp. When endDrawer open and i call Navigator.of(context).pop() or Navigator.pop(context), it will back to page1 instead close endDrawer. How to close the endDrawer and not back to page1? all i know to close drawer is use pop

I think you are using wrong context:
Check the code below which can complete your task:
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
title: 'Navigation Basics',
home: FirstRoute(),
));
}
class FirstRoute extends StatelessWidget {
const FirstRoute({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('First Route'),
),
body: Center(
child: ElevatedButton(
child: const Text('Open route'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondRoute()),
);
},
),
),
);
}
}
class SecondRoute extends StatelessWidget {
const SecondRoute({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("Second Route"),
),
endDrawer: Builder(
builder: (context) {
return Container(
width: 100,
color: Colors.black,
alignment: Alignment.center,
child: GestureDetector(
child: const Text(
"close drawer",
style: TextStyle(
color: Colors.white,
),
),
onTap: () {
Navigator.pop(context);
},
),
);
},
),
body: Builder(builder: (context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () {
Scaffold.of(context).openEndDrawer();
},
child: const Text('open drawer'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go back!'),
),
],
),
);
}),
),
);
}
}

Related

I cannot add appBar

I am trying to do a log in app, after finishing with the design I faced a problem where I could not add the function of going back to the old page, I searched and found about the appBar but I could not apply it.
class Body extends StatelessWidget {
const Body({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Background(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"LOGIN",
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: size.height * 0.03),
SizedBox(height: size.height * 0.03),
RoundedInputField(
hintText: "Your Email",
onChanged: (value) {},
),
RoundedPasswordField(
onChanged: (value) {},
),
RoundedButton(
text: "LOGIN",
press: () {},
),
SizedBox(height: size.height * 0.03),
AlreadyHaveAnAccountCheck(
press: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return SignUpScreen();
},
),
);
},
),
],
),
),
);
}
}
You must use Scaffold Widget for displaying the AppBar try below code hope its helpful to you In Scaffold widget you declare AppBar for Appbar refer ducumentation here :
class AppBarPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Your Title'),
),
body: Container(),
);
}
}
Your Screen look like this->
use Scaffold.
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("MyHomePage"),
actions: [
TextButton(onPressed: (){
Navigator.of(context).pop();
}, child: Icon(Icons.arrow_back))
],
),
body:Body() ,
);
}
}
on onPressed event you can return to old page
appBar can be used inside Scaffold.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AppBar Demo'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back),
tooltip: 'Show Snackbar',
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
body: const Center(
child: Text(
'This is the home page',
style: TextStyle(fontSize: 24),
),
),
);
}

Is there a built in alternative to BottomNavigationBar but for a bottom menu?

I'm new to flutter and am building my first app. I have a small app with maybe 4 pages, there is a bottom bar which has icons on it. Some icons call functions such as copy text (which does not need its own page but is a function to be called) while others are complex and call a new page using Navigator.push().
I tried to achieve this using a custom Row() widget on each page, but it became very odd and I was rewriting a lot of functions. I felt there had to be a bottom menu bar, but couldn't find one.
I found BottomNavigationBar and was excited that it would achieve what I wanted. But I don't think its meant to be used as a menu, but rather as the name implies, a navigation bar.
I also want the navigation bar to change menu icons as it moves to a new page. The tutorial I am following uses it as follows:
class _HomeState extends State<Home> {
int _currentIndex = 0;
final List<Widget> _children = [
PlaceholderWidget(Colors.white),
PlaceholderWidget(Colors.deepOrange),
PlaceholderWidget(Colors.green)
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Bottem Navi '),
),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Profile')
)
],
),
);
}
The body: is changed based on the index selected. Can this be repurposed to act as a menu? to make use of Navigator.push or is my previous method of having a Row() widget on each page the best option?
You can copy paste run full code below
You can use BottomAppBar
code snippet
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondRoute()));
},
),
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.title),
onPressed: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Show Snackbar'),
duration: Duration(seconds: 3),
));
},
)),
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Tasks - Bottom App Bar')),
body: ListView(
padding: const EdgeInsets.all(8),
children: <Widget>[
Container(
height: 50,
color: Colors.amber[600],
child: const Center(child: Text('Entry A')),
),
Container(
height: 50,
color: Colors.amber[500],
child: const Center(child: Text('Entry B')),
),
Container(
height: 50,
color: Colors.amber[100],
child: const Center(child: Text('Entry C')),
),
],
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondRoute()));
},
),
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.title),
onPressed: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Show Snackbar'),
duration: Duration(seconds: 3),
));
},
)),
IconButton(
icon: Icon(Icons.settings),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.sync),
onPressed: () {},
),
],
),
),
);
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}

A dialog has a delay to open, I get an error when navigating to another view before the dialog is generated. How can I make it not be generated?

I am new to flutter.
In my real problem, my client is in places where it is very frequent that the internet is very slow, so sometimes an attempt is made to make a web request and this may take time, so the user leaves the screen before the web request is completed. Sometimes my app after completing a web request generates a dialog. So here is where my problem lies, the user is trying to make a web request and while it is done, they leave the screen and then the dialog is generated.
I am trying to simulate this problem with a delay that later generates the dialog.
I am not thinking of any strategy to end the web request, what I want is to find a way that once I leave the screen, causes the dialog not to be generated something like a dispose
I made an example where I have 2 screens. On the second screen a dialog is generated with a delay of 5 seconds when the button is clicked. If I navigate to another screen before the dialog is opened I get an error. I assume this occurs because the view was destroyed and therefore the dialog cannot be opened.
What can I do to avoid the error when the dialog is generated after being in another view? if I am in another view I DO NOT WANT the dialog to be generated.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("main");
return MaterialApp(title: 'Provider Example', initialRoute: '/', routes: {
'/': (context) => Home(),
'home': (context) => Home(),
'dialogpage': (context) => Dialogpage(),
});
}
}
class Home extends StatelessWidget {
Home() {
print("home");
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text('home'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.add_alert),
tooltip: 'Show Snackbar',
onPressed: () {
Navigator.pushNamed(context, "dialogpage");
},
),
],
),
body: const Center(
child: Text(
'home',
style: TextStyle(fontSize: 24),
),
),
);
}
}
class Dialogpage extends StatelessWidget {
Dialogpage() {
print("dialogpage");
}
dialog(BuildContext context) {
Future.delayed(const Duration(seconds: 5), () {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
title: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(19.0),
topRight: Radius.circular(19.0),
),
),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 5),
child: Text(
'Error',
style: TextStyle(color: Colors.white),
),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Icon(
Icons.error,
size: 50,
),
),
Text("dialog"),
],
),
titlePadding: EdgeInsets.all(0),
actions: <Widget>[
FlatButton(
child: Text('Aceptar'),
onPressed: () {
return Navigator.of(context).pop();
}),
],
);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dialog'),
),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
}),
),
);
}
}
use Globalkey in scaffold in then check the context in dialog method is it != null
then run dialog otherwise don't...
GlobalKey _scafolldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scafolldKey,
appBar: AppBar(
title: const Text('dialog'),),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
}),
),
);
}
}
dialog(BuildContext context) {
Future.delayed(const Duration(seconds: 2), () {
if(_scafolldKey.currentContext !=null){
showDialog();
}
});
}
Instead of Future.delayed, you should use Timer, which can be cancelled in onDispose method.
Working solution:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("main");
return MaterialApp(
title: 'Provider Example',
initialRoute: '/',
routes: {
'/': (context) => Home(),
'home': (context) => Home(),
'dialogpage': (context) => Dialogpage(),
},
);
}
}
class Home extends StatelessWidget {
Home() {
print("home");
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('home'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.add_alert),
tooltip: 'Show Snackbar',
onPressed: () {
Navigator.pushNamed(context, "dialogpage");
},
),
],
),
body: const Center(
child: Text(
'home',
style: TextStyle(fontSize: 24),
),
),
);
}
}
class Dialogpage extends StatefulWidget {
#override
_DialogpageState createState() => _DialogpageState();
}
class _DialogpageState extends State<Dialogpage> {
Timer _timer;
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
dialog(BuildContext context) {
_timer = Timer(
const Duration(seconds: 3),
() {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
title: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(19.0),
topRight: Radius.circular(19.0),
),
),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 5),
child: Text(
'Error',
style: TextStyle(color: Colors.white),
),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Icon(
Icons.error,
size: 50,
),
),
Text("dialog"),
],
),
titlePadding: EdgeInsets.all(0),
actions: <Widget>[
FlatButton(
child: Text('Aceptar'),
onPressed: () {
return Navigator.of(context).pop();
},
),
],
);
},
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dialog'),
),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
},
),
),
);
}
}
Try this code
class Dialogpage extends StatelessWidget {
...
Timer t;
dialog(BuildContext context) {
t = Timer(Duration(seconds: 5), () {
showDialog(...);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dialog'),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
t?.cancel();
Navigator.of(context).pop();
},
),
),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
}),
),
);
}
}
Hope it helps.

I am trying to open another activity from a button click in flutter but it is showing some error

import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
appBar: AppBar(
title: Text("Page 1"),
),
body: Center(
child: Column(
children: <Widget>[
MaterialButton(
child: Text("Next Page"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => nextPage()),
);
},
color: Colors.red,
)
],
),
),
),
);
}
}
class nextPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.green),
home: Scaffold(
appBar: AppBar(
title: Text("Page 2"),
),
body: Center(
child: Column(
children: <Widget>[
MaterialButton(
child: Text("Go Back!"),
onPressed: () {
Navigator.pop(context);
},
color: Colors.red,
)
],
),
),
),
);
}
}
This is the code i am using to navigate to a new page but i am facing a problem even though compiler is not throwing any error
This is the error message i am getting
Another exception was thrown: Navigator operation requested with a context that does not include a Navigator.
I am getting this error when i am clicking on the button
You need home : Builder( builder: (context) => and Second Page remove MaterialApp
MaterialApp(
home: Builder(
builder: (context) => Scaffold(
appBar: AppBar(
title: Text("Page 1"),
),
body: Center(
child: Column(
children: <Widget>[
MaterialButton(
child: Text("Next Page"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => nextPage()),
);
},
color: Colors.red,
)
],
),
),
),
),
full code
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => Scaffold(
appBar: AppBar(
title: Text("Page 1"),
),
body: Center(
child: Column(
children: <Widget>[
MaterialButton(
child: Text("Next Page"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => nextPage()),
);
},
color: Colors.red,
)
],
),
),
),
),
);
}
}
class nextPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Page 2"),
),
body: Center(
child: Column(
children: <Widget>[
MaterialButton(
child: Text("Go Back!"),
onPressed: () {
Navigator.pop(context);
},
color: Colors.red,
)
],
),
),
);
}
}
try this
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
appBar: AppBar(
title: Text("Page 1"),
),
body: Center(
child: Column(
children: <Widget>[
MaterialButton(
child: Text("Next Page"),
onPressed: () {
Navigator.of(context).pushNamed(NextPage.routeName),
);
},
color: Colors.red,
)
],
),
),
),
);
}
}
class NextPage extends StatelessWidget {
static const routeName = '/next-page';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Page 2"),
),
body: Center(
child: Column(
children: <Widget>[
MaterialButton(
child: Text("Go Back!"),
onPressed: () {
Navigator.of(context).pop();
},
color: Colors.red,
)
],
),
),
);
}
}
You cant use the Navigator (in MyApp class) with the context you got before creating MaterialApp(). Wrap your MaterialButton with a Builder like so:
children: <Widget>[
Builder(
builder: (BuildContext innerContext) {
MaterialButton(
child: Text("Go Back!"),
onPressed: () {
Navigator.pop(innerContext);
},
color: Colors.red,
)
}
);
],
Besides this, some more issues:
i would also think about using onWillPop property for all those android use cases.
never ever use another MaterialApp() while navigating your app, use Scaffold() for example for childs.
You can also just move the MaterialApp Widget to the main()
main() => runApp(
MaterialApp(
theme: ThemeData(primarySwatch: Colors.blue),
home: MyApp(),
),
);
and remove it from the other classes.
You only need one MaterialApp widget for your app. It's like "root" widget which configures the top-level Navigator, as you can see in documentation.

How to keep AppBar and back arrow stationary when navigating to a new screen

I want to have the top half of by screen appear static when navigating between pages in Flutter.
To try to make this happen I put use the Hero widget and use it on a column that contains an AppBar and some other content that I want to appear static when pushing a new page.
The App Bar itself remains static but the back arrow disappears when the animation starts and reappears when the animation is done.
How can I have the back arrow remain visible the entire time while the rest of the page is animating into place?
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Hero(
tag: 'top',
child: Column(
children: <Widget>[
AppBar(
title: Text('First'),
backgroundColor: Color.fromARGB(255, 50, 64, 182),
),
Container(
height: 80.0,
)
],
),
),
RaisedButton(
child: Text('Next'),
onPressed: () {
Navigator.pushNamed(context, '/second');
},
),
],
),
);
}
}
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Hero(
tag: 'top',
child: Column(
children: <Widget>[
AppBar(
title: Text('Second'),
),
Container(
height: 80.0,
// color: Colors.green,
),
],
),
),
RaisedButton(
child: Text('Back'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
);
}
}
Things weren't quite set up right in your code. It should go Scaffold/Hero/your content. I've also used this simple fading page route when performing the navigation:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First'),
leading: Icon(null),
backgroundColor: Color.fromARGB(255, 50, 64, 182)),
body: Hero(
tag: 'top',
child: Column(
children: <Widget>[
Container(height: 80.0),
RaisedButton(
child: Text('Next'),
onPressed: () {
Navigator.push(context, MyCustomRoute(builder: (context) {
return SecondScreen();
}));
},
),
],
),
),
);
}
}
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second'),
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: () {
Navigator.pop(context);
},),
backgroundColor: Color.fromARGB(255, 50, 64, 182)),
body: Hero(
tag: 'top',
child: Column(
children: <Widget>[
Container(height: 80.0),
RaisedButton(
child: Text('Back'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
),
);
}
}
class MyCustomRoute<T> extends MaterialPageRoute<T> {
MyCustomRoute({ WidgetBuilder builder, RouteSettings settings })
: super(builder: builder, settings: settings);
#override
Widget buildTransitions(BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
if (settings.isInitialRoute)
return child;
// Fades between routes. (If you don't want any animation,
// just return child.)
return new FadeTransition(opacity: animation, child: child);
}
}
You could do automaticallyImplyLeading: false and then do
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
I have it done this way, by adding automaticallyImplyLeading: true,
Hope this solves your problem!
appBar: AppBar(
automaticallyImplyLeading: true,
),