Flutter GNav disappears when using Navigator.pushNamed() - flutter

I implemented an app that uses GNav from google_nav_bar.dart. Most things work fine and I really like it, i can manually switch pages by clicking on the tab icons of the navbar. But when I am trying to change the page in the code (e.g. Buttonpress or after the User did a certain action) with Navigator.pushNamed the navbar disappears. I know that this is normal bcs why should the navbar stay, but I have no idea how to manage this. There are not that many examples on the internet. Would be pretty nice if someone could help me!
Explanation of the app: the User scans a barcode of a book, and after the scan he gets redirected to the app page, where the user can find reviews to that book. And i want the navbar on that reviews page as well.
Here the simplified code where the navigation happens, the different screens just return a Scaffold, they are not implemented yet:
class MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
}
int _selectedIndex = 0;
static final List<Widget> _pages = <Widget>[
const ScannerScreen(),
const AllReviewsScreen(),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('ReviewApp'),
),
body: _pages.elementAt(_selectedIndex),
bottomNavigationBar: Container(
color: Colors.black,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 0),
child: GNav(
gap: 8,
backgroundColor: Colors.black,
color: Colors.white,
activeColor: Colors.white,
tabBackgroundColor: Colors.grey.shade800,
padding: const EdgeInsets.all(12),
tabs: const [
GButton(
icon: Icons.qr_code_scanner,
text: 'Scanner',
),
GButton(
icon: Icons.reviews,
text: 'Alle Reviews',
),
],
selectedIndex: _selectedIndex,
onTabChange: (index) {
setState(() {
_selectedIndex = index;
});
},
),
),
),
),
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case '/scanner':
return FadeRoute(const ScannerScreen());
case '/all_reviews':
return FadeRoute(const AllReviewsScreen());
default:
return null;
}
});
}
}
I tried to use a tabController, but GoogleNavBar has no controller, so I think it handles this another way, but I hardly found stuff on the internet, in every example they only switch screen using the navbar. Of course i could just implement the navbar on every screen, but there must be an easier way

It is obvious behavior because when you call Navigator. push it immediately pushes the UI to a new screen where your bottomAppBar is not available.
Here is an article about what you want:
Multiple Navigators with BottomNavigationBar

Related

Flutter Feature Discovery Not SHowing Overlay

I had built an application and I'm trying to show feature discovery but, It is not showing discovery. Also not showing any error.
Question I Referred To Solve Issue
Main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) async {
runApp(
FeatureDiscovery(
child: MaterialApp(
home: SplashScreen(),
),
),
);
});
}
ServicesOffersReviewsPhotos.dart
class ServicesOffersReviewsPhotos extends StatefulWidget {
#override
_ServicesOffersReviewsPhotosState createState() =>
_ServicesOffersReviewsPhotosState();
}
class _ServicesOffersReviewsPhotosState
extends State<ServicesOffersReviewsPhotos>
with SingleTickerProviderStateMixin {
late TabController _controller;
#override
void initState() {
_controller = TabController(length: 4, vsync: this, initialIndex: 0);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kOffWhiteBackGroundColor,
appBar: AppBar(
backgroundColor: kOffWhiteBackGroundColor,
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: kDarkGrey,
size: MediaQuery.of(context).size.width*0.05,
),
onPressed: () {
Navigator.pop(context);
},
),
actions: [
IconButton(
icon: Icon(
Icons.favorite_border,
color: kDarkGrey,
size: MediaQuery.of(context).size.width*0.05,
),
onPressed: () {},
),
IconButton(
icon: Icon(
Icons.share,
color: kDarkGrey,
size: MediaQuery.of(context).size.width*0.05,
),
onPressed: () {},
),
],
),
body: SafeArea(
child: DescribedFeatureOverlay(
featureId: 'add_item_feature_id', // Unique id that identifies this overlay.
tapTarget: const Icon(Icons.add), // The widget that will be displayed as the tap target.
title: Text('Add item'),
description: Text('Tap the plus icon to add an item to your list.'),
backgroundColor: Theme.of(context).primaryColor,
targetColor: Colors.black,
textColor: Colors.black,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
children's...
],
),
),
),
);
}
}
My Application Flow
SplashScreen (PushReplacement) -> onBoardingScreen(PushReplacement) -> LoginScreen(PushReplacement) -> Home(push) -> ServicesOffersReviewsPhotos
I have tried placing feature discovery on the top of servicesoffersReviewsPhotos Scaffold but didn't get any output.
I have referred every blog on medium, youtube videos. But didn't found any solution.
Question's probably old and abandoned but I think Feature Discovery is a great plugin with a lot of potential so, here goes...
I had cause to use the feature discovery plugin and ran into the same issue. After hours of reading their documentation, it turns out that the reason it seems like nothing is happening is because once the feature is show cased once or twice, Feature discovery automatically records that you have seen it in the shared preferences without telling you.
So, you'll notice that when you uninstall the app completely and clear all data, feature discovery seems to work again the first few times until it saves to shared preference again and doesn't seem to work again.
This is the expected behaviour in production environment as it saves you the hassle to writing an onComplete callback function that involves shared preferences.
For the sake of development, they provided this function
FeatureDiscovery.clearPreferences
It essentially deletes the shared preferences data. So you can see it as many times as you want.
But the neatest way to stop auto save to shared preferences is to set this
recordStepsInSharedPreferences: false,
or this
persistenceProvider: NoPersistenceProvider(),
as an argument to the FeatureDiscovery widget like this
home: FeatureDiscovery(
recordStepsInSharedPreferences: false,
child: DemoApp()
),
If none of these work, try using only the compulsory parameters of DescribedFeatureOverlay before adding optional parameters one by one and testing each to see the faulty code.
link to the pub
Cheers.

