flutter, RenderBox was not laid out: RenderRepaintBoundary#4de33 relayoutBoundary=up14 NEEDS-PAINT - flutter

I use tabBar,
I want to change appBar title when a tab is selected, but I get a error.
code that I get error.
Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.clear, color: Colors.black),
onPressed: () {
Navigator.pop(context);
},
),
title: TabBarView(
children: [Text("Album"), Text("사진"), Text("동영상")],
),
backgroundColor: Colors.white,
),
bottomNavigationBar: TabBar(
tabs: [
Tab(text: "앨범"),
Tab(text: "사진"),
Tab(text: "동영상"),
],
labelColor: Colors.black,
indicatorColor: Colors.black,
unselectedLabelColor: Colors.black26,
),
),
If I get rid of appBar title, I don't get error.
How to solve this problem?

You can add TabController and add a listener so that whenever you are switching the Tab, the setState() kicks in and the appBar title change.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
TabController _controller;
final List<Tab> _tab = [new Tab(text: "First",), Tab(text: "Second",), Tab(text: "Third",)];
var _currentTab;
#override
void initState() {
// TODO: implement initState
super.initState();
_controller = new TabController(length: _tab.length, vsync: this);
_currentTab = _tab[0];
_controller.addListener(_selectedTab);
}
void _selectedTab() {
setState(() {
_currentTab = _tab[_controller.index];
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text(_currentTab.text),
),
bottomNavigationBar: TabBar(controller: _controller,tabs: _tab),
);
}
}

Related

How to use tabBar controller in flutter?

import 'dart:ffi';
import 'package:flutter/material.dart';
import 'package:whatsapp_clone/chatScreen.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin{
late TabController _tabcontroller;
#override
void initState(){
super.initState();
_tabcontroller=TabController(length: 4, vsync: this, initialIndex: 1);
}
#override
void dispose(){
_tabcontroller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xff075E54),
title: const Text('Whatsapp Clone'),
actions: [IconButton(onPressed: (){}, icon: const Icon(Icons.search)),
PopupMenuButton(onSelected: (value){print(value);},
itemBuilder: (BuildContext context){return [
PopupMenuItem(child: Text('New Group'),value: 'New Group'),
PopupMenuItem(child: Text('New Broadcast'),value: 'New Broadcast'),
PopupMenuItem(child: Text('Whatsapp web'),value: 'Whatsapp web'),
PopupMenuItem(child: Text('Starred messages'),value: 'Starred messages'),
PopupMenuItem(child: Text('Settings'),value: 'Settings')
];
}
),
],
bottom: TabBar(
controller: _tabcontroller,
tabs: const [
Tab(icon: Icon(Icons.camera_alt)),
Tab(text:'CHAT'),
Tab(text: 'STATUS'),
Tab(text: 'CALLS')
],
indicatorColor: Colors.white,
),
),
body: TabBarView(controller: _tabcontroller,
children: const [
Text('camera'),
ChatPage(),
Text('status'),
Text('calls')
],
),
);
}
}
I want to show the chatscreen which is at 1 index, when opening the app, and i have given the initial index to 1 also, but whenever i open my app, although the chat tabbar was selected but the content was showing from first screen. how can i solve this problem

Bottom navigation bar item underline selected icon

in-app bottom bar I want to add an underline for specifically selected icon. Is there any way for BottomNavigationBarItem or do I need to use a different widget?
class CustomAppBottomBar extends StatefulWidget {
const CustomAppBottomBar({Key? key}) : super(key: key);
#override
State<CustomAppBottomBar> createState() => _CustomAppBottomBarState();
}
class _CustomAppBottomBarState extends State<CustomAppBottomBar> {
int _currentIndex = 0;
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
showUnselectedLabels: true,
showSelectedLabels: true,
type: BottomNavigationBarType.fixed,
selectedItemColor: RJDriverColors.primary,
unselectedItemColor: RJDriverColors.bottomBarSecondary,
selectedFontSize: 12,
unselectedFontSize: 12,
currentIndex: _currentIndex,
onTap: (value) {
setState(() {
_currentIndex = value;
});
},
Try using 'TabBar' and use onTap as onChanged since they are both of type ValueChanged<int>
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late final TabController controller;
#override
void initState() {
controller = TabController(length: 4, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
bottomNavigationBar: TabBar(
controller: controller,
unselectedLabelColor: Colors.grey,
labelColor: Colors.blue,
onTap: (index) {},
tabs: const [
Tab(icon: Icon(Icons.menu_rounded)),
Tab(icon: Icon(Icons.piano)),
Tab(icon: Icon(Icons.car_rental_rounded)),
Tab(icon: Icon(Icons.settings_rounded)),
],
),
);
}
}
Using Tabbar with DefaultTabController you can get your desired result. I'm sharing a reference code you can customize it as per your need.
return DefaultTabController(
length: 4,
child: Scaffold(
body: TabBarView(
children: [
Center(
child: Text("1st Screen"),
),
Center(
child: Text("2nd Screen"),
),
Center(
child: Text("3rd Screen"),
),
Center(
child: Text("4th Screen"),
),
],
),
bottomNavigationBar: TabBar(
labelColor: Colors.red,
tabs: [
Tab(child: Icon(Icons.more_vert_sharp),),
Tab(child: Icon(Icons.local_play_sharp),),
Tab(child: Icon(Icons.directions_car),),
Tab(child: Icon(Icons.home),),
]),
),
);

