Flutter how to load new screen by tap on navigation bar - flutter

I have created a custom bottom navigation bar for my app but I messed up my code. Right now its just shifting screen by true false value. I want to load screen but what I done is simple showing screen in body by bool.
My code
bottomNavigationBar: CustomBottomNavigationBar(
iconList: [
'images/ichome.png',
'images/icservice.png',
'images/icstore.png',
'images/Component 7 – 1#2x.png',
],
iconList2: [
'images/ichomeactive.png',
'images/icserviceactive.png',
'images/icstoreactive.png',
'images/icaccount.png',
],
onChange: (val) {
setState(() {
_selectedItem = val;
print(val);
if (val == 0) {
setState(() {
home = true;
service = false;
shop = false;
account = false;
});
}
if (val == 1) {
home = false;
service = true;
shop = false;
account = false;
}
if (val == 2) {
home = false;
service = false;
shop = true;
account = false;
}
if (val == 3) {
home = false;
service = false;
shop = false;
account = true;
}
});
},
defaultSelectedIndex: 0,
),
You can see on click I am changing bool value and in body show my widget. I know its wrong I do very stupid thing. That's why I need to know how I can load the page instead of just show and hide ? Also I need to show the navigation bar also on each page.

Please refer below code of Navigation bar
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: SettingView(),
);
}
}
class SettingView extends StatefulWidget {
#override
_SettingViewState createState() => _SettingViewState();
}
class _SettingViewState extends State<SettingView> {
final tabs = [DashboardView(), NotificationView(), ProfileView()];
int _currentIndex = 0;
#override
void initState() {
setState(() {});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 40.0,
elevation: 0,
centerTitle: true,
backgroundColor: Colors.blue,
title: Text("Navigation Bar"),
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.blue,
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white.withOpacity(0.5),
items: [
BottomNavigationBarItem(
icon: InkResponse(
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Container(
padding: EdgeInsets.only(
left: 10,
),
child: Icon(
Icons.dashboard,
),
),
),
title: Padding(padding: EdgeInsets.zero),
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Container(
padding: EdgeInsets.only(
right: 10,
),
child: Icon(Icons.notifications),
),
title: Padding(padding: EdgeInsets.zero),
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Container(
padding: EdgeInsets.only(
right: 10,
),
child: Icon(Icons.account_box),
),
title: Padding(padding: EdgeInsets.zero),
backgroundColor: Colors.blue,
)
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
body: tabs[_currentIndex],
);
}
}
/*Dashboard*/
class DashboardView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("Dashboard"),
),
);
}
}
/*Notification*/
class NotificationView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("Notification"),
),
);
}
}
/*Profile*/
class ProfileView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("Profile"),
),
);
}
}

You can do something like that:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'app name',
home: HomeScreen(),
routes: <String, WidgetBuilder>{
'/route1': (BuildContext context) => FirstScreen(),
'/route2': (BuildContext context) => SecondScreen(),
},
);
}
Create reusable navigation bar Widget and for selected content just tell navigator where it needs to bring you:
Navigator.pushNamed(context, '/route1');

Related

I am trying to insert the bottom navigation bar which i have created into my main page which is the hunt view but its not working when i run the code

This is the main page where I have written the bottom bar navigation code. I have run the code, but the bar does not appear on my home page. It does not give me any errors. which means the code is fine and i just need to call it properly. How do I call the function so it displays across all other pages?
import 'package:thehunt/views/hunt/profile_view.dart';
import 'package:thehunt/views/hunt/settings_view.dart';
import 'package:thehunt/views/login_view.dart';
class HuntView extends StatelessWidget {
const HuntView({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return HomeView();
} else {
return AuthView();
}
},
),
);
}
}
//bottom navigation bar
class BottomNavView extends StatefulWidget {
const BottomNavView({super.key});
#override
State<BottomNavView> createState() => _BottomNavViewState();
}
class _BottomNavViewState extends State<BottomNavView> {
List views = [
const HomeView(),
const CurrentLocationView(),
const ProfileView(),
const SettingsView()
];
int currentIndex = 0;
void onTap(int index) {
currentIndex = index;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: views[currentIndex],
bottomNavigationBar: BottomNavigationBar(
unselectedFontSize: 0,
selectedFontSize: 0,
type: BottomNavigationBarType.fixed,
backgroundColor: Color.fromARGB(255, 198, 176, 235),
onTap: onTap,
currentIndex: currentIndex,
selectedItemColor: Colors.black54,
unselectedItemColor: Colors.grey.withOpacity(0.5),
showUnselectedLabels: false,
showSelectedLabels: false,
elevation: 0,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'Home',
icon: Icon(Icons.home_outlined),
),
BottomNavigationBarItem(
label: 'Map',
icon: Icon(Icons.map),
),
BottomNavigationBarItem(
label: 'Settings',
icon: Icon(Icons.settings),
),
BottomNavigationBarItem(
label: 'Profile',
icon: Icon(Icons.person),
),
],
),
);
}
}
Below is my home page view where I want the bottom navigation bar to be displayed
class HomeView extends StatefulWidget {
const HomeView({super.key});
#override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
final user = FirebaseAuth.instance.currentUser!;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(' Home'),
backgroundColor: Colors.deepPurple[200],
elevation: 0,
),
drawer: const NavigationDrawer(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Logged In as: ' + user.email!),
MaterialButton(
onPressed: () {
FirebaseAuth.instance.signOut();
},
color: Colors.deepPurple[200],
child: Text('sign out'),
),
MaterialButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return const CurrentLocationView();
},
),
);
},
color: Colors.deepPurple[200],
child: const Text('User location')),
],
),
),
);
}
}
Call BottomNavView in HuntView instead of HomeView as below code
class HuntView extends StatelessWidget {
const HuntView({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return BottomNavView(); <------- call here BottomNavView
} else {
return AuthView();
}
},
),
);
}
}

Flutter: Change Bottom Navigation Bar Items on real time

I am trying to see how I can change the items in BottomNavigationBar within a DefaultTabController on real time but came to no avail.
Basically these are the scenario:
Non-admin Tab Bar: [Screen A and B]
Admin Tab Bar: [Screen C, D and B]
Within screen B, there is a button to change from Admin to non-admin view or vice versa.
Thank you in advance.
Try this, it works. But, I would advise using a Provider rather than passing a WidgetState as an argument to any function or to any Widget.
List<Widget> nonAdminWidgets(_BottomNavScaffoldState parent) {
return <Widget>[
ProfilePage(parent),
ClothesPage(),
ColorsPage(),
];
}
List<Widget> adminWidgets(_BottomNavScaffoldState parent) {
return <Widget>[
ProfilePage(parent),
IdeasPage(),
];
}
int _currentIndex = 0;
bool isAdmin = true;
List<dynamic> nonAdminNavBars = [
BottomNavigationBarItem(
title: Text('Profile'),
icon: Icon(Icons.face_rounded),
),
BottomNavigationBarItem(
title: Text('Clothes'),
icon: Icon(Icons.design_services_rounded),
),
BottomNavigationBarItem(
title: Text('Colors'),
icon: Icon(Icons.colorize_rounded),
),
];
List<dynamic> adminNavBars = [
BottomNavigationBarItem(
title: Text('Profile'),
icon: Icon(Icons.face_rounded),
),
BottomNavigationBarItem(
title: Text('Ideas'),
icon: Icon(Icons.lightbulb_outline_rounded),
),
];
class BottomNavScaffold extends StatefulWidget {
#override
_BottomNavScaffoldState createState() => _BottomNavScaffoldState();
}
class _BottomNavScaffoldState extends State<BottomNavScaffold> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: isAdmin ? adminWidgets(this)[_currentIndex] : nonAdminWidgets(this)[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
backgroundColor: Colors.orangeAccent,
selectedItemColor: Colors.white,
onTap: (value) {
_currentIndex = value;
setState(() {});
},
items: isAdmin ? adminNavBars : nonAdminNavBars,
),
);
}
}
class ClothesPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(10),
child: Text(
"Clothes",
),
),
);
}
}
class ColorsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(10),
child: Text(
"Colors",
),
),
);
}
}
class IdeasPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(10),
child: Text(
"Ideas",
),
),
);
}
}
class ProfilePage extends StatelessWidget {
_BottomNavScaffoldState parent;
ProfilePage(this.parent);
#override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
isAdmin = !isAdmin;
_currentIndex = 0;
parent.setState(() {});
},
child: Text("Change"),
);
}
}

How do I add a bottomNavigationBar on one screen, but not others?

