I tried to create nested tabs bar views in the following way, by returning a column containing another tab view from the outer tab view, however it just shows a blank screen instead of the second tab view. How can I fix this?
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DefaultTabController(length: 2, child: MyHomePage(title: '')),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context){
final view = LayoutBuilder(builder: (context, constraints){
//created a widget here with content
});
return Scaffold(
appBar: AppBar(
title: Text('Tab bar view'), bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_transit)),
]),
),
body: TabBarView(children: [DefaultTabController(length: 2, child: Column(
children: <Widget>[TabBar(tabs: [Tab(text: 'Home'),Tab(text: 'News')
, Container(child: TabBarView(
children: <Widget>[ view, Icon(Icons.access_alarms)],
))])]
) ), Icon(Icons.directions_car)]));
}
}
Best Way to do it just like in Google Play store.
import 'package:flutter/material.dart';
class Locationstat extends StatefulWidget {
#override
_LocationstatState createState() => _LocationstatState();
}
class _LocationstatState extends State<Locationstat>
with SingleTickerProviderStateMixin {
late TabController _tabController;
#override
void initState() {
super.initState();
_tabController = new TabController(length: 2, vsync: this);
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
title: Text('Statistics'),
bottom: TabBar(
controller: _tabController,
indicatorColor: Colors.orange,
labelColor: Colors.orange,
unselectedLabelColor: Colors.black54,
tabs: <Widget>[
Tab(
text:('Pokhara Lekhnath'),
),
Tab(
text:('Outside Pokhara-Lekhnath'),
),
]),
),
body: TabBarView(
children: <Widget>[
NestedTabBar(),
NestedTabBar(),
],
controller: _tabController,
),
);
}
}
class NestedTabBar extends StatefulWidget {
#override
_NestedTabBarState createState() => _NestedTabBarState();
}
class _NestedTabBarState extends State<NestedTabBar>
with TickerProviderStateMixin {
late TabController _nestedTabController;
#override
void initState() {
super.initState();
_nestedTabController = new TabController(length: 2, vsync: this);
}
#override
void dispose() {
super.dispose();
_nestedTabController.dispose();
}
#override
Widget build(BuildContext context) {
double screenHeight = MediaQuery.of(context).size.height;
return Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
TabBar(
controller: _nestedTabController,
indicatorColor: Colors.orange,
labelColor: Colors.orange,
unselectedLabelColor: Colors.black54,
isScrollable: true,
tabs: <Widget>[
Tab(
text: "Inside Pokhara",
),
Tab(
text: "Outside Pokhara",
),
],
),
Container(
height: screenHeight * 0.70,
margin: EdgeInsets.only(left: 16.0, right: 16.0),
child: TabBarView(
controller: _nestedTabController,
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.blueGrey[300],
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.blueGrey[300],
),
),
],
),
)
],
);
}
}
import 'package:flutter/material.dart';
class Locationstat extends StatefulWidget {
#override
_LocationstatState createState() => _LocationstatState();
}
class _LocationstatState extends State<Locationstat>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = new TabController(length: 2, vsync: this);
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
title: Text('Statistics'),
bottom: TabBar(
controller: _tabController,
indicatorColor: Colors.orange,
labelColor: Colors.orange,
unselectedLabelColor: Colors.black54,
tabs: <Widget>[
Tab(
text:('Pokhara Lekhnath'),
),
Tab(
text:('Outside Pokhara-Lekhnath'),
),
]),
),
body: TabBarView(
children: <Widget>[
NestedTabBar(),
NestedTabBar(),
],
controller: _tabController,
),
);
}
}
class NestedTabBar extends StatefulWidget {
#override
_NestedTabBarState createState() => _NestedTabBarState();
}
class _NestedTabBarState extends State<NestedTabBar>
with TickerProviderStateMixin {
TabController _nestedTabController;
#override
void initState() {
super.initState();
_nestedTabController = new TabController(length: 2, vsync: this);
}
#override
void dispose() {
super.dispose();
_nestedTabController.dispose();
}
#override
Widget build(BuildContext context) {
double screenHeight = MediaQuery.of(context).size.height;
return Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
TabBar(
controller: _nestedTabController,
indicatorColor: Colors.orange,
labelColor: Colors.orange,
unselectedLabelColor: Colors.black54,
isScrollable: true,
tabs: <Widget>[
Tab(
text: "Inside Pokhara",
),
Tab(
text: "Outside Pokhara",
),
],
),
Container(
height: screenHeight * 0.70,
margin: EdgeInsets.only(left: 16.0, right: 16.0),
child: TabBarView(
controller: _nestedTabController,
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.blueGrey[300],
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.blueGrey[300],
),
),
],
),
)
],
);
}
}
you can do that by adding BottomNavigationBar to the Scaffold and route to a widget that contains the TabBar
Check this, https://github.com/whatsupcoders/Flutter-Nested-Tabs
It has code, which has a UI replication of the Google Play Store.
Here' the image
Here's the tutorial for it: https://www.youtube.com/watch?v=bSywfMPuwaw
Cheers!
Related
I want something like this.
Im able to implement something similar with SwitchTab,but not able to use gradient color for selected tab.
Please help
SwitchTab(
text: const [ "Personal",
"Group",
],
selectedTextColor: Colors.black,
unselectedTextColor:Colors.white,
shape: SwitchTabShape.rounded,
thumbColor: Colors.white,
backgroundColour:
Color.fromARGB(255, 31, 89, 169),
onValueChanged: (index) {
setState(() {
selected = index;
});
},
),
You can use a combination of a normal TabBar, BoxDecoration and LinearGradient for this:
Code Example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
MyWidgetState createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin {
late TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 2, vsync: this);
super.initState();
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Stack Overflow Example'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Container(
height: 45,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(
25.0,
),
),
child: TabBar(
controller: _tabController,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(
25.0,
),
color: Colors.green,
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Colors.pink,
Colors.deepPurple,
],
),
),
labelColor: Colors.white,
unselectedLabelColor: Colors.black,
tabs: [
const Tab(text: 'Personal'),
const Tab(text: 'Group'),
],
),
),
Expanded(
child: TabBarView(
controller: _tabController,
children: [
Center(child: Text('Personal Content')),
Center(child: Text('Group Content')),
],
),
),
],
),
),
);
}
}
I tried to make TabBarView in TabBar, but When I put the code below, It became white screen totally and there's nothing and not specific error.. How Çan I solve it? please advice me and appreciate it.
class Buttons extends StatefulWidget {
const Buttons({Key? key}) : super(key: key);
#override
State<Buttons> createState() => _ButtonsState();
}
class _ButtonsState extends State<Buttons> with TickerProviderStateMixin { // 다중 AnimationController를 사용할 때..???
#override
Widget build(BuildContext context) {
TabController _tabController = TabController(length: 3, vsync: this);
return Container(
child: Column(
children: [
Container(
child: TabBar(
controller: _tabController,
tabs: [
Tab(icon: ClipRRect(child: Text("Shop"),),),
Tab(icon: ClipRRect(child: Text("Donate"),),),
Tab(icon: ClipRRect(child: Text("BId"),),)
],
),
),
Container(
// width: double.maxFinite,
height: 300,
child: TabBarView(
controller: _tabController,
children: [
ShopPage(),
DonatePage(),
BidPage()
],
)
)
])
);
}
}
In children, Each pages are in other file and I imported those.
I have checked your code there are some corrections in it I have made it so please try the below code. I have checked with the below code and it works well.
class Buttons extends StatefulWidget {
const Buttons({Key? key}) : super(key: key);
#override
State<Buttons> createState() => _ButtonsState();
}
class _ButtonsState extends State<Buttons> with TickerProviderStateMixin { // 다중 AnimationController를 사용할 때..???
late TabController _tabController;
#override
Widget build(BuildContext context) {
#override
void initState() {
// TODO: implement initState
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Text(
'Tabbar Demo',
style: TextStyle(
fontSize: 20
),
),
bottom: PreferredSize(
preferredSize: Size.fromHeight(50),
child: Container(
child: TabBar(
controller: _tabController,
tabs: [
Tab(icon: ClipRRect(child: Text("Shop", style: TextStyle(color: Colors.white),),),),
Tab(icon: ClipRRect(child: Text("Donate", style: TextStyle(color: Colors.white)),),),
Tab(icon: ClipRRect(child: Text("BId", style: TextStyle(color: Colors.white)),),)
],
),
),
),
),
body: SafeArea(
child: Container(
child: Column(
children: [
Expanded(
child: Container(
child: TabBarView(
controller: _tabController,
children: [
Container(color: Colors.red,),
Container(color: Colors.black87,),
Container(color: Colors.yellow,)
],
)
),
)
])
),
)
);
}
}
Here is the out put from the above code
Let me know if any query
I want to do when I click on each chip, it will change the content of my body but I am using tab controller which I wrap with chip widget.
I also want to decorate my chip when it is selected and unselected. I try to declare my text for each widget using list array but I am stuck. Can someone help me. This is what I have been done so far
class YearTab extends StatefulWidget {
const YearTab({
Key? key,
}) : super(key: key);
#override
State<YearTab> createState() => _YearTabState();
}
class _YearTabState extends State<YearTab>
with SingleTickerProviderStateMixin {
late TabController _controller;
bool _selectedTab = false;
List<Widget> list = const [
Chip(label: Text('This year')),
Chip(label: Text('2021')),
Chip(label: Text('2020')),
Chip(label: Text('2019')),
Chip(label: Text('2018')),
];
#override
void initState() {
super.initState();
_controller = TabController(length: list.length, vsync: this);
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
color: AppColor.white,
width: MediaQuery.of(context).size.width,
child: TabBar(
// unselectedLabelColor: Colors.yellow,
// labelColor: Colors.red,
physics: const BouncingScrollPhysics(),
indicator: BoxDecoration(
border: Border.all(color: Colors.red),
borderRadius: BorderRadius.circular(10),
color: const Color.fromARGB(255, 176, 208, 255)
// color: !widget.selected
// ? Color.fromARGB(255, 176, 208, 255)
// : Colors.transparent
),
controller: _controller,
onTap: (index) {},
isScrollable: true,
tabs: list,
)),
SizedBox(
height: MediaQuery.of(context).size.height * 2.2,
child: TabBarView(
controller: _controller,
children: const [
//Content for Demografi Pengguna
Content1(),
Content2(),
Content3(),
Content4(),
Content5(),
],
),
)
],
);
}
}
Instead of Tabview ,you can use this :
dependencies:
toggle_switch: ^2.0.1
Example:
ToggleSwitch(
minWidth: 90.0,
initialLabelIndex: 1,
cornerRadius: 20.0,
activeFgColor: Colors.white,
inactiveBgColor: Colors.grey,
inactiveFgColor: Colors.white,
totalSwitches: 2,
labels: ['Tab1', 'Tab2'],
icons: [FontAwesomeIcons.mars, FontAwesomeIcons.venus],
activeBgColors: [[Colors.blue],[Colors.pink]],
onToggle: (index) {
print('switched to: $index');
//change the view as per index
},
),
When you request a chip widget, it is called in initState() the first time you call the widget. You can initialize the array from there and get the data. Alternatively, you can call the widget by observing the array using the Stream structure using getX or Provider.
i changed a few things
class YearTab extends StatefulWidget {
const YearTab({
Key? key,
}) : super(key: key);
#override
State<YearTab> createState() => _YearTabState();
}
class _YearTabState extends State<YearTab> with SingleTickerProviderStateMixin {
List<Widget> tabs = const [
Chip(label: Text('This year')),
Chip(label: Text('2021')),
Chip(label: Text('2020')),
Chip(label: Text('2019')),
Chip(label: Text('2018')),
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
indicatorColor: Theme.of(context).colorScheme.secondary,
tabs: tabs,
),
),
body: TabBarView(
children: [
//Content for Demografi Pengguna
Container(
color: Colors.blueAccent,
),
Container(
color: Colors.red,
),
Container(
color: Colors.green,
),
Container(
color: Colors.yellow,
),
Container(
color: Colors.grey,
),
],
),
),
);
}
}
Following is a custom tab I've created:
class Tabs extends StatefulWidget {
final tabs;
final views;
Tabs({this.tabs, this.views});
#override
_TabsState createState() => _TabsState();
}
class _TabsState extends State<Tabs> with SingleTickerProviderStateMixin {
late TabController _tabController;
#override
void initState() {
_tabController = TabController(length: widget.tabs.length, vsync: this);
super.initState();
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Container(
height: size.height,
child: Column(
children: [
Container(
height: 45,
decoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey, width: 3),
),
),
child: TabBar(
isScrollable: true,
controller: _tabController,
indicatorPadding: EdgeInsets.only(bottom: -2.5),
indicator: UnderlineTabIndicator(
borderSide: BorderSide(
width: 4,
color: Colors.blue,
),
),
labelColor: Colors.blue,
unselectedLabelColor: Colors.black,
tabs: [
...widget.tabs.map(
(e) => Tab(
child: Container(
child: Text(
e,
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
),
)
],
),
),
Expanded(
child: TabBarView(
controller: _tabController,
children: [...widget.views],
),
),
],
),
),
);
}
}
But as soon as I use it inside my app screen, I'm getting overflow issues:
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: "Home"
elevation: 0,
toolbarHeight: 36,
),
drawer: DrawerNavigation(),
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: [
SizedBox(
height: 100,
),
Tabs(
tabs: ['tab1', 'tab2'],
views: [
Tab1(),
Tab2(),
],
),
],
),
),
);
}
}
The error I'm getting from is from single child scroll view.
I've tried removing single child scroll view from the tabs, but then the custom tabs wont' work.
just remove single child scroll view from custom Tab like this,
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class Tabs extends StatefulWidget {
final tabs;
final views;
Tabs({this.tabs, this.views});
#override
_TabsState createState() => _TabsState();
}
class _TabsState extends State<Tabs> with SingleTickerProviderStateMixin {
late TabController _tabController;
#override
void initState() {
_tabController = TabController(length: widget.tabs.length, vsync: this);
super.initState();
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Container(
height: size.height,
child: Column(
children: [
Container(
height: 45,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Colors.grey, width: 3),
),
),
child: TabBar(
isScrollable: true,
controller: _tabController,
indicatorPadding: EdgeInsets.only(bottom: -2.5),
indicator: UnderlineTabIndicator(
borderSide: BorderSide(
width: 4,
color: Colors.blue,
),
),
labelColor: Colors.blue,
unselectedLabelColor: Colors.black,
tabs: [
...widget.tabs.map(
(e) => Tab(
child: Container(
child: Text(
e,
style: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
),
)
],
),
),
Expanded(
child: TabBarView(
controller: _tabController,
children: [...widget.views],
),
),
],
),
);
}
}
and convert stateless widget (Home) into stateful widget and it will work fine like this,
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 100,
),
Tabs(
tabs: ["tab1", "tab2"],
views: [okay(), okay()],
)
],
),
),
);
}
}
How to create basic design like from sketch from image. First container with list of tabs is static, and containers above it is dynamic, and contain text - Text for tab 1 if tab 1 is clicked, or Text for tab 2 if tab 2 is clicked. Also text for Tab1 or Tab2 must be underline if we click on it.
Something like this:
Using a GestureDetector widget to tell when a user taps on the tabs and then updating the state with setState is maybe the most simple way of doing this.
Here is a very basic example of using a stateful widget and setState to update the page. You can approach this problem with any state management strategy, though.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Scaffold(body: MyHomePage()),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String selectedTab;
static const String TAB_1 = 'Tab 1';
static const String TAB_2 = 'Tab 2';
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 1,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
setState(() => selectedTab = TAB_1);
},
child: Container(
padding: const EdgeInsets.all(12.0),
child: Text(TAB_1),
decoration: selectedTab == TAB_1
? BoxDecoration(border: Border(bottom: BorderSide()))
: null,
),
),
GestureDetector(
onTap: () {
setState(() => selectedTab = TAB_2);
},
child: Container(
padding: const EdgeInsets.all(12.0),
child: Text(TAB_2),
decoration: selectedTab == TAB_2
? BoxDecoration(border: Border(bottom: BorderSide()))
: null,
),
)
],
),
),
Container(height: 20.0),
Expanded(
flex: 2,
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 5),
borderRadius: BorderRadius.circular(12.0)),
child: Center(child: Text(textForTab(selectedTab))),
),
)
],
);
}
String textForTab(String tabId) {
switch (tabId) {
case TAB_1:
return 'Text for Tab 1';
case TAB_2:
return 'Text for Tab 2';
default:
return 'Select Tab';
}
}
}
You can do it using TabBar widget.
class CustomTabBar extends StatefulWidget {
#override
_CustomTabBarState createState() => _CustomTabBarState();
}
class _CustomTabBarState extends State<CustomTabBar>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 2, vsync: this);
super.initState();
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Tab Demo',
),
),
body:
Column(
children: [
Container(
height: 45,
child: TabBar(
controller: _tabController,
indicator: BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.blue,width: 2,style:
BorderStyle.solid)),
),
labelColor: Colors.blue,
unselectedLabelColor: Colors.black,
tabs: [
// first tab
Tab(
text: 'Home',
),
// second tab
Tab(
text: 'Profile',
),
],
),
),
// tab bar view
Expanded(
child: TabBarView(
controller: _tabController,
children: [
// first tab widget
Center(
child: Text(
'Home',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
),
// Second tab widget
Center(
child: Text(
'Profile',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
],
),
);
}
}
I'm not sure I understand you completely.
But if you want to implement a TabBar, you can use flutter TabBar() documented here:
https://flutter.dev/docs/cookbook/design/tabs
Or there is a package you can use:
https://pub.dev/packages/tabbar