I want to change the title in AppBar, when when I switch from one tab to another. In my current code do not do that because on the change tab build is not called.
Thanks to all!!
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/task.dart';
import '../widgets/tasks_list_mob.dart';
import '../widgets/new_task.dart';
class TaskListName {
final String shortName;
final String longName;
TaskListName(this.shortName, this.longName);
}
class TaksScreen extends StatefulWidget {
#override
_TaksScreenState createState() => _TaksScreenState();
}
class _TaksScreenState extends State<TaksScreen> with TickerProviderStateMixin {
TabController _tabController;
final Map<int, TaskListName> tasksListLabels = {
0: TaskListName('Backlog', 'Tasks in backlog'),
1: TaskListName('Tomorrow', 'Planed tasks for tomorrow'),
2: TaskListName('Today', 'Your tasks for today'),
3: TaskListName('Last work day', 'Tasks completed last working day'),
4: TaskListName('Completed', 'Completed Tasks'),
5: TaskListName('Backlog', 'Archived not completed tasks'),
};
#override
void initState() {
super.initState();
_tabController = TabController(length: 6, vsync: this, initialIndex: 2);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
Future<void> showAddTaskDialog(BuildContext context) async {
print(_tabController);
await showDialog(
context: context,
builder: (context) => SimpleDialog(
children: [NewTask(TaksBucket.backlog)],
),
);
}
#override
Widget build(BuildContext context) {
final tasks = Provider.of<List<Task>>(context);
return Scaffold(
appBar: AppBar(
title: Text(tasksListLabels[_tabController.index].longName),
bottom: TabBar(
controller: _tabController,
isScrollable: true,
tabs: [
Tab(text: tasksListLabels[0].shortName),
Tab(text: tasksListLabels[1].shortName),
Tab(text: tasksListLabels[2].shortName),
Tab(text: tasksListLabels[3].shortName),
Tab(text: tasksListLabels[4].shortName),
Tab(text: tasksListLabels[5].shortName),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
TasksListMob(tasks, TaksBucket.backlog),
TasksListMob(tasks, TaksBucket.tomorrow),
TasksListMob(tasks, TaksBucket.today),
TasksListMob(tasks, TaksBucket.completed),
TasksListMob(tasks, TaksBucket.completed),
TasksListMob(tasks, TaksBucket.archived),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => showAddTaskDialog(context),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
After #mkobuolys solution implementation - the scroll handle is still missing.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/task.dart';
import '../widgets/tasks_list_mob.dart';
import '../widgets/new_task.dart';
class TaskListName {
final String shortName;
final String longName;
TaskListName(this.shortName, this.longName);
}
class TaksScreen extends StatefulWidget {
#override
_TaksScreenState createState() => _TaksScreenState();
}
class _TaksScreenState extends State<TaksScreen> with TickerProviderStateMixin {
TabController _tabController;
var _tabIndex = ValueNotifier(2);
final Map<int, TaskListName> tasksListLabels = {
0: TaskListName('Backlog', 'Tasks in backlog'),
1: TaskListName('Tomorrow', 'Planed tasks for tomorrow'),
2: TaskListName('Today', 'Your tasks for today'),
3: TaskListName('Last work day', 'Tasks completed last working day'),
4: TaskListName('Completed', 'Completed Tasks'),
5: TaskListName('Backlog', 'Archived not completed tasks'),
};
#override
void initState() {
super.initState();
_tabController = TabController(length: 6, vsync: this, initialIndex: 2);
}
Future<void> showAddTaskDialog(BuildContext context) async {
await showDialog(
context: context,
builder: (context) => SimpleDialog(
children: [NewTask(TaksBucket.backlog)],
),
);
}
#override
Widget build(BuildContext context) {
final tasks = Provider.of<List<Task>>(context);
return Scaffold(
appBar: AppBar(
title: **ValueListenableBuilder(
valueListenable: _tabIndex,
builder: (context, value, child) =>
Text(tasksListLabels[value].longName),
),**
bottom: TabBar(
controller: _tabController,
isScrollable: true,
onTap: (value) {
_tabIndex.value = _tabController.index;
},
tabs: [
Tab(text: tasksListLabels[0].shortName),
Tab(text: tasksListLabels[1].shortName),
Tab(text: tasksListLabels[2].shortName),
Tab(text: tasksListLabels[3].shortName),
Tab(text: tasksListLabels[4].shortName),
Tab(text: tasksListLabels[5].shortName),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
TasksListMob(tasks, TaksBucket.backlog),
TasksListMob(tasks, TaksBucket.tomorrow),
TasksListMob(tasks, TaksBucket.today),
TasksListMob(tasks, TaksBucket.completed),
TasksListMob(tasks, TaksBucket.completed),
TasksListMob(tasks, TaksBucket.archived),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => showAddTaskDialog(context),
),
);
}
}
At the end it was really easy to make it done!
I did create ValueNotifier _tabIndex
At init state I addListener on TabControler. Listener set value for _tabIndex(ValueNotifier). I did dispose Listener in dispose...
In the build method add ValueListenableBuilder to the title.
Final code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/task.dart';
import '../widgets/tasks_list_mob.dart';
import './tasks_notifications_screen.dart';
import '../widgets/new_task.dart';
import '../widgets/count_batch_icon_button.dart';
import '../consts/consts.dart';
import '../util/tasks_helper.dart';
class TaskListName {
final String shortName;
final String longName;
final TaksBucket bucket;
TaskListName(this.shortName, this.longName, this.bucket);
}
class TaksScreen extends StatefulWidget {
#override
_TaksScreenState createState() => _TaksScreenState();
}
class _TaksScreenState extends State<TaksScreen> with TickerProviderStateMixin {
TabController _tabController;
var _tabIndex = ValueNotifier(2);
final Map<int, TaskListName> tasksListLabels = {
0: TaskListName('Backlog', 'Tasks in backlog', TaksBucket.backlog),
1: TaskListName(
'Tomorrow', 'Planed tasks for tomorrow', TaksBucket.tomorrow),
2: TaskListName('Today', 'Your tasks for today', TaksBucket.today),
3: TaskListName('Last work day', 'Tasks completed last working day',
TaksBucket.completed),
4: TaskListName('Completed', 'Completed Tasks', TaksBucket.completed),
5: TaskListName(
'Archived', 'Archived not completed tasks', TaksBucket.archived),
};
#override
void initState() {
super.initState();
_tabController = TabController(length: 6, vsync: this, initialIndex: 2);
_tabController.addListener(() {
_tabIndex.value = _tabController.index;
});
}
#override
void dispose() {
super.dispose();
_tabController.removeListener(() {
_tabIndex.value = _tabController.index;
});
}
Future<void> showAddTaskDialog(BuildContext context) async {
await showDialog(
context: context,
builder: (context) => SimpleDialog(
children: [NewTask(tasksListLabels[_tabController.index].bucket)],
),
);
}
#override
Widget build(BuildContext context) {
final tasks = Provider.of<List<Task>>(context);
return Scaffold(
appBar: AppBar(
title: ValueListenableBuilder(
valueListenable: _tabIndex,
builder: (context, value, child) =>
Text(tasksListLabels[value].longName),
),
actions: [
CountBatchIconButton(
TasksHelper.numberOfTasksForNotification(tasks),
Icon(Icons.notifications_none),
() => Navigator.of(context).pushNamed(TasksNotifications.routeName),
),
],
bottom: TabBar(
controller: _tabController,
isScrollable: true,
indicatorColor: TaskConsts.mainColor,
tabs: [
Tab(text: tasksListLabels[0].shortName),
Tab(text: tasksListLabels[1].shortName),
Tab(text: tasksListLabels[2].shortName),
Tab(text: tasksListLabels[3].shortName),
Tab(text: tasksListLabels[4].shortName),
Tab(text: tasksListLabels[5].shortName),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
TasksListMob(tasks, TaksBucket.backlog),
TasksListMob(tasks, TaksBucket.tomorrow),
TasksListMob(tasks, TaksBucket.today),
TasksListMob(
tasks,
TaksBucket.completed,
isLastWorkingDay: true,
),
TasksListMob(tasks, TaksBucket.completed),
TasksListMob(tasks, TaksBucket.archived),
],
),
floatingActionButton: FloatingActionButton(
backgroundColor: TaskConsts.mainColor,
child: Icon(Icons.add),
onPressed: () => showAddTaskDialog(context),
),
);
}
}
Inside the TabBar widget, add onTap callback with the setState method to trigger rebuild, hence updating the name:
...
appBar: AppBar(
title: Text(tasksListLabels[_tabController.index].longName),
bottom: TabBar(
controller: _tabController,
isScrollable: true,
tabs: [
Tab(text: tasksListLabels[0].shortName),
Tab(text: tasksListLabels[1].shortName),
Tab(text: tasksListLabels[2].shortName),
Tab(text: tasksListLabels[3].shortName),
Tab(text: tasksListLabels[4].shortName),
Tab(text: tasksListLabels[5].shortName),
],
onTap: (_) {
setState((){});
},
),
),
...
You can also use the same text of your tab where you switching with the help of the list
You can use a provider to make/view the changes without rebuilding the whole page
Simple.
After you calling async method, when you calling Navigator.of(context).pop() or Navigator.pop(context), use these as below.
if(mounted){
Navigator.of(context).pop();
}
Related
I have a TabBarthat show total of 16 catgories that shows dynamically,
Usecase:when i click on category any cateogry let's say i click on category 5 then if i press back button in phone i need to go to the category 1,as default i can show category 1 also if i press backbutton 2 time i need to close the app too
but i need to show category 1 when i press back button how do i do that, i have tried with willpopScope but nothing happens,This is what i tried so far
WillPopScope(
onWillPop: _onWillPop,
child: category_list.length != 0
? DefaultTabController(
length: category_list.length,
initialIndex: 1,
child: Column(
children: <Widget>[
Column(
children: [
Container(
constraints: BoxConstraints.expand(height: 40),
child: TabBar(
controller: _tabController,
isScrollable: true,
indicatorColor: Color(0xff00ADEE),
labelColor: Color(0xff00ADEE),
unselectedLabelColor: Colors.black,
tabs: getTab(),
),
),
],
),
Expanded(
child: Container(
child: TabBarView(
controller: _tabController,
children: createDynamicslugWIdget()),
),
)
],
),
)
: Center(
child: CircularProgressIndicator(),
),
)
Tried to return index 1
Future<bool> _onWillPop() async {
return _tabController.index==1;
}
You can copy paste run full code below
I use official example to simulate this case
You can set _tabController.index
code snippet
Future<bool> _onWillPop() async {
print("on will pop");
if (_tabController.index == 0) {
await SystemNavigator.pop();
}
Future.delayed(Duration(milliseconds: 200), () {
print("set index");
_tabController.index = 0;
});
print("return");
return _tabController.index == 0;
}
working demo
full code
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class MyTabbedPage extends StatefulWidget {
const MyTabbedPage({Key key}) : super(key: key);
#override
_MyTabbedPageState createState() => _MyTabbedPageState();
}
class _MyTabbedPageState extends State<MyTabbedPage>
with SingleTickerProviderStateMixin {
final List<Tab> myTabs = <Tab>[
Tab(text: '0'),
Tab(text: '1'),
Tab(text: '2'),
Tab(text: '3'),
Tab(text: '4'),
];
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: myTabs.length);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
Future<bool> _onWillPop() async {
print("on will pop");
if (_tabController.index == 0) {
await SystemNavigator.pop();
}
Future.delayed(Duration(milliseconds: 200), () {
print("set index");
_tabController.index = 0;
});
print("return");
return _tabController.index == 0;
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _tabController,
tabs: myTabs,
),
),
body: TabBarView(
controller: _tabController,
children: myTabs.map((Tab tab) {
final String label = tab.text.toLowerCase();
return Center(
child: Text(
'This is the $label tab',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
),
),
);
}
}
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: MyTabbedPage(),
);
}
}
I am working on a flutter project where I have 3 tabs in tabbar.
In tab 1 and tab 2, there are lists showing some data using ListView. When clicked on a list item, I want to pass some string to tab 3 and show tab 3.
Below is my home page code:
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
TabController controller;
#override
void initState() {
super.initState();
controller = new TabController(vsync: this, length: 3);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {},
child: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: Text("InstaPost"),
backgroundColor: Colors.black45,
bottom: TabBar(
tabs: [
Tab(
text: "Tab 1",
),
Tab(text: "Tab 2"),
Tab(text: "Tab 3"),
// Tab(text: "My Posts")
],
),
),
body: TabBarView(
children: <Widget>[
Tab1(),
Tab2(),
Tab3(),
],
),
),
),
);
}
}
So, when the app is launched the first screen will be tab 1 which shows a list of names. The code is below
class _HashtagPageState extends State<HashtagPage> {
Future<dynamic> getLists() async {
// get list
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {},
child: FutureBuilder(
future: getLists(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
child: SpinKitDoubleBounce(color: Colors.green, size: 100),
);
} else {
// log(snapshot.data.toString());
var data = snapshot.data;
return Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.separated(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(data[index]),
onTap: () {
// code to pass a string value to tab 3 and navigate to tab 3
},
);
},
),
)
],
),
);
}
},
),
);
}
}
When clicked on a list item in tab 1, how should I pass a string to tab 3 and navigate to tab 3 at the same time ?
You can copy paste run full code below
You can use https://pub.dev/packages/provider
Step 1: You can save parameter in YourModel
Step 2: In Tab3 , you can access with (context.watch<YourModel>().parameter)
code snippet
class YourModel extends ChangeNotifier {
String _parameter = "";
String get parameter => _parameter;
void passParameter(String parameter) {
_parameter = parameter;
print(_parameter);
notifyListeners();
}
}
...
ChangeNotifierProvider(
create: (context) => YourModel(),
child: MyApp(),
),
...
class Tab3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(child: Text("${(context.watch<YourModel>().parameter)}"));
}
}
...
return ListTile(
title: Text(data[index]),
onTap: () {
Provider.of<YourModel>(context, listen: false)
.passParameter(data[index]);
widget.controller.animateTo(2);
},
);
working demo
full code
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'dart:collection';
import 'package:provider/provider.dart';
class YourModel extends ChangeNotifier {
String _parameter = "";
String get parameter => _parameter;
void passParameter(String parameter) {
_parameter = parameter;
print(_parameter);
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => YourModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(title: 'Flutter Demo Home Page'),
);
}
}
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
TabController controller;
#override
void initState() {
super.initState();
controller = TabController(vsync: this, length: 3);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {},
child: Scaffold(
appBar: AppBar(
title: Text("InstaPost"),
backgroundColor: Colors.black45,
bottom: TabBar(
controller: controller,
tabs: [
Tab(
text: "Tab 1",
),
Tab(text: "Tab 2"),
Tab(text: "Tab 3"),
// Tab(text: "My Posts")
],
),
),
body: TabBarView(
controller: controller,
children: <Widget>[
HashtagPage(controller),
Tab2(),
Tab3(),
],
),
),
);
}
}
class Tab2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("Tab2");
}
}
class Tab3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(child: Text("${(context.watch<YourModel>().parameter)}"));
}
}
class HashtagPage extends StatefulWidget {
TabController controller;
HashtagPage(this.controller);
#override
_HashtagPageState createState() => _HashtagPageState();
}
class _HashtagPageState extends State<HashtagPage> {
Future<List<String>> _future;
Future<List<String>> getLists() async {
await Future.delayed(Duration(seconds: 1), () {});
return Future.value(["a", "b", "c", "d", "e", "f", "g"]);
}
#override
void initState() {
_future = getLists();
super.initState();
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {},
child: FutureBuilder(
future: _future,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
child: SpinKitDoubleBounce(color: Colors.green, size: 100),
);
} else {
// log(snapshot.data.toString());
var data = snapshot.data;
return Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.separated(
separatorBuilder: (BuildContext context, int index) =>
Divider(),
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(data[index]),
onTap: () {
Provider.of<YourModel>(context, listen: false)
.passParameter(data[index]);
widget.controller.animateTo(2);
},
);
},
),
)
],
),
);
}
},
),
);
}
}
This is what worked for me.
Set up the TabController as such:
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _tabController,
tabs: ...,
),
),
body: TabBarView(
controller: _tabController,
children: ...
And then to switch between tabs
// Set desired state to cause changes in target tab
setState(() {
appState.targetValue = newValue;
});
// Important for the timing/sequence of the actions
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_tabController.animateTo(newTabIndex);
});
Is it possible to change the TabBar indicator programmatically when swiping?
I've tried using a builder to get the index but I haven't had any luck .. I'm sure there must be a way to do this but haven't figured it out yet
Color _indicatorColor(index) {
switch (index) {
case 0:
return Colors.purple;
break;
case 1:
return colorInfoLighter;
break;
case 2:
return Colors.pink;
break;
}
}
Widget _buildScreen() {
var index;
return Scaffold(
appBar: AppBar(),
bottomNavigationBar: TabBar(
onTap: (_) {
setState(() {});
},
indicatorWeight: 4,
indicatorColor: _indicatorColor(index),
tabs: [
_requestedLabel(),
_completedLabel(),
_cancelledLabel(),
]),
body: Container(
child: TabBarView(children: [
_requestedTab(),
_completedTab(),
_cancelledTab(),
]),
),
);
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: _buildScreen(),
);
}
Did you mean by changing tabs?
import 'dart:math';
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,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({
Key key,
}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
final colors = [Colors.purple, Colors.green, Colors.pink];
Color indicatorColor;
TabController _controller;
#override
void initState() {
super.initState();
_controller = TabController(length: 3, vsync: this)
..addListener(() {
setState(() {
indicatorColor = colors[_controller.index];
});
});
indicatorColor = colors[0];
}
Widget _buildScreen() {
return Scaffold(
appBar: AppBar(),
bottomNavigationBar: Container(
color: Colors.blue,
child: TabBar(
labelColor: Colors.black,
controller: _controller,
indicatorWeight: 4,
indicatorColor: indicatorColor,
tabs: [
Tab(
child: Container(
child: Text('A'),
),
),
Tab(
child: Text('B'),
),
Tab(
child: Text('C'),
),
]),
),
body: Container(
child: TabBarView(
controller: _controller,
children: [
Center(
child: Text('aa'),
),
Center(
child: Text('bb'),
),
Center(
child: Text('cc'),
),
],
),
),
);
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: _buildScreen(),
);
}
}
I am using a tab controller in flutter, but how do i able to navigate to a certain tab screen with a button click. I put my tab controller in my main screen then 3 different screens. Below is my example codes.
Main Screen (with tab controller)
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
int _currentIndex = 0;
TabController _tabController;
final List<Widget> _children = [
firstscreen.FirstScreen(),
secondscreen.SecondScreen(),
thirdscreen.ThirdScreen()
];
List<Widget> _tabs = <Widget>[
Tab(icon: Icon(Icons.home), text: 'Home'),
Tab(icon: Icon(Icons.history), text: 'History'),
Tab(icon: Icon(Icons.account_circle), text: 'Profile'),
];
#override
void initState() {
super.initState();
_tabController = TabController(length: _tabs.length, vsync: this);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
title: Text("My Title"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.account_balance_wallet),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => Data Screen()));
},
),
],
bottom: TabBar(
controller: _tabController,
tabs: _tabs,
),
),
body: TabBarView(
controller: _tabController,
children: _children,
),
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
First Screen
class FirstScreen extends StatefulWidget {
FirstScreen({Key key}) : super(key: key);
#override
FirstScreenState createState() {
return new FirstScreenState();
}
}
class FirstScreenState extends State<FirstScreen>
with AutomaticKeepAliveClientMixin<FirstScreen> {
Widget get historyCard {
return Container(
height: 280,
width: MediaQuery.of(context).size.width / 1,
padding: EdgeInsets.only(top: 10.0, left: 20.0, right: 20.0),
child: Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Stack(
children: <Widget>[
ClipPath(
clipper: ShapeBorderClipper(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
),
Stack(alignment: Alignment.center, children: <Widget>[
Positioned(
top: 10,
left: 10,
child: Text("RECENT ACTIVITY",
style: TextStyle(
fontSize: 14.0,
)),
),
Positioned(
top: 0,
right: 0,
child: FlatButton(
child: Text('MORE >'),
onPressed: () => {},
textColor: Colors.blueAccent, // JUMP TO SECOND TAB or ANY
//OTHER TAB
),
),
]),
],
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
historyCard,
]),
);
}
}
So right now i'm not sure how do i able to navigate to any tab with button click, because i place my tab bar in MainScreen.dartor must i change the FirstPage code into the MainScreen.dart, then only can click and navigate?
You can set index in TabController to change current tab as below:
In below example, one button is in TabBarView screen in "MyFirstTab", from that button press we call the changeMyTab() of parent class which is "StateKeeper".
import 'package:flutter/material.dart';
class MyTabController extends StatefulWidget {
createState() {
return StateKeeper();
}
}
class StateKeeper extends State<MyTabController> with SingleTickerProviderStateMixin {
TabController _tabController;
final List<Tab> myTabs = <Tab>[
new Tab(icon: Icon(Icons.directions_car),),
new Tab(icon: Icon(Icons.directions_bike),),
new Tab(icon: Icon(Icons.directions_boat),),
new Tab(icon: Icon(Icons.directions_railway),),
];
#override
void initState() {
// TODO: implement initState
super.initState();
_tabController = new TabController(vsync: this, length: myTabs.length);
}
changeMyTab(){
setState(() {
_tabController.index = 2;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(tabs: myTabs, controller: _tabController,),
title: Text('Tabs Demo'),
),
body: TabBarView(controller: _tabController, children: [
MyFirstTab(onTabChangeCallback: () => {
changeMyTab()
},),
Icon(Icons.directions_bike),
Icon(Icons.directions_boat),
Icon(Icons.directions_railway),
]),
),
),
);
}
}
class MyFirstTab extends StatefulWidget {
const MyFirstTab({this.onTabChangeCallback});
final TabChangeCallback onTabChangeCallback;
createState() {
return MyFirstTabStateKeeper(onTabChangeCallback);
}
}
class MyFirstTabStateKeeper extends State<MyFirstTab> {
TabChangeCallback onTabChangeCallback;
MyFirstTabStateKeeper(TabChangeCallback onTabChangeCallback){
this.onTabChangeCallback = onTabChangeCallback;
}
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
RaisedButton(
child: Text('Change Tab'),
onPressed: onTabChangeCallback,
),
],
);
}
}
typedef TabChangeCallback = void Function();
In flutter implementing a tab layout is easy and straightforward. This is a simple example from the official documentation:
import 'package:flutter/material.dart';
void main() {
runApp(new TabBarDemo());
}
class TabBarDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new DefaultTabController(
length: 3,
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
tabs: [
new Tab(icon: new Icon(Icons.directions_car)),
new Tab(icon: new Icon(Icons.directions_transit)),
new Tab(icon: new Icon(Icons.directions_bike)),
],
),
title: new Text('Tabs Demo'),
),
body: new TabBarView(
children: [
new Icon(Icons.directions_car),
new Icon(Icons.directions_transit),
new Icon(Icons.directions_bike),
],
),
),
),
);
}
}
But here is the thing, I want to get the active tab index so I can apply some logic on certain tabs. I search the documentation but I wasn't able to figure it out. Can you guys help and thanks?
The whole point of DefaultTabController is for it to manage tabs by itself.
If you want some custom tab management, use TabController instead.
With TabController you have access to much more informations, including the current index.
class MyTabbedPage extends StatefulWidget {
const MyTabbedPage({Key key}) : super(key: key);
#override
_MyTabbedPageState createState() => new _MyTabbedPageState();
}
class _MyTabbedPageState extends State<MyTabbedPage>
with SingleTickerProviderStateMixin {
final List<Tab> myTabs = <Tab>[
new Tab(text: 'LEFT'),
new Tab(text: 'RIGHT'),
];
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = new TabController(vsync: this, length: myTabs.length);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
controller: _tabController,
tabs: myTabs,
),
),
body: new TabBarView(
controller: _tabController,
children: myTabs.map((Tab tab) {
return new Center(child: new Text(tab.text));
}).toList(),
),
);
}
}
In this case, using StatefulWidget and State isn't a good idea.
You can get current index by DefaultTabController.of(context).index;.
Follow the code:
...
appBar: AppBar(
bottom: TabBar(
tabs: [
Tab(~), Tab(~)
]
),
actions: [
// At here you have to get `context` from Builder.
// If you are not sure about this, check InheritedWidget document.
Builder(builder: (context){
final index = DefaultTabController.of(context).index;
// use index at here...
})
]
)
You can access the current index when the tab is selected by onTap event of TabBar.
TabBar(
onTap: (index) {
//your currently selected index
},
tabs: [
Tab1(),
Tab2(),
]);
Just apply a listener on the TabController.
// within your initState() method
_tabController.addListener(_setActiveTabIndex);
void _setActiveTabIndex() {
_activeTabIndex = _tabController.index;
}
Use DefaultTabController you can get current index easily whether the user changes tabs by swiping or tap on the tab bar.
Important: You must wrap your Scaffold inside of a Builder and you can then retrieve the tab index with DefaultTabController.of(context).index inside Scaffold.
Example:
DefaultTabController(
length: 3,
child: Builder(builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
bottom: TabBar(
isScrollable: true,
tabs: [Text('0'), Text('1'), Text('2')]),
),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
print(
'Current Index: ${DefaultTabController.of(context).index}');
},
),
);
}),
),
New working solution
I'd suggest you to use TabController for more customisations. To get active tab index you should use _tabController.addListener and _tabController.indexIsChanging.
Use this full code snippet:
class CustomTabs extends StatefulWidget {
final Function onItemPressed;
CustomTabs({
Key key,
this.onItemPressed,
}) : super(key: key);
#override
_CustomTabsState createState() => _CustomTabsState();
}
class _CustomTabsState extends State<CustomTabs>
with SingleTickerProviderStateMixin {
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
TabController _tabController;
int _activeIndex = 0;
#override
void initState() {
super.initState();
_tabController = TabController(
vsync: this,
length: myTabs.length,
);
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
_tabController.addListener(() {
if (_tabController.indexIsChanging) {
setState(() {
_activeIndex = _tabController.index;
});
}
});
return Container(
color: Colors.white,
child: TabBar(
controller: _tabController,
isScrollable: true,
indicatorPadding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(10.0), color: Colors.green),
tabs: myTabs
.map<Widget>((myTab) => Tab(
child: Container(
width: width / 3 -
10, // - 10 is used to make compensate horizontal padding
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color:
_activeIndex == myTabs.indexOf(myTab)
? Colors.transparent
: Color(0xffA4BDD4),
),
margin:
EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
child: Align(
alignment: Alignment.center,
child: Text(
myTab.text,
style: TextStyle(color: Colors.white),
),
),
),
))
.toList(),
onTap: widget.onItemPressed,
),
);
}
}
Thanks to the example of RĂ©mi Rousselet, you can do it, the code like this:
_tabController.index
This will return the current index of the position of your TabBarView
You can add a listener to listen to changes in tabs like below
tabController = TabController(vsync: this, length: 4)
..addListener(() {
setState(() {
switch(tabController.index) {
case 0:
// some code here
case 1:
// some code here
}
});
});
Well, nothing here was working in my case.
I tried several responses so as a result i used a provider to keep and retrieve the current index selected.
First the model.
class HomeModel extends ChangeNotifier {
int _selectedTabIndex = 0;
int get currentTabIndex => _selectedTabIndex;
setCurrentTabIndex(final int index){
_selectedTabIndex = index;
// notify listeners if you want here
notifyListeners();
}
...
}
Then i used _tabController.addListener() to update my model.
class HomePageState extends State<HomeScreen> {
late TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: _tabs.length);
_tabController.addListener(() {
context.read<HomeModel>().setCurrentTabIndex(_tabController.index);
});
}
...
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _tabs.length,
child :
Scaffold(
backgroundColor: Colors.white70,
appBar: AppBar(
/*iconTheme: IconThemeData(
color: Colors.black
),*/
bottom: TabBar(
controller: _tabController,
tabs: _tabs,
),
title: Text(_getAppBarTitle(),style: const TextStyle(/*color: Colors.red,*/fontSize: 22.0),)
...
)
)
);
}
...
}
Finally last but not least retrieve value when you need.
class _AppState extends State<App> {
#override
Widget build(BuildContext context) {
return Consumer<HomeModel>(
builder: (context, homeModel, child) {
return Text(homeModel.currentTabIndex); // herre we get the real current index
});
}
}
Set the variable in top.
class _MainTabWidgetState extends State<MainTabWidget> {
#override void initState() {
// TODO: implement initState
super.initState();
}
int selected_index = 0;
}
Now set index in Tabbar onTap
onTap: (index) {
setState(() {
selected_index = index;
});
},
This Code will give you index of Active tab , also save the tab index for future use, and when you back to the tab page the the previous active page will be displayed.
import 'package:flutter/material.dart';
void main() {
runApp(new TabBarDemo());
}
class TabBarDemo extends StatelessWidget {
TabScope _tabScope = TabScope.getInstance();
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new DefaultTabController(
length: 3,
index: _tabScope.tabIndex, //
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
onTap: (index) => _tabScope.setTabIndex(index), //current tab index
tabs: [
new Tab(icon: new Icon(Icons.directions_car)),
new Tab(icon: new Icon(Icons.directions_transit)),
new Tab(icon: new Icon(Icons.directions_bike)),
],
),
title: new Text('Tabs Demo'),
),
body: new TabBarView(
children: [
new Icon(Icons.directions_car),
new Icon(Icons.directions_transit),
new Icon(Icons.directions_bike),
],
),
),
),
);
}
}
class TabScope{ // singleton class
static TabScope _tabScope;
int tabIndex = 0;
static TabScope getInstance(){
if(_tabScope == null) _tabScope = TabScope();
return _tabScope;
}
void setTabIndex(int index){
tabIndex = index;
}
}