I have 3 screens for my app: an enter screen, login, and the main screen. I want the nav bar only to be on the main screen. Which widget should I return in my HomeScreen() page or what should I have in my main.dart page?
Here is the code for the main.dart:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Doctor Consultation App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
scaffoldBackgroundColor: Colors.white,
visualDensity: VisualDensity.adaptivePlatformDensity),
home: EnterScreen(),
);
}
}
home_screen.dart:
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
);
}
}
bottom_nav_bar.dart:
class BottomNavScreen extends StatefulWidget {
#override
_BottomNavScreenState createState() => _BottomNavScreenState();
}
class _BottomNavScreenState extends State<BottomNavScreen> {
final List _screens = [
MainScreen(),
Scaffold(),
Scaffold(),
Scaffold(),
];
int _currentIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: _screens[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) => setState(() => _currentIndex = index),
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
showSelectedLabels: false,
showUnselectedLabels: false,
selectedItemColor: Palette.primaryRed,
unselectedItemColor: Colors.grey,
elevation: 0.0,
items: [
Icons.home,
Icons.chat_bubble,
Icons.calendar_today,
Icons.person
]
.asMap()
.map((key, value) => MapEntry(
key,
BottomNavigationBarItem(
title: Text(''),
icon: Container(
padding: const EdgeInsets.symmetric(
vertical: 6.0, horizontal: 16.0),
decoration: BoxDecoration(
color: _currentIndex == key
? Palette.primaryRed
: Colors.transparent,
borderRadius: BorderRadius.circular(20.0)),
child: Icon(value)))))
.values
.toList()),
);
}
}
I have already tried doing bottomNavigationBar: BottomNavScreen(), on the home_screen.dart, but I get a ton of errors from the MaterialApp widget.
Change the build of the ButtomNavScreen to this:
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: _currentIndex,
...
And change the build of the MainScreen to something like this:
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavScreen(),
body: SafeArea(
child: Center(
child: Container(
width: 200,
height: 100,
color: Colors.redAccent,
child: Center(child: Text("sample text")),
),
)
),
);
}

Change color of Theme

I wanted to change the color of the counter in my app. I want to do that: change the color of the counter to blue when counter bigger than 0. if counter smaller than 0 change the color of the counter to red.if counter equal to 0 change the color of the counter to green. is it possible? I did just for 2 colors.
it is my codes :
import 'package:flutter/material.dart';
void main() {
runApp(Myapp());
}
class Myapp extends StatefulWidget {
#override
_MyappState createState() => _MyappState();
}
class _MyappState extends State<Myapp> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Myhomepage(
title: "My Home Page",
),
);
}
}
class Myhomepage extends StatefulWidget {
final String title;
Myhomepage({this.title});
#override
_MyhomepageState createState() => _MyhomepageState();
}
class _MyhomepageState extends State<Myhomepage> {
int counter = 0;
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
backgroundColor: Colors.grey[850],
onPressed: () {
setState(() {
counter++;
});
}),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text(
"Increase",
),
color: Colors.green,
onPressed: () {
setState(() {
counter++;
});
},
),
Text("The count of press button:"),
Text(
"$counter",
style: Theme.of(context).textTheme.display2.copyWith(color: counter<=0 ? Colors.red : Colors.blue)
),
RaisedButton(
child: Text(
"Decrease",
),
color: Colors.red,
onPressed: () {
setState(() {
counter--;
});
},
),
],
),
),
);
}
}
it is my results :
Here's one way you can implement the system you want. I just made a function that returns the desired color.
class _MyhomepageState extends State<Myhomepage> {
int counter = 0;
Color _getCounterColor() {
if (counter > 0) return Colors.blue;
else if (counter < 0) return Colors.red;
else return Colors.green;
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
backgroundColor: Colors.grey[850],
onPressed: () {
setState(() {
counter++;
});
}),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text(
"Increase",
),
color: Colors.green,
onPressed: () {
setState(() {
counter++;
});
},
),
Text("The count of press button:"),
Text(
"$counter",
style: Theme.of(context).textTheme.display2.copyWith(color: _getCounterColor()),
),
RaisedButton(
child: Text(
"Decrease",
),
color: Colors.red,
onPressed: () {
setState(() {
counter--;
});
},
),
],
),
),
);
}
}

Hide bottom navigation bar on scroll down and vice versa

