Use AppBar Style on a TabBar that is not its child - flutter

I want my tab bar to have the same styling as the AppBar.
(using the same icon color, text color and background color)
My layout features a tabbed section to the left and another column to the right. Whithout additional styling the TabBar icons are not visible because both they and the background are white.
So what would be the most convinient way to apply the AppBar style to the TabBar? Thanks for any suggestions!
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => MyAppState();
}
class MyAppState extends State<MyApp> with TickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(
vsync: this,
length: 3,
initialIndex: 0,
);
}
#override
Widget build(BuildContext context) {
double bigSize = IconTheme.of(context).size * 4.0;
return MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Flutter TabBar Style Problem'),
),
body: Row(
children: <Widget>[
Expanded(
child: Column(
children: [
SizedBox(
height: 36.0,
child: TabBar(
controller: _tabController,
tabs: [
Tab(icon: Icon(Icons.cake)),
Tab(icon: Icon(Icons.train)),
Tab(icon: Icon(Icons.info)),
],
),
),
Flexible(
child: TabBarView(
controller: _tabController,
children: [
Icon(Icons.cake, size: bigSize),
Icon(Icons.train, size: bigSize),
Icon(Icons.info, size: bigSize),
],
),
),
],
),
),
Expanded(
child: Container(
color: Colors.amber.shade400,
alignment: FractionalOffset.center,
child: Text('Another Column'),
),
),
],
),
),
);
}
}

When you place a TabBar as the bottom: widget of the AppBar you get a lot of things for free. It gets the background color and a Material widget that has a special feature. It forces the indicator to use white color when the default indicator color is the same as the background color (give a look here).
So when you place a TabBar outside the AppBar you have to handle everything yourself.
But it seems pretty easy wrapping the TabBar in a Material and giving it the primaryColor (the same the framework does here).
So at the end is just replacing your SizedBox by a Material and setting the color. And you get the ink splash for free.
Material(
color: Theme.of(context).primaryColor,
child: TabBar(
controller: _tabController,
tabs: [
Tab(icon: Icon(Icons.cake)),
Tab(icon: Icon(Icons.train)),
Tab(icon: Icon(Icons.info)),
],
),
),

What appBar interally do is inserting a custom DefaultTextStyle that points to Theme.textTheme.title
You can do the exact same thing by wrapping a part of your widget tree into a custom DefaultTextStyle.
DefaultTextStyle(
style: Theme.of(context).textTheme.title,
child: Text('Title')
);
As for the background color, it is availble in another field of Theme.
Container(
color: Theme.of(context).primaryColor,
);

Change Your Code to Something Like This:
class ProductsAdminPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Scaffold is inside DefaultTabController because DefaultTabController needs access to Scaffold
return DefaultTabController(
// Number of tabs
length: 2,
child: Scaffold(
drawer: Drawer(
child: Column(
children: <Widget>[
AppBar(
automaticallyImplyLeading: false,
title: Text('Choose'),
),
ListTile(
title: Text('All Products'),
onTap: () {
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) => ProductsPage()));
},
)
],
),
),
appBar: AppBar(
title: Text('Manage Products'),
// TabBar is added as the bottom widget of the appBar
bottom: TabBar(
tabs: <Widget>[
// No of Tab() Widgets Should be equal to length
Tab(
icon: Icon(Icons.create),
text: 'Create Product',
),
Tab(
icon: Icon(Icons.list),
text: 'My Products',
),
],
),
),
body: TabBarView(
// No of pages should be equal to length
children: <Widget>[ProductCreatePage(), ProductListPage()],
),
),
);
}
}

ThemeData(
appBarTheme: AppBarTheme(
...
),
tabBarTheme: TabBarTheme(
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
indicatorSize: TabBarIndicatorSize.label,
labelStyle: TextStyle(
fontSize: 20.0,
color: Colors.green,
),
unselectedLabelStyle: TextStyle(
fontSize: 20.0,
color: Colors.green,
),
),
);
We can customise theme in App Level theme as our app bar and then every where in app, tabs will use same style as we define in theme.
I hope it is the easiest approach...

Related

Persisting BottomNavigationBar with changing AppBar