Flutter: Persistent BottomAppBar and BottomNavigationBar

I am new to flutter, and I am building an app that has BottomAppBar which takes the property bottomNavigationBar: of Scaffold() for my home_screen, because I needed it to be at the bottom of the screen and persistent throughout the pages, and I also need a BottomNavigationBar to be persistent also at the top of BottomAppBar, but I can't make that happen because BottomAppBar already takes the bottomNavigationBar: property.
How can I make my BottomNavigationBar persistent alongside my BottomAppBar?
Note: I am using PageView() to scroll through my pages and it will be controlled by the BottomNavigationBar
Edit: attached here is the UI that I am trying to achieve
code snippet:
import 'package:flutter/material.dart';
//screens
import 'package:timewise_flutter/screens/calendar_screen.dart';
import 'package:timewise_flutter/screens/covey_quadrants_screen.dart';
import 'package:timewise_flutter/screens/kanban_screen.dart';
import 'package:timewise_flutter/screens/todo_list_screen.dart';
class OverviewScreen extends StatefulWidget {
static const String id = 'overview_screen';
//const OverviewScreen({Key? key}) : super(key: key);
#override
_OverviewScreenState createState() => _OverviewScreenState();
}
class _OverviewScreenState extends State<OverviewScreen> {
PageController _pageController = PageController(initialPage: 2);
int _bottomNavBarCurrentIndex = 2;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
bottomNavigationBar: SafeArea(
child: BottomAppBar(
elevation: 16.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
tooltip: 'Menu',
icon: Icon(Icons.menu_rounded),
onPressed: () {
print('menu icon pressed!');
//TODO: show bottom modal bottom sheet
},
),
IconButton(
tooltip: 'Pomodoro Timer',
icon: Icon(Icons.hourglass_empty_rounded),
onPressed: () {
print('pomo icon pressed!');
//TODO: show pomodoro timer modal bottom sheet
},
),
IconButton(
tooltip: 'Add',
icon: Icon(Icons.add_circle_outline_outlined),
onPressed: () {
print('add icon pressed!');
//TODO: show add task modal bottom sheet
},
),
],
),
);,
),
body: PageView(
controller: _pageController,
onPageChanged: (page) {
setState(() {
_bottomNavBarCurrentIndex = page;
});
},
children: [
CalendarScreen(),
ToDoListScreen(),
SafeArea(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Overview Screen'),
BottomNavigationBar(
currentIndex: _bottomNavBarCurrentIndex,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
elevation: 0.0,
iconSize: 16.0,
selectedItemColor: Colors.black,
unselectedItemColor: Colors.grey,
showSelectedLabels: false,
showUnselectedLabels: false,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.calendar_today_rounded),
label: 'Calendar',
tooltip: 'Calendar',
),
BottomNavigationBarItem(
icon: Icon(Icons.checklist_rounded),
label: 'To-Do',
tooltip: 'To-Do List',
),
BottomNavigationBarItem(
icon: Icon(Icons.panorama_fish_eye_rounded),
label: 'Overview',
tooltip: 'Overview',
),
BottomNavigationBarItem(
icon: Icon(Icons.border_all_rounded),
label: 'Covey\'s 4 Quadrants',
tooltip: 'Covey\'s 4 Quadrants',
),
BottomNavigationBarItem(
icon: Icon(Icons.view_column_rounded),
label: 'Kanban Board',
tooltip: 'Kanban Board',
),
],
onTap: (index) {
setState(() {
_bottomNavBarCurrentIndex = index;
_pageController.jumpToPage(index);
});
},
),
],
),
),
),
CoveyQuadrantsScreen(),
KanbanScreen(),
],
),
);
}
}
Unfortunately, this is not a standard way in which the mobile app UI should be designed. This will result in bad user experience.
What if user accidently touches on NavigationBar instead of
AppBar. You will be taken to the new screen and action that I
performed there will be lost or need to handle.
So proper UI guidelines should be met, while we design and develop for the mobile app. Based on guidelines from material.io
Bottom app bars should be used for:
Mobile devices only
Access to a bottom navigation drawer
Screens with two to five actions
Bottom app bars shouldn't be used for:
Apps with a bottom navigation bar
Screens with one or no actions
Refer this link for more useful information about the UI and UX guidelines https://material.io/
I would suggest making a scaffold() with the bottomNavigationBar() as you did. Then you could create a list of Container() objects each representing a different page. For your PageView I'm assuming you have done that, if not then that's the way to do it. Then you could cycle through your pages by setting the body: property of the scaffold to myPages[_currentIndex] or something like that.
Additionally: Like the comment asks, I am also not sure why you would want both BottomNavigationBar and BottomAppBar they both do exactly the same thing. In either case the process is the same as what I described above.

