How to add a TabBar in AppBar in Flutter? - flutter

I have a AppBar as defined below. I want to add a TabBar below it. When i try to supply it in the bottom: property of the AppBar it throws a renderflex error:
This is my code:
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
body: Stack(
children: [
Positioned(
top: 15,
left: 15,
right: 15,
child: SafeArea(
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: AppBar(
title: Text('Hello', style: kTasksStyle),
centerTitle: true,
backgroundColor: kGreen,
),
),
),
),
],
)),
);
}
This is what the appBar looks like:

Probably you are trying to this
#override
Widget build(BuildContext context) {
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
return DefaultTabController(
length: 2,
child: SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("Hello"),
centerTitle: true,
bottom: TabBar(
tabs: myTabs,
),
),
body: TabBarView(
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(),
),
),
),
);

Related

counter that counts pages when its scroll down

I'm beginner in flutter and programming, want To set counter that count images number when it scroll down and also tell remaining pages instead of hardcoded text('1/6'). here it's code
int counter=0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Center(
child: Text('Surah Yaseen' ' (1/6) ', style: TextStyle(fontSize: 30.0),
),
),
),
body: PageView(
scrollDirection: Axis.vertical,
allowImplicitScrolling: true,
children: [
Container(
child: Image.asset('images/1.jpg'),
),
Container(
child: Image.asset('images/2.jpg'),
),
Container(
child: Image.asset('images/3.jpg'),
),
Container(
child: Image.asset('images/4.jpg'),
),
Container(
child: Image.asset('images/5.jpg'),
),
Container(
child: Image.asset('images/6.jpg'),
)
],
),
);
}
}
Try this:
final controller = PageController(
initialPage: 0,
);
final _currentPageNotifier = ValueNotifier<int>(0);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Center(
child: Text('Surah Yaseen' ' (${_currentPageNotifier.value}/6) ', style: TextStyle(fontSize: 30.0),
),
),
),
body: PageView(
scrollDirection: Axis.vertical,
allowImplicitScrolling: true,
onPageChanged: (index) {
setState(() {
_currentPageNotifier.value = index+1;
});
},
controller: controller,
children: [
Container(
child: Image.asset('images/1.jpg'),
),
Container(
child: Image.asset('images/2.jpg'),
),
Container(
child: Image.asset('images/3.jpg'),
),
Container(
child: Image.asset('images/4.jpg'),
),
Container(
child: Image.asset('images/5.jpg'),
),
Container(
child: Image.asset('images/6.jpg'),
)
],
),
);
}

Back Button displaced title of AppBar

Like the title, i have created a button with Navigator.push() into a new page, the page auto generates a back button on the appBar, i appreciated that autogenerated back button but i want the back button to not displace my title's positioning, is that possible?
Below is my code for the button to enter this page:
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return FeedTest();
},
),
);
This is my full code for the new page:
Widget build(BuildContext context) {
return ScrollConfiguration(
behavior: MyBehavior(),
child: SafeArea(
child: DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.grey[850],
title: Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'images/logo2.png',
isAntiAlias: true,
scale: 1.8,
fit: BoxFit.fitHeight,
alignment: Alignment.center,
)
]),
bottom: TabBar(
indicatorColor: Colors.white,
tabs: [
Tab(
text: 'Submit Feedback',
),
Tab(
text: 'Submit Testimony',
)
],
),
),
body: TabBarView(
children: [SubmitFeedback(), SubmitTestimony()],
),
),
),
),
);
}
P.S. Title image has been blurred for privacy purposes
centerTitle: true is used to make the title center in AppBar
AppBar(
centerTitle: true,
...
)
Output:
Use centerTitle: true like below.
AppBar(
centerTitle: true, // this is all you need
//Rest Code here
)
I upload the full code like below.
Widget build(BuildContext context) {
return ScrollConfiguration(
behavior: MyBehavior(),
child: SafeArea(
child: DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
centerTitle: true,
elevation: 0,
backgroundColor: Colors.grey[850],
title: Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'images/logo2.png',
isAntiAlias: true,
scale: 1.8,
fit: BoxFit.fitHeight,
alignment: Alignment.center,
)
]),
bottom: TabBar(
indicatorColor: Colors.white,
tabs: [
Tab(
text: 'Submit Feedback',
),
Tab(
text: 'Submit Testimony',
)
],
),
),
body: TabBarView(
children: [SubmitFeedback(), SubmitTestimony()],
),
),
),
),
);
}