So I'm creating an screen with 5 BottomNavigationBarItem(s) that would ideally persist, while the AppBar changes according to the design. Here's my code:
class ExploreState extends State<ExploreScreen> {
int _selectedIndexForBottomNavigationBar = 0;
void _onItemTappedForBottomNavigationBar(int index) {
setState(() {
_selectedIndexForBottomNavigationBar = index;
});
}
#override
Widget build(BuildContext context) {
return new DefaultTabController(
length: 3,
child: new Scaffold(
appBar: AppBar(
bottom: ColoredTabBar(
color: Colors.white,
tabBar: TabBar(
labelColor: Colors.grey[900],
indicator: UnderlineTabIndicator(
borderSide: BorderSide(color: kPrimaryColor, width: 2.0),
),
tabs: <Widget>[
Tab(text: "Job Posts"),
Tab(
text: "Agencies",
),
Tab(
text: "All Applicants",
),
],
),
),
elevation: 0.7,
centerTitle: true,
title: Text(
"Looking For",
style: TextStyle(fontSize: 18),
),
actions: <Widget>[
Icon(Icons.search),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
),
],
),
body: TabBarView(
children: <Widget>[
Jobs(),
Agencies(),
Applicants(),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: <BottomNavigationBarItem>[
_bottomNavigationBarItem(
AssetImage("assets/images/search-job.png"), 'Explore'),
_bottomNavigationBarItem(
AssetImage("assets/images/message.png"), 'Messaging'),
_bottomNavigationBarItem(
AssetImage("assets/images/forums.png"), 'Forums'),
_bottomNavigationBarItem(
AssetImage("assets/images/notifications.png"), 'Notifications'),
_bottomNavigationBarItem(
AssetImage("assets/images/profile.png"), 'Edit Profile'),
],
currentIndex: _selectedIndexForBottomNavigationBar,
selectedItemColor: kPrimaryColor,
onTap: _onItemTappedForBottomNavigationBar,
selectedFontSize: 0,
),
),
);
}
BottomNavigationBarItem _bottomNavigationBarItem(
AssetImage icon, String label) {
return BottomNavigationBarItem(
activeIcon: _navItemIcon(
icon, label, [kPrimaryColor, kPrimaryWelcomeColor], Colors.white),
icon: _navItemIcon(
icon, label, [Colors.white, Colors.white], Color(0xFF4E5969)),
label: "",
);
}
Row _navItemIcon(AssetImage icon, String label, List<Color> backgrondColor,
Color? foregroundColor) {
return Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomLeft,
end: Alignment.topRight,
colors: backgrondColor,
transform: GradientRotation(1.5),
),
),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: [
ImageIcon(
icon,
size: 25,
color: foregroundColor,
),
Text(
label,
style: TextStyle(color: foregroundColor, fontSize: 8),
)
],
),
),
),
),
],
);
}
}
class ColoredTabBar extends Container implements PreferredSizeWidget {
ColoredTabBar({this.color = Colors.white, this.tabBar});
final Color color;
final TabBar? tabBar;
#override
Size get preferredSize => tabBar!.preferredSize;
#override
Widget build(BuildContext context) => Container(
color: color,
child: tabBar,
);
}
Upon Login, they land on the 'Explore' screen but was wondering if this structure is ideal. What I was thinking was a 'common' scaffold for a blank body, 5 BottomNavigationBarItems and a changing AppBar Menu items (for instance, "Messaging" has "Conversations" and "Appointments" AppBar items)
How do we go about properly coding this.
Here is a Sample for the "Messaging" BottomNavigationBarItem.
One possible approach:
Create one Scaffold with your BottomNavigationBar.
Use an IndexedStack (see here) as the body of your Scaffold.
Create the different bodies (Explore, Messaging etc.) and use setState when user clicks on one of your BottomNavigationBarItems and change the current index for IndexedStack.
Also create different AppBar widgets for the different bodies (for example in an array), assign it to the Scaffold based on a state value and in the previously mentioned setState update also its value.
This will work until you can or want to keep all these widgets in the same Stateless Widget class. Chances are good that later you will want to separate them into different classes so that your code is more easy to read. In that case you can use ChangeNotifier, ChangeNotifierProvider and Consumer to communicate state changes between your widgets, as explained here.

Flutter app bar layout: action buttons in the same row with tab bar?

Let's say I would like to create an app bar with two action buttons at the start and in the end while having a tab bar right in the middle, all in the same row. Here is the code I worked on to get the tab bar working:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
hoverColor: Colors.transparent,
),
title: 'Flutter Demo',
home: DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
leading: Icon(
Icons.menu,
),
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
flexibleSpace: new Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TabBar(
indicatorColor: Colors.pink[100],
tabs: [
Tab(text: 'Dogs'),
Tab(text: 'Cats'),
],
labelColor: Colors.black,
), //tabbar
], //chilren on Tabbar
), //new Column
), //appbar
body: TabBarView(
children: [
Center(child: Text('DOGS')),
Center(child: Text('CATS')),
],
),
),
),
);
}
}
appbar current
app bar expected, with two action buttons (and the indicator position needs a bit more work:
But how could I add the two action buttons without having the tab bar overlaying them? Using grid maybe?
try to put tabbar into title:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
hoverColor: Colors.transparent,
),
title: 'Flutter Demo',
home: DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
leading: Icon(
Icons.menu,
color: Colors.black,
),
actions: [
Icon(
Icons.settings,
color: Colors.black,
),
],
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
title: Padding(
padding: EdgeInsets.only(left: 50, right: 50),
child:TabBar(
indicatorColor: Colors.pink[100],
tabs: [
Tab(text: 'Dogs'),
Tab(text: 'Cats'),
],
labelColor: Colors.black,
),),
), //appbar
body: TabBarView(
children: [
Center(child: Text('DOGS')),
Center(child: Text('CATS')),
],
),
),
),
);
}
}

Flutter Tab Bar Indicator: How to change the vertical position of the indicator?

