How can I reuse the BottomNavigationBar Widget across multiple screens in Flutter? - flutter

So I want to know if it is possible to create a separate class using the BottomNavigationBar widget and then use it in other classes. A working example would be helpful.

You can write your own class:
class BottomNavigation extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
...
);
}
Then import the page and use this inside the sacffold:
bottomNavigationBar: BottomNavigation,

maybe I don't really understand your problem. Is the BottomNavigationBar not supposed to be seen across many screens?
My BottomNavigationBar is in my MyApp class, which is called by the main. From the MyApp class, I start all the screens of my App. My code looks like:
class MyApp extends StatefulWidget
{
MyApp({Key key,}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp>
{
int _selectedIndex = 1;
#override
Widget build(BuildContext context)
{
///////////Here are your different screens//////////////
final _widgetOptions = [
Screen1(),
Screen2(),
Screen3(),
];
/////////////////////////////////
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Name of your App',
theme: ThemeData(primarySwatch: Colors.grey,
accentColor: Colors.blueAccent,),
home: Scaffold(
backgroundColor: Colors.black12,
body: Center
(
child: _widgetOptions.elementAt(_selectedIndex),
),
//////////Bottom navigation////////////////
bottomNavigationBar: Theme
(
data: Theme.of(context).copyWith(
// sets the background color of the `BottomNavigationBar`
canvasColor: Colors.white,
// sets the active color of the `BottomNavigationBar`
primaryColor: Colors.blueAccent,
textTheme: Theme.of(context).textTheme.copyWith(
caption: new TextStyle(
color: Colors.grey))), // sets the inactive color of the
`BottomNavigationBar`
child: new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: <BottomNavigationBarItem>
[
new BottomNavigationBarItem(icon: Icon(Icons.local_hospital), title: Text('Screen 1')),
new BottomNavigationBarItem(icon: Icon(Icons.search), title: Text('Screen 2')),
new BottomNavigationBarItem(icon: Icon(Icons.play_for_work), title: Text('Screen 3')),
],
currentIndex: _selectedIndex,
fixedColor: Colors.deepPurple,
onTap: _onItemTapped,
),
),
/////////////End of Bottom Navigation
),
);
}
void _onItemTapped(int index)
{
setState(()
{
_selectedIndex = index;
});
}
}
You have to define Screen1(), Screen2() and Screen3(). In my case, they are Statefulwidgets

Related

Flutter Firebase authstatechanges

My app has a bottom navigation bar with 4 items one of them being home.
Basically you need to login to use the other 3 items such as profile,messages,orders
So i created a login page for profile if the user is not logged it sends the user to that page and if the user is logged in it sends the user to the actual profile page. It works but the problem is if the user is logged in and wants to switch between tabs using the navigation bar like going to messages and back to profile etc. It shows the login page for a split second then the actual page it is hard to notice but very annoying.
I dont understand why it doesnt see that it has data the first time. I tried delaying reading of data by 100ms so that it wouldnt rush it but couldnt get that to work. I used a pageview to animate between tabs on navigation bar that way it didnt flicker between loginPage and the actual page but animating from Home(page 0) to profile(page 3) it animates between the pages and it looks like fast forwarded rubbish. Is there a way to make this work?
class AuthProfile extends StatelessWidget {
AuthProfile({Key? key}) : super(key: key);
final Stream<User?> authInstance = FirebaseAuth.instance.authStateChanges();
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: authInstance,
builder: (context, snapshot) {
if (snapshot.hasData) {
return const Profile();
}
else{
return const ProfileLogin();
}
});
}
}
main:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.light,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.light));
return const MaterialApp(
home: Main(),
);
}
}
class Main extends StatefulWidget {
const Main({super.key});
#override
State<Main> createState() => _Main();
}
class _Main extends State<Main> {
int _selectedIndex = 0;
final PageController _pageController = PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
children: [
const Home(),
AuthListings(),
AuthMessages(),
AuthProfile(),
]),
bottomNavigationBar: BottomNavigationBar(
iconSize: 30,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_rounded),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart_rounded),
label: 'Listings',
),
BottomNavigationBarItem(
icon: Icon(Icons.mark_as_unread_rounded),
label: 'Messages',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle_rounded), label: 'Profile')
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.purple,
unselectedItemColor: Colors.black,
showUnselectedLabels: false,
showSelectedLabels: false,
onTap: _onTappedBar,
),
);
}
void _onTappedBar(int value) {
setState(() {
_selectedIndex = value;
});
_pageController.jumpToPage(value);
}
}
EDIT: Fixed it with the help of solution
created two different lists for loggedIn and loggedOut scenario
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.light,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.light));
return const MaterialApp(
home: Main(),
);
}
}
class Main extends StatefulWidget {
const Main({super.key});
#override
State<Main> createState() => _Main();
}
class _Main extends State<Main> {
int _selectedIndex = 0;
static final List<Widget> _loggedIn = <Widget>[
const Home(),
const Listings(),
const Messages(),
const Profile(),
];
static final List<Widget> _loggedOut = <Widget>[
const Home(),
const ListingsLogin(),
const MessagesLogin(),
const ProfileLogin(),
];
#override
Widget build(BuildContext context) {
final Stream<User?> authInstance = FirebaseAuth.instance.authStateChanges();
return Scaffold(
resizeToAvoidBottomInset: false,
body: Center(
child: StreamBuilder<User?>(
stream: authInstance,
builder: (context, snapshot) {
if (snapshot.hasData) {
return _loggedIn.elementAt(_selectedIndex);
} else {
return _loggedOut.elementAt(_selectedIndex);
}
}),
),
bottomNavigationBar: BottomNavigationBar(
iconSize: 30,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_rounded),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart_rounded),
label: 'Listings',
),
BottomNavigationBarItem(
icon: Icon(Icons.mark_as_unread_rounded),
label: 'Messages',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle_rounded), label: 'Profile')
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.purple,
unselectedItemColor: Colors.black,
showUnselectedLabels: false,
showSelectedLabels: false,
onTap: _onTappedBar,
),
);
}
void _onTappedBar(int value) {
setState(() {
_selectedIndex = value;
});
}
}
The problem is everytime you go to AuthProfile() Stream checks the authStateChanges() so everytime it shows ProfileLogin() by default and update it with Profile() screen, what you can do is move the logic in your onTapBar function, there check if the user is logged in, then send him only to Profile() or ProfileLogin().

