TabView inside CustomScrollView - flutter

Wrapping TabBarView with SliverFillRemaining (fill remaining empty space like Expanded) gives the following error output.
flutter: A RenderPositionedBox expected a child of type RenderBox but received a child of type
flutter: RenderSliverList.
TabController tabContoller;
#override
void initState() {
tabContoller = new TabController(
vsync: this,
length: 3,
);
#override
Widget build(BuildContext context) {
return new Scaffold(
floatingActionButton:floatActionBtn(...),
bottomNavigationBar: bottomNavigationBar(...),
appBar: apBar(),
body: Stack(
children: <Widget>[
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
expandedHeight: 195.0,
flexibleSpace: FlexibleSpaceBar(
background: new Stack(
children: <Widget>[
...
]),
),
),
new SliverFillRemaining(
child: TabBarView(
controller: tabContoller,
children: <Widget>[
Tab(...),
Tab(...),
Tab(...)
],
),
),
],
),
],
)

Don't forget the DefaultTabController, this code is working fine:
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Container(
child: CustomScrollView(slivers: <Widget>[
SliverAppBar(),
new SliverFillRemaining(
child: TabBarView(
children: <Widget>[
Text("Tab 1"),
Text("Tab 2"),
Text("Tab 3"),
],
),
),
])),
);
}

You can use NestedScrollView like this
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
forceElevated: innerBoxIsScrolled,
automaticallyImplyLeading: false,
expandedHeight: 195.0,
flexibleSpace: FlexibleSpaceBar(
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, int index) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TabBar(
labelColor: AppColors.black,
unselectedLabelColor: AppColors.gray,
indicatorColor: AppColors.primaryColor,
indicatorWeight: 4,
indicatorPadding: EdgeInsets.symmetric(horizontal: 16),
labelPadding: EdgeInsets.zero,
tabs: [
Text("FIRST"),
Text("SECOND"),
Text("THIRD"),
],
)
],
);
},
childCount: 1,
),
),
];
},
body: TabBarView(
children: <Widget>[
Text("FIRST TAB"),
Text("SECOND TAB"),
Text("THIRD TAB"),
],
),
),
),
);
}

Related

Flutter large sliverappbar not working with tabs

I have a SliverAppBar.large and Tab inside NestedScrollView. However the app bar not displaying properly. Title in app bar hides behind Tabs and a padding above appbar title is present while scrolling. Is this a bug ?
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({super.key});
#override
Widget build(BuildContext context) {
final List<String> tabs = <String>['Tab 1', 'Tab 2'];
return DefaultTabController(
length: tabs.length,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar.large(
leading: const Icon(Icons.arrow_back),
title: const Text('Books'),
forceElevated: innerBoxIsScrolled,
bottom: TabBar(
tabs: tabs.map((name) => Tab(text: name)).toList(),
),
),
),
];
},
body: TabBarView(
children: tabs.map((name) {
return SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (context) {
return CustomScrollView(
key: PageStorageKey<String>(name),
slivers: <Widget>[
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: SliverFixedExtentList(
itemExtent: 48.0,
delegate: SliverChildBuilderDelegate(
(context, index) {
return ListTile(
title: Text('Item $index'),
);
},
childCount: 30,
),
),
),
],
);
},
),
);
}).toList(),
),
),
),
);
}
}

TabBarView with dynamic container height and futurebuilder Flutter

