Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have two tabs each with some data to be filled by the user. In Tab-1 (Personal Data) I have a form with some widgets where the user has to enter his personal details.
After filling all the details in the Tab-1 (form-1) only the Tab-2 (Medical History) should be navigated and visible. How to achieve this in Flutter?
We can achieve this by maintaining the count of tab bar items to be displayed.
Basically we have to create two List<Widget> which will depend on tab bar count to be displayed.
Example:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _tabBarCount = 1;
List<Widget> getTabBarList() { // Tab Bar items displayed on the App Bar
switch (_tabBarCount) {
case 1:
return [Tab(icon: Icon(Icons.search))];
case 2:
return [
Tab(icon: Icon(Icons.search)),
Tab(icon: Icon(Icons.file_download))
];
default:
return [];
}
}
List<Widget> getTabScreen() { // Screens to be displayed on Tab Bar
switch (_tabBarCount) {
case 1:
return [
RaisedButton(onPressed: () {
setState(() {
_tabBarCount = 2; // Click event, here tab bar count should increse, so that multiple tab bar can be visible.
});
}, child: Text('Save'),)
];
case 2:
return [
Text('First Screen'),
Text('Second Screen')
];
default:
return [];
}
}
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: _tabBarCount,
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
bottom: TabBar(
tabs: getTabBarList(),
),
title: Text('Tabs Demo'),
),
body: TabBarView(
children: getTabScreen(),
),
),
),
);
}
}
Related
(For example, I have a Detailpage with Similar products have the same Detailpage info(Nested navigation to same ).. how do we navigate to that page in Getx navigation or material route......and when the back button click we navigate to previous pages how do to do like that?)
flutter
class DetailPage extends StatefulWidget { final String redirectScreen; const DetailPage({super.key, required this.redirectScreen});
#override State<DetailPage> createState() => _DetailPageState(); }
class _DetailPageState extends State<DetailPage> { DetailPageController detailPageController = Get.put(DetailPageController()); late ThemeData themeData; #override void initState() {
super.initState();
detailPageController.fetchDetailPageScreen(
redirectScreen: widget.redirectScreen); } #override Widget build(BuildContext context) {
themeData = Theme.of(context);
return Scaffold(
appBar: AppBar(
backgroundColor: themeData.colorScheme.onSecondary,
title: Text(
'Detail Page',
style: AppTheme.getTextStyle(
themeData.textTheme.headline1!,
fontSize: MySize.s25,
fontWeight: 400,
color: themeData.colorScheme.primary,
),
),
),
body: ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: [
HorizontalList(
padding: EdgeInsets.only(left: MySize.s10!, right: MySize.s10!),
itemCount: detailPageController.similarProducts.length,
itemBuilder: ((context, index) {
HomeProducts model = detailPageController.similarProducts[index];
return MainProductCard(
onTap: () {
Get.to(
DetailPage (
redirectScreen: model.redirectApi!,
),
popGesture: true,
preventDuplicates: false,
);
},
productOptions: model.productOptions!,
textfieldKG: model.productOptions!.isEmpty?false :true,
imagePath: model.image!,
productName: model.productName!,
strikePrice: model.strikePrice!,
sellingPrice: model.sellingPrice!,
savePercent: model.savePercentage!,
akshyamPrice: model.akshyamPrice!,
);
}),
),
],
),
); }; };
``
Perhaps this is what you are talking about.
= change "MaterialApp " with "GetMaterialApp".
= use. Get.to(DetailedPage(details: details,))
but after some time you may use routes professionally so it most likely be like.
https://padymies.medium.com/flutter-getx-route-managment-b47635abd832 . Hope this Helps ( :
I didn't follow with the last sentence, but I'm assuming you want to navigate over routes using Getx:
if you want to navigate to other screens and be able to go back to the previous route you can use:
Get.to(YourWidgetScreen()),
and if you want to navigate to other screens with preventing user to go back to the previous screen, you can use:
Get.off(YourWidgetScreen()),
and if you want to prevent the user to go back to ant previous screens in your app (set a screen to be the only one in the stack of routes), then use:
Get.offAll(YourWidgetScreen()),
I used the bottom navigation bar and added 3 buttons to the bottom.I was swapping pages with setsate. These buttons work fine. But if I call another page inside the pages called with those buttons, the BottomNavigationBar disappears. I've researched it and found this click me. Is it logical to use Provider to call pages? I think it keeps every page in memory, doesn't it consume too much ram?
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int selectedPage = 0;
final _pageOptions = [
HomeScreen(),
InboxScreen(),
SignInScreen()
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: _pageOptions[selectedPage],
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home, size: 30), title: Text('Home')),
BottomNavigationBarItem(icon: Icon(Icons.mail, size: 30), title: Text('Inbox')),
BottomNavigationBarItem(icon: Icon(Icons.account_circle, size: 30), title: Text('Account')),
],
selectedItemColor: Colors.green,
elevation: 5.0,
unselectedItemColor: Colors.green[900],
currentIndex: selectedPage,
backgroundColor: Colors.white,
onTap: (index){
setState(() {
selectedPage = index;
});
},
)
);
}
}
This is my home page.BottomNavigationBar disappears If I clicked on the text
class _HomePage extends State<HomePage> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(appBar: AppBar(title: Text('Page2')),body:GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
},
child:Text('clik')));
}
}
I want to like this
But my code run this
The code you posted looks perfectly fine !
Your problem must be elsewhere, probably in one of the Widgets
final _pageOptions = [
HomeScreen(),
InboxScreen(),
SignInScreen()
];
There can be many root causes to your problem, you could start by identifying which Widget is making the bottomNavigationBar disappear.
Once you found it out, be sure that you didn't use the method Navigator.of(context).push which could be one of the reasons why your bottomNavigationbar is disappearing.
If you're still stuck, feel free to update your question with the source code of the Widget making the bar disappear and I'll update my answer
EDIT
As I mentioned earlier, whenever you call the Navigator.push(context, UserProfilePage); method, you're replacing your previous widget with a new one.
Since your previous Widget was holding your bottomNavigationBar, that's why it's disappearing.
What you are looking for here is, how to have a persistent navigation bar.
Here are two useful links that'll help you and the other people having this need, a Video tutorial to understand how to implement it and a well written article to fully understand how it works
One of the main mecanism of Flutter Navigator 2.0 it the function onPopPage inside RouterDelegate > build > Navigator. However, I do not understand when route.didPop(result) returns false.
We can use the John Ryan's famous example to show my question. His demo code.
onPopPage: (route, result) {
if (!route.didPop(result)) {
return false;
}
// Update the list of pages by setting _selectedBook to null
_selectedBook = null;
show404 = false;
notifyListeners();
return true;
},
On all of my tests, using AppBar autogenerated back button, route.didPop(result) returns true.
The doc stays :
bool didPop(dynamic result)
package:flutter/src/widgets/navigator.dart
A request was made to pop this route. If the route can handle it internally (e.g. because it has its own stack of internal state) then return false, otherwise return true (by returning the value of calling super.didPop). Returning false will prevent the default behavior of [NavigatorState.pop].
When this function returns true, the navigator removes this route from the history but does not yet call [dispose]. Instead, it is the route's responsibility to call [NavigatorState.finalizeRoute], which will in turn call [dispose] on the route. This sequence lets the route perform an exit animation (or some other visual effect) after being popped but prior to being disposed.
This method should call [didComplete] to resolve the [popped] future (and this is all that the default implementation does); routes should not wait for their exit animation to complete before doing so.
See [popped], [didComplete], and [currentResult] for a discussion of the result argument.
But was does "If the route can handle it internally (e.g. because it has its own stack of internal state) then return false" mean ? The route has its own stack of internal state ? How to produce this result ?
Thank you, stay safe
After some research to fully understand the Navigator 2.0, I think this might be the answer to the question:
route.didPop(result) will return false, when the Route, which are asked to pop, keeps local history entries and they have to be removed before popping the complete Route.
So what are local history entries (the stack of internal states)?
Local history entries are a way to implement local navigation within a page. You can do so using the method addLocalHistoryEntry. To understand this better, take a look at the official Flutter Docs sample:
The following example is an app with 2 pages: HomePage and SecondPage.
The HomePage can navigate to the SecondPage. The SecondPage uses a
LocalHistoryEntry to implement local navigation within that page.
Pressing 'show rectangle' displays a red rectangle and adds a local
history entry. At that point, pressing the '< back' button pops the
latest route, which is the local history entry, and the red rectangle
disappears. Pressing the '< back' button a second time once again pops
the latest route, which is the SecondPage, itself. Therefore, the
second press navigates back to the HomePage.
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (BuildContext context) => HomePage(),
'/second_page': (BuildContext context) => SecondPage(),
},
);
}
}
class HomePage extends StatefulWidget {
HomePage();
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('HomePage'),
// Press this button to open the SecondPage.
ElevatedButton(
child: Text('Second Page >'),
onPressed: () {
Navigator.pushNamed(context, '/second_page');
},
),
],
),
),
);
}
}
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
bool _showRectangle = false;
void _navigateLocallyToShowRectangle() async {
// This local history entry essentially represents the display of the red
// rectangle. When this local history entry is removed, we hide the red
// rectangle.
setState(() => _showRectangle = true);
ModalRoute.of(context).addLocalHistoryEntry(
LocalHistoryEntry(
onRemove: () {
// Hide the red rectangle.
setState(() => _showRectangle = false);
}
)
);
}
#override
Widget build(BuildContext context) {
final localNavContent = _showRectangle
? Container(
width: 100.0,
height: 100.0,
color: Colors.red,
)
: ElevatedButton(
child: Text('Show Rectangle'),
onPressed: _navigateLocallyToShowRectangle,
);
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
localNavContent,
ElevatedButton(
child: Text('< Back'),
onPressed: () {
// Pop a route. If this is pressed while the red rectangle is
// visible then it will will pop our local history entry, which
// will hide the red rectangle. Otherwise, the SecondPage will
// navigate back to the HomePage.
Navigator.of(context).pop();
},
),
],
),
),
);
}
}
To see the sample in the docs, click here.
I hope I answered the question in an understandable way.
I am trying to implement a custom navigation drawer using Flutter. I would like to attach log out option to the bottom of the drawer. The problem is that number of elements above log out option is unknow (from 3 to 17).
So if these widgets take half of the space of a drawer, then log out option will be on the bottom, and if there is too much of them and you have to scroll to see them all, then the log out option will be simply the last.
I am also trying to give the first two options a green background color. Which widget tree would you recommend me? I had a thought about the ListView widget, it takes List of widgets as an argument in constructor.
Therefore I can solve the different background color for the first two items. But I still can't figure out how to attach the log out option to the bottom. In this case it's at the bottom of drawer, but it can happen, that other options will be bigger than screen size and in that case, it should be placed at the bottom of whole list.
EDIT: I've add a design to the question. The logout option is the one called Odhlášení. In this case it's at the bottom of drawer, but it can happen, that other options will be bigger than the screen size and in that case, it should be placed at the bottom of whole list.
Design:
You can simply use ListView to manage the "17" navigation options. Wrap this ListView inside an Column. The ListView will be the first child of the Column the second child, therefore placing at the bottom, will be your logout action.
If you are using transparent widgets (like ListTile) inside your ListView to display the navigation options, you can simply wrap it inside a Container. The Container, besides many other widgets, allows you to set a new background color with its color attribute.
Using this approach the widget tree would look like the following:
- Column // Column to place your LogutButton always below the ListView
- ListView // ListView to wrap all your navigation scrollable
- Container // Container for setting the color to green
- GreenNavigation
- Container
- GreenNavigation
- Navigation
- Navigation
- ...
- LogOutButton
Update 1 - Sticky LogOutButton :
To achieve the LogOutButton sticking to the end of the ListView you'll neeed to do two things:
Replace the Expanded with an Flexible
Set shrinkWrap: true inside the ListView
Update 2 - Spaced LogOutButton until large List:
Achieving the described behavior is a more difficult step. You'll have to check if the ListView exceeds the screen and is scrollable.
To do this I wrote this short snippet:
bool isListLarge() {
return controller.positions.isNotEmpty && physics.shouldAcceptUserOffset(controller.position);
}
It will return true if the ListView exceeds its limitations. Now we can refresh the state of the view, depending on the result of isListViewLarge. Below again a full code example.
Standalone code example (Update 2: Spaced LogOutButton until large List):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
),
);
}
}
class MyDrawer extends StatefulWidget {
#override
_MyDrawerState createState() => _MyDrawerState();
}
class _MyDrawerState extends State<MyDrawer> {
ScrollController controller = ScrollController();
ScrollPhysics physics = ScrollPhysics();
int entries = 4;
#override
Widget build(BuildContext context) {
Widget logout = IconButton(
icon: Icon(Icons.exit_to_app),
onPressed: () => {setState(() => entries += 4)});
List<Widget> navigationEntries = List<int>.generate(entries, (i) => i)
.map<Widget>((i) => ListTile(
title: Text(i.toString()),
))
.toList();
if (this.isListLarge()) { // if the List is large, add the logout to the scrollable list
navigationEntries.add(logout);
}
return Drawer(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // place the logout at the end of the drawer
children: <Widget>[
Flexible(
child: ListView(
controller: controller,
physics: physics,
shrinkWrap: true,
children: navigationEntries,
),
),
this.isListLarge() ? Container() : logout // if the List is small, add the logout at the end of the drawer
],
),
);
}
bool isListLarge() {
return controller.positions.isNotEmpty && physics.shouldAcceptUserOffset(controller.position);
}
}
Standalone code example (Update 1: Sticky LogOutButton):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
),
);
}
}
class MyDrawer extends StatefulWidget {
#override
_MyDrawerState createState() => _MyDrawerState();
}
class _MyDrawerState extends State<MyDrawer> {
int entries = 4;
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: <Widget>[
Flexible(
child: ListView(
shrinkWrap: true,
children: List<int>.generate(entries, (i) => i)
.map((i) => ListTile(
title: Text(i.toString()),
))
.toList(),
),
),
IconButton(
icon: Icon(Icons.exit_to_app),
onPressed: () => {setState(() => entries += 4)})
],
),
);
}
}
Standalone code example (Old: Sticking to bottom):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
drawer: MyDrawer(),
),
);
}
}
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: <Widget>[
Expanded(
child: ListView(
children: List<int>.generate(40, (i) => i + 1)
.map((i) => ListTile(
title: Text(i.toString()),
))
.toList(),
),
),
IconButton(icon: Icon(Icons.exit_to_app), onPressed: () => {})
],
),
);
}
}
This question already has answers here:
Flutter Navigation pop to index 1
(12 answers)
Closed 4 years ago.
So I have this kind of walkthrough that pushes the user from one screen to another (Six screens in total). In the last page, I would like the user to press "Done" and take him back to the first screen without being able to pop back to any of the previous screens.
Right now it will pop the last screen but not any of the other ones so it take me back to my original screen with the ability to pop back to the screen before the last screen (hopefully that last sentence makes sense to you, because the last screen was popped it takes me back to the screen before that one).
Any idea how to get around that?
Thanks!
To pop multiple screens from the navigation stack, like in your given scenario we can use Navigator.popUntil. It takes a BuildContextand a RoutePredicate as parameters. The Navigator calls pop until the returned value by the given RoutePredicate is true.
Here is very basic example. It uses ModalRoute.withName to create the RoutePredicate:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/': (BuildContext context) => Home(),
'/another': (BuildContext context) => Another()
},
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Home'),
RaisedButton(
child: Text('Take me to another screen!'),
onPressed: () => Navigator.pushNamed(context, '/another'),
)
],
),
),
);
}
}
class Another extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Next screen.'),
onPressed: () => Navigator.pushNamed(context, '/another'),
),
RaisedButton(
child: Text('Pop em all.'),
onPressed: () => Navigator.popUntil(
context,
ModalRoute.withName('/'),
),
),
],
),
),
);
}
}
The method you are looking for is popUntil(), take a look at the implementation https://api.flutter.dev/flutter/widgets/Navigator/popUntil.html or https://api.flutter.dev/flutter/widgets/NavigatorState/popUntil.html
You will have to do probably something like
Navigator.of(context).popUntil(ModalRoute.withName('/my-target-screen'));
(take a look at https://api.flutter.dev/flutter/widgets/ModalRoute/withName.html)
or use some custom function (https://api.flutter.dev/flutter/widgets/RoutePredicate.html).