How to make Flutter animation like this?

Any code example is welcome, thanks!
TabBar does this animation by default...
You can look at its source code from the IDE or here to see how it's done.
This page has documentation, video, and examples if you want to use it directly in your app.
Here is the code sample.
You can use TabBar widget.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this, initialIndex: 0);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
title: Text("Demo"),
bottom: TabBar(
controller: _tabController,
indicatorColor: Colors.white,
tabs: [
Tab(
text: "Wellbeing",
),
Tab(
text: "Cleaning",
),
Tab(
text: "Self-care",
),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
Center(child: Text("Wellbeing")),
Center(child: Text("Cleaning")),
Center(child: Text("Self-care")),
],
),
);
}
}

No TabController for TabBar

I'm trying to add a tabBar to my app, but I'm having some issues getting it to build. I've followed the documentation and added a tabController, but I'm getting an error saying I don't have one. My code is below.
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
TabController _controller;
final List<Tab> topTabs = <Tab>[
new Tab(text: 'Profile'),
new Tab(text: 'Match'),
new Tab(text: 'Chat'),
];
#override
void initState() {
super.initState();
_controller = TabController(vsync: this, length: 3);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MyApp'),
bottom: TabBar(
controller: _controller,
tabs: topTabs,
),
),
body: TabBarView(
controller: _controller,
children: [
new Container(
color: Colors.lightBlueAccent,
child: Center(child: Text('Profile', style: TextStyle(color: Colors.white),),),
),
new Container(
color: Colors.purpleAccent,
child: Center(child: Text('Match', style: TextStyle(color: Colors.white),),),
),
new Container(
color: Colors.lightGreenAccent,
child: Center(child: Text('Chat', style: TextStyle(color: Colors.white),),),
)
]),
);
}
}
The exact error is flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building MediaQuery(MediaQueryData(size: Size(375.0, 667.0),
flutter: devicePixelRatio: 2.0, textScaleFactor: 1.0, platformBrightness: Brightness.light, padding:
flutter: EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets: EdgeInsets.zero, physicalDepth:
flutter: 1.7976931348623157e+308, alwaysUse24HourFormat: false, accessibleNavigation: false, highContrast:
flutter: false, disableAnimations: false, invertColors: false, boldText: false)):
flutter: No TabController for TabBar.
flutter: When creating a TabBar, you must either provide an explicit TabController using the "controller"
flutter: property, or you must ensure that there is a DefaultTabController above the TabBar.
flutter: In this case, there was neither an explicit controller nor a default controller.
I could run it without a problem, did you overwrote MyApp example when creating your project that has the MaterialApp with your code or something before the main?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyAppState()
);
}
}
class MyAppState extends StatefulWidget{
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyAppState> with TickerProviderStateMixin {
TabController _controller;
final List<Tab> topTabs = <Tab>[
new Tab(text: 'Profile'),
new Tab(text: 'Match'),
new Tab(text: 'Chat'),
];
#override
void initState() {
super.initState();
_controller = TabController(vsync: this, length: 3);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MyApp'),
bottom: TabBar(
controller: _controller,
tabs: topTabs,
),
),
body: TabBarView(
controller: _controller,
children: [
new Container(
color: Colors.lightBlueAccent,
child: Center(child: Text('Profile', style: TextStyle(color: Colors.white),),),
),
new Container(
color: Colors.purpleAccent,
child: Center(child: Text('Match', style: TextStyle(color: Colors.white),),),
),
new Container(
color: Colors.lightGreenAccent,
child: Center(child: Text('Chat', style: TextStyle(color: Colors.white),),),
)
]),
);
}
}

How to get current tab index in Flutter

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;
}
}