I use a DefaultTabController tabbar which looks like this
class MainTabs extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: MyAppbar(),
body: TabBarView(
children: [
CardContentPage(title: 'Tab1'),
DeviceContentPage(title: 'Tab2'),
SettingsPage(title: 'Tab2'),
],
),
),
);
}
}
But with this design the back button always leads to a very first page of the application.
How can I set up it so that back button works as expected with DefaultTabController?
Using a DefaultTabController you can just prevent the user from return, using the WillPopScope:
DefaultTabController(
initialIndex: 0,
length: 3,
child: WillPopScope(
onWillPop: () async {
return false;
},
child: Scaffold(
appBar: AppBar(
bottom: TabBar(tabs: [
Tab(text: 'First',),
Tab(text: 'Second',),
Tab(text: 'Third',),
]),
),
body: TabBarView(
children: [
Container(color: Colors.red,),
Container(color: Colors.yellow,),
Container(color: Colors.green,),
],
),
),
),
);
But with a TabController you can set the route to the previous page:
class TabPage extends StatefulWidget {
#override
_TabPageState createState() => _TabPageState();
}
class _TabPageState extends State<TabPage> with SingleTickerProviderStateMixin {
TabController tabController;
#override
void initState() {
tabController = TabController(length: 3, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: tabController,
tabs: [
Tab(
text: 'First',
),
Tab(
text: 'Second',
),
Tab(
text: 'Third',
),
],
),
),
body: TabBarView(
controller: tabController,
children: [
Container(
color: Colors.red,
),
WillPopScope(
onWillPop: () async {
tabController.animateTo(0, duration: Duration(milliseconds: 500),);
return false;
},
child: Container(
color: Colors.yellow,
),
),
WillPopScope(
onWillPop: ()async {
tabController.animateTo(1, duration: Duration(milliseconds: 500),);
return false;
},
child: Container(
color: Colors.green,
),
),
],
),
);
}
}
Related
Default the Tabar Tab width are equal.
How i can change each tabbar tab width differently ?
I tried this but not work
TabBar(
controller: _navController,
tabs: [
Expanded(
flex: 30,
child: IconButton(
icon: SvgPicture.asset("assets/svg/home.svg",height: height * .02,),
onPressed: () { },
)),
Expanded(
flex: 40,
child: Center(
child: IconButton(
icon: SvgPicture.asset("assets/svg/user.svg",height: height * .02,),
onPressed: () { },
),
)),
Expanded(
flex: 20,
child: Center(
child: IconButton(
icon: SvgPicture.asset("assets/svg/settings.svg",height: height * .02,),
onPressed: () { },
),
)),
Expanded(
flex: 10,
child: Container()),
],
),
Expecting Result
To have different sizes in tab bar you have to add isScrollable: true. Please try this example
class MyTabbedPage extends StatefulWidget {
const MyTabbedPage({Key? key}) : super(key: key);
#override
State<MyTabbedPage> createState() => _MyTabbedPageState();
}
class _MyTabbedPageState extends State<MyTabbedPage>
with SingleTickerProviderStateMixin {
List<Widget> myTabs = [
SizedBox(
width: 20.0,
child: Tab(text: 'hello'),
),
SizedBox(
width: 70,
child: Tab(text: 'world'),
),
];
late TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: myTabs.length);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _tabController,
tabs: myTabs,
isScrollable: true,
),
),
body: TabBarView(
controller: _tabController,
children: myTabs.map((Widget tab) {
final String label = "Test";
return Center(
child: Text(
'This is the $label tab',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
),
);
}
}
I want to implement listview builder inside ExpansionPanelList. Under each expansion tile it will have tabbar and tabbarview. Inside the tabbarview it will display listview. I have tried to implemented but the view is not rendering properly. Throwing error
RenderBox was not laid out: RenderRepaintBoundary#92515 NEEDS-LAYOUT NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1930 pos 12: 'hasSize'
My Implemented code:
List<bool> _isOpen =[true, false]
ExpansionPanelList(
children: [
ExpansionPanel(
isExpanded: _isOpen[0],
headerBuilder: (context, isOpen){
return Text("Activities");
},
body: Column(
children: [
Container(child: tabView()),
Flexible(child: tabBarView()),
],
),),
ExpansionPanel(
isExpanded: _isOpen[1],
headerBuilder: (context, isOpen){
return Text("Ports");
},
body: Text("Test"),)
],
expansionCallback: (i, isOpen) {
setState(() {
_isOpen[i] =!isOpen;
});
},
),
tabview widget
Widget tabView(){
return PreferredSize(
preferredSize: Size.fromHeight(70),
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: MediaQuery.of(context).size.width ,
child: TabBar(
isScrollable: true,
labelPadding: EdgeInsets.symmetric(horizontal: 8),
controller: _tabController,
unselectedLabelColor: CustomColors.Black,
indicatorSize: TabBarIndicatorSize.label,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: CustomColors.oldLace),
tabs: tabNames),
),
));
}
tabbarView
Widget tabBarView(){
return TabBarView(controller: _tabController,
children: [
listItems(),
Center(child: Text("Favorite list will be shown here")),
Center(child: Text("Filter")),
],);
}
Wrap your ExpansionPanelList in SingleChildScrollView and Give fix height to your TabBarView by Wraping Inside Container.
Full Code Snippet below
import 'package:flutter/material.dart';
class Expand extends StatefulWidget {
const Expand({Key? key}) : super(key: key);
#override
_ExpandState createState() => _ExpandState();
}
class _ExpandState extends State<Expand> with SingleTickerProviderStateMixin {
List<bool> _isOpen = [false, false];
late TabController _tabController;
static const List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: myTabs.length);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
Widget tabView() {
return PreferredSize(
preferredSize: Size.fromHeight(70),
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: MediaQuery.of(context).size.width,
// height: 200,
child: TabBar(
isScrollable: true,
labelPadding: EdgeInsets.symmetric(horizontal: 8),
controller: _tabController,
unselectedLabelColor: Colors.black,
indicatorSize: TabBarIndicatorSize.label,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(50),
// color: CustomColors.oldLace
),
tabs: myTabs,
// tabs: tabNames
),
),
));
}
Widget tabBarView() {
return Container(
height: 400,
width: 400,
child: TabBarView(
controller: _tabController,
children: [
// listItems(),
Center(child: Text("Favorite list will be shown here")),
Center(child: Text("Filter")),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
// height: 400,
// width: 500,
child: ExpansionPanelList(
children: [
ExpansionPanel(
isExpanded: _isOpen[0],
headerBuilder: (context, isOpen) {
return Text("Activities");
},
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
tabView(),
tabBarView(),
],
),
),
ExpansionPanel(
isExpanded: _isOpen[1],
headerBuilder: (context, isOpen) {
return Text("Ports");
},
body: Text("Test"),
)
],
expansionCallback: (i, isOpen) {
setState(() {
_isOpen[i] = !isOpen;
});
},
),
),
);
}
}
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Expansion Panel List',
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 TickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 2, vsync: this);
super.initState();
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
List<bool> _isOpen = [true, false];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Draggable Test'),
),
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
ExpansionPanelList(
children: [
ExpansionPanel(
isExpanded: _isOpen[0],
headerBuilder: (context, isOpen) {
return Text("Activities");
},
body: Column(
children: [
Container(child: tabView()),
tabBarView(),
],
)),
ExpansionPanel(
isExpanded: _isOpen[1],
headerBuilder: (context, isOpen) {
return Text("Ports");
},
body: Column(
children: [
Container(child: tabView()),
tabBarView(),
],
))
],
expansionCallback: (i, isOpen) {
setState(() {
_isOpen[i] = !isOpen;
});
},
),
],
));
}
Widget tabView() {
return PreferredSize(
preferredSize: Size.fromHeight(70),
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: MediaQuery.of(context).size.width,
child: TabBar(
isScrollable: true,
labelPadding: EdgeInsets.symmetric(horizontal: 8),
controller: _tabController,
unselectedLabelColor: Colors.blue,
indicatorSize: TabBarIndicatorSize.label,
indicator: BoxDecoration(borderRadius: BorderRadius.circular(50), color: Colors.black),
tabs: [
Tab(text: 'Activities'),
Tab(text: 'Ports'),
],
),
),
));
}
Widget tabBarView() {
return Container(
height: 100,
child: TabBarView(
controller: _tabController,
children: [
// listItems(),
Center(child: Text("Favorite list will be shown here")),
Center(child: Text("Filter")),
],
),
);
}
}
...
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'haha',
theme: ThemeData(
fontFamily: 'Iransans',
primaryColor: Palette.primaryColor,
canvasColor: Palette.primaryColor, //my custom color
accentColor: Palette.accentColor),
initialRoute: "/",
routes: {
"/": (context) => Directionality(
textDirection: TextDirection.rtl, child: SplashPage()),
...
and in SplashPage, I navigate from here to login page
#override
Widget build(BuildContext context) {
Future.delayed(const Duration(seconds: 4), () {
Navigator.pushNamed(context,"/login" /*,arguments: "free"*/);
});
return const Scaffold(
backgroundColor: Palette.white,
body: Center(
child:
SizedBox(height: 60, width: 60, child: CircularProgressIndicator()),
),
);
}
here is my TextField in LoginPage. the page that doesn't work
but when navigating here from MainPage directly everything is well
Scaffold(
backgroundColor: Palette.primaryColor,
body: SingleChildScrollView(
child: Column(
children: <Widget>[
TextField(
autofocus: true,
textAlign: TextAlign.center,
//controller: _textFieldControllerUserName,
keyboardType: TextInputType.name,
),
],
),
),
);
flutter keeps pages in a stack and the whole thing in the stack is working :(
build method runs even when the page is in backStack,
so I moved my navigator to initState()
class SplashPageState extends State<SplashPage> {
#override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 4), () {
_navigateLoginPage(context);
});
}
#override
Widget build(BuildContext context) {
return const Scaffold(
backgroundColor: Palette.white,
body: Center(
child:
SizedBox(height: 60, width: 60, child: CircularProgressIndicator()),
),
);
}
I am developing a flutter app, I need to create a navigation bar like this
the problem is I don't know how to add that bar under categories (which changes to red when the component is selected).
any idea?
You can use TabBar and TabBarView.
you can change UI the way you like.
Demo result
demo Widget
class MyStatelessWidget extends StatefulWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
#override
_MyStatelessWidgetState createState() => _MyStatelessWidgetState();
}
class _MyStatelessWidgetState extends State<MyStatelessWidget>
with SingleTickerProviderStateMixin {
final tabs = <Widget>[
Tab(
icon: Icon(Icons.cloud_outlined),
),
Tab(
icon: Text("second tab"),
),
Tab(
icon: Icon(Icons.brightness_5_sharp),
),
];
late final TabController controller;
#override
void initState() {
controller = TabController(length: tabs.length, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) => Scaffold(
appBar: AppBar(
title: TextField(
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
suffixIcon: Icon(Icons.search),
),
),
actions: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(Icons.more),
),
],
bottom: TabBar(controller: controller, tabs: tabs),
),
body: TabBarView(
controller: controller,
children: <Widget>[
Center(
child: Text("It's cloudy here"),
),
Center(
child: Text("It's rainy here"),
),
Center(
child: Text("It's sunny here"),
),
],
),
),
);
}
}
Top Tabs Shows Only HomePage and they show 3 different page via Scrolling or tapping, and bottom tabs for whole Apps like menu.
when i code written then i get views like below images, but i cant tap or redirect page.
navigation code i only given top or bottom tabs not both tabs.
homePage.dart
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
TabController tabController;
//TabController bottomController;
Icon searchBtn = new Icon(Icons.search);
Widget appBarTitle = new Text('Invoices');
#override
void initState(){
super.initState();
tabController = new TabController(vsync: this, length: 3);
//bottomController = new TabController(vsync: this, length: 4);
}
#override
void dispose(){
tabController.dispose();
//bottomController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
color: Colors.purpleAccent,
debugShowCheckedModeBanner: false,
home: DefaultTabController(
length: 3,
child:Scaffold(
appBar: new AppBar(
centerTitle: false,
title: appBarTitle,
backgroundColor: Color(0xFF832685),
actions: <Widget>[
new IconButton(
icon: searchBtn,
onPressed: (){
setState(() {
if(this.searchBtn.icon == Icons.search){
this.searchBtn = new Icon(Icons.close);
this.appBarTitle = new TextField(
style: new TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
//fillColor: Colors.white,
border: InputBorder.none,
// focusedBorder: OutlineInputBorder(
// borderRadius: BorderRadius.all(Radius.circular(5.0)),
// borderSide: BorderSide(color: Colors.white)),
filled: true,
prefixIcon: new Icon(Icons.search,
color: Colors.white),
hintText: "Search...",
hintStyle: new TextStyle(color: Colors.white),
),
);
}
else{
this.searchBtn = new Icon(Icons.search);
this.appBarTitle = new Text('Invoices');
}
});
},
)
],
bottom: new TabBar(
indicatorColor: Color(0xFF832685),
controller: tabController,
tabs: <Tab>[
new Tab(text: 'Unpaid'.toUpperCase(),),
new Tab(text: 'Draft'.toUpperCase(),),
new Tab(text: 'All'.toUpperCase(),),
],
),
),
//bottomNavigationBar: BottomNavBar(),
//bottomNavigationBar: _BottomBar(),
// bottomNavigationBar: new TabBar(
// indicatorColor: Color(0xFF832685),
// labelColor: Color(0xFF832685),
// //controller: bottomController,
// tabs: <Tab>[
// new Tab(icon: new Icon(Icons.home),text: 'Home',),
// new Tab(icon: new Icon(Icons.home),text: 'Home',),
// new Tab(icon: new Icon(Icons.home),text: 'Home',),
// new Tab(icon: new Icon(Icons.home),text: 'Home',),
// ],
// ),
body: new TabBarView(
controller: tabController,
children: <Widget>[
new first.UnpaidInvoicePage(),
new second.PaidInvoicePage(),
new third.AllInvoicePage(),
],
),
//body: Container(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
tooltip: 'New Invoice',
backgroundColor: Color(0xFF832685),
onPressed: (){
//Navigator.of(context).pushNamed('NewInvoicePage');
Navigator.push(context, MaterialPageRoute(builder: (context) => NewInvoicePage()));
},
),
),
),
);
}
}
Thank you !
Try this
import 'package:flutter/material.dart';
void main() => runApp(HomeScreen());
class HomeScreen extends StatefulWidget {
#override
_HomeScreenPage createState() => _HomeScreenPage();
}
class _HomeScreenPage extends State<HomeScreen>
with SingleTickerProviderStateMixin {
TabController tabController;
String title = "Home";
#override
void initState() {
super.initState();
tabController = new TabController(length: 3, vsync: this);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.purple,
brightness: Brightness.light,
accentColor: Colors.red),
darkTheme: ThemeData(
brightness: Brightness.dark,
),
home: new Scaffold(
appBar: AppBar(
title: Text(title),
centerTitle: true,
),
body: TabBarView(
children: <Widget>[
FirstTab(),
MyBody("Page Two"),
MyBody("Page Three")
],
// if you want yo disable swiping in tab the use below code
// physics: NeverScrollableScrollPhysics(),
controller: tabController,
),
bottomNavigationBar: Material(
color: Colors.purple,
child: TabBar(
onTap: (indedx) {
if (indedx == 0) {
setState(() {
title = "Home";
});
} else if (indedx == 1) {
setState(() {
title = "Tab Two";
});
} else if (indedx == 2) {
setState(() {
title = "Tab Three";
});
}
},
indicatorColor: Colors.blue,
unselectedLabelColor: Colors.grey,
tabs: <Widget>[
Tab(
icon: Icon(Icons.favorite_border),
text: "ONE",
),
Tab(
icon: Icon(Icons.favorite),
text: "TWO",
),
Tab(
icon: Icon(Icons.add_box),
text: "THREE",
),
],
controller: tabController,
),
),
));
}
}
class FirstTab extends StatefulWidget {
#override
FirstTabState createState() => FirstTabState();
}
class FirstTabState extends State<FirstTab>
with SingleTickerProviderStateMixin {
TabController tabController;
#override
void initState() {
super.initState();
tabController = new TabController(length: 3, vsync: this);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: Container(
height: 50.0,
child: new TabBar(
indicatorColor: Colors.blue,
unselectedLabelColor: Colors.grey,
labelColor: Colors.blue,
tabs: [
Tab(
text: "ONE",
),
Tab(
text: "TWO",
),
Tab(
text: "THREE",
),
],
),
),
),
body: TabBarView(
children: [
Text("TAB ONE CONTENT"),
Text("TAB TWO CONTENT"),
Text("TAB THREE CONTENT"),
],
),
),
);
}
}
class MyBody extends StatelessWidget {
final String title;
MyBody(this.title);
final mySnackBar = SnackBar(
content: Text(
"Hello There!",
style: TextStyle(color: Colors.white),
),
duration: Duration(seconds: 3),
backgroundColor: Colors.blue,
);
#override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text(title + " Click me"),
onPressed: () => {Scaffold.of(context).showSnackBar(mySnackBar)}),
],
),
);
}
}
OUTPUT
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
int tabIndex = 0;
TabController tabController;
String title = "Home";
#override
void initState() {
super.initState();
tabController = new TabController(length: 3, vsync: this);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: Container(
height: 50.0,
child: new TabBar(
indicatorColor: Colors.blue,
unselectedLabelColor: Colors.grey,
labelColor: Colors.blue,
tabs: [
Tab(
text: "Home",
),
Tab(
text: "Mail",
),
Tab(
text: "Profile",
),
],
),
),
),
body: TabBarView(
children: [
MyHomeScreen(),
MailScreen(),
ProfileScreen(),
],
),
),
);
}
}
class MyHomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: NestedTabBar()
),
);
}
}
class NestedTabBar extends StatefulWidget {
#override
_NestedTabBarState createState() => _NestedTabBarState();
}
class _NestedTabBarState extends State<NestedTabBar> with TickerProviderStateMixin {
int tabIndex=0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.white,
child: tabIndex ==0 ?BottomTabBarHome()
:tabIndex == 1? BottomTabBarMail(): BottomTabBarProfile()
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home, color: Colors.grey,),
activeIcon: Icon(Icons.home, color: Colors.blue,),
title: Text('')
),
BottomNavigationBarItem(
icon: Icon(Icons.mail, color: Colors.grey,),
activeIcon: Icon(Icons.mail, color: Colors.blue,),
title: Text('')
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle, color: Colors.grey,),
activeIcon: Icon(Icons.account_circle, color: Colors.blue,),
title: Text('')
)
],
currentIndex: tabIndex,
selectedItemColor: Colors.blueAccent,
onTap: (int index){
setState(() {
tabIndex = index;
});
},
),
);
}
}
class BottomTabBarHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Bottom Tab Bar Home Screen"),
),
);
}
}
class BottomTabBarMail extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Bottom Tab Bar Mail Screen"),
),
);
}
}
class BottomTabBarProfile extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Bottom Tab BarM Profile Screen"),
),
);
}
}
Oh! you got your answer, but i also tried :)
Look: