Adding tabs on clicking button - flutter

How can I add a new tab in TabBar by clicking on a button?
I tried to call 'setState' in order to add a new tab.
class _MyHomeState extends State<MyHome> {
final List<Tab> _tabs = [
Tab(
child: Text("tab 1"),
),
Tab(
child: Text("tab 2"),
)
];
int _length = 2;
final List<Widget> _tabBarView = [
Icon(Icons.ac_unit),
Icon(Icons.access_alarm)
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _length,
child: Scaffold(
appBar: AppBar(
title: Text("New"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
_tabs.add(
Tab(
child: Text("another"),
),
);
_tabBarView.add(
Icon(Icons.access_alarms),
);
_length = _tabs.length;
});
},
),
],
bottom: TabBar(
tabs: _tabs,
),
),
body: TabBarView(
children: _tabBarView,
),
),
);
}
}
By doing so, got an error message,
RangeError (index): Invalid value: Not in range 0..1, inclusive: 2

You need minor tweaking in your code:
change:
bottom: TabBar(
tabs: _tabs,
),
),
body: TabBarView(
children: _tabBarView,
),
to
bottom: TabBar(
tabs: _tabs.toList(), // Creates a [List] containing the elements of this [Iterable].
),
),
body: TabBarView(
children: _tabBarView.toList(), // Creates a [List] containing the elements of this [Iterable].
),

Related

How to open a new screen within the same tab and keep showing the TabBar

I would like when I click on button "Nova Reserva", it opens a new screen, but in the same tab, without losing the TabBar.
APP
enter image description here
Current
enter image description here
Code TabBarView
TabBarView(
controller: _tabController,
children: const [
HomeTab(),
ResearchesTab(),
SchedulesTab(),
Center(
child: Text('MENSAGENS'),
),
Center(
child: Text('CADASTROS'),
),
],
),
Code TabBar
child: TabBar(
physics: const BouncingScrollPhysics(),
controller: _tabController,
isScrollable: true,
indicatorPadding: EdgeInsets.symmetric(
vertical: size.height * .005,
),
indicatorSize: TabBarIndicatorSize.label,
indicator: BoxDecoration(
border: Border(
bottom: BorderSide(
color: CustomColors.orange,
width: size.height * .004,
),
),
),
labelPadding: EdgeInsets.symmetric(
horizontal: size.width * .04,
),
tabs: const [
TabBarTile(
image: 'assets/images/home.png',
label: 'Home',
),
TabBarTile(
image: 'assets/images/pesquisas.png',
label: 'Pesquisas',
),
TabBarTile(
image: 'assets/images/agendamentos.png',
label: 'Agendamentos',
),
TabBarTile(
image: 'assets/images/mensagens.png',
label: 'Mensagens',
),
TabBarTile(
image: 'assets/images/cadastros.png',
label: 'Cadastros',
),
],
),
In the button I'm using navigation with GetX, but I've also tried with MaterialPageRoute and I wasn't successful.
My objective
enter image description here
Create a Boolean variable that will change the tab bar's body according to its value. Change the tab bar's body content by changing the flag of the Boolean value.
Complete Code : -
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: _title,
home: MyStatelessWidget(),
);
}
}
class MyStatelessWidget extends StatefulWidget {
const MyStatelessWidget({super.key});
#override
State<MyStatelessWidget> createState() => _MyStatelessWidgetState();
}
class _MyStatelessWidgetState extends State<MyStatelessWidget> {
bool buttonOnePressed = false; // Declare the Boolean variable
#override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 1,
length: 3,
child: Scaffold(
appBar: AppBar(
title: const Text('TabBar Widget'),
bottom: const TabBar(
tabs: <Widget>[
Tab(
text: "Tab 1",
),
Tab(
text: "Tab 2",
),
Tab(
text: "Tab 3",
),
],
),
),
body: TabBarView(
children: <Widget>[
const Center(
child: Text("Tab 1"),
),
buttonOnePressed // Display widgets according to Boolean variable
? const Center(
child: Text("From Button 1"),
)
: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
setState(() {
buttonOnePressed = true; // change Boolean value
});
},
child: const Text("Button 1"),
),
const SizedBox(width: 30),
ElevatedButton(
onPressed: () {}, child: const Text("Button 2"))
],
),
),
const Center(
child: Text("It's sunny here"),
),
],
),
),
);
}
}
Output : -

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

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

