How to display popup after navigating back to previous screen in flutter? - flutter

I have a scenario where i have one popup in screen A .It will be triggerd from initstate() . User can navigate to screen B after clicking on button inside the popup . How can i show the popup if user come back to screen 1 by clicking on arrow back button ?

You can achieve with the help of .then() : https://api.flutter.dev/flutter/dart-async/Future/then.html
Navigator.of(context)
.push(CupertinoPageRoute<Screen1>(
builder: (context) => Screen2(
))).then((value) {
//ShowPopUpMenu() <-- Your PopUpMenu.
});

Add a boolean parameter to your screen if you want the popup:
class PopScreen extends StatefulWidget {
PopScreen (this.displayPopup);
final bool displayPopup;
#override
_PopScreenState createState() => _PopScreenState();
}
class _PopScreenState extends State<PopScreen> {
#override
void initState() {
super.initState();
if(widget.displayPopup){
// display your popup here
}
}
}
call to the navigator:
Navigator.pushNamed(context, '/yourscreen', arguments: true);

Related

Flutter : clear child Texfield when Navigator pop

I am making the sign up of my app in Flutter and this process is on 2 pages. On the first page, I use my own class for Textfields, passing arguments to them.
When I pop from the second page to the first, I would like to reset all data from the first page. I succeed to do that but I have a problem : the text in the textfields is not cleared despite the fact that the variables that receive the content of the Textfields are reset.
It gives for the first page :
class Inscription1 extends StatefulWidget {
Inscription1({Key? key}) : super(key: key);
#override
State<Inscription1> createState() => _Inscription1State();
}
class _Inscription1State extends State<Inscription1> {
String nom = "";
and
TextFieldInscription(hint: "Nom", updateValeur: (nouvelleValeur){setState(() {nom = nouvelleValeur;});},),
To pass the data to the second page and force rebuild the first page when the second page pops, I have the following in the first page
Navigator.of(context).push(MaterialPageRoute(builder: (context) => Inscription2(nom: nom,)).then(remettreAZero);
where remettreAZero is the following function
FutureOr remettreAZero(value){
setState(() {
nom = "";
});
rebuildAllChildren(context);
}
and rebuildAllChildren
void rebuildAllChildren(BuildContext context) {
void rebuild(Element el) {
el.markNeedsBuild();
el.visitChildren(rebuild);
}
(context as Element).visitChildren(rebuild);
print("rebuild");
}
All this works ("nom" is set empty) but the page still shows the TextField with the value previously entered inside.
I even tried to pass a controller argument in the TextField and adding
controller?.clear();
inside the function remettreAZero but it doesn't help.
Could you help me?
Many thanks !
Bernard
You have to dispose your textfield controller like this on first screen
#override
void dispose() {
textEditingController.dispose();
}

How to detect tap on the scrim of the modal bottom sheet?

I am using a modal sheet, when I tap somewhere outside the modal bottom sheet (In the transparent area), it closes the bottom sheet, so I need a callback of this tap gesture so that I can perform certain actions before closing the bottom sheet.
There is property isDismissible in the showModalBottomSheet, which disables the tap on the scrim, but I don't want it to disable it, just need a callback so that certain actions can be performed before closing.
Not sure if this is exactly what you're looking for but you could return a StatefulWidget in the showModalBottomSheet builder and in that widget trigger a callback with the deactivate or dispose overrides. Deactivate is triggered first.
To trigger a callback you'd need to pass that function into the StatefulWidget's constructor.
e.g.
void callback() {
debugPrint('>>> my callback triggered');
}
void showMyModalBottomSheet() {
showModalBottomSheet(
context: context,
builder: (context) {
return MyBottomSheetWidget(
callback: callback,
);
},
);
}
class MyBottomSheetWidget extends StatefulWidget {
final VoidCallback callback;
const MyBottomSheetWidget({
Key key,
this.callback,
}) : super(key: key);
#override
State<MyBottomSheetWidget> createState() => _MyBottomSheetWidgetState();
}
class _MyBottomSheetWidgetState extends State<MyBottomSheetWidget> {
#override
void deactivate() {
debugPrint('>>> bottom sheet closing');
widget.callback(); // This will be trigger when the bottom sheet finishes closing
super.deactivate();
}
#override
Widget build(BuildContext context) {
return Container();
}
}

Load a Route before pushing it via Navigator in flutter?

I'm currently trying to push a named route in flutter.
Works good so far, but the Asset Image for the background is loaded after the route was pushed via Navigator, which does not look good.
Currently I push the route like this:
#override
void initState() {
super.initState();
Timer(Duration(seconds: 5), () =>
Navigator.pushNamed(context, routeToPage2)
);
}
Is there any way to load a Page / Route without pushing it in the first place, so everything is build correctly when the route is pushed after the set time?
I guess the Asset Image gets loaded on routeToPage2? In this case the push happens everytime before the image gets load.
you can use a routegenerator for your page route and popUntil. This behavior will remove existing pages off the stack and push a single page called home page on. The MaterialPageRoute builds your route that is pushed to the navigator. Load your asset in the scaffold of HomePage. It should not require a delay to render
Navigator.of(context)
.popUntil(ModalRoute.withName(RouteGenerator.homePage));
class RouteGenerator {
static const String homePage = "/home";
static const String customPage = "/custom";
RouteGenerator._();
static Route<dynamic> handleRoute(RouteSettings routeSettings) {
Widget childWidget;
switch (routeSettings.name) {
case homePage:
{
childWidget = HomePageWidget(title: 'Home Page');
}
break;
case customPage:
{
final args = routeSettings.arguments as CustomView;
childWidget = CustomPageWidget(args);
}
break;
default:
throw FormatException("Route Not Found");
}
return MaterialPageRoute(builder: (context) => childWidget);
}
}
Sorry for being a bit too vague, maybe I should have explained my problem more detailed.
So basically I have Page1, which pushes after a Timer finished to Page2.
On Page2, I have an Image-Asset, which loads shortly after Page2 is displayed, which did not look nice. This asset is saved on the device.
I wanted to load Page2 somehow in the background, while Page1 is still being displayed to the user, so the Background-image of Page2 does not pop up after the Page is shown.
But I have found myself a suitable solution on my problem.
I use the following code on Page1:
late Image backgroundImage;
#override
void initState() {
super.initState();
backgroundImage = Image.asset("path to image");
Timer(Duration(seconds: 5),
() => Navigator.pushReplacementNamed(context, page2));
}
#override
void didChangeDependencies(){
super.didChangeDependencies();
precacheImage(backgroundImage.image, context);
}
This results in preloading the image while Page1 is shown, so the Background-Image does not pop up after Page2 is displayed to the user.

Splash screen appears with delay

I'm trying to implement the flutter Getx plugin and made some splash screen like this one:
class SplashScreen extends StatelessWidget {
const SplashScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetBuilder(
init: SplashController(),
builder: (_) => Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
)
);
}
}
Then I wrote a splashcontroller like this one:
class SplashController extends GetxController {
#override
void onReady() {
// TODO: implement onReady
super.onReady();
myFunctionCalculations();
final Controller _controller = Get.put(controller());
List items = _controller.items;
if (items > 0) {
Get.off(OnePlayerScreen());
}
}
}
Now from my HomeScreen I tap a button to navigate to this splash screen. It does nothing more only
onPressed: () {
Get.to(SplashScreen());
}
The thing is that I want to show the circular indicator while my function is running and when it populates the List items to go to the Items Screen, but when I tap the button to get the splash screen it gets a while until the splash screen appear and then immediately goes to the Items screen, because meanwhile it populated the List. When I don't initialize the function it get immediately to the splash screen and I see the indicator.
Why I get this functionality? I thought it's supposed to show the loading indicator and meanwhile populate the List. But it seems when I tap the button from HomeScreen the function is initialized and when its donde the splashcreen appears.
What am I doing wrong?
Didn't you forget to call update() in your calculation function?
And it seems that your function is async, so you should await it before populating the list

