How to set the indicator color to more than one tab at a time in Tab bar view flutter? - flutter

I have a tab bar view with 3 tabs, Email, Profile, Complete. Now there is a button in Email like such:
1. Email Tab
widget.theTabController.animateTo(
(widget.theTabController.index + 1),
);
When this button is clicked the tab changes and so does the indicator color changes.
But is there any way I could keep the indicator color of the Email tab same as the Profile tab like such:
Below is the code of my tab bar view.
Tabbar view
class SignupEmail extends StatefulWidget {
#override
_SignupEmailState createState() => _SignupEmailState();
}
class _SignupEmailState extends State<SignupEmail>
with SingleTickerProviderStateMixin {
bool entryPermission = false;
final List<Tab> myTabs = <Tab>[
new Tab(text: 'EMAIL'),
new Tab(text: 'PROFILE'),
new Tab(text: 'COMPLETE'),
];
TabController _tabController;
void initState() {
super.initState();
_tabController = new TabController(vsync: this, length: myTabs.length);
_tabController.addListener(() {
print("Selected Index: " + _tabController.index.toString());
});
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Padding(
padding:
const EdgeInsets.only(left: 16, right: 16, top: 50, bottom: 0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
MainTitle(),
Container(
child: DefaultTabController(
length: myTabs.length,
child: Column(
children: [
Container(
// height: 50,
width: double.infinity,
child: IgnorePointer(
child: TabBar(
controller: _tabController,
unselectedLabelColor:
Color.fromRGBO(0, 0, 0, 0.87),
labelColor: Color(0xff9E9E9E),
indicatorColor: Color.fromRGBO(98, 0, 238, 1),
tabs: myTabs,
),
),
),
SizedBox(
height: 28,
),
Container(
width: double.infinity,
height: 500,
child: TabBarView(
// physics: NeverScrollableScrollPhysics(),
controller: _tabController,
children: [
EmailTab(
theTabController: _tabController,
),
ProfileTab(
theTabController: _tabController,
),
CompleteEmail()
]),
),
],
)),
),
],
),
),
),
),
),
);
}
}

I think the only way is to build a tab navigation by yourself. I recently had a similar issue, when I wanted the tab color to match the screen color.
I simply created a stack with
Positioned(
left: safeAreaPos.dx,
width: safeAreaPos.dx + safeAreaSize.width,
bottom: 0,
child: Row(
children: [
for (var i = 0; i < 4; i++)
TabButton(i, context)
],
and render my tab
Widget TabButton(int tabIndex, BuildContext context) {
final List<String> labels = ['A', 'B', 'C', 'D'];
final List<double> labelSizes = [12, 16, 16, 12];
return SizedBox(
height: 50, width: safeAreaSize.width / 4,
child:
FlatButton(
color: bgTabColors[tabIndex],
padding: EdgeInsets.all(0),
textColor: CupertinoColors.white,
child: Text(labels[tabIndex], style: TextStyle(fontSize: labelSizes[tabIndex]),),
onPressed: () =>
BlocProvider.of<ProjectKanbanBloc>(context).add(ProjectKanbanTabRequested(tabIndex)),
)
);
}
}

Related

Use ListView horizontal like Tabview with flutter

I'm looking for a tutorial on using a horizontal ListView that behaves like a Tabview, ie displaying the link on the same screen.
Some links to propose?
thanks
Tab child can others widget too, use height on Tab and isScrollable:true on TabBar
class TabBarDemo extends StatelessWidget {
const TabBarDemo({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
isScrollable: true,
tabs: [
Tab(
height: 100, // height
icon: Card(
child: Container(
height: 100,
width: 100,
color: Colors.red,
),
)),
Tab(
icon: Icon(Icons.directions_transit),
),
Tab(
icon: Icon(Icons.directions_bike),
),
],
),
title: const Text('Tabs Demo'),
),
body: const TabBarView(
children: [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
],
),
),
),
);
}
}
more about tabs
And using PageView & ListView, it will be
class TabBarDemo extends StatelessWidget {
const TabBarDemo({super.key});
#override
Widget build(BuildContext context) {
final PageController controller = PageController();
return Scaffold(
body: Column(
children: [
SizedBox(
height: 100, //tab item height
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => GestureDetector(
onTap: () {
controller.animateToPage(index,
duration: Duration(milliseconds: 100),
curve: Curves.bounceIn);
},
child: Container(
height: 100,
width: 100,
color: Colors.red,
child: Card(
child: Text("tab $index"),
),
),
),
),
),
Expanded(
child: PageView.builder(
controller: controller,
itemBuilder: (context, index) {
return Center(
child: Text("$index"),
);
},
),
),
],
),
);
}
}
Also you can check CustomScrollView.
run this example and you will get the whole idea :
class ListTapPage extends StatefulWidget {
const ListTapPage({Key? key}) : super(key: key);
#override
State<ListTapPage> createState() => _ListTapPageState();
}
class _ListTapPageState extends State<ListTapPage> {
List<Widget> pages = [const Center(child: Text("one")),const Center(child: Text("two")),const Center(child: Text("three"),)];
List<String> names = ["one","two","three"];
List<Color> colors = [Colors.red,Colors.blue,Colors.yellow];
int _index = 0 ;
void changeIndex({required int num}){
setState((){
_index = num;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Positioned(top: 0,right: 0,left: 0,bottom: MediaQuery.of(context).size.height * 0.75,
child: SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 3,
itemBuilder: (context, index) {
return GestureDetector(
onTap:()=>changeIndex(num: index) ,
child: Container(alignment: Alignment.center,width: 200,height: 50,color: colors[index],child: Text(names[index])),
);
},
),
)
),
Positioned(
left: 0,
right: 0,
bottom: 0,
height: MediaQuery.of(context).size.height * 0.30,
child: pages[_index]
),
]
),
),
);
}
}
just return this widget in the material app ,see the result and look at the code , you will understand , it's a simple demo.

