Flutter navigation, reopen page instead of pushing it again - flutter

I'm new to flutter and I'm working on an App that have multiple screens.
I would like to know to stop flutter from creating multiple screens of the same route,
for example I have Page 1 and Page 2, if I click on the button to navigate to Page 2 and clicked again on the same button the application will push a new screen of Page 2 and i will have it twice and if I click on the return button it will send me back to the first created Page 2
is there a way to create every page only once and then reopen it if it's already pushed to the stack ?
I'm using Navigator.push for all my navigation

These answers are not correct. PushReplacement will still create a new instance of the widget. That's very simple to identify if you have a scrollable list in RouteA and scroll it before navigating to a RouteB. When you pushReplacement(RouteA) , you'll see the scrolling position have changed to its initial state. That is because a new widget was instantiated, and that is not the behaviour you want.
You should use popUntil, as previously answered by JoaoSoares

Using a simple logic here
If the new page is the same as the current page then use Navigator.push(context,route).
If the new page is the different then use Navigator.pushReplacement(context,route).
In case if you are using named routes then for
same page as current, use Navigator.pushNamed(context,name).
different page, use Navigator.pushReplacementNamed(context,name).
Complete code sample
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SO(),
);
}
}
class SO extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageOne(),
);
}
}
class PageOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 1'),
),
backgroundColor: Colors.amber,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
onPressed: () {
print('creating replacement page 1');
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) {
return PageOne();
}));
},
child: Text('Go to page 1'),
),
RaisedButton(
onPressed: () {
print('creating new page 2');
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
return PageTwo();
}));
},
child: Text('Go to page 2'),
),
],
),
),
);
}
}
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 2'),
),
backgroundColor: Colors.brown,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
onPressed: () {
print('creating new page 1');
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => PageOne()));
},
child: Text('Go to page 1'),
),
RaisedButton(
onPressed: () {
print('creating replacement page 2');
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) => PageTwo()));
},
child: Text('Go to page 2'),
),
],
),
),
);
}
}

You should use Navigator.pushReplacement(). Here are the docs: https://api.flutter.dev/flutter/widgets/NavigatorState/pushReplacement.html
In summary, when you call pushReplacement, it pushes a new page but also disposes of the previous one. Now, when you press the back button, it should bring you back to page 1.

Related

back page with flutter android back button

What codes should I use to go back to the page with the flutter android back button? I looked inside youtube, especially on Stackoverflow, but I couldn't get any results.
You can use WillPopScope
Here is example code:
class MyPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// This function will handle back button
// When true, it's can back to previous page
// when false, back button will do nothing
return true;
},
child: Scaffold(
appBar: AppBar(
title: const Text('Flutter WillPopScope demo'),
),
body: Center(
child: Text('Hello world')
),
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new WillPopScope(
child: new Scaffold(
appBar: new AppBar(
title: new Text('Page 2'),
),
body: new Center(
child: new Text('PAGE 2'),
),
),
onWillPop: () async {
return false;
},
);
}
}
Future<T> pushPage<T>(BuildContext context, Widget page) {
return Navigator.of(context)
.push<T>(MaterialPageRoute(builder: (context) => page));
}
Can call the page like:
pushPage(context, Page2());

onPressed of an Image I want to place that image in other page's container in flutter, How can I achieve this?

I have a Home page for an ecommerce website and I am showing every product in image format on click of every product(image) I want that image to be filled up in next screen which is of info screen of product that also contains the product (image) and some other related stuff. I want to build info page only a single time and just change the product image and other stuff accordingly as per the image (product) being clicked. How can I achieve this I am new to flutter and dont know this logic i came across valuelistenablebuilder but not getting how can i achieve this, Please Help.
Thank you,
By default, Flutter is providing this support by using the Hero widget. Find the code snippets below.
import 'package:flutter/material.dart';
void main() => runApp(HeroApp());
class HeroApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Transition Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
),
body: GestureDetector(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen();
}));
},
),
);
}
}
class DetailScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Center(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
Make sure the Hero.tag property should be maintained as same for both pages.
Refer the link for more details

How to close the Drawer while keeping the Dialog opened?

Minimal code:
void main() => runApp(MaterialApp(home: MainPage()));
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
);
}
}
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: RaisedButton(
onPressed: () {
// Close the Drawer, not the Dialog.
Timer(Duration(seconds: 2), () => Navigator.of(context, rootNavigator: true).pop());
// Show the Dialog and keep it in opened state.
showDialog(
context: context,
builder: (_) => AlertDialog(title: Text('FooDialog')),
);
},
child: Text('Show Dialog'),
),
);
}
}
On pressing the button, I am showing dialog and after 2s I want to close the Drawer while keeping the Dialog opened on the screen. For this I am using Timer and rootNavigator property of Navigator. However, my dialog is getting dismissed.
Is there any solution for closing the drawer besides using GlobalKey<DrawerControllerState> stuff?
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: RaisedButton(
onPressed: () {
// Close the Drawer, not the Dialog.
Timer(Duration(seconds: 2), () => Scaffold.of(context).openEndDrawer());
// Show the Dialog and keep it in opened state.
showDialog(
context: context,
builder: (_) => AlertDialog(title: Text('FooDialog')),
);
},
child: Text('Show Dialog'),
),
);
}
}
You can use ScaffoldState, to close the drawer. Just keep a track of the time and you are good to go. In this answer, I have told you on how to use the ScaffoldState with your drawer.
This code will help you achieve what you want. I have used the second option from my previous answer, that is, using everything in the MainPage only
class MainPage extends StatelessWidget {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
// this will check for the drawer state and close it
// using _scaffoldKey
timer() {
return Future.delayed(Duration(seconds: 2), (){
// checking whether it is open
if(_scaffoldKey.currentState.isDrawerOpen){
// here how you close it
_scaffoldKey.currentState.openEndDrawer();
}
});
}
Future<void> _showMyDialog(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('AlertDialog Title'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('This is a demo alert dialog.'),
Text('Would you like to approve of this message?'),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Approve'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
// Our drawer
Drawer _drawer(BuildContext context) => Drawer(
child: RaisedButton(
onPressed: (){
timer();
_showMyDialog(context);
},
child: Text('Show Dialog'),
)
);
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(),
drawer: _drawer(context)
);
}
}
Result
Please note: I have not clicked anywhere on the screen, to close the drawer. It goes automatically by the timer()