I'm using a SizedBox and passing the screen height to it, but I have a RenderFlex overflowed error.
This is the structure of my code:
return DefaultTabController(
length: 2,
initialIndex: 0,
child: Scaffold(
drawer: const NavigationDrawerWidget(),
appBar: CustomAppBar(),
body: SingleChildScrollView(
child: Column(
children: [
TabBar(
isScrollable: true,
tabs: myTabs,
),
SizedBox(
height: (MediaQuery.of(context).size.height),
child: TabBarView(
children: [
futureBuilder,
const Icon(Icons.directions_bike),
],
),
)
],
),
)),
);
FutureBuilder returns a container where the height is varied and has other expansive children. How can I get rid of using SizedBox and pass a height to it? I tried using Expanded in place of SizedBox but got errors.
Try using NestedScrollView
return Scaffold(
body: DefaultTabController(
length: 2,
initialIndex: 0,
child: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
bottom: TabBar(isScrollable: true, tabs: [
Tab(
icon: Icon(Icons.ac_unit),
),
Tab(
icon: Icon(Icons.ac_unit),
),
]))
];
},
body: TabBarView(
children: [
SingleChildScrollView(
child: Container(
color: Colors.red,
child: Column(
children: [
for (int i = 0; i < 34; i++)
Container(
height: kToolbarHeight,
color: i.isEven ? Colors.amber : Colors.pink,
)
],
),
),
),
const Icon(Icons.directions_bike),
],
),
),
),
);

Use a Row as title in FlexibleSpaceBar [Flutter]

I would like to use a Row widget instead of a Text widget as the title for the FlexibleSpaceBar.
Unfortunately Flutter returns an error.
Here is my code:
CustomScrollView(
slivers: [
const SliverAppBar(
pinned: true,
floating: false,
backgroundColor: Color(0xFF172A3A),
snap: false,
expandedHeight: 200,
centerTitle: true,
flexibleSpace: FlexibleSpaceBar(
background: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Demo'),
]
),
centerTitle: true,
),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('List Item $index'),
);
},
),
),
],
),
I know that the title should be a Text widget but I actually need a Row.
You can copy paste run full code below
Step 1 : Remove const keyword in front of SliverAppBar
Step 2 : Use Row like this
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('title'),
Text('test'),
]),
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
floating: false,
backgroundColor: Color(0xFF172A3A),
snap: false,
expandedHeight: 200,
centerTitle: true,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('title'),
Text('test'),
]),
flexibleSpace: FlexibleSpaceBar(
background: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Demo'),
]),
centerTitle: true,
),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('List Item $index'),
);
},
),
),
],
),
);
}
}

Flutter - How to add custom tabbar inside sliverappbar?

I'm trying to implement a custom tab bar widget inside a SliverAppBar. So far I've tried wrapping my CustomTabBar within a PreferredSize widget.
Here's my code:
Widget _buildBody(){
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool boxIsScrolled) {
return <Widget>[
SliverAppBar(
leading: Container(),
backgroundColor: Colors.transparent,
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
"Item 1"
),
Text(
"Item 2"
),
Text(
"Item 3"
)
],
)
]),
),
bottom: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: CustomTabWidget(
items: ['Challenge', 'My friends'],
activeColor: secondaryColor,
currentIndex: currentIndex,
backgroundColor: tabColor,
activeTextColor: Colors.white,
backgroundTextColor: Colors.white,
onTabSelect: (int index) {
onLeaderboardTabSelect(index);
},
),
),];
},
body: ListView.separated(
itemCount: 50,
itemBuilder: (context, index) {
return ListTile(
title: Text('row $index'),
);
},
separatorBuilder: (context, index) {
return Divider();
},
) // should return listview depending on the tab
);
}
CustomTabWidget
Widget build(BuildContext context) {
return Container(
height: height,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(
30.0,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _buildItems(context),
),
);
}
The code successfully shows the my custom tab bar widget but whenever I scroll down or tap another tab, it disappears.
I might have overlooked something within the code.
Can anyone help me?
this work on my project
class TabBarInSliverAppbar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
title: Text("Tabbar in SliverAppbar Example"),
pinned: true,
floating: true,
bottom: TabBar(
tabs: <Widget>[
Tab(
text: "First Tab",
),
Tab(
text: "Second Tab",
),
],
),
),
SliverToBoxAdapter(
child: TabBarView(
children: <Widget>[
Center(
child: Text("First Tab"),
),
Center(
child: Text("Second Tab"),
),
],
),
),
],
),
),
);
}
}
you can also checki this link :Flutter TabBar and SliverAppBar that hides when you scroll down

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),
],
),
),
),
],
),
),
);
}