Flutter: Navigation Architecture with BottomBar Menu

I still don't have full grasp of the build and navigation process in Flutter and was wondering on how to design the main navigation architecture of a more complex app with many pages.
In my case, I have a HomePage with a Scaffold, a Bottom Bar for Navigation and 5 items in it.
class _HomePageState extends State<HomePage> {
int _selectedIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(...),
body: [Page1(), Page2(), Page3(), Page4(), Page5()].elementAt(_selectedIndex),
bottomNavigationBar: BottomAppBar(
child: Row(
children: [
IconButton(
icon: Icon(
FeatherIcons.home,
size: 27,
color: (_selectedIndex == 0)
? Colors.yellow[600]
: Colors.grey[800],
),
onPressed: () {
setState(() {
_selectedIndex = 0;
});
},
),
// four more Icon Buttons ....
]
),
),
);
and on Page2() for example I have two tabs on the page, and I used the pattern:
Scaffold(
...
body: (_index = 0) ? TabPage1() : TabPage2(),
)
where I have two IconButtons in the AppBar.title to change the index.
Are these bad practices for Navigation? Should I use a PageView for the 5 main pages in the HomePage or what is the best way to handle the Navigation?

How to update a custom Stateful Widget using Floating Action Button

I've recently started using Flutter just for fun, and I'm stuck on adding actual functionality to the code without having everything inside one class.
Essentially, I'm trying to use a FloatingActionButton to increment the value of a Text Widget which stores the value of the user's level as an integer, but I don't want to have the whole app as a StatefulWidget because only the level is going to be updated. When the button is pressed, the value should increment by 1 and then show the new value on the screen.
I have the Level Text Widget inside a StatefulWidget class along with a function to update the level by one and set the state; the MaterialApp inside a StatelessWidget class; and the main body code inside another StatelessWidget class.
If this isn't the best way to do it please do let me know so I can improve for future projects, thanks.
main.dart
import 'package:flutter/material.dart';
main() => runApp(Start());
/// The Material App
class Start extends StatelessWidget{
#override
Widget build(BuildContext context){
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.grey[800],
appBar: AppBar(
title: Text("Home Page"),
backgroundColor: Colors.cyan,
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.orange,
child: Icon(Icons.add, color: Colors.black,),
),
body: HomePage(),
),
);
}
}
/// Main Content for the page (body)
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// removed other children so there's less code to scan through for you :)
Padding(
padding: EdgeInsets.fromLTRB(30, 0, 0, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Text that just says "Level"
Text(
"Level",
style: TextStyle(
color: Colors.orange,
fontWeight: FontWeight.bold,
fontSize: 32,
),
),
// space between text and actual level value
SizedBox(height: 10),
// Create new level widget
Level(),
],
),
),
],
),
);
}
}
/// Updating level using a Stateful Widget
class Level extends StatefulWidget{
#override
State<StatefulWidget> createState(){
return _LevelState();
}
}
class _LevelState extends State<Level>{
int level = 0;
void incrementLevel(){
setState(() {
level += 1;
});
}
#override
Widget build(BuildContext context){
return Text(
"$level",
style: TextStyle(
color: Colors.grey[900],
fontWeight: FontWeight.normal,
fontSize: 28,
),
);
}
}
It actually is a weird way of doing it. However, there is various ways of achieving this
To give an example:
You can use KEYs to remotely redraw the child state
If you want an advanced solution that can assist you in bigger projects. You can use state management tecniques. You can find a lot of tutorials in the internet but these are some of them. BLOC, Provider, InheritedWidget.
Basicaly all of them does the same thing. Lifts up the state data so the place of the redrawn widget on the widget tree will not be important.
I strongly encourage you to watch some tutorials starting with the Provider. I hope this helps

Changing the Bottom Navigation Bar when switching between screens in the body of a Scaffold

HomeScreen() function call the Home screen of App.
How I Can route/move to "Team", "Add", etcetera page without BottomNavigationBar and AppBar.
I want show another page and back button, with new Bottom Navigation Bar.
I have this on my Flutter Project:
class APPMain extends StatefulWidget {
#override
_APPMainState createState() => _APPMainState();
}
class _APPMainState extends State<APPMain> {
int _currentIndex = 0;
_onTapped(int index) {
setState(() {
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
List<Widget> screens = [
HomeScreen(),
Center(child: Text("Team")),
Center(child: Text("Add")),
Center(child: Text("Search")),
Center(child: Text("Settings")),
];
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xffffffff),
iconTheme: IconThemeData(color: Colors.grey),
title: Text("Test App", style: TextStyle(color: Colors.grey),),
actions: <Widget>[
IconButton(
icon: Icon(Icons.account_circle),
onPressed: (){},
),
],
),
body: Container(
color: Color(0xfff4f4f4),
child: Center(
child: screens[_currentIndex],
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
fixedColor: Colors.red,
onTap: _onTapped,
items: [
BottomNavigationBarItem(
title: Text('Home'), icon: Icon(Icons.home)),
BottomNavigationBarItem(
title: Text('Team'), icon: Icon(Icons.group)),
BottomNavigationBarItem(
title: Text('Add'), icon: Icon(Icons.add)),
BottomNavigationBarItem(
title: Text('Search'), icon: Icon(Icons.search)),
BottomNavigationBarItem(
title: Text('Settings'), icon: Icon(Icons.settings)),
]),
);
}
}
Thank you so much for help.
This is almost certainly a duplicate but I wasn't able to find a question asking something similar with a quick search so I'll answer anyways.
The answer is actually quite simple, but requires understanding a bit more about how to write flutter applications - you should be using a Navigator or the navigator built right into MaterialApp or WidgetApp rather than making your own navigation. The simplest way is to use MaterialApp's routes property and pass in a map with each of your pages. Then when you want to switch pages, you simply use Navigator.pushNamed(context, <name>) from wherever you want to switch the page (i.e. a button).
The part that can be slightly confusing when you come from other frameworks is that rather than having one Scaffold and switching the body of it, the entire page should switch and each page should have a Scaffold.
Here's an example in the documentation showing how to navigate between pages.
For the record, although it's a bad idea you could make it work with your original code as well - all you'd have to do is build a different BottomNavigationBar with different options depending on what _currentIndex is set to. But I don't recommend that. With what I've suggested you also get animations between pages, back button functionality, you can hook up analytics to track page usage, and a bunch more things that flutter provides as part of navigation.