How to go back and refresh the previous page in Flutter?

I have a home page which when clicked takes me to another page through navigates, do some operations in then press the back button which takes me back to the home page. but the problem is the home page doesn't get refreshed.
Is there a way to reload the page when i press the back button and refreshes the home page?
You can trigger the API call when you navigate back to the first page like this pseudo-code
class PageOne extends StatefulWidget {
#override
_PageOneState createState() => new _PageOneState();
}
class _PageOneState extends State<PageOne> {
_getRequests()async{
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new RaisedButton(onPressed: ()=>
Navigator.of(context).push(new MaterialPageRoute(builder: (_)=>new PageTwo()),)
.then((val)=>val?_getRequests():null),
),
));
}
}
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
//somewhere
Navigator.pop(context,true);
}
}
Or you can just use a stream if the API is frequently updated, the new data will be automatically updated inside your ListView
For example with firebase we can do this
stream: FirebaseDatabase.instance.reference().child(
"profiles").onValue
And anytime you change something in the database (from edit profile page for example), it will reflect on your profile page. In this case, this is only possible because I am using onValue which will keep listening for any changes and do the update on your behalf.
(In your 1st page): Use this code to navigate to the 2nd page.
Navigator.pushNamed(context, '/page2').then((_) {
// This block runs when you have returned back to the 1st Page from 2nd.
setState(() {
// Call setState to refresh the page.
});
});
(In your 2nd page): Use this code to return back to the 1st page.
Navigator.pop(context);
use result when you navigate back from nextScreen as follow :
Navigator.of(context).pop('result');
or if you are using Getx
Get.back(result: 'hello');
and to reload previous page use this function :
void _navigateAndRefresh(BuildContext context) async {
final result = await Get.to(()=>NextScreen());//or use default navigation
if(result != null){
model.getEMR(''); // call your own function here to refresh screen
}
}
call this function instead of direct navigation to nextScreen
The solution which I found is simply navigating to the previous page:
In getx:
return WillPopScope(
onWillPop: () {
Get.off(() => const PreviousPage());
return Future.value(true);
},
child: YourChildWidget(),
or if you want to use simple navigation then:
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) =>PreviousPage() ,));
Simply i use this:
onPressed: () {
Navigator.pop(context,
MaterialPageRoute(builder: (context) => SecondPage()));
},
this to close current page:
Navigator.pop
to navigate previous page:
MaterialPageRoute(builder: (context) => SecondPage())
In FirtsPage, me adding this for refresh on startUpPage:
#override
void initState() {
//refresh the page here
super.initState();
}
For a more fine-grained, page-agnostic solution I came up with this Android Single LiveEvent mimicked behaviour.
I create such field inside Provider class, like:
SingleLiveEvent<int> currentYearConsumable = SingleLiveEvent<int>();
It has a public setter to set value. Public consume lets you read value only once if present (request UI refresh). Call consume where you need (like in build method).
You don't need Provider for it, you can use another solution to pass it.
Implementation:
/// Useful for page to page communication
/// Mimics Android SingleLiveEvent behaviour
/// https://stackoverflow.com/questions/51781176/is-singleliveevent-actually-part-of-the-android-architecture-components-library
class SingleLiveEvent<T> {
late T _value;
bool _consumed = true;
set(T val) {
_value = val;
_consumed = false;
}
T? consume() {
if (_consumed) {
return null;
} else {
_consumed = true;
return _value;
}
}
}
await the navigation and then call the api function.
await Navigator.of(context).pop();
await api call
You can do this with a simple callBack that is invoked when you pop the route. In the below code sample, it is called when you pop the route.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
_someFunction()async{
Navigator.of(context).push(MaterialPageRoute(builder: (_)=> PageTwo(
onClose():(){
// Call setState here to rebuild this widget
// or some function to refresh data on this page.
}
)));
}
#override
Widget build(BuildContext context) {
return SomeWidget();
}
...
} // end of widget
class PageTwo extends StatelessWidget {
final VoidCallback? onClose;
PageTwo({Key? key, this.onClose}) : super(key: key);
#override
Widget build(BuildContext context) {
return SomeWidget(
onEvent():{
Navigate.of(context).pop();
onClose(); // call this wherever you are popping the route
);
}
}