DefaultTabController.of always return null - flutter

I have a flutter app with a DefaultTabController, at multiple places in the code I tried to get the associated TabController, but I go null.
TabController? c = DefaultTabController.of(context);
I have tried just after the widget creation and also at other places, but I always receive null.
If someone has an idea why and how to solve this ?

You need to make sure you are trying to access the DefaultTabController in a context that actually has a DefaultTabController. In many cases, this means adding an extra Builder widget:
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);
},
),
))
],
);
}
),
),
),
);

Related

How to get the index of the current NavigationBar?

So in here i want to change some condition if my tabview switch to the second tab but i don't know how to get the tabbar index, already try this and that. Im hoping some solution without statefull, Im using GetX thanks.
im planning to change the extendBody: true, in my main page to false when the tab switch to the second tab i had the logic for that hopefully but the only problem is the index :(.
My tabs :
List<Tab> myTabs = [
Tab(
text: 'Following',
),
Tab(
text: 'Trending',
),
Tab(
text: 'Search',
),
];
DefaultController code :
DefaultTabController(
length: myTabs.length,
child: Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: bgColor,
// APPBAR
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
toolbarHeight: 60,
// BOTTOM
bottom: PreferredSize(
preferredSize: const Size.fromHeight(0),
child: Align(
alignment: Alignment.centerLeft,
child: TabBar(
isScrollable: true,
labelPadding: EdgeInsets.only(left: 20),
labelColor: Colors.white,
labelStyle: poppins.copyWith(
fontSize: 15,
fontWeight: bold,
),
unselectedLabelColor: Color(0xff585861),
indicatorColor: Colors.white.withOpacity(0),
indicatorSize: TabBarIndicatorSize.label,
// TABS
tabs: myTabs,
),
),
),
),
body: TabBarView(
children: [
FollowingTab(),
TrendingTab(),
search(),
],
),
),
);
Use TabBar with TabController and you can find current index while switching to next tab
#override
void initState() {
super.initState();
_controller = TabController(length: 6, vsync: this);
_controller!.addListener(() {
print(_controller!.index);
});
}
your build method be like:
#override
Widget build(BuildContext context) {
return Scaffold(
bottom:TabBar(
controller: _controller,
tabs:[
//your tabs will be here
]
),
body:TabBarView(
controller: _controller,
children: [
//your tabbarview will be here
]
),
);
}
Here i just code of Tabbar with using Getx and Stateless Widget.
CheckOut my code and if you find solution then give up me. Thanks in advance
class TabDemo extends StatelessWidget {
TabDemo({Key? key}) : super(key: key);
final DemoController demoController = Get.put(DemoController());
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: const TabBar(
tabs: [
Tab(icon: Icon(Icons.flight)),
Tab(icon: Icon(Icons.directions_transit)),
Tab(icon: Icon(Icons.directions_car)),
],
),
title: const Text('Tabs Demo'),
),
body: TabBarView(
controller: demoController.controller?.value,
children: const [
Icon(Icons.flight, size: 350),
Icon(Icons.directions_transit, size: 350),
Icon(Icons.directions_car, size: 350),
],
),
),
);
}
}
You need to create controller class to define controller and initmethod
class DemoController extends GetxController with SingleGetTickerProviderMixin {
Rx<TabController>? controller;
#override
void onInit() {
// TODO: implement onInit
controller?.value = TabController(length: 6, vsync: this);
controller?.value.addListener(() {
print(controller?.value.index);
});
super.onInit();
}
}

Correct TabBarView usage

my code getting 32x tabs from JSON and putting them to Appbar as scrollable. Now I am looking for a way to execute a categoryListTile according to selection Tab and send correct id number to categoryListTile widget on another page.
unfortunately I cannot find a correct way to do this.
List<Tab> tabs = <Tab>[];
for (int i = 0; i < snapshot.data!.data!.length; i++) {
tabs.add(
Tab(
child: Text(' ${name?[i].name}'.toUpperCase()),
),
);
}
return DefaultTabController(
length: snapshot.data!.data!.length,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Image.asset("assets/images/logo.png",
fit: BoxFit.contain, height: 22),
bottom: TabBar(
indicatorColor: Colors.black,
isScrollable: true,
tabs: tabs),
),
body: TabBarView(
children: categoryListTile(name!, id!, context),
),
),
);
}
I am pretty sure this is completely wrong but how can I to do this
try replace body:
body: TabBarView(
children: [for (final tab in snapshot.data!.data!) categoryListTile(tab.name!, tab.id!, context)],
),

useTabController index never changes