How to Move This TabBar Position from Upper to Middle One in Flutter?

I was trying to move the TabBar based on picture below. I wanted to move its position right under the piano picture in the middle. I am confused of which place should I put the TabBar. Here is my complete codes:
class MusicAmbient extends StatefulWidget {
const MusicAmbient({super.key});
#override
State<MusicAmbient> createState() => _MusicAmbientState();
}
class _MusicAmbientState extends State<MusicAmbient> with SingleTickerProviderStateMixin {
late final TabController controller = TabController(length: 2, vsync: this);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: controller,
tabs: const [
Tab(text: "Epic"),
Tab(text: "Relax"),
],
),
),
body: Container(
color: Colors.black,
child: SafeArea(
child: Container(
height: MediaQuery.of(context).size.height,
color: const Color.fromARGB(115, 55, 55, 55),
child: Stack(
children: [
//Piano Background & Tabs
Align(
alignment: Alignment.topLeft,
child: Wrap(
children: [
ImageButton(path: bgamb, callback: () => Get.back(), type: IconType.NONE),
Container(
height: MediaQuery.of(context).size.height,
color: const Color.fromARGB(115, 55, 55, 55),
child: TabBarView(
controller: controller,
children: [
MusicEpic(),
MusicRelax(),
],
),
),
],
),
),
],
),
),
),
),
);
}
}
Is there a way to achieve it correctly in Flutter? Any tips and tricks will be appreciated.

Vertically Scroll PageView from SingleChildScrollView