Flutter Navigation - How do you set up file pages in index?

I would like to use different dart files/scripts to show the pages when clicking on the navigation bar. The error is within the final screen section where I have set different pages to be loaded. However methods can't be found and I would like to ask how do I make this appear and all work together?
I have been following this tutorial and he skips what to do when linking up files within the navigation bar.
This is the youtube video I am following: (goto 6:44)
https://www.youtube.com/watch?v=xoKqQjSDZ60
main.dart:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int currentIndex = 0;
final screens = [
HomePage(),
BuisnessPage(),
SchoolPage(),
SettingsPage(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bottom Navigation Bar'),
centerTitle: true,
),
body: screens[currentIndex],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white70,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey,
type: BottomNavigationBarType.fixed,
iconSize: 30,
//selectedFontSize: 16,
//unselectedFontSize: 14,
//showUnselectedLabels: false,
currentIndex: currentIndex,
onTap: (index) => setState(() => currentIndex = index),
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.red,
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
backgroundColor: Colors.green,
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
backgroundColor: Colors.purple,
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
backgroundColor: Colors.pink,
),
],
),
);
}
}
buisnesspage.dart:
import 'package:flutter/material.dart';
class BuisnessPage extends StatelessWidget {
const BuisnessPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text('Test B'),
),
body: Center(child: Text('Test B', style: TextStyle(fontSize: 60))),
);
}
Error:
lib/main.dart:29:5: Error: Method not found: 'HomePage'.
HomePage(),
^^^^^^^^ lib/main.dart:30:5: Error: Method not found: 'BuisnessPage'.
BuisnessPage(),
^^^^^^^^^^^^ lib/main.dart:31:5: Error: Method not found: 'SchoolPage'.
SchoolPage(),
^^^^^^^^^^ lib/main.dart:32:5: Error: Method not found: 'SettingsPage'.
SettingsPage(),
^^^^^^^^^^^^
I think the problem comes from the fact that you did not import the classes used in main.dart
try to :
import 'pathtoyour/HomePage.dart';
import 'pathtoyour/BuisnessPage.dart';
import 'pathtoyour/SchoolPage.dart';
import 'pathtoyour/SettingsPage.dart';
and also try to type your variables to avoid errors that are difficult to understand,
final List<Widget> screens = const [
HomePage(),
BuisnessPage(),
SchoolPage(),
SettingsPage(),
];
if it's ok, give me a feedback 😊
or at least try to specify what your list contains :
final screens = <Widget>[
HomePage(),
BuisnessPage(),
SchoolPage(),
SettingsPage(),
];

NoSuchMethodError: The getter was called on null

