I need to add a tab bar without an app bar and I got a solution from StackOverflow to use flexible space and it is working but it makes additional unwanted space in tab bar bottomHow to remove this or hide this?
My full code
import 'package:flutter/material.dart';
class TemplesListingWithTabMode extends StatefulWidget {
TemplesListingWithTabMode({Key key}) : super(key: key);
#override
_TemplesListingWithTabModeState createState() =>
_TemplesListingWithTabModeState();
}
class _TemplesListingWithTabModeState extends State<TemplesListingWithTabMode> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height-kToolbarHeight-kMaterialListPadding.top-kTabLabelPadding.top,
child: DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
flexibleSpace: TabBar(
indicatorColor: Colors.pink,
tabs: [
Tab(
child: Text("ALL",style: TextStyle(color: Colors.pink),),
),Tab(
child: Text("Favorites",style: TextStyle(color: Colors.pink),),
)
]),
),
body : Container(
color: Colors.grey,
child: TabBarView(
children: [
ListView.builder(
itemCount: 100,
itemBuilder: (context,index){
return Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("i am $index"),
),
),
);
}),
ListView.builder(
itemCount: 5,
itemBuilder: (context,index){
return Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("i am $index"),
),
),
);
})
]),
),
),
),
)
],
);
}
}
The solution provided by #Darshan is not solved my issue and the solution is
Wrap TabBar in SafeArea widget.
and the result is
How to remove this small bottom from appbar?
The reason is AppBar have its size + status bar size. There are multiple ways fix this. As other answer mentioned, simple way is to add SafeArea.
And note that even after you will get ugly little space under two tabs.
To solve that you can use PreferredSize (there are other ways for this also).
Code for the above screenshot:
class _TemplesListingWithTabModeState extends State<TemplesListingWithTabMode> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: SafeArea(
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size(double.infinity, 60),
child: TabBar(
indicatorColor: Colors.pink,
tabs: [
Tab(
child: Text("ALL",style: TextStyle(color: Colors.pink),),
),Tab(
child: Text("Favorites",style: TextStyle(color: Colors.pink),),
)
]),
),
body : Container(
color: Colors.grey,
child: TabBarView(
children: [
ListView.builder(
itemCount: 100,
itemBuilder: (context,index){
return Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("i am $index"),
),
),
);
}),
ListView.builder(
itemCount: 5,
itemBuilder: (context,index){
return Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("i am $index"),
),
),
);
})
]),
),
),
),
);
}
}
By default, ListView acts as if SafeArea is turned on.
Setting the padding to zero will remove that white space.
ListView(
padding: EdgeInsets.zero;
...
);
Discussion on ListView and the default SafeArea
Wrap TabBar in SafeArea widget. It adds the necessary padding to the child widget which in your case, minimizes the space you are seeing. Working code below:
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
flexibleSpace: SafeArea(
child: TabBar(
indicatorColor: Colors.pink,
tabs: [
Tab(
child: Text("ALL",style: TextStyle(color: Colors.pink),),
),Tab(
child: Text("Favorites",style: TextStyle(color: Colors.pink),),
)
]),)
),
Hope this answers your question.
You can create your appbar by extending AppBar
class MyAppBar extends AppBar {
MyAppBar({PreferredSizeWidget child, Color backgroundColor})
: super(bottom: child, backgroundColor: backgroundColor);
#override
Size get preferredSize => bottom.preferredSize;
}
class TemplesListingWithTabMode extends StatefulWidget {
TemplesListingWithTabMode({Key key}) : super(key: key);
#override
_TemplesListingWithTabModeState createState() =>
_TemplesListingWithTabModeState();
}
class _TemplesListingWithTabModeState extends State<TemplesListingWithTabMode> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: MyAppBar(
backgroundColor: Colors.white,
child: TabBar(indicatorColor: Colors.pink, tabs: [
Tab(
child: Text(
"ALL",
style: TextStyle(color: Colors.pink),
),
),
Tab(
child: Text(
"Favorites",
style: TextStyle(color: Colors.pink),
),
)
]),
),
body: Container(
color: Colors.grey,
child: TabBarView(children: [
ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("i am $index"),
),
),
);
}),
ListView.builder(
itemCount: 5,
itemBuilder: (context, index) {
return Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("i am $index"),
),
),
);
})
]),
),
),
);
}
}
import 'package:bubble_tab_indicator/bubble_tab_indicator.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class AppBarrTest extends StatefulWidget {
#override
_AppBarrTestState createState() => _AppBarrTestState();
}
class _AppBarrTestState extends State<AppBarrTest>with SingleTickerProviderStateMixin {
int index = 0;
TabController _controller;
#override
void initState() {
_controller = new TabController(length: 2, vsync: this);
_controller.addListener(() {
setState(() {});
});
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
flexibleSpace: fun_Appbar(),
bottom: PreferredSize(
preferredSize: Size.fromHeight(50),
child: Column(
children: <Widget>[
Card(
shape: Border.all(color: Colors.blue),
color: Colors.white,
child: fun_tabBar(20),
),
],
),
),
),
),
);
}
fun_Appbar(){
double h = MediaQuery.of(context).size.height;
return Container(
height: 50,
child: Center(
child: Text(
"Messages",
style: TextStyle(
fontSize: 22,
color: Colors.white,
letterSpacing: 2.0,
fontFamily: 'Nunito',
),
),
),
);
}
fun_tabBar(double fontSize){
return TabBar(
controller: _controller,
//indicatorWeight: 20,
indicatorSize: TabBarIndicatorSize.label,
labelPadding: EdgeInsets.only(left: 0, right: 0),
dragStartBehavior: DragStartBehavior.start,
unselectedLabelColor: Colors.black,
indicatorColor: Colors.red,
indicator: new BubbleTabIndicator(
indicatorHeight: 40.0,
indicatorColor: Color(0xFF343193),
//padding: EdgeInsets.all(20),
tabBarIndicatorSize: TabBarIndicatorSize.tab,
indicatorRadius: 30,
),
tabs: <Widget>[
Tab(
child: Container(
alignment: Alignment.center,
child: Text(
"Inbox",
style: TextStyle(
fontFamily: 'Nunito',
fontSize: fontSize,
),
),
),
),
Tab(
child: Container(
alignment: Alignment.center,
child: Text(
"Sent",
style: TextStyle(
fontFamily: 'Nunito',
fontSize: fontSize,
),
),
),
),
],
);
}
}
bubble_tab_indicator: "^0.1.4"
or Just wrap your flexible space with SizeBox and set height
flexibleSpace: SizedBox(
height: 100,
child: TabBar(
indicatorColor: Colors.pink,
tabs: [
Tab(
child: Text("ALL",style: TextStyle(color: Colors.pink),),
),Tab(
child: Text("Favorites",style: TextStyle(color: Colors.pink),),
)
]),
),
Related
My doubt is similar to this unanswered question. I need a multicolor tabbar. Also, each tab should be empty, being represented just by the backgroundcolor.
Trying to achieve:
For now, I have this:
Code:
class LinhaPage extends StatefulWidget {
const LinhaPage({Key? key}) : super(key: key);
#override
_LinhaPageState createState() => _LinhaPageState();
}
class _LinhaPageState extends State<LinhaPage> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 1,
length: 9,
child: Scaffold(
appBar: AppBar(
title: Text(
'Linha 1',
style: TextStyle(color: Colors.white, fontSize: 36.0),
),
toolbarHeight: 80.0,
bottom: const TabBar(
tabs: <Widget>[
Tab(
child: BtnAppBar(corBotao: 0xFFAECA0A),
),
Tab(
child: BtnAppBar(corBotao: 0xFFE4003A),
),
Tab(
child: BtnAppBar(corBotao: 0xFF09BBEF),
),
Tab(
child: BtnAppBar(corBotao: 0xFF154194),
),
Tab(
child: BtnAppBar(corBotao: 0xFF50842C),
),
Tab(
child: BtnAppBar(corBotao: 0xFFC7077F),
),
Tab(
child: BtnAppBar(corBotao: 0xFFF49200),
),
Tab(
child: BtnAppBar(corBotao: 0xFFF2E500),
),
Tab(
child: BtnAppBar(corBotao: 0xFF000000),
),
],
),
),
and:
class BtnAppBar extends StatelessWidget {
final corBotao;
const BtnAppBar({Key? key, required this.corBotao}) : super(key: key);
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
color: Color(corBotao),
child: Text("1"),
));
}
}
TKS!
You can provide size on tabBar container using MediaQuery or LayoutBuilder. And use zero padding on labelPadding
bottom: const TabBar(
padding: EdgeInsets.only(left: 24),
labelPadding: EdgeInsets.zero,
isScrollable: true,
tabs: <Widget>[
#override
Widget build(BuildContext context) {
return Container(
color: Color(corBotao),
width: MediaQuery.of(context).size.width / 9,
alignment: Alignment.center,
child: Text("1"),
);
}
i want to implement the sliver app bar as shown in the the 2 pictures given below. After much googling , I fount about the CustomScrollView widget and the SliverAppBar widget but all the tutorials and blogs online about sliver app bars show a simple one where an image disappears into an app bar with a text as title on scrolling. However here what I want to achieve is slightly different and I am having a hard time trying to figure out how to do it. Can anyone help me with it?
You can try this:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ScrollController? _scrollController;
bool lastStatus = true;
double height = 200;
void _scrollListener() {
if (_isShrink != lastStatus) {
setState(() {
lastStatus = _isShrink;
});
}
}
bool get _isShrink {
return _scrollController != null &&
_scrollController!.hasClients &&
_scrollController!.offset > (height - kToolbarHeight);
}
#override
void initState() {
super.initState();
_scrollController = ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
_scrollController?.removeListener(_scrollListener);
_scrollController?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).textTheme;
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
title: 'Horizons Weather',
home: Scaffold(
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
elevation: 0,
backgroundColor: Colors.blueGrey,
pinned: true,
expandedHeight: 275,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: _isShrink
? const Text(
"Profile",
)
: null,
background: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 48),
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: Image.network(
headerImage,
fit: BoxFit.cover,
height: 100,
width: 100,
),
),
),
const SizedBox(
height: 16,
),
Text(
"Flipkart",
style: textTheme.headline4,
),
const SizedBox(
height: 8,
),
const Text(
"flipkart.com",
),
const SizedBox(
height: 5,
),
const Text(
"Info about the company",
),
],
),
),
),
actions: _isShrink
? [
Padding(
padding: const EdgeInsets.only(left: 8, right: 12),
child: Row(
children: [
Padding(
padding:
const EdgeInsets.only(left: 8, right: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
"Flipkart",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
"flipkart.com",
style: TextStyle(
fontSize: 12,
),
),
],
),
),
ClipRRect(
borderRadius: BorderRadius.circular(100),
child: Image.network(
headerImage,
fit: BoxFit.cover,
height: 30,
width: 30,
),
),
],
),
),
]
: null,
),
];
},
body: CustomScrollView(
scrollBehavior: const ConstantScrollBehavior(),
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(child: Text("Item: $index")),
);
},
childCount: 50,
),
),
],
),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
const UserAccountsDrawerHeader(
accountName: Text("Zakaria Hossain"),
accountEmail: Text("zakariaaltime#gmail.com"),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.orange,
child: Text(
"A",
style: TextStyle(fontSize: 40.0),
),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text("Home"),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text("Settings"),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.contacts),
title: Text("Contact Us"),
onTap: () {
Navigator.pop(context);
},
),
],
),
),
),
);
}
}
Demo
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 tweaking with physics, but didn't turn out the way I wanted it to.
IMAGE :
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)),
);
}
}
Here is a different approach to do it simply.
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
late final TabController controller;
#override
void initState() {
super.initState();
controller = TabController(length: 3, vsync: this);
}
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)),
);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverAppBar(
snap: true,
floating: true,
pinned: false,
toolbarHeight: 80,
title: Text("Home"),
// title: Search(),
centerTitle: true,
bottom: PreferredSize(
preferredSize: Size(0.0, 48.0),
child: Container(
decoration: buildTabBarStyle(),
alignment: Alignment.center,
width: double.infinity,
child: TabBar(
controller: controller,
isScrollable: true,
labelColor: Colors.green,
unselectedLabelColor: Colors.grey,
labelStyle:
TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0),
tabs: [
Tab(text: "1"),
Tab(text: "2"),
Tab(text: "3"),
],
),
),
),
),
],
body: TabBarView(
controller: controller,
children: [
...List.generate(
3,
(t) => 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("tab $t index $index"),
),
),
);
},
),
),
).toList(),
],
),
),
),
);
}
}
I've stuck with one problem.
I'm a novice with a flutter trying to figure out how to do a simple swipe-left/swipe-right gallery.
I'm looking for a widget that supports gestures and some kind of overflow.
So I want a container with a fixed(width/height which I can define) and everything outside of this container should be hidden and when user swipes inner content it should show the next slide. Can you please point me out what is the best way to implement this with a Flutter and what is the best kind of containers fit these goals. Thanks
UPD 1:
It shouldn't be a whole screen, but a specific container.
You just need to use the PageView widget for the viewpager functionality , you can use it horizontal or vertical as your requirement,As you want horizontal PageView so i have used the scrollDirection: Axis.horizontal for it. I have created the demo of it, please check it once
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _HomeScreen();
}
}
class _HomeScreen extends State<HomeScreen> {
static final GlobalKey<ScaffoldState> _scaffoldKey =
GlobalKey<ScaffoldState>();
///Page Controller for the PageView
final controller = PageController(
initialPage: 0,
);
#override
Widget build(BuildContext context) {
Size _screenSize = MediaQuery.of(context).size;
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
centerTitle: true,
title: Text(
'Horizontal Viewpager',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,),
),
),
///A Page View with 3 children
body: PageView(
controller: controller,
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
pageSnapping: true,
children: <Widget>[
Container(
color: Colors.white,
child: Card(
color: Colors.lightBlue,
elevation: 4,
margin: EdgeInsets.all(24),
child: Center(
child: Text(
"Card 1",
style: TextStyle(
color: Colors.white,
fontSize: 24),
),
),
),
),
Container(
color: Colors.white,
child: Card(
color: Colors.purpleAccent,
elevation: 4,
margin: EdgeInsets.all(24),
child: Center(
child: Text(
"Card 2",
style: TextStyle(
color: Colors.white,
fontSize: 24),
),
),
),
),
Container(
color: Colors.white,
child: Card(
color: Colors.pink,
elevation: 4,
margin: EdgeInsets.all(24),
child: Center(
child: Text(
"Card 3",
style: TextStyle(
color: Colors.white,
fontSize: 24),
),
),
),
),
],
),
);
}
}
And output of above program as follow
You can check my another example where i have created the swipable with fixed height Click here
I am posting another example, as you need the indicator at the both side of the PagewView for it , you need to use the Row with Expaned as follow
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _HomeScreen();
}
}
class _HomeScreen extends State<HomeScreen> {
static final GlobalKey<ScaffoldState> _scaffoldKey =
GlobalKey<ScaffoldState>();
var selectedPage = 0;
PageController _controller = PageController(initialPage: 0, keepPage: true);
#override
Widget build(BuildContext context) {
Size _screenSize = MediaQuery
.of(context)
.size;
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
centerTitle: true,
title: Text(
'Horizontal Viewpager',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,),
),
),
///A Page View with 3 children
body: Container(
child:Container(
height: MediaQuery.of(context).size.height*0.4,
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child:
IconButton(
icon: Icon(Icons.arrow_back),
highlightColor: Colors.pink,
onPressed: () {
setState(() {
if (selectedPage > 0) {
selectedPage = selectedPage - 1;
_controller.jumpToPage(selectedPage);
print("VALUES==>>>>> $selectedPage");
}
});
},
),
), Expanded(
flex: 8,
child: PageView(
controller: _controller,
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
onPageChanged: (index)
{
selectedPage= index;
},
pageSnapping: true,
children: <Widget>[
Container(
color: Colors.white,
child: Card(
color: Colors.lightBlue,
elevation: 4,
margin: EdgeInsets.all(24),
child: Center(
child: Text(
"Card 1",
style: TextStyle(
color: Colors.white,
fontSize: 24),
),
),
),
),
Container(
color: Colors.white,
child: Card(
color: Colors.purpleAccent,
elevation: 4,
margin: EdgeInsets.all(24),
child: Center(
child: Text(
"Card 2",
style: TextStyle(
color: Colors.white,
fontSize: 24),
),
),
),
),
Container(
color: Colors.white,
child: Card(
color: Colors.pink,
elevation: 4,
margin: EdgeInsets.all(24),
child: Center(
child: Text(
"Card 3",
style: TextStyle(
color: Colors.white,
fontSize: 24),
),
),
),
),
],
),
),
Expanded(
flex: 1,
child:
IconButton(
icon: Icon(Icons.arrow_forward),
highlightColor: Colors.pink,
onPressed: () {
if (selectedPage <3) {
selectedPage = selectedPage + 1;
_controller.jumpToPage(selectedPage);
print("VALUES==>> $selectedPage");
}
},
),
)
],
),
)
,
)
,
);
}
}
Please check the below output of it
You can use carousel_slider 2.1.0 package.
It also has many customisation options.
https://pub.dev/packages/carousel_slider
I'm using Flutter Default Tab Controller for shows the tab View. And I need to change the tab while clicking the button, I tried to change tab using setState, but I faild. These are my codes:
class _TabPageState extends State<TabPage> implements TabView {
int tabIndex = 0;
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
initialIndex: tabIndex,
child: Scaffold(
appBar: AppBar(),
body: TabBarView(
physics: NeverScrollableScrollPhysics(),
children: [
Container(
color: Colors.green,
child: Center(
child: RaisedButton(
child: Text('to Tab 3'),
onPressed: () {
setState(() {
tabIndex = 2;
});
}),
),
),
Container(color: Colors.red),
Container(color: Colors.yellow),
Container(color: Colors.cyan),
],
),
bottomNavigationBar: TabBar(
labelColor: Colors.black45,
tabs: [
Padding(padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text('green')),
Padding(padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text('red')),
Padding(padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text('yellow')),
Padding(padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text('cyan')),
],
),
),
);
}
}
You can do this without stateful widgets by retrieving the controller with DefaultTabController.of(context) and then calling .animateTo(index) on it.
class TabPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
initialIndex: tabIndex,
child: Scaffold(
appBar: AppBar(),
body: TabBarView(
physics: NeverScrollableScrollPhysics(),
children: [
Container(
color: Colors.green,
child: Center(
child: GoToThirdTabButton(),
),
),
Container(color: Colors.red),
Container(color: Colors.yellow),
Container(color: Colors.cyan),
],
),
bottomNavigationBar: TabBar(
labelColor: Colors.black45,
tabs: [
Padding(padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text('green')),
Padding(padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text('red')),
Padding(padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text('yellow')),
Padding(padding: const EdgeInsets.only(top: 12, bottom: 12), child: Text('cyan')),
],
),
),
);
}
}
class GoToThirdTabButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text('to Tab 3'),
onPressed: () {
DefaultTabController.of(context).animateTo(2);
}
);
}
}
The button must be its own widget so the context it sees will have the tab controller attached to it.
Try this:
import 'package:flutter/material.dart';
class TabExample extends StatefulWidget {
#override
_TabExampleState createState() => _TabExampleState();
}
class _TabExampleState extends State<TabExample> {
var tabIndex = 0;
#override
Widget build(BuildContext context) {
var childList = [
Container(
color: Colors.green,
child: Center(
child: RaisedButton(
child: Text('to Tab 3'),
onPressed: () {
setState(() {
tabIndex = 2;
});
}),
),
),
Container(color: Colors.red),
Container(color: Colors.yellow),
Container(color: Colors.cyan),
];
return DefaultTabController(
length: 4,
initialIndex: tabIndex,
child: Scaffold(
appBar: AppBar(),
body: childList[tabIndex],
bottomNavigationBar: TabBar(
onTap: (index) {
setState(() {
tabIndex = index;
});
},
labelColor: Colors.black,
tabs: <Widget>[
Tab(text: 'Green'),
Tab(text: 'Red'),
Tab(text: 'Yellow'),
Tab(text: 'Cyan'),
],
),
),
);
}
}
Code Golf Solution
If you want to access the DefaultTabController without creating an entirely new StatelessWidget, you can add a Builder widget to add a new context layer. This makes it so your button can "see" the DefaultTabController:
Scaffold(
body: SafeArea(
child: DefaultTabController(
initialIndex: 0,
length: 3,
child: Builder( // Add this
builder: (context) {
return Column(
children: [
TabBar(
controller: DefaultTabController.of(context),
labelColor: Colors.black,
tabs: [
Tab(text: 'One'),
Tab(text: 'Two'),
Tab(text: 'Three'),
],
),
Expanded(child: Center(
child: OutlinedButton(
child: Text('Three'),
onPressed: (){
DefaultTabController.of(context)?.animateTo(2);
},
),
))
],
);
}
),
),
),
);