I am using hooks for handling tab controller, the problem is when i print the current index of the controller it is right. But when i try to display it inside a text widget it is never changes! how could i use it?
The code is:
class TabBarDemo extends HookWidget {
final List<Widget> list = const [
Tab(icon: Icon(Icons.card_travel)),
Tab(icon: Icon(Icons.add_shopping_cart)),
Tab(icon: Icon(Icons.ac_unit)),
];
#override
Widget build(BuildContext context) {
final _controller =
useTabController(initialLength: list.length, initialIndex: 0);
_controller.addListener(() {
print("\n ${_controller.index} \n");
});
return Scaffold(
appBar: AppBar(
bottom: TabBar(
onTap: (index) {},
controller: _controller,
tabs: list,
),
),
body: TabBarView(
controller: _controller,
children: [
Center(
child: Text(
'${_controller.index}',
)),
Center(
child: Text(
'${_controller.index}',
)),
Center(
child: Text(
'${_controller.index}',
)),
],
),
);
}
}
You have to rebuild the tree using :
setState(() {
});

Flutter Widgets are moving if Keyboard appears even though resize is set to false

I have a weird behavior inside my app... I am using a PageView and on the 2nd page I am focusing a TextField on the pages init. That is working fine. However on the first page my Widgets are moving a little bit even though I set in the wrapping Scaffold: resizeToAvoidBottomInset = false.
Here is a Screenvideo for a better understanding.
This is my Wrapping View:
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: AppColors.secondary,
body: SafeArea(
child: Column(
children: [
_buildDismissButton(),
Expanded(
child: PageView(
controller: _pageController,
onPageChanged: (index) {
if (index == 0) {
FocusScope.of(context).unfocus();
}
},
children: [
AddPhotoPage(onNextPage: () {
_pageController.nextPage(
duration: Duration(milliseconds: 350),
curve: Curves.easeInOut);
}),
AddTitlePage(),
],
),
),
],
),
),
);
}
And the problem is on the AddPhotoPage:
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: sidePadding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Foto wählen',
style: AppTextStyles.montserratH2SemiBold,
),
Spacer(flex: 1),
_buildEmptyImageStateWidgets(),
Spacer(
flex: 3,
),
RoundedCornersTextButton(
title: 'Weiter',
isLoading: isUploadingImage,
onTap: () {
widget.onNextPage();
},
),
SizedBox(
height: 50.scaled,
),
],
),
);
}
I feel like it has got something to do with the Spacer(). Because if I removed them and just place SizedBoxes with a specific height, the behavior does not occur.
What am I missing here? Let me know if you need any more info!

Flutter - moving between tabs cause to lose the state

I am issue a weird situation. I have the following code that display the tabs and their contents:
Scaffold(
appBar: AppBar(
title: Text('Title'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () => _onTabAdd(_tabController.index),
),
IconButton(
icon: Icon(Icons.delete),
onPressed: () => _onTabRemoved(_tabController.index),
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () => _onTabEdit(context),
),
],
bottom: PreferredSize(
preferredSize: Size.fromHeight(40.0),
child: Container(
color: Colors.blue,
child: TabBar(
controller: _tabController,
tabs: tabTitles
.map(
(title) => Tab(text: title),
)
.toList(),
labelColor: Colors.yellow,
unselectedLabelColor: Colors.white,
indicatorSize: TabBarIndicatorSize.label,
indicatorPadding: EdgeInsets.all(5.0),
indicatorColor: Colors.red,
),
),
),
),
body: TabBarView(
controller: _tabController,
children: tabContents.map((e) => e).toList(),
),
You can see that I have the option to add or delete Tabs using the buttons, and it seems to work fine using the following functions:
void _onTabRemoved(int index) {
setState(() {
tabTitles.removeAt(index);
tabContents.removeAt(index);
_tabController = TabController(length: tabTitles.length, vsync: this);
});
}
void _onTabAdd(int index) {
setState(() {
tabTitles.add("Tab ${tabTitles.length}");
tabContents.add(GenesTable(this));
_tabController = TabController(length: tabTitles.length, vsync: this);
});
}
The problem is that when I move between tabs I lose the state of GenesTable component (that store the table's content), meaning that when I move between tabs The table that been display is empty.
You can go through the answer given here
https://stackoverflow.com/a/51225319/13927133
In case you want to keep the state of your screen in your TabBarView, you can use the mixin class called AutomaticKeepAliveClientMixin in your State class.
And after that override the wantKeepAlive method and return true.
#diegoveloper also posted a medium article for this
https://medium.com/#diegoveloper/flutter-persistent-tab-bars-a26220d322bc