I have a list in the body and bottom navigation bar. I want to hide bottom navigation bar with a slide down animation when the posts list is scrolled down and visible with a slide up animation when scrolled up. How to do it?
While Naveen's solution works perfectly, I didn't like the idea of using setState to handle the visibility of the navbar. Every time the user would change scroll direction, the entire homepage including the appbar and body would rebuild which can be an expensive operation. I created a separate class to handle the visibility that uses a ValueNotifier to track the current hidden status.
class HideNavbar {
final ScrollController controller = ScrollController();
final ValueNotifier<bool> visible = ValueNotifier<bool>(true);
HideNavbar() {
visible.value = true;
controller.addListener(
() {
if (controller.position.userScrollDirection ==
ScrollDirection.reverse) {
if (visible.value) {
visible.value = false;
}
}
if (controller.position.userScrollDirection ==
ScrollDirection.forward) {
if (!visible.value) {
visible.value = true;
}
}
},
);
}
void dispose() {
controller.dispose();
visible.dispose();
}
}
Now all you do is create a final instance of HideNavbar in your HomePage widget.
final HideNavbar hiding = HideNavbar();
Now pass the instance's ScrollController to the ListView or CustomScrollView body of your Scaffold.
body: CustomScrollView(
controller: hiding.controller,
...
Then surround your bottomNavigationBar with a ValueListenableBuilder that takes the ValueNotifier from the HideNavbar instance and then set the height property of the bottomNavigationBar to be either 0 or any other value depending on the status of the ValueNotifier.
bottomNavigationBar: ValueListenableBuilder(
valueListenable: hiding.visible,
builder: (context, bool value, child) => AnimatedContainer(
duration: Duration(milliseconds: 500),
height: value ? kBottomNavigationBarHeight : 0.0,
child: Wrap(
children: <Widget>[
BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.blue,
fixedColor: Colors.white,
unselectedItemColor: Colors.white,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.card_giftcard),
title: Text('Offers'),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box),
title: Text('Account'),
),
],
),
],
),
),
),
This approaches keeps avoids countless rebuilds and doesn't require any external libraries. You can also implement this as a stream-based approach but that would require another library such as dart:async and would not be changing anything. Make sure to call the dispose function of HideNavbar inside HomePage's dispose function to clear all resources used.
Working code with BottomNavigationBar.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ScrollController _hideBottomNavController;
bool _isVisible;
#override
initState() {
super.initState();
_isVisible = true;
_hideBottomNavController = ScrollController();
_hideBottomNavController.addListener(
() {
if (_hideBottomNavController.position.userScrollDirection ==
ScrollDirection.reverse) {
if (_isVisible)
setState(() {
_isVisible = false;
});
}
if (_hideBottomNavController.position.userScrollDirection ==
ScrollDirection.forward) {
if (!_isVisible)
setState(() {
_isVisible = true;
});
}
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: CustomScrollView(
controller: _hideBottomNavController,
shrinkWrap: true,
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(10.0),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => _getItem(context),
childCount: 20,
),
),
),
],
),
),
bottomNavigationBar: AnimatedContainer(
duration: Duration(milliseconds: 500),
height: _isVisible ? 56.0 : 0.0,
child: Wrap(
children: <Widget>[
BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.blue,
fixedColor: Colors.white,
unselectedItemColor: Colors.white,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.card_giftcard),
title: Text('Offers'),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box),
title: Text('Account'),
),
],
),
],
),
),
);
}
_getItem(BuildContext context) {
return Card(
elevation: 3,
margin: EdgeInsets.all(8),
child: Row(
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Item',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
)
],
),
),
),
],
),
);
}
}
Working Model
Screenshot (Null safe + Optimized)
Code:
class MyPage extends StatefulWidget {
#override
State<MyPage> createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
late final ScrollListener _model;
late final ScrollController _controller;
final double _bottomNavBarHeight = 56;
#override
void initState() {
super.initState();
_controller = ScrollController();
_model = ScrollListener.initialise(_controller);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: _model,
builder: (context, child) {
return Stack(
children: [
ListView.builder(
controller: _controller,
itemCount: 20,
itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
),
Positioned(
left: 0,
right: 0,
bottom: _model.bottom,
child: _bottomNavBar,
),
],
);
},
),
);
}
Widget get _bottomNavBar {
return SizedBox(
height: _bottomNavBarHeight,
child: BottomNavigationBar(
backgroundColor: Colors.amber,
items: [
BottomNavigationBarItem(icon: Icon(Icons.call), label: 'Call'),
BottomNavigationBarItem(icon: Icon(Icons.message), label: 'Message'),
],
),
);
}
}
class ScrollListener extends ChangeNotifier {
double bottom = 0;
double _last = 0;
ScrollListener.initialise(ScrollController controller, [double height = 56]) {
controller.addListener(() {
final current = controller.offset;
bottom += _last - current;
if (bottom <= -height) bottom = -height;
if (bottom >= 0) bottom = 0;
_last = current;
if (bottom <= 0 && bottom >= -height) notifyListeners();
});
}
}