How do I place the text to the left of the tabbar?

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Viewer',
theme:
ThemeData(primaryColor: Colors.cyan, accentColor: Colors.tealAccent),
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: PreferredSize(
child: Container(
margin: EdgeInsets.fromLTRB(100, 0, 0, 0),
child: Row(
children: <Widget>[
//Icon(Icons.settings)
Text('Blah'),
TabBar(
tabs: <Widget>[
Tab(icon: Icon(Icons.search)),
Tab(icon: Icon(Icons.file_download)),
Tab(icon: Icon(Icons.settings))
],
),
],
),
),
preferredSize: Size.fromHeight(-8),
),
),
body: TabBarView(
children: [
Icon(Icons.search),
Icon(Icons.file_download),
Icon(Icons.settings),
],
),
),
),
);
}
}
I want to place the text on the blah part visible in the image.
Either way, I want to put a text in the left part of the tabbar.
I tried using row, but an overflow error occurred.
This occurred an error message.
How can I enter the text in that space?
Here is the code which will work for you
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hitomi Viewer',
theme:
ThemeData(primaryColor: Colors.cyan, accentColor: Colors.tealAccent),
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: PreferredSize(
preferredSize: Size.fromHeight(-8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20),
child: Center(child: Text('Title'))),
SizedBox(
width: 50,
),
Flexible(
child: TabBar(
tabs: [
Tab(icon: Icon(Icons.search)),
Tab(icon: Icon(Icons.file_download)),
Tab(icon: Icon(Icons.settings)),
],
),
),
],
),
)),
body: TabBarView(
children: [
Icon(Icons.search),
Icon(Icons.file_download),
Icon(Icons.settings),
],
),
),
),
);
}
}
OutPut:
I fixed it by fixing the code as follows:
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hitomi Viewer',
theme:
ThemeData(primaryColor: Colors.cyan, accentColor: Colors.tealAccent),
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: PreferredSize(
child: Container(
margin: EdgeInsets.fromLTRB(100, 0, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
child: InkWell(
child: Text('Blah'),
),
margin: EdgeInsets.fromLTRB(10, 0, 80, 0),
),
TabBar(
tabs: <Widget>[
Tab(icon: Icon(Icons.search)),
Tab(icon: Icon(Icons.file_download)),
Tab(icon: Icon(Icons.settings))
],
),
),
],
),
),
preferredSize: Size.fromHeight(-8),
),
),
body: TabBarView(
children: [
Icon(Icons.search),
Icon(Icons.file_download),
Icon(Icons.settings),
],
),
),
),
);
}
}
Since your tabs property is takinig a List<Widget>, use the Text widget in the tabs property instead.
Check the code below:
TabBar(
tabs: <Widget>[
// put your text widget here
Text('Blah'),
Tab(icon: Icon(Icons.search)),
Tab(icon: Icon(Icons.file_download)),
Tab(icon: Icon(Icons.settings))
],
),

Display TabBar in body of scaffold

I'm want to display a tabbar as it is used with the appbar, but in the middle of the scaffold body. Is it possible?
I have this code, but the TabBarView breaks the ui. If I comment the TabBarView, the TabBars are displayed correctly. What is wrong with the code? In case it's possible to use it this way..
#override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
Text('Some other random content'),
TabBar(
controller: _tabController,
isScrollable: true,
tabs: <Widget>[
Tab(
child: Text(
'Posts',
style: TextStyle(
fontSize: 18.0,
color: Colors.black87,
),
),
),
Tab(
child: Text(
'Fotos',
style: TextStyle(
fontSize: 18.0,
color: Colors.black87,
),
),
),
],
),
TabBarView(
controller: _tabController,
children: <Widget>[
Center(
child: Text("User"),
),
Center(
child: Text("Email"),
),
],
),
],
),
);
}
Just Change ListView to Column and add SingleChildScrollView
SingleChildScrollView(
child: Column(
children: <Widget>[]
),
)
Hey i guess it's already too late but maybe it helps others.
If you just want to have your tabbar e.g. in the middle of the screen to divide your screen in 2 sections (top: content, bottom: tabbar) you can place your scaffold inside a column, e.g. following:
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
flex: 1,
child: Center(
child: Text("Header"),
),
),
Expanded(
flex: 1,
child: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
flexibleSpace: TabBar(
tabs: [
Tab(text: "First"),
Tab(text: "Second"),
Tab(text: "Third"),
],
),
),
body: TabBarView(
children: [
Center(child: Text("First screen")),
Center(child: Text("Second screen")),
Center(child: Text("Third screen")),
],
),
),
),
),
],
);
}
Let me know if this fits your needs / if you need further help :-)