I am having an error with Flutter saying: Controller's length property does not match

I have been having this issue in Flutter when I am trying to use the DefaultTabController() widget but it seems it does not work the way I have specified the layout. Can anyone help me with it.
This is my code:
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
initialIndex: 0,
child: Scaffold(
appBar: AppBar(
leading: IconButton(icon: Icon(Icons.menu), onPressed: () {}),
title: Text('Home'),
bottom: TabBar(tabs: [
Tab(child: Text("Videos")),
Tab(child: Text("Live Videos")),
Tab(child: Text("Gallery")),
]),
),
body: TabBarView(
children: [
new Card(
color: Colors.blue,
),
],
),
));
}
}
What am I doing wrong here?
Your body element only contains one element. According to your length attribute of the DefaultTabController, there should be 3 elements in that array.
body: TabBarView(
children: [
new Card(
color: Colors.blue,
),
new Card(
color: Colors.red,
),
new Card(
color: Colors.orange,
),
],
),

How to change tab in the Flutter Default Tab Controller?

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);
},
),
))
],
);
}
),
),
),
);

How to place tabs of TabBar in middle and upon clicking next tab, it starts shifting to left?

Mock I am trying to implement:
Then when starting clicking the next tab i.e 2 it shows 3rd tab (named 3) and 1st tab moves to left. Till eventually to far left slide of the screen.
Mock:
Finally, when clicked further it shows three dots to left also. FYI clicking on dots does nothing its a representation of showing that more that is there.
Now following is my code.
class TabbedAppBarSample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: choices.length,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white, // status bar color
brightness: Brightness.light,
title: TimerPage(),
bottom: TabBar(
// isScrollable: true,
indicatorColor:Colors.red,
unselectedLabelColor:Colors.grey,
labelColor: Colors.red,
tabs: choices.map((Choice choice) {
return Tab(
text: choice.title,
// icon: Icon(choice.icon),
);
}).toList(),
),
),
body: TabBarView(
children: choices.map((Choice choice) {
return Padding(
padding: const EdgeInsets.all(6.0),
child: ChoiceCard(choice: choice),
);
}).toList(),
),
),
),
);
}
}
class Choice {
const Choice({this.title, this.icon});
final String title;
final IconData icon;
}
const List<Choice> choices = const <Choice>[
const Choice(title: '1'),
const Choice(title: '2'),
const Choice(title: '...'),
]; // above is a mock array which eventually will be coming from API call
Thanks and bear my coding I am new to flutter.
Use TabController to animate to next tab and use PreferredSize to control positon of tabbar
code snippet
appBar: AppBar(
title: Text(widget.title),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(20.0),
child: Padding(
padding: const EdgeInsets.only(left: 200.0),
child: TabBar(
controller: _tabController,
tabs: [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
),
),
),
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: 3);
}
void _toggleTab() {
_tabIndex = _tabController.index + 1;
_tabController.animateTo(_tabIndex);
}
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Tabs Demo';
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key key, this.title}) : super(key: key);
#override
State<StatefulWidget> createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
int _tabIndex = 0;
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: 3);
}
void _toggleTab() {
_tabIndex = _tabController.index + 1;
_tabController.animateTo(_tabIndex);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomAppBar(
notchMargin: 20,
child: new Row(
// mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
InkWell(
onTap: () {
_toggleTab();
},
child: Text(
'Next >',
style: TextStyle(fontSize: 20, color: Colors.red),
),
)
],
),
),
appBar: AppBar(
title: Text(widget.title),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(20.0),
child: Padding(
padding: const EdgeInsets.only(left: 200.0),
child: TabBar(
controller: _tabController,
tabs: [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
),
),
),
body: TabBarView(
controller: _tabController,
children: [
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Icon(Icons.album),
title: Text('Hello 1'),
subtitle: Text('Click on Next Button to go to Tab 2.'),
),
],
),
),
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Icon(Icons.album),
title: Text('Hello 2'),
subtitle: Text('Click on Next Button to go to Tab 3'),
),
],
),
),
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Icon(Icons.album),
title: Text('Hello 3'),
subtitle: Text('The End'),
),
],
),
),
],
),
));
}
}