I am working on this Tab Bar for a while, it now looks like this:
, and here is my code now:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
hoverColor: Colors.transparent,
),
title: 'Flutter Demo',
home: DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
leading: Icon(
Icons.menu,
color: Colors.black,
),
actions: [
Icon(
Icons.settings,
color: Colors.black,
),
],
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
title: Padding(
padding: EdgeInsets.only(left: 50, right: 50),
child:TabBar(
indicatorColor: Colors.pink[100],
indicatorSize: TabBarIndicatorSize.label,
tabs: [
Tab(text: 'Dogs'),
Tab(text: 'Cats'),
],
labelColor: Colors.black,
),),
), //appbar
body: TabBarView(
children: [
Center(child: Text('DOGS')),
Center(child: Text('CATS')),
],
),
),
),
);
}
}
It is clear to see that the position of the indicator is not quite right. Ideally, I would like the indicator to vertically move up, closer to (and under) the text("Dogs" && "Cats"), or vertically move down, stay at the bottom of the app bar. How could I achieve that? Any help is appreciated.
You can controls those things using more parameters of the TabBar widget.
I have added this,
indicatorWeight: 4,
indicatorPadding: EdgeInsets.symmetric(vertical: 8),
and I got this,
If you want it much closer, play with the vertical value of indicatorPadding.
If you want it thicker or thinner, play with the indicatorWeight.

I want to place a custom widget in place of appbar and want a tab view below it

I have built a custom widget("cab") which displays a account login circle and no. of points user have.
I want a tab view just below it . For now I have placed this custom widget inside the tab itself :
bottomNavigationBar: Material(child: TabBar(
tabs: const <Widget>[
Tab( icon: Icon(Icons.add)),
Tab( icon: Icon(Icons.search))
]
),),
body :TabBarView(
children: <Widget>[
Column(
children: <Widget>[
cab
],
),
But I want this widget to be displayed above tab view but not the part of tab view itself . How can I do
this?
Thanks.
Replace the AppBar widget with your own widget below:
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 2,
child: Scaffold(
bottomNavigationBar: TabBar(
labelColor: Colors.orange,
unselectedLabelColor: Colors.grey,
indicatorSize: TabBarIndicatorSize.tab,
indicatorColor: Colors.orange,
tabs: [
Tab(
icon: Icon(Icons.add),
),
Tab(
icon: Icon(Icons.search),
),
],
),
body: Column(
children: <Widget>[
// Remove the AppBar code below and put your "cab" widget
AppBar(
primary: true,
title: Text('Here is your cab'),
centerTitle: true,
backgroundColor: Colors.orange,
),
Expanded(
child: TabBarView(
children: [
Icon(Icons.add),
Icon(Icons.search),
],
),
),
],
),
),
),
);
}
}

Flutter TabBar height Icons

import 'package:flutter/material.dart';
class TestTab extends StatelessWidget {
final List<Tab> myTabs = <Tab>[
Tab(
child: Image.asset(
'assets/icons/project/proj_001.png',
),
),
...
Tab(
icon: Image.asset(
'assets/icons/project/all.png',
height: 100,
),
),
...
Tab(
icon: Image.asset(
'assets/icons/project/proj_009.png',
),
),
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 5,
length: myTabs.length,
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(150.0),
child: AppBar(
bottom: TabBar(
isScrollable: true,
tabs: myTabs,
),
),
),
body: TabBarView(
children: myTabs.map((Tab tab) {
return Center(
child: Text(
'Test',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
),
),
);
}
}
I try to increase the icon height, but it doesn't work. It's as if he selects all the parameters himself. Does not allow reducing the width or increasing the height of elements.
Screen
Please tell me how you can increase the height of elements?
if u want set the icon size small then u can do like that
Tab(
text: "Category List",
icon: Icon(Icons.home,size: 15,),
),
Tab(
text: "Product List",
icon: Icon(Icons.view_list,size: 15,),
),
Tab(
text: "Contact Us",
icon: Icon(Icons.contacts,size: 15,),
),
Tab(
text: "Darshan Timing",
icon: Icon(Icons.access_time,size: 15,),
)
here size: 15, will make the icon size as you want
You gotta make custom TabBar for customization. Something like this.
CustomTabbar
import 'package:flutter/material.dart';
class ChangeTextSizeTabbar extends StatefulWidget {
#override
ChangeTextSizeTabbarState createState() {
return new ChangeTextSizeTabbarState();
}
}
class ChangeTextSizeTabbarState extends State<ChangeTextSizeTabbar> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: Text("Change Text Size Tabbar Example"),
bottom: TabBar(
tabs: <Tab>[
Tab(
child: Image.asset(
'assets/icons/project/proj_001.png',
height : 100,
width : 100,
),
),
Tab(
icon: Image.asset(
'assets/icons/project/all.png',
height : 100,
width : 100,
),
),
Tab(
icon: Image.asset(
'assets/icons/project/proj_009.png',
height : 100,
width : 100,
),
),
] ),
),
body: TabBarView(
children: <Widget>[
Container(),
Container(),
Container(),
],
),
),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'change_text_size_tabbar_task-3.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',
debugShowCheckedModeBanner: false,
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
//home: MyHomePage(title: 'Flutter Demo Home Page'),
home: ChangeTextSizeTabbar(),
);
}
}