Related
I'm trying to design a tabbar with this pattern of design
But so I'm not able to make the unselected tab have a background just like this
When I try I end with something like this
Is it posible to use the Defaulttabbarcontroller to make this design or there is a way to design my own custom tab bar
This widget will provide you idea, I am using color.withOpacity, you may use colorFilter or others widget,
class TabBarDemo extends StatefulWidget {
const TabBarDemo({super.key});
#override
State<TabBarDemo> createState() => _TabBarDemoState();
}
class _TabBarDemoState extends State<TabBarDemo>
with SingleTickerProviderStateMixin {
late final TabController controller = TabController(length: 3, vsync: this)
..addListener(() {
setState(() {});
});
#override
Widget build(BuildContext context) {
final textStyle = TextStyle(color: Colors.blue.shade900);
return Scaffold(
backgroundColor: Colors.purple,
appBar: AppBar(
title: const Text('Tabs Demo'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
ActionChip(
backgroundColor: Colors.white,
shape: StadiumBorder(),
onPressed: () {
controller.animateTo(0);
},
label: Text(
"All",
style: controller.index == 0
? textStyle
: textStyle.copyWith(
color: textStyle.color!.withOpacity(.3),
),
),
),
SizedBox(width: 10),
ActionChip(
backgroundColor: Colors.white,
shape: StadiumBorder(),
onPressed: () {
controller.animateTo(1);
},
label: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(
Icons.ac_unit,
size: 12,
color: controller.index == 1
? Colors.red
: Colors.red.withOpacity(.4),
),
),
Text(
"Income",
style: controller.index == 1
? textStyle
: textStyle.copyWith(
color: textStyle.color!.withOpacity(.3),
),
),
],
),
),
SizedBox(
width: 10,
),
ActionChip(
backgroundColor: Colors.white,
shape: StadiumBorder(),
onPressed: () {
controller.animateTo(2);
},
label: Text(
"Expenses",
style: controller.index == 2
? textStyle
: textStyle.copyWith(
color: textStyle.color!.withOpacity(.3),
),
)),
],
),
Expanded(
child: TabBarView(
controller: controller,
children: [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
],
),
),
],
),
),
);
}
}
Based on your case and design, I go with Chips and Wrap widgets if I want to build your design. Then I have a Container below them that acts as TabBarView. So, you need to change the child of the Container when the user taps on the Chip.
However, if you want to stick with TabBar then you need to set a theme when you launch the app. This is how I assign my themes, usually.
What I want to do is to when pressed a movie tab it shows NowPlayedMovies() and BestMovies(), and when pressed tv shows tab to show NowPlayedTV() and BestTV(). At first I was using ListView but because I'm using tabs i need to use TabBarView. So in my child method I created 2 methods buildPage1 and buildPage2 in which I have put my 2 methods as mentioned above.
When I tried to run the code it displayed this error:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building _TabControllerScope:
The getter 'key' was called on null.
Receiver: null
Tried calling: key
The relevant error-causing widget was:
DefaultTabController
Here is my code:
class HomePageMovie extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePageMovie> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF151C26),
appBar: AppBar(
backgroundColor: Color(0xFF151C26),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SvgPicture.asset(
logo,
height: 195,
),
],
),
actions: <Widget>[
IconButton(
onPressed: () {},
icon: Icon(
EvaIcons.searchOutline,
color: Colors.white,
))
],
titleSpacing: 0.0,
bottom: PreferredSize(
preferredSize: Size.fromHeight(75.0),
child: DefaultTabController(
length: 2,
child: TabBar(
indicatorColor: Color(0xFFf4C10F),
indicatorWeight: 4.0,
unselectedLabelColor: Colors.white,
labelColor: Colors.white,
tabs: [
Tab(
icon: Icon(Icons.movie),
child: Text(
"Movies",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14.0),
),
),
Tab(
icon: Icon(Icons.live_tv),
child: Text(
"TV Shows",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14.0),
),
)
]),
),
),
),
body: DefaultTabController(
length: 2,
child: TabBarView(
children: [buildPage1(), buildPage2()],
),
));
}
buildPage1() {
NowPlayingMovies();
BestMovie();
}
buildPage2() {
NowPlayingTV();
BestTV();
}
}
Here is visual representation of what I'm trying to achieve:
Any help would be great. Thanks in advance :)
Use only one DefaultTabBarController at the top of your widget tree (in this case, as a parent of your Scaffold and remove the other two).
You should only use a single DefaultTabBarController that is shared between the TabBar and the TabBarView.
So with help of Miguel what I did wrong was I didn't put a specific controller to connect these 2, in my case as a solution I wrapped them both in common default controller. As of the 2nd part of the problem what I did is simply put ListView to display these 2
The code of solution:
class _HomePageState extends State<HomePageMovie> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Color(0xFF151C26),
appBar: AppBar(
backgroundColor: Color(0xFF151C26),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SvgPicture.asset(
logo,
height: 195,
),
],
),
actions: <Widget>[
IconButton(
onPressed: () {},
icon: Icon(
EvaIcons.searchOutline,
color: Colors.white,
))
],
titleSpacing: 0.0,
bottom: PreferredSize(
preferredSize: Size.fromHeight(75.0),
child: TabBar(
indicatorColor: Color(0xFFf4C10F),
indicatorWeight: 4.0,
unselectedLabelColor: Colors.white,
labelColor: Colors.white,
tabs: [
Tab(
icon: Icon(Icons.movie),
child: Text(
"Movies",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14.0),
),
),
Tab(
icon: Icon(Icons.live_tv),
child: Text(
"TV Shows",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14.0),
),
)
]),
),
),
body: TabBarView(
children: [
ListView(
children: <Widget>[NowPlayingMovies(), BestMovie()],
),
ListView(
children: <Widget>[
NowPlayingTV(),
BestTV(),
],
),
],
),
),
);
}
}
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 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),),
)
]),
),
I have two tab. I want elevation bottom of Appbar and elevation bottom of TabBarView. How can I do this?
Here is my Code,
class MyOrder extends StatefulWidget {
#override
_MyOrderState createState() => _MyOrderState();
}
class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin{
var strTitle = Translations.globalTranslations.myOrders;
TabController _tabController;
#override
void initState() {
_tabController = new TabController(length: 2, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
elevation: 1.0,
leading: new IconButton(
icon: Image.asset('images/keyboard_backspace.png', width: 24.0, height: 24.0,),
onPressed: () => Navigator.of(context).pop(),
),
title: Text(strTitle,textAlign: TextAlign.left , style: UIUtills().getTextStyle(
fontName: AppFontName.appFontSemiBold,
fontsize: 20,
color: AppColor.redColor),),
backgroundColor: Colors.white,
iconTheme: IconThemeData(
color: AppColor.redColor),
bottom: TabBar(
indicatorColor: AppColor.redColor,
labelColor: AppColor.blackColor,
labelStyle: UIUtills().getTextStyle(
fontName: AppFontName.appFontSemiBold,
fontsize: 16,
color: AppColor.blackColor),
tabs: [
new Tab(text:Translations.globalTranslations.pastOrder),
new Tab(text: Translations.globalTranslations.upComing)
],
controller: _tabController,
indicatorSize: TabBarIndicatorSize.tab),
),
body: TabBarView(
children: [
PastOrder(),
UpComingOrder(),
],
controller: _tabController,),
);
}
}
As per my code layout looks like,
https://i.stack.imgur.com/D9BND.png
I want to design my layout like this,
https://i.stack.imgur.com/tPv8A.png
Try this one
ScreenShot https://imgur.com/Fz95DEr
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 8.0, // top bar
leading: InkWell(
onTap: () {},
child: Icon(
Icons.keyboard_arrow_left,
color: Colors.red,
),
),
backgroundColor: Colors.white,
title: Text(
"My Order",
style: TextStyle(color: Colors.red),
),
),
body: Scaffold(
body: DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
elevation: 8.0, // bottom bar
bottom: PreferredSize(
child: TabBar(
labelColor: Colors.black,
indicatorColor: Colors.red,
tabs: <Widget>[
Tab(
text: "Past Order",
),
Tab(
text: "Upcoming",
),
],
),
preferredSize: Size.fromHeight(0.0),
),
backgroundColor: Colors.white,
),
body: TabBarView(
children: <Widget>[
Container(
color: Colors.white,
child: Center(
child: Text("Past Order"),
),
),
Container(
color: Colors.white,
child: Center(
child: Text("Upcoming"),
),
),
],
),
),
),
),
);
}
}