I have a tab bar with a PageView inside each TabBarView. Each PageView has a ListView and when I scroll it, how can I scroll from the Scaffold's SingleChildScrollView rather than just scrolling the ListView inside the TabBarView?
Currently, the tab bar stays on the same position when I scroll the TabBarView, which looks terrible. How can I scroll individual TabBarView from SingleChildScrollView?
I tried applying primary:false and NeverScrollablePhysics() to the ListView, but didn't turn out the way I wanted it to.
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
int currIndex = 0;
TabController _tabController;
#override
void initState() {
super.initState();
_tabController =
TabController(vsync: this, length: 3, initialIndex: currIndex);
_tabController.addListener(() {
_handleTabSelection();
});
}
void _handleTabSelection() {
setState(() {
currIndex = _tabController.index == null ? 0 : _tabController.index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: DefaultTabController(
length: 3,
initialIndex: 0,
child: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
Container(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8),
child: Container(
child: Text(
"HOME",
style: TextStyle(fontSize: 25),
)),
),
_buildTabBar(context),
],
),
),
Expanded(
child: _buildTabBarView(
context,
),
)
],
),
),
),
),
),
);
}
TabBarView _buildTabBarView(BuildContext context) {
return TabBarView(
controller: _tabController,
children: List.generate(
3,
(index) => Container(
color: Colors.red,
child: ListView.builder(
itemCount: 50,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 60,
color: Colors.blue,
child: Center(child: Text("$index"))),
);
},
),
)));
}
Widget _buildTabBar(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8),
child: Container(
height: 45,
width: double.infinity,
decoration: buildTabBarStyle(),
child: TabBar(
controller: _tabController,
isScrollable: false,
tabs: List.generate(
3,
(index) => Center(
child: Text(
"$index",
style: TextStyle(color: Colors.black),
),
)),
),
),
);
}
BoxDecoration buildTabBarStyle() {
return BoxDecoration(
color: Color.fromARGB(255, 230, 248, 255),
border: Border.all(
width: 1,
color: Colors.black,
),
borderRadius: BorderRadius.all(Radius.circular(10)),
);
}
}

Flutter TabBar and TabBarView inside body of the application

I was trying to build a UI for my application like this. But views of tabs are not visible. I've used tabs in many flutter applications but the UI has to exactly like below
Appbar with image as background
Half portion of user image in appbar section and rest below it
A tabbar below these.
.
.
.
My code here
class _MyHomePageState extends State<MyHomePage> with
TickerProviderStateMixin{
double screenSize;
double screenRatio;
AppBar appBar;
List<Tab> tabList = List();
TabController _tabController;
#override
void initState() {
tabList.add(new Tab(text:'Overview',));
tabList.add(new Tab(text:'Workouts',));
_tabController = new TabController(vsync: this, length:
tabList.length);
super.initState();
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size.width;
appBar = AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
);
return Container(
color: Colors.white,
child: Stack(
children: <Widget>[
new Container(
height: 300,
width: screenSize,
decoration:new BoxDecoration(
image: new DecorationImage(
image: new AssetImage("images/app_image.jpg"),
fit: BoxFit.cover,
),
),
),
Scaffold(
backgroundColor: Colors.transparent,
appBar: appBar,
body:
Stack(
children: <Widget>[
new Positioned(
child: Column(
children: <Widget>[
Center(
child: Container(
child: CircleAvatar(
backgroundImage:
NetworkImage('http://res.cloudinary.com/'),
backgroundColor: Colors.green,
radius: 20,
),
),
),
SingleChildScrollView(
child: Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text('* * * * *',textAlign: TextAlign.center,style: TextStyle(fontSize: 18.0,color: Colors.pink),),
new Text('CAPTAIN',textAlign: TextAlign.center,style: TextStyle(fontSize: 18.0)),
],
crossAxisAlignment: CrossAxisAlignment.center,
),
),
),
],
),
width: screenSize,
top: 170,
),
new Positioned(
width: screenSize,
top: 310,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: new Column(
children: <Widget>[
new Container(
decoration: new BoxDecoration(color: Theme.of(context).primaryColor),
child: new TabBar(
controller: _tabController,
indicatorColor: Colors.pink,
indicatorSize: TabBarIndicatorSize.tab,
tabs: tabList
),
),
new Container(
height: 20.0,
child: new TabBarView(
controller: _tabController,
children: tabList.map((Tab tab){
_getPage(tab);
}).toList(),
),
)
],
),
),
)
],
),
),
],
),
);
}
Widget _getPage(Tab tab){
switch(tab.text){
case 'Overview': return OverView();
case 'Orders': return Workouts();
}
}
}
tabList.map((Tab tab){
_getPage(tab);
}).toList()
The piece above is from your provided code, you called _getPage(tab) in the map without a return statement. Simply make a slight change to this
tabList.map((Tab tab){
return _getPage(tab);
}).toList()
Or
tabList.map((Tab tab) => _getPage(tab)).toList()
children: tabList.map((Tab tab){
_getPage(tab);
}).toList(),
Some how this above your logic will getting null children for TabBarView, So views of tabs are not visible, need to check for it.
OtherWise you can assign children of TabBarView manualy
children: <Widget>[
OverView(),
Workouts(),
],

