I have two Stateful Widget classes. In one of them is a PageView. I have no problem going between pages in this pageview. I have a setstate with an int which holds the position of the current page and a controller on the pageview. From my second class I have one of these
Navigator.push(context, MaterialPageRoute(builder: (context) => SwitchPages()));
This call brings me back to the first page of the pageview, but id like to go to a different page. Im sure theres a simple way but ive tried
setState(() {
ind = 1;
controller.jumpToPage(1);
});
But it gives me an error that the controller has not been assigned and there are no positions. Any sugguestions?
Related
In my app, I have a homepage that has 5 tabs on the bottom. On each tabbed page, there is an app bar that has a '+' symbol as an action, which navigates you to a different page. The navigation with that '+' button to the new page is done with the following code, alongside the Flutter Platform Widgets package:
Navigator.of(context, rootNavigator: true)
.push(
platformPageRoute(
context: context,
builder: (context) => Page1(),
),
);
I use the platformPageRoute feature as an easy way to navigate with a native feel. Now, that works fine to navigate to a new page, but the issue comes when I use
Navigator.pop(context);
to navigate back to the original page. When I use that to navigate back to that original page, it pays no attention to the tab that was selected originally. For example, if I were originally on the second tab on the homepage and then use the '+' button on that tab and then finally use
Navigator.pop(context);
on that new page, it returns the first tab of the homepage. Is there any way of ensuring when I use the above command, it goes to the right tab? I have tried something along the lines of:
Navigator.popUntil(context, '/homepageTab2');
alongside a named route, to return to the correct tab on the homepage, although that returns a black screen. Why might that be? I have also tried using:
Navigator.pushAndRemoveUntil(
context,
platformPageRoute(
context: context,
builder: (context) =>
HomePage(selectedPage: 1),
),
(route) => false,
);
This does not work either, since it returns the selected/correct page tab content, but with the first tab selected. In addition, the other
'problem' for me is that the animation is a 'push' one and that doesn't 'match' with the animation when I have more often used
Navigator.pop(context);
to navigate back to a screen. Is there a way to maybe use pushAndRemoveUntil but then change the animation to match a pop animation?
Thanks!
EDIT:
I have just noticed that with the situation I have described above, it is actually returning the correct screen content when I use Navigator.pop(context); but the tab in the tab bar at the bottom is showing as the first tab, in the second tab's position, essentially duplicating the first tab, until I navigate to a new tab and back, at which time it shows the correct tab in the correct position. I hope that makes sense!
As it turns out, the issue wasn't related to Navigator.pop(context); being used. It was the way I was controlling the selected tab. I'm posting this as an answer incase it helps someone else.
Initially, I created late values for a tab controller and the current selected page, like so:
late TabController _tabController;
late ScrollController _scrollController;
late int _selectedPage;
Then, I created a list of widgets that represented the actual page to display for each selected tab:
List<Widget> _pageWidgets = <Widget>[
Page1();
Page2();
Page3();
Page4();
Page5();
];
Then (and I think this was the bit that wasn't working) I used initState() as follows:
void initState() {
super.initState();
// Initialising a value that allows the 'final' page selector to be changed
_selectedPage = widget.selectedPage;
// Initialising the tab controller
_tabController = TabController(
length: 5,
vsync: this,
initialIndex: _selectedPage,
);
// updating the tab index when a new item is selected
_tabController.addListener(() {
setState(() {
_selectedPage = _tabController.index;
//_tabIndex = _tabController.index;
});
});
// Creating the scroll controller
_scrollViewController = ScrollController();
// Scrolling view to top when a new tab is selected
_tabController.addListener(() {
setState(() {
_scrollViewController
.jumpTo(_scrollViewController.position.minScrollExtent);
});
});
}
I then controlled the page content like this:
body: _pageWidgets.elementAt(_selectedPage),
I'm not 100% sure why this wasn't working, although I believe it would have something to do with the fact that initState() would only be called during the build and therefore placing the functionality inside there would mean changes wouldn't be detected. Either way, my new method, which works perfectly, is:
/// Controls the screen to display first
int _index = 0;
/// Creating a navigation key to control tab bar navigation
final _navigationKey = GlobalKey<CurvedNavigationBarState>();
Then, within the Scaffold() I show the page content like this:
body: _pageWidgets.elementAt(_index),
And finally, within the navigation bar (which is the CurvedNavigationBar() package from pub.dev) I give it a key and the index:
key: _navigationKey,
index: _index,
And this controls it perfectly, showing the correct tab.
Sub-pages of a TabBarView cannot be navigated using Navigator.
You can use TabController to go to your desired tab page after awaiting Navigator.push():
await Navigator.of(context, rootNavigator: true)
.push(
platformPageRoute(
context: context,
builder: (context) => Page1(),
),
);
tabController.animateTo(<index of tab>);
I have a problem with a BottomNavigationBar. My app has two screens and one of them has a bottom sheet that doesn't hide on disposing of the first screen when I'm tapping on the icon of the second screen. BottomNavigationBar works normally and screens switch, but how to hide the bottom sheet of the first screen when shown the second screen I have no ideas. Could you help me solve that?
Calling:
void _showForm(int id) async {
showBottomSheet(
context: context,
elevation: 5,
builder: (context) => BottomSheetSwitch(_refreshJournals, id));
}
Closing (inside bottom sheet):
Navigator.of(context).pop();
P.S. I couldn't solve that problem, so I change showBottomSheet to showModalBottomSheet like that was in: how to set showModalBottomSheet to full height?
You can try to provide a key to the stateful widget of BottomSheet. And access that key's context just use the Navigator's pop method on tap or change of the screen. It might help you.
You can use it like
Navigator.of(key.currentContext).pop();
Else call the function of your BottomSheet using the key. Like as below
Declare GlobalKey with state in your currentWidget.
GloabalKey<BottomSheetWidgetState> closeKey = GloabalKey();
Declare one function closeBottomSheet in your bottomsheet widget state.
void closeBottomSheet(){ Navigator.of(conext).pop(); }
Call the function using the key.
closeKey.currentState.closeBottomSheet();
And done.
Currently, I can submit edits to a single page in a PageView and then either Navigator.push to a newly created single edited page or Navigator.pop back to the original Pageview containing the unedited page.
But I'd prefer to pop back to the the same place in an updated/refreshed Pageview. I was thinking I could do this on the original PageView page:
Navigator.pushReplacement(context,new MaterialPageRoute(
builder: (BuildContext context) => EditPage()),);
But after editing, how can I pop back to a refreshed PageView which is scrolled to the now updated original page? Or is there a better way? Someone mentioned keys, but I've not yet learned to use them.
The question deals with the concept of Reactive App-State. The correct way to handle this is through having an app state management solution like Bloc or Redux.
Explanation: The app state takes care of the data which you are editing. the EditPage just tells the store(App-State container) to edit that data and the framework takes care of the data that should be updated in the PageView.
as a temporary solution you can use an async call to Navigation.push() and refresh the PageView State once the EditPage comes back. you can also use an overloaded version of pop() to return a success condition which aids for a conditional setState().
Do you know that Navigator.pushReplacement(...) returns a Future<T> which completes when you finally return to original context ?
So how are you going to utilize this fact ?
Lets say you want to update a String of the original page :
String itWillBeUpdated="old value";
#override
Widget build(BuildContext ctx)
{
.
.
.
onPressesed:() async {
itWillBeUpdated= await Navigator.pushReplacement(context,new MaterialPageRoute(
builder: (BuildContext context) => EditPage()),);
setState((){});
},
}
On your editing page , you can define Navigator.pop(...) like this :
Navigator.pop<String>(context, "new string");
by doing this , you can provide any data back to the original page and by calling setState((){}) , your page will reflect the changes
This isn't ideal, but works somewhat. First I created a provider class and added the following;
class AudioWidgetProvider with ChangeNotifier {
int refreshIndex;
setRefreshIndex (ri) {
refreshIndex = ri;
return refreshIndex;
}
}
Then in my PageView Builder on the first page, I did this;
Widget build(context) {
var audioWidgetProvider = Provider.of<AudioWidgetProvider>(context);
return
PreloadPageView.builder(
controller: PreloadPageController(initialPage: audioWidgetProvider.refreshIndex?? 0),
Then to get to the EditPage (2nd screen) I did this;
onPressed: () async {
audioWidgetProvider.setRefreshIndex(currentIndex);
Navigator.pushReplacement(context,new MaterialPageRoute(builder: (BuildContext context) => EditPage()),); }
And finally I did this to return to a reloaded PageView scrolled to the edited page;
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) =>HomePage()));
The only problem now is that the PageView list comes from a PHP/Mysql query and I'm not sure what to do if new items are added to the list from the Mysql database. This means the currentIndex will be wrong. But I guess that's the topic of another question.
I'm making an app with Dart/ flutter now and having an issue with routing pages.
The app is basically the matching app, and I want to add the functionality that a user can edit their profile. Currently, we have 4 stateful widgets: Match(), Message(), MyProfile() and EditProfile(). In the bottom navigation bar, I put three widgets, Match(), Message(), and MyProfile(); when the user wants to change the profile information, the person goes to MyProfile() and click "edit profile" button, which takes the user to EditProfile. After the user changes the information, I want to rout the page to MyProfile() for allow the user to check the profile info.
The code below is some part of the Navigation bar.
class _NavigationHomeState extends State<NavigationHome> {
int _currentIndex = 0;
final List<Widget> _children = [
Match(),
Messages(),
MyProfile(),
];
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
and I put the code below in one of the button of the EditProfile().
Navigator.of(context).pushNamed('/myProfile');
Then gave me an error saying
Error: Could not find the correct Provider above this
MyProfile Widget
This likely happens because you used a BuildContext that does not
include the provider of your choice. There are a few common scenarios:
The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route,
then other routes will not be able to access that provider.
You used a BuildContext that is an ancestor of the provider you are trying to read.
Make sure that MyProfile is under your
MultiProvider/Provider. This usually happen when you are
creating a provider and trying to read it immediatly.
I guess I'm getting this error because we set the _currentIndex in the bottom navigation bar as 0, which is Match(), so Navigator.of(context).pushNamed('/myProfile'); trying to take the user to the Match() page (?)
How can we take the user to the MyProfile page after the person save and click the button on EditProfile()?
Try using MaterialPageRoute instead of named routes.
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
}
If you going to EditProfile from MyProfile widget then I suggest to just pop the screen. As this will not reload complete screen, you can always pass data while doing pop.
RaisedButton(
onPressed: () {
// The Yep button returns "Yep!" as the result.
Navigator.pop(context, 'Yep!');
},
child: Text('Yep!'),
);
Another option is to use pushNamedAndRemoveUntil method.
navigator.pushNamedAndRemoveUntil('/MyProfile', ModalRoute.withName('/MyProfile'));
I guess I'm getting this error because we set the _currentIndex in the
bottom navigation bar as 0, which is Match(), so
Navigator.of(context).pushNamed('/myProfile'); trying to take the user
to the Match() page (?)
Not sure about your error but you can pass data as argument while doing pushNamed.
RaisedButton(
child: Text("Navigate to MyProfile"),
onPressed: () {
Navigator.pushNamed(
context,
'/myProfile',
arguments: [
'Extract Arguments Screen',
'This message is extracted in the build method.',
],
);
},
),
In my project I move from the current page (2) to the previous one (1) an updated a List, through the constructor, with the back button in the appBar
onPressed: () => Navigator.pushReplacement (
context,
new MaterialPageRoute (
builder: (context) => new PagesListPage (
pagesList: list Pages,
)),
)
The data is updated correctly on the previous page
but Flutter rightly 'duplicates' the previous page (1) in the stack, that is:
on page 1 - tap on button to go to page 2 ---> I see page 2
on page 2 - tap on button back in appBar (navigator.pushReplacement) ---> I see page 1
on page 1 - tap on button back in appBar ---> I see again page 1
How can I eliminate the double occurrence of page 1 in the stack?
Or alternatives to update the List on page 1 from page 2.
I'm starting out with Flutter, and I've also tried with Provider, but if I understand correctly it can only update a 'value', I couldn't get it to update a 'List'!
Thanks for any valuable suggestions
On page 1 navigator:
buttonPressPage1() async {
var updatedData = await Navigator.pushReplacement(
context, new MaterialPageRoute(builder: (context) => page1));
setState(() {
oldData = updatedData;
});
}
On page 2 navigator:
buttonPressPage2() {
Navigator.of(context).pop(updatedData);
}
Using await to you can update the data.