`
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider( //Comes from provider class
create: (context) => LocaleProvider(),
builder: (context, child) {
final provider = Provider.of<LocaleProvider>(context);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Forts In Maharashtra',
locale: provider.locale,
supportedLocales: L10n.all,
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
home: MyApp(),
);
});
}
}
class MyApp extends StatefulWidget {
//static const String _title = 'Forts in Maharashtra';
MyApp({Key key, this.title}) : super(key: key);
final String title;
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
new FirebaseNotifications().setUpFirebase();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(accentColor: Colors.deepOrange),
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text(
AppLocalizations.of(context).apptitle, //Translates the text
style: TextStyle(fontSize: 23.0),
),
backgroundColor: Colors.deepOrange,
actions: [
Icon(Icons.language, size: 28),
LanguagePick(),
const SizedBox(width: 12),
],
),
body: MyBottomNavigationBar()),
);
}
}
class MyBottomNavigationBar extends StatefulWidget {
class _MyBottomNavigationBarState extends State<MyBottomNavigationBar> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: _children[_currentindex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.deepOrange,
onTap: onTappedBar,
currentIndex: _currentindex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.explore),
label: AppLocalizations.of(context).exploreforts), //Giving error here
BottomNavigationBarItem(
icon: Icon(MdiIcons.waves), label: 'Sea Forts'),
BottomNavigationBarItem(
icon: Icon(Icons.terrain), label: 'Attractions'),
BottomNavigationBarItem(
icon: Icon(Icons.location_city), label: 'City Forts'),
],
));
}
}`
I am trying to use the Localization feature in my application. I am able to change the AppBar title to another language but I am unable to change the language on the BottomNavigationBar. I am having trouble changing the language in BottomNavigationBar. The App language is changed on the AppBar but I am facing issues while using the same in the BottomNavigationBar. I have added comments where required.
This is because you are defining two MaterialApp. You only need to define one in the main and render it in the entire app, remove the MaterialApp from the _MyAppState class

Flutter Changing pages with BottomNavigationBar using one Scaffold

I have a flutter app with bottomNavigationBar, appBar, etc also I need to do a navigation.
Is it possible to do something like layouts in web dev instead of using Scaffold on each page?
'cause I wouldn't like to draw bottom navigation on each screen.
This way doesn't work
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('f'),
),
bottomNavigationBar: BottomBar(),
body: Com(),
),
);
class Com extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text('go'),
onPressed: () => {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Cam()),
)
},
);
}
}
class Cam extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Text('Cam');
}
}
It renders a button in good way, but after I use navigation, layout crashes and I get only text on black screen
P.S. BottomBar is just my custom BottomNavigationBar
I created a dartpad to show how this would work dynamically:
https://dartpad.dev/4125ebd6684e4cb2c69c5ec4560caab3
The way to approach this would be to use only one scaffold high up in the widget tree, and just change the widgets below, specifically in the Scaffold body: parameter. Note: you cannot use the Navigation widget with this method because it would pop off the Scaffold.
Just in case the dartpad doesn't work, you can see the code here.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: MyPages());
}
}
class MyPages extends StatefulWidget {
MyPages({Key key}) : super(key: key);
#override
MyPagesState createState() => MyPagesState();
}
class MyPagesState extends State<MyPages> {
int _selectedIndex = 0;
List<Widget> _widgetOptions = <Widget>[
Container(
color: Colors.green,
child: Center(child: Text("put your pages here")),
constraints: BoxConstraints.expand(),
),
Container(
color: Colors.green,
child: Center(child: Text("you just have to build them and...")),
constraints: BoxConstraints.expand(),
),
Container(
color: Colors.green,
child: Center(child: Text("put them in the _widgetOption list")),
constraints: BoxConstraints.expand(),
)
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('School'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
As you can see, there is only one Scaffold and bottomNavigationBar, but three pages that can be displayed. Hitting the nav buttons just updates the index to _widgetOptions. Therefore, to use this method, you just have to populate _widgetOptions with the pages that you want to show, either dynamically or statically:

Flutter: animate appbar color with bottomnavigationbar

I wonna animate my appbar color the same way as the bottomnavigationbar does with the shifting type. So the appbar and bottomnavigationbar change color together.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _tabIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Dash')),
body: Container(),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _tabIndex,
onTap: (value) => setState(() => _tabIndex = value),
type: BottomNavigationBarType.shifting,
unselectedItemColor: Theme.of(context).unselectedWidgetColor,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.dashboard), title: Text('Dash'), backgroundColor: Colors.blue),
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart), title: Text('Data'), backgroundColor: Colors.red),
BottomNavigationBarItem(
icon: Icon(Icons.monetization_on), title: Text('Income'), backgroundColor: Colors.orange),
]),
);
}
}
How can I do this? (I'm fairly new to flutter) Thanks!
It's very simple. Simply change color based on the selected index.
Here you go
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _tabIndex = 0;
var colors = [Colors.blue, Colors.red, Colors.orange];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Dash'),
backgroundColor: colors[_tabIndex],
),
body: Container(),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _tabIndex,
onTap: (value) => setState(() => _tabIndex = value),
type: BottomNavigationBarType.shifting,
unselectedItemColor: Theme.of(context).unselectedWidgetColor,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.dashboard),
title: Text('Dash'),
backgroundColor: colors[0]),
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart),
title: Text('Data'),
backgroundColor: colors[1]),
BottomNavigationBarItem(
icon: Icon(Icons.monetization_on),
title: Text('Income'),
backgroundColor: colors[2]),
]),
);
}
}
See the live demo here.