how to implement a sliverAppBar with a tabBar

the flutter document show a demo for SliverAppBar + TabBar + TabBarView with ListView use NestedScrollView, and it's a bit complex, so I wonder is there a simply and clear way to implement it. I tried this:
CustomScrollView
slivers:
SliverAPPBar
bottom: TabBar
TabBarView
children: MyWidget(list or plain widget)
got error:
flutter: The following assertion was thrown building Scrollable(axisDirection: right, physics:
flutter: A RenderViewport expected a child of type RenderSliver but received a child of type _RenderExcludableScrollSemantics.
flutter: RenderObjects expect specific types of children because they coordinate with their children during layout and paint. For example, a RenderSliver cannot be the child of a RenderBox because a RenderSliver does not understand the RenderBox layout protocol.
and
flutter: Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3497 pos 14: 'owner._debugCurrentBuildTarget == this': is not true.
HERE IS MY CODE:
import 'package:flutter/material.dart';
main(List<String> args) {
runApp(MyScrollTabListApp());
}
class MyScrollTabListApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: "aa", home: MyScrollTabListHomePage());
}
}
class MyScrollTabListHomePage extends StatefulWidget {
#override
MyScrollTabListHomePageState createState() {
return new MyScrollTabListHomePageState();
}
}
class MyScrollTabListHomePageState extends State<MyScrollTabListHomePage>
with SingleTickerProviderStateMixin {
final int _listItemCount = 300;
final int _tabCount = 8;
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: _tabCount, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 240.0,
title: Text("Title"),
pinned: true,
bottom: TabBar(
controller: _tabController,
isScrollable: true,
tabs: List<Tab>.generate(_tabCount, (int i) {
return Tab(text: "TAB$i");
}),
),
),
TabBarView(
controller: _tabController,
children: List<Widget>.generate(_tabCount, (int i) {
return Text('line $i');
}),
),
],
),
);
}
}
and for the official demo, it use struct like this
DefaultTabController
NestedScrollView
headerSliverBuilder
SliverOverlapAbsorber
handle
SliverAppBar
TabBarView
CustomScrollView
SliverOverlapInjector
handle
SliverPadding
Use NestedScrollView, here is the working code.
#override
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
length: 2,
child: NestedScrollView(
headerSliverBuilder: (context, value) {
return [
SliverAppBar(
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.call), text: "Call"),
Tab(icon: Icon(Icons.message), text: "Message"),
],
),
),
];
},
body: TabBarView(
children: [
CallPage(),
MessagePage(),
],
),
),
),
);
}
Here is an example for TabView with SilverAppBar
class SilverAppBarWithTabBarScreen extends StatefulWidget {
#override
_SilverAppBarWithTabBarState createState() => _SilverAppBarWithTabBarState();
}
class _SilverAppBarWithTabBarState extends State<SilverAppBarWithTabBarScreen>
with SingleTickerProviderStateMixin {
TabController controller;
#override
void initState() {
super.initState();
controller = new TabController(length: 3, vsync: this);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new CustomScrollView(
slivers: <Widget>[
new SliverAppBar(
title: Text("Silver AppBar With ToolBar"),
pinned: true,
expandedHeight: 160.0,
bottom: new TabBar(
tabs: [
new Tab(text: 'Tab 1'),
new Tab(text: 'Tab 2'),
new Tab(text: 'Tab 3'),
],
controller: controller,
),
),
new SliverList(
new SliverFillRemaining(
child: TabBarView(
controller: controller,
children: <Widget>[
Text("Tab 1"),
Text("Tab 2"),
Text("Tab 3"),
],
),
),
],
),
);
}
}
Yeah. You can use NestedScrollView To Achieve Tabs. Here is Some Addition Code To That.
class AppView extends StatelessWidget {
final double _minValue = 8.0;
#override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: MyAppBar(),
drawer: DrawerDialog(),
body: DefaultTabController(
length: 3,
child: SafeArea(
child: NestedScrollView(
body: TabBarView(
children: [Text("Page 1"), Text("Page 2"), Text("Page 3")],
),
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) => [
SliverPadding(
padding: EdgeInsets.all(_minValue * 2.5),
sliver: SliverToBoxAdapter(
child: Text(
"Hiding Header",
style: textTheme.headline6,
textAlign: TextAlign.center,
),
),
),
SliverAppBar(
backgroundColor: Colors.grey[100],
pinned: true,
elevation: 12.0,
leading: Container(),
titleSpacing: 0.0,
toolbarHeight: 10,
bottom: TabBar(tabs: [
Tab(
child: Text(
"All",
style: textTheme.subtitle2,
),
),
Tab(
child: Text(
"Categories",
style: textTheme.subtitle2,
),
),
Tab(
child: Text(
"Upcoming",
style: textTheme.subtitle2,
),
),
]),
),
],
),
),
),
);
}
}
You can Also Implement it by Providing _tabController to Both TabBar() and TabBarView So It will bind.
And For the children of TabBarView if you're using ListView then give it physics: NeverScrollablePhysics() so it won't move, kindly not You have to give dynamic height to the Container of ListView So it will load all Children's.
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 2, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
floating: true,
expandedHeight: 50,
title: Column(
children: [
Row(
children: [
Text('Hello, User'),
Spacer(),
InkWell(
child: Icon(Icons.map_rounded),
),
],
),
],
),
),
SliverList(
delegate: SliverChildListDelegate([
_tabSection(context),
])),
],
));
}
Widget _tabSection(BuildContext context) {
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
double mainAxisHeight = height > width ? height : width;
return DefaultTabController(
length: 2,
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Container(
height: 48,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(10),
bottomLeft: Radius.circular(10))),
child: TabBar(
indicatorColor: Colors.white,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(color: Colors.white, width: 5.0),
insets: EdgeInsets.symmetric(horizontal: 40),
),
labelColor: Colors.white,
unselectedLabelColor: Colors.grey[300],
tabs: [
Tab(
iconMargin: EdgeInsets.only(top: 5),
text: "Tab Bar 1",
),
Tab(
iconMargin: EdgeInsets.only(top: 5),
text: "Tab bar 2",
),
]),
),
Container(
height: 200 * 6 // 200 will be Card Size and 6 is number of Cards
child: TabBarView(controller: _tabController, children: [
tabDetails(),
tabDetails(),
]))
]));
}
tabDetails() {
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
double mainAxisHeight = height > width ? height : width;
return Container(
padding: EdgeInsets.symmetric(horizontal: 15),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.red[100],
Colors.red[200],
])),
child: ListView(
physics: NeverScrollableScrollPhysics(), // This will disable LitView'Scroll so only Scroll is possible by TabBarView Scroll.
children: [
SizedBox(height: 10),
Container(
height:140,
width: width,
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: 6,
itemBuilder: (BuildContext context, int indexChild) {
return Row(
children: [
MyListTile(
name: "Name",
),
SizedBox(width: 5),
],
);
},
),
),
SizedBox(height: 1000),
],
),
);
}
}
If you want to use CustomScrollView, you can make use of SliverToBoxAdapter:
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
length: 2,
child: CustomScrollView(
slivers: [
SliverAppBar(
title: Text('SliverAppBar'),
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.call), text: "Call"),
Tab(icon: Icon(Icons.message), text: "Message"),
],
),
),
SliverToBoxAdapter(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: TabBarView(
children: [
Container(color: Colors.red),
Container(color: Colors.blue),
],
),
),
),
],
),
),
);
}