Related
How can I achieve something like this in Flutter? Is it possible in
I have tried these codes and it gave me an error which said no DefaultTabController. I was confused where I should put it in. Example in internet wrote I have to put it in Scaffold body. But my code required some widgets to display image using Stack before using TabBar.
import 'package:felix_idn/library/widget/components/enums.dart';
import 'package:felix_idn/library/widget/components/path.dart';
import 'package:felix_idn/library/widget/ui/button.dart';
import 'package:felix_idn/library/widget/ui/text.dart';
import 'package:felix_idn/menu/music/music_therapy.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class MusicAmbient extends StatelessWidget {
const MusicAmbient({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: const TabBar(
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_transit)),
],
),
title: Text('Flutter Tabs Example'),
),
body: Container(
color: Colors.black,
child: SafeArea(
child: Container(
color: const Color.fromARGB(115, 55, 55, 55),
child: Stack(
children: [
//Icon back
Align(
alignment: Alignment.topLeft,
child: IconButton(
icon: const Icon(Icons.arrow_back_sharp),
iconSize: 32.0,
constraints: const BoxConstraints(),
padding: const EdgeInsets.all(3.0),
onPressed: () => Get.off(() => const MusicTherapy()),
color: Colors.white,
),
),
//Piano Background & Tabs
Align(
alignment: Alignment.topCenter,
child: Column(
children: [
const SizedBox(height: 40.0),
ImageButton(path: bgamb, callback: () => Get.back(), type: IconType.NONE),
const TabBarView(
children: [
MusicTherapy(),
MusicTherapy(),
],
),
],
),
),
//Title
Align(
alignment: Alignment.topCenter,
child: Column(
children: const [
SizedBox(height: 6.0),
Titles("Ambient", color: Colors.white, size: 20.0),
],
),
),
],
),
),
),
),
);
}
}
You need to create and use TabController.
class MusicAmbient extends StatefulWidget {
const MusicAmbient({super.key});
#override
State<MusicAmbient> createState() => _MusicAmbientState();
}
class _MusicAmbientState extends State<MusicAmbient>
with SingleTickerProviderStateMixin {
late final TabController controller = TabController(length: 2, vsync: this);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: controller, //here
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_transit)),
],
),
title: Text('Flutter Tabs Example'),
),
body: Container(
color: Colors.black,
child: SafeArea(
child: Container(
color: const Color.fromARGB(115, 55, 55, 55),
child: Stack(
children: [
//Icon back
Align(
alignment: Alignment.topLeft,
child: IconButton(
icon: const Icon(Icons.arrow_back_sharp),
iconSize: 32.0,
constraints: const BoxConstraints(),
padding: const EdgeInsets.all(3.0),
onPressed: null,
color: Colors.white,
),
),
//Piano Background & Tabs
Align(
alignment: Alignment.topCenter,
child: Column(
children: [
const SizedBox(height: 40.0),
// ImageButton(
// path: bgamb,
// callback: () => Get.back(),
// type: IconType.NONE),
Expanded(
child: TabBarView(
controller: controller,//here
children: [
// MusicTherapy(),
// MusicTherapy(),
Text("A"),
Text("B"),
],
),
),
],
),
),
//Title
Align(
alignment: Alignment.topCenter,
child: Column(
children: const [
SizedBox(height: 6.0),
// Titles("Ambient", color: Colors.white, size: 20.0),
],
),
),
],
),
),
),
),
);
}
}
Also you can try using flexibleSpace on AppBar for the image.
More about using TabBar
Please create customTabar it's very easy to create with provider.
I am trying to have tabs layout similar to Twitter profile page, where tabs layout shows in the middle of the page. To start with, I have created a basic layout with column as parent and other widgets including tabs as child. Code looks similar to below
Column(
children: [
Text("some text");
//some image widget
ElevatedButton(
style: style,
onPressed: () {},
child: Text("click me"),
);
DefaultTabController(
length: 2,
child: Flexible( //without flexible wrapper, it throws unbounded height issue.
child: Column(
children: [
TabBar(
labelColor: Colors.pink,
indicatorColor: Colors.pink,
tabs: [
Tab(child: Text("Tweets")),
Tab(child: Text("Tweets and replies")),
],
),
Expanded(
child: new TabBarView(children: [
//some widgets here
],),
)
],
),
),
);
]
)
but with above code issue is that, tabs layout doesn't fill remaining space, but just fill some fixed space.
i want to cover the remaining space with tabs, till bottom. if i remove remaining children of parent column and just keep tab, it fill all available space as expected.
If you face any issue, do flutter clean and restart
Result
Test widget
class SWidget extends StatelessWidget {
const SWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("some text"),
//some image widget
ElevatedButton(
// style: style,
onPressed: () {},
child: Text("click me"),
),
DefaultTabController(
length: 2,
child: Flexible(
//without flexible wrapper, it throws unbounded height issue.
child: Column(
children: [
TabBar(
labelColor: Colors.pink,
indicatorColor: Colors.pink,
tabs: [
Tab(child: Text("Tweets")),
Tab(child: Text("Tweets and replies")),
],
),
Expanded(
child: new TabBarView(
children: [
//some widgets here
Container(
child: Text("item 1"),
color: Colors.deepPurple,
),
Container(
child: Column(
children: [
Text("item 2"),
Text("item 2"),
],
),
color: Colors.deepOrange,
),
],
),
)
],
),
),
),
],
),
);
}
}
Let me know if it does solve the issue.
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))
],
),
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 :-)
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),
],
),
),
),
],
),
),
);
}