how to implement a sliverAppBar with a tabBar

the flutter document show a demo for SliverAppBar + TabBar + TabBarView with ListView use NestedScrollView, and it's a bit complex, so I wonder is there a simply and clear way to implement it. I tried this:
CustomScrollView
slivers:
SliverAPPBar
bottom: TabBar
TabBarView
children: MyWidget(list or plain widget)
got error:
flutter: The following assertion was thrown building Scrollable(axisDirection: right, physics:
flutter: A RenderViewport expected a child of type RenderSliver but received a child of type _RenderExcludableScrollSemantics.
flutter: RenderObjects expect specific types of children because they coordinate with their children during layout and paint. For example, a RenderSliver cannot be the child of a RenderBox because a RenderSliver does not understand the RenderBox layout protocol.
and
flutter: Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3497 pos 14: 'owner._debugCurrentBuildTarget == this': is not true.
HERE IS MY CODE:
import 'package:flutter/material.dart';
main(List<String> args) {
runApp(MyScrollTabListApp());
}
class MyScrollTabListApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: "aa", home: MyScrollTabListHomePage());
}
}
class MyScrollTabListHomePage extends StatefulWidget {
#override
MyScrollTabListHomePageState createState() {
return new MyScrollTabListHomePageState();
}
}
class MyScrollTabListHomePageState extends State<MyScrollTabListHomePage>
with SingleTickerProviderStateMixin {
final int _listItemCount = 300;
final int _tabCount = 8;
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: _tabCount, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 240.0,
title: Text("Title"),
pinned: true,
bottom: TabBar(
controller: _tabController,
isScrollable: true,
tabs: List<Tab>.generate(_tabCount, (int i) {
return Tab(text: "TAB$i");
}),
),
),
TabBarView(
controller: _tabController,
children: List<Widget>.generate(_tabCount, (int i) {
return Text('line $i');
}),
),
],
),
);
}
}
and for the official demo, it use struct like this
DefaultTabController
NestedScrollView
headerSliverBuilder
SliverOverlapAbsorber
handle
SliverAppBar
TabBarView
CustomScrollView
SliverOverlapInjector
handle
SliverPadding
Use NestedScrollView, here is the working code.
#override
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
length: 2,
child: NestedScrollView(
headerSliverBuilder: (context, value) {
return [
SliverAppBar(
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.call), text: "Call"),
Tab(icon: Icon(Icons.message), text: "Message"),
],
),
),
];
},
body: TabBarView(
children: [
CallPage(),
MessagePage(),
],
),
),
),
);
}
Here is an example for TabView with SilverAppBar
class SilverAppBarWithTabBarScreen extends StatefulWidget {
#override
_SilverAppBarWithTabBarState createState() => _SilverAppBarWithTabBarState();
}
class _SilverAppBarWithTabBarState extends State<SilverAppBarWithTabBarScreen>
with SingleTickerProviderStateMixin {
TabController controller;
#override
void initState() {
super.initState();
controller = new TabController(length: 3, vsync: this);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new CustomScrollView(
slivers: <Widget>[
new SliverAppBar(
title: Text("Silver AppBar With ToolBar"),
pinned: true,
expandedHeight: 160.0,
bottom: new TabBar(
tabs: [
new Tab(text: 'Tab 1'),
new Tab(text: 'Tab 2'),
new Tab(text: 'Tab 3'),
],
controller: controller,
),
),
new SliverList(
new SliverFillRemaining(
child: TabBarView(
controller: controller,
children: <Widget>[
Text("Tab 1"),
Text("Tab 2"),
Text("Tab 3"),
],
),
),
],
),
);
}
}
Yeah. You can use NestedScrollView To Achieve Tabs. Here is Some Addition Code To That.
class AppView extends StatelessWidget {
final double _minValue = 8.0;
#override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: MyAppBar(),
drawer: DrawerDialog(),
body: DefaultTabController(
length: 3,
child: SafeArea(
child: NestedScrollView(
body: TabBarView(
children: [Text("Page 1"), Text("Page 2"), Text("Page 3")],
),
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) => [
SliverPadding(
padding: EdgeInsets.all(_minValue * 2.5),
sliver: SliverToBoxAdapter(
child: Text(
"Hiding Header",
style: textTheme.headline6,
textAlign: TextAlign.center,
),
),
),
SliverAppBar(
backgroundColor: Colors.grey[100],
pinned: true,
elevation: 12.0,
leading: Container(),
titleSpacing: 0.0,
toolbarHeight: 10,
bottom: TabBar(tabs: [
Tab(
child: Text(
"All",
style: textTheme.subtitle2,
),
),
Tab(
child: Text(
"Categories",
style: textTheme.subtitle2,
),
),
Tab(
child: Text(
"Upcoming",
style: textTheme.subtitle2,
),
),
]),
),
],
),
),
),
);
}
}
You can Also Implement it by Providing _tabController to Both TabBar() and TabBarView So It will bind.
And For the children of TabBarView if you're using ListView then give it physics: NeverScrollablePhysics() so it won't move, kindly not You have to give dynamic height to the Container of ListView So it will load all Children's.
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 2, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
floating: true,
expandedHeight: 50,
title: Column(
children: [
Row(
children: [
Text('Hello, User'),
Spacer(),
InkWell(
child: Icon(Icons.map_rounded),
),
],
),
],
),
),
SliverList(
delegate: SliverChildListDelegate([
_tabSection(context),
])),
],
));
}
Widget _tabSection(BuildContext context) {
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
double mainAxisHeight = height > width ? height : width;
return DefaultTabController(
length: 2,
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Container(
height: 48,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(10),
bottomLeft: Radius.circular(10))),
child: TabBar(
indicatorColor: Colors.white,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(color: Colors.white, width: 5.0),
insets: EdgeInsets.symmetric(horizontal: 40),
),
labelColor: Colors.white,
unselectedLabelColor: Colors.grey[300],
tabs: [
Tab(
iconMargin: EdgeInsets.only(top: 5),
text: "Tab Bar 1",
),
Tab(
iconMargin: EdgeInsets.only(top: 5),
text: "Tab bar 2",
),
]),
),
Container(
height: 200 * 6 // 200 will be Card Size and 6 is number of Cards
child: TabBarView(controller: _tabController, children: [
tabDetails(),
tabDetails(),
]))
]));
}
tabDetails() {
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
double mainAxisHeight = height > width ? height : width;
return Container(
padding: EdgeInsets.symmetric(horizontal: 15),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.red[100],
Colors.red[200],
])),
child: ListView(
physics: NeverScrollableScrollPhysics(), // This will disable LitView'Scroll so only Scroll is possible by TabBarView Scroll.
children: [
SizedBox(height: 10),
Container(
height:140,
width: width,
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: 6,
itemBuilder: (BuildContext context, int indexChild) {
return Row(
children: [
MyListTile(
name: "Name",
),
SizedBox(width: 5),
],
);
},
),
),
SizedBox(height: 1000),
],
),
);
}
}
If you want to use CustomScrollView, you can make use of SliverToBoxAdapter:
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
length: 2,
child: CustomScrollView(
slivers: [
SliverAppBar(
title: Text('SliverAppBar'),
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.call), text: "Call"),
Tab(icon: Icon(Icons.message), text: "Message"),
],
),
),
SliverToBoxAdapter(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: TabBarView(
children: [
Container(color: Colors.red),
Container(color: Colors.blue),
],
),
),
),
],
),
),
);
}