Flutter: Can't navigate to another page

So, after building my first flutter page, I began working on some other flutter pages for my app. I successfully made two other flutter pages and was able to allows users to navigate between the two, however when I try to allow users to navigate on the original flutter page to any of the other two flutter pages, nothing happens.
Note: the code for navigation seems to be properly written (it works for the other two pages)
Note 2: The line where I navigate to another page is linked to a raised button labeled 'View Record'
Can anyone point anything out in this page that wouldn't allow users to navigate to another page? Thank you!
import 'package:flutter/material.dart';
import 'package:faui/faui.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:provendor3/FlutterAuthUiDemoy.dart';
import 'main.dart';
void firestore_page() => runApp(firestore_pagey());
class firestore_pagey extends StatefulWidget {
#override
MyApps createState() => MyApps();
}
class MyApps extends State<firestore_pagey> {
final databaseReference = Firestore.instance;
#override
Widget build(BuildContext context) {
return MaterialApp(home:
Scaffold(
appBar: AppBar(
title: Text('FireStore Demo'),
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
RaisedButton(
child: Text('Create Record'),
onPressed: () {
createRecord();
},
),
RaisedButton(
child: Text('View Record'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyApp()),
);
},
),
RaisedButton(
child: Text('Update Record'),
onPressed: () {
updateData();
},
),
RaisedButton(
child: Text('Delete Record'),
onPressed: () {
deleteData();
},
),
new Expanded(child:
new StreamBuilder<QuerySnapshot>(
stream: databaseReference.collection('books').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['title']),
subtitle: new Text('${document['description']} description'),
);
}).toList(),
);
},
)
)
],
)),
) //center
);
}
Main.dart
import 'package:flutter/material.dart';
import 'FlutterAuthUiDemoy.dart';
import "firestore_page.dart";
void 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) => FlutterAuthUiDemo()),
);
},
color: Colors.red,
)
],
),
),
),
),
);
Can you remove your MaterialApp then try again? You already have a MaterialApp in your main.dart I assume.
Widget build(BuildContext context) {
return
Scaffold(
appBar: AppBar(
title: Text('FireStore Demo'),
...
So, the problem appeared to be coming from the page that I was navigating from. I was using a library that seemed to prevent navigation after use, so I removed the library from the page before it and now I can navigate on the page just fine! Thanks for all the help!

How to go from one screen to another clearing all the previous screens from stack?

In my Flutter project, I have three screens - Screen0, Screen1, Screen2. Now, from the screen0 , I can go to the screen1 with a button click, then from screen1 we can go to screen2 with a button. In screen2, I have a button which I have used to go back to screen0. As Screen0 is the initial screen, I want to clear all the previous screens when I come back to Screen0 and don't want to have any back option like in this image-
And here's my code-
main.dart
import 'package:flutter/material.dart';
import 'screen0.dart';
import 'screen1.dart';
import 'screen2.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context)=> Screen0(),
'/first': (context)=> Screen1(),
'/second': (context)=> Screen2(),
},
);
}
}
I have set all the routes in my main.dart file. Then in the Screen0, I have a button to go to screen1 like below-
screen0.dart
import 'package:flutter/material.dart';
class Screen0 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Text('Screen 0'),
),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
color: Colors.red,
child: Text('Go to screen 1'),
onPressed: (){
Navigator.pushNamed(context, '/first');
},
),
],
),
),
);
}
}
In the screen1, I have a button to go to screen2-
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Text('Screen 1'),
),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
color: Colors.red,
child: Text('Go to screen 2'),
onPressed: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return Screen2();
})
);
},
),
],
),
),
);
}
}
Now, in the screen2, i have the button to go to screen0 -
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Text('Screen 2'),
),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
color: Colors.red,
child: Text('Go to screen 0'),
onPressed: (){
Navigator.pop(context, Screen2);
Navigator.pushNamed(context, '/');
},
),
],
),
),
);
}
}
I have tried some solution like using Navigator.pushAndRemoveUntil given in the below link-
Flutter - Navigate to a new screen, and clear all the previous screens
But this solution didn't work for me.
So, it would be nice if someone help me out this code to solve the problem.
use pushNamedAndRemoveUntil
for example
Navigator.of(context).pushNamedAndRemoveUntil('/screen4', (Route<dynamic> route) => false);