I'm trying to change the AppBar title depending on which page the user is on - the pages are controlled by a BottomNavigationBar which loads the different classes(pages)
The only way i've managed to change this is by including a appbar for each page, which i believe is not the way to go ahead.
class HomePage extends StatefulWidget {
final String title;
HomePage({Key key, this.auth, this.userId, this.onSignedOut, this.title})
: super(key: key);
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
#override
State<StatefulWidget> createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _currentIndex = 0;
final List<Widget> _children = [
Projects(),
TimedProject(),
Overview(),
Clients(),
];
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey();
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('TITLE I NEED TO CHANGE DEPENDING ON PAGE',
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
),
endDrawer: AppDrawer(),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: _currentIndex,
selectedItemColor: Theme.of(context).primaryColor,
type: BottomNavigationBarType.fixed,
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.storage),
title: Text('Jobs'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.timer),
title: Text('Timer'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.pie_chart_outlined),
title: Text('Overview'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.supervisor_account), title: Text('Clients'))
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
Create a variable that holds the appbar title or you can use the same title variable that is passed in your HomePage class but you have to remove the final.
If you are using the title variable in HomePage class, make sure to use
"widget.title"
class HomePage extends StatefulWidget {
final String title;
HomePage({Key key, this.auth, this.userId, this.onSignedOut, this.title})
: super(key: key);
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
#override
State<StatefulWidget> createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _currentIndex = 0;
String _title;
final List<Widget> _children = [
Projects(),
TimedProject(),
Overview(),
Clients(),
];
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey();
#override
initState(){
_title = 'Some default value';
}
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(_title,
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
),
endDrawer: AppDrawer(),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: _currentIndex,
selectedItemColor: Theme.of(context).primaryColor,
type: BottomNavigationBarType.fixed,
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.storage),
title: Text('Jobs'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.timer),
title: Text('Timer'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.pie_chart_outlined),
title: Text('Overview'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.supervisor_account), title: Text('Clients'))
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
switch(index) {
case 0: { _title = 'Jobs'; }
break;
case 1: { _title = 'Timer'; }
break;
case 2: { _title = 'Overview'; }
break;
case 3: { _title = 'Clients'; }
break;
}
});
}
}
Thanks for the solution, is there away to take the bottom nav and out it in its own .dart file like
bottomNavigationBar: BottomNavBar(),
and get the selected tab index
class _HomeScreen2State extends State<HomeScreen2> {
//Hold current Tab Index
int _selectTab = 0;
final _pageOptions = [
HomeScreen(),
NewOrderScreen(),
OrderHistoryScreen(),
ContactScreen(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Gift Shop Dashboard'),
),
body: _pageOptions[_selectTab],
bottomNavigationBar: BottomNavBar(),
);
}
}
To separate BottomNavigationBar in a different .dart file, for example, in config.dart:
import 'package:flutter/material.dart';
class Config {
static List<BottomNavigationBarItem> navigationBarItems = [
BottomNavigationBarItem(
icon: Icon(
Icons.date_range,
),
title: Text(
"Calendar",
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.list, // event_note
),
title: Text(
"List",
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.bookmark_border, // grid_on
),
title: Text(
"Bookmarks",
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.account_circle,
),
title: Text(
"Me",
),
),
];
static BottomNavigationBar navigationBar = BottomNavigationBar(
items: navigationBarItems,
type: BottomNavigationBarType.fixed,
fixedColor: Colors.red,
);
}
and in main.dart:
import 'package:flutter/material.dart';
import 'config.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _index = 0;
Text _title;
_onTap(int index) {
setState(() {
_index = index;
_title = Config.navigationBarItems[_index].title;
});
}
#override
void initState() {
_title = Config.navigationBarItems[_index].title;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: _title,
),
body: Container(
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _index,
type: Config.navigationBar.type,
fixedColor: Config.navigationBar.fixedColor,
items: Config.navigationBar.items,
onTap: _onTap,
),
);
}
}
just make condition in ontap
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 1;
String appbarTitleString = "Home";
var appBarTitleText = new Text("Home");
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Profile',
style: optionStyle,
),
Text(
'Index 1: Home',
style: optionStyle,
),
Text(
'Index 2: More',
style: optionStyle,
),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: appBarTitleText,
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Profile'),
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('More'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
if (index == 0){
appbarTitleString = "Profile" ;
appBarTitleText = new Text(appbarTitleString);
}else if (index == 1){
appbarTitleString = "Home" ;
appBarTitleText = new Text(appbarTitleString);
}else if(index == 2){
appbarTitleString = "More" ;
appBarTitleText = new Text(appbarTitleString);
}
});
}
}
it is too Simple, using only two variable we can handle it.
Take Two Variables like:
int _selectedIndex = 0;
String _page_title = "DashBoard";
and Replace page title on every index changed. like as below,
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
if (index == 0) {
_page_title = "DashBoard";
} else if (index == 1) {
_page_title = "Search";
} else if (index == 2) {
_page_title = "Setting";
} else if (index == 3) {
_page_title = "Profile";
}
});
make a list which decide to perform task from selected tab.
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Home 1',
style: optionStyle,
),
Text(
'Home 2',
style: optionStyle,
),
Text(
'Home 3',
style: optionStyle,
),
Profile(),
];
And adjust code in Build() function as below.
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text(_page_title),
automaticallyImplyLeading: false, // hide back arrow from appbar
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
Icons.home,
color: Colors.grey,
),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(
Icons.search,
color: Colors.grey,
),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(
Icons.settings,
color: Colors.grey,
),
label: 'Setting',
),
BottomNavigationBarItem(
icon: Icon(
Icons.face_retouching_natural_sharp,
color: Colors.grey,
),
label: 'Profile',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.blue,
onTap: _onItemTapped,
),
);
}
}
☻♥ Done.
In case you also have different actions, leadings, etc for different pages, You can have different appbars in separate files and import them like pages.
static final List<Widget> _pages = [
const HomeScreen(),
const BookingScreen(),
const AccountScreen(),
];
static final List<Widget> _appBars = [
const HomeAppBar(),
const BookingAppBar(),
const AccountAppBar(),
];
Then you can have variable:
int _selectedIndex = 0;
Your onTapped function will be like:
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
Your build would be like this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(56), // 56 is default height
child: _appBars[_selectedIndex],
), // PreferredSize
body: _pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onItemTapped,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home_rounded),
label: "Home",
), // BottomNavigationBarItem
BottomNavigationBarItem(
icon: Icon(Icons.schedule_rounded),
label: "Bookings",
), // BottomNavigationBarItem
BottomNavigationBarItem(
icon: Icon(Icons.person_rounded),
label: "Account",
), // BottomNavigationBarItem
],
), // BottomNavigationBar
); // Scaffold
}
Related
I need to have the transactions screen and the categories screen widgets as BottomNavigationBarItems
However, i receive this error
The method 'Transactions' isn't defined for the type 'HomeState'. Try correcting the name of an existing method, or defining a method named 'Transactions'.
lib\screens\home.dart
import 'package:demo_app/screens/categories.dart';
import 'package:demo_app/screens/transactions.dart';
import 'package:flutter/material.dart';
class Home extends StatefulWidget{
const Home({super.key});
#override
State<Home> createState() => HomeState();
}
class HomeState extends State<Home> {
List<Widget> widgetOptions = [Transactions(), Categories()];
int selectedIndex = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: const Text('Logged in!'),
),
body: widgetOptions.elementAt(selectedIndex),
bottomNavigationBar: BottomAppBar(
shape: const CircularNotchedRectangle(),
notchMargin: 4,
child: BottomNavigationBar(
backgroundColor: Theme.of(context).primaryColor.withAlpha(0),
elevation: 0,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.account_balance_wallet),
label: 'Transactions'),
BottomNavigationBarItem(
icon: Icon(Icons.category),
label: 'Categories'),
BottomNavigationBarItem(
icon: Icon(Icons.logout),
label: 'Log out'),
],
currentIndex: selectedIndex,
onTap: onItemTapped,
)
)
),
);
}
void onItemTapped(int index){
if(index == 2){
}else{
setState((){
selectedIndex = index;
});
}
}
}
Simply add SizedBox() to widgetOptions list.
List<Widget> widgetOptions = [Transactions(), Categories(), SizedBox()];
Actually the code in the below had issues
screens/categories.dart';
screens/transactions.dart';
lib\screens\home.dart
Solved those and the issue is no longer there.
I want to keep the bottomnavigationbar AppBar when navigating to a new screen. with save state when i back to page.
The trick is to use "IndexedStack" in the body, IndexedStack workers like Stack Widget :
IndexedStack(
index: _selectedIndex,
children: _widgetOptions,
)
pass list of widgets in 'children' & index of the widget in 'index'.
Here is the example code:
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: BottomNavPage(),
);
}
}
class BottomNavPage extends StatefulWidget {
const BottomNavPage({Key? key}) : super(key: key);
#override
_BottomNavPageState createState() => _BottomNavPageState();
}
class _BottomNavPageState extends State<BottomNavPage> {
int _selectedIndex = 0;
static const List<Widget> _widgetOptions = <Widget>[
NavHome(),
Text('Index 1: Business'),
Text('Index 2: School'),
Text('Index 3: Settings'),
];
Widget navHome() {
return Text(
'Index 0: Home',
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('BottomNavigationBar Sample')),
body: IndexedStack(
index: _selectedIndex,
children: _widgetOptions,
),
bottomNavigationBar: Material(
child: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Home',
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'Home',
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Home',
backgroundColor: Colors.blue,
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.orange[400],
onTap: _onItemTapped,
),
),
);
}
}
class NavHome extends StatefulWidget {
const NavHome({
Key? key,
}) : super(key: key);
#override
_NavHomeState createState() => _NavHomeState();
}
class _NavHomeState extends State<NavHome> {
double _counter = 0;
void incrementCounter() {
setState(() {
_counter++;
});
}
void decrementCounter() {
setState(() {
_counter--;
});
}
#override
Widget build(BuildContext context) {
// Return a widget number in the center of the screen and button to increment the number
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Counter',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Increment'),
onPressed: incrementCounter,
),
SizedBox(width: 10),
ElevatedButton(
child: Text('Decrement'),
onPressed: decrementCounter,
),
],
),
],
),
);
}
}
In my application I have a BottomNavigationBar.
I am implementing multi navigation with bottom navigation bar as shown here;
https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf
I have put the title text like this.
BottomNavigationBarItem _buildItem(
{TabItem tabItem, String tabText, IconData iconData}) {
return BottomNavigationBarItem(
icon: Icon(
iconData,
color: widget.currentTab == tabItem
? active_button_color
: Colors.grey,
),
//label: tabText,
title: Text(
tabText,
style: widget.currentTab == tabItem
? Archivo_12_0xff002245
: Archivo_12_grey,
),
);
I get message title is deprecated.
When I use the label parameter how do I style it?
You style it using properties on the BottomNavigationBar.
Example:
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'First',
),
BottomNavigationBarItem(
icon: Icon(Icons.exit_to_app),
label: 'Second',
),
],
selectedLabelStyle: TextStyle(fontSize: 22),
selectedItemColor: Colors.red,
),
There are of course more properties. Check the documentation on: https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html
As the message already indicates that the 'title' property is deprecated, it has been replace with 'label'.
Concerning the selected color not changing, you need to add a function call 'onItemTapped' to change the color by setting the state.
The following is the working code:
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 = 'TEST';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: First',
style: optionStyle,
),
Text(
'Index 1: Second',
style: optionStyle,
),
];
//METHOD TO SET STATE
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'First',
),
BottomNavigationBarItem(
icon: Icon(Icons.exit_to_app),
label: 'Second',
),
],
selectedLabelStyle: TextStyle(fontSize: 22),
selectedItemColor: Colors.red,
//THIS METHOD NEEDS TO BE CALLED TO CHANGE THE STATE
onTap: _onItemTapped,
currentIndex: _selectedIndex),
);
}
}
There is a bit differences and changes in new flutter for BottomNavigationBar for example title has been changed to label , style in that title moved from BottomNavigationBarItem to BottomNavigationBar as selectedLabelStyle and unselectedLabelStyle etc. So the new solution will something like below:
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentTab,
onTap: (index) => setState(() => currentTab = index),
type: BottomNavigationBarType.fixed,
selectedLabelStyle: Archivo_12_0xff002245,
unselectedLabelStyle: Archivo_12_grey,
items: [
_buildItem(),
],
),
);
}
BottomNavigationBarItem _buildItem(
{TabItem tabItem, String tabText, IconData iconData}) {
return BottomNavigationBarItem(
icon: Icon(
iconData,
color: widget.currentTab == tabItem ? active_button_color : Colors.grey,
),
label: tabText,
);
}
I use bottomNavigationBar with two items which I define in a map:
final List<Map<String, Object>> _pages = [
{
'page': CategoriesScreen(),//My categories widget
'title': 'Categories',//title of current page (with navigation bar)
},
{
'page': FavoritesScreen(),//My favorites widget
'title': 'Your Favorite',//title of current page (with navigation bar)
},
];
When I use it like this:
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title']),
),
body: _pages[_selectedPageIndex]['page'],//<----- ERROR HERE
bottomNavigationBar: BottomNavigationBar(....
When I try to run the app from Android studio I got the error:
type 'String' is not a subtype of type 'Widget' in type cast
Any Idea why?
Update 1 (full code):
import 'package:flutter/material.dart';
import './favorites_screen.dart';
import './categories_screen.dart';
class TabsScreen extends StatefulWidget {
#override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
final List<Map<String, Object>> _pages = [
{
'page': CategoriesScreen(),
'title': 'Categories',
},
{
'page': FavoritesScreen(),
'title': 'Your Favorite',
},
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title']),
),
body: _pages[_selectedPageIndex]['page'],
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
backgroundColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.white,
selectedItemColor: Theme.of(context).accentColor,
currentIndex: _selectedPageIndex,
// type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.category),
title: Text('Categories'),
),
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.star),
title: Text('Favorites'),
),
],
),
);
}
}
Thank you
You don't need to put it into a Map. Just use the List<Widget> for _pages and add the title the State widget of the TabsScreen.
import 'package:flutter/material.dart';
import './favorites_screen.dart';
import './categories_screen.dart';
class TabsScreen extends StatefulWidget {
#override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
final List<Widget> _pages = [
CategoriesScreen(),
FavoritesScreen(),
];
String title = 'Categories';
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
if (_selectedPageIndex == 0) {
title = 'Categories';
} else if (_selectedPageIndex == 1) {
title = 'Your Favorite';
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: _pages[_selectedPageIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
backgroundColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.white,
selectedItemColor: Theme.of(context).accentColor,
currentIndex: _selectedPageIndex,
// type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.category),
title: Text('Categories'),
),
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.star),
title: Text('Favorites'),
),
],
),
);
}
}
I'm still looking for a more elegant way of achieving but this approach works.
following your code this is what you should do instead
import 'package:flutter/material.dart';
import './categories_screen.dart';
import './favourite_screen.dart';
class TabsScreeen extends StatefulWidget {
#override
_TabsScreeenState createState() => _TabsScreeenState();
}
class _TabsScreeenState extends State<TabsScreeen> {
final List<Map<String, Object>> _pages = [
{
"page": CategoriesScreen(),
"title": "Categories",
},
{
"page": FavouriteScreen(),
"title": "Favourites",
}
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title']),
),
body: _pages[_selectedPageIndex]['page'],
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.black38,
selectedItemColor: Colors.white,
backgroundColor: Theme.of(context).primaryColor,
onTap: _selectPage,
currentIndex: _selectedPageIndex,
items: [
BottomNavigationBarItem(
backgroundColor: Colors.orangeAccent,
icon: Icon(
Icons.category,
),
label: 'Categories',
),
BottomNavigationBarItem(
backgroundColor: Colors.orangeAccent,
icon: Icon(
Icons.favorite,
),
label: 'Favourites',
)
],
),
);
}
}
Try this
Instead of typing:final List<Map<String, Object>> _pages
do: final List<Map<String, dynamic>> _pages
So replace Object with dynamic type.
I got the same error and it was solved like this:
1- Keep your final List<Map<String, Object>> _pages as it's.
2- Add "As String" to the Text: title: Text(_pages[_selectedPageIndex]['title'] as String),
3- Add "as Widget" to the body: body: _pages[_selectedPageIndex]['page'] as Widget,
The full code:
class _TabsState extends State<Tabs> {
final List<Map<String, Object>> _pages = [
{'page': Categories(), 'title': "Categories"},
{'page': Favorites(), 'title': "Your Favorites"},
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title'] as String),<-- Add this
centerTitle: true,
),
body: _pages[_selectedPageIndex]['page'] as Widget, <-- Add this
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.black54,
selectedItemColor: Colors.deepPurple,
currentIndex: _selectedPageIndex,
type: BottomNavigationBarType.shifting,
onTap: _selectPage,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.category),
label: 'Catogries',
),
BottomNavigationBarItem(
icon: Icon(Icons.star),
label: 'Favorites',
),
]),
);
}
}
In your list mapping, change object to dynamic(your value can take any form)
like...
final List<Map<String, dynamic>> _pages
i need to set background color to selected BottomNavigationBarItem like this image
It's not possible to change the background of a selected item of the BottomNavigationBar because that doesn't follow the design guidelines.
If you still want to use it that way, following the example given in BottomNavigationBar class, here is a workaround:
final _selectedItemColor = Colors.white;
final _unselectedItemColor = Colors.white30;
final _selectedBgColor = Colors.indigo;
final _unselectedBgColor = Colors.blue;
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
Color _getBgColor(int index) =>
_selectedIndex == index ? _selectedBgColor : _unselectedBgColor;
Color _getItemColor(int index) =>
_selectedIndex == index ? _selectedItemColor : _unselectedItemColor;
Widget _buildIcon(IconData iconData, String text, int index) => Container(
width: double.infinity,
height: kBottomNavigationBarHeight,
child: Material(
color: _getBgColor(index),
child: InkWell(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(iconData),
Text(text,
style: TextStyle(fontSize: 12, color: _getItemColor(index))),
],
),
onTap: () => _onItemTapped(index),
),
),
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
selectedFontSize: 0,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: _buildIcon(Icons.home, 'Home', 0),
title: SizedBox.shrink(),
),
BottomNavigationBarItem(
icon: _buildIcon(Icons.business, 'Business', 1),
title: SizedBox.shrink(),
),
BottomNavigationBarItem(
icon: _buildIcon(Icons.school, 'School', 2),
title: SizedBox.shrink(),
),
],
currentIndex: _selectedIndex,
selectedItemColor: _selectedItemColor,
unselectedItemColor: _unselectedItemColor,
),
);
}
Result:
As far as I can see, you cannot change the color of a tab when using BottomNavigationBar. However, can I suggest to switch to TabBar? It would look something like this:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: 3);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: TabBarView(
controller: _tabController,
children: <Widget>[
Scaffold(appBar: AppBar(title: Text('First'))),
Scaffold(appBar: AppBar(title: Text('Second'))),
Scaffold(appBar: AppBar(title: Text('Third'))),
],
),
bottomNavigationBar: BottomAppBar(
child: Container(
color: Colors.blue,
child: TabBar(
indicator: BoxDecoration(color: Colors.redAccent),
tabs: <Widget>[
Tab(child: Text("Stuff", style: TextStyle(color: Colors.white))),
Tab(child: Text("Things", style: TextStyle(color: Colors.white))),
Tab(child: Text("Items", style: TextStyle(color: Colors.white))),
],
controller: _tabController,
),
),
),
);
}
}
Result:
This is a very minimal example, but it demonstrates the gist of it.
Here is example for changing background on click:
class _MyWidgetState extends State<MyWidget> {
IconData selectedItem = Icons.dashboard;
List<IconData> get itemsList => _items.keys;
Map<IconData, String> _items = {
Icons.home: 'Home',
Icons.drive_eta: 'Delivery',
Icons.shopping_basket: 'Products',
Icons.mail: 'Messages'
};
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
onTap: (int index) {
// todo something
setState(() {
selectedItem = itemsList[index];
});
},
currentIndex: itemsList.indexOf(selectedItem),
items: _items.entries.map((MapEntry<IconData, String> entry) {
return BottomNavigationBarItem(
icon: Icon(entry.key, color: Colors.white),
backgroundColor: entry.key == selectedItem ? Colors.black : Colors.blueGrey,
title: Text(entry.value),
);
}).toList());
}
}