I'd basically like to have a TabView navigation in the middle of my page.
To do so I used a SliverAppBar inside a NestedScrollView.
My SliverAppBar is only composed by my TabView, I wrapped my TabView inside a SliverAppBar in order to used the pinned: true propriety.
return Scaffold(
appBar: AppBar(
title: Text('Title'),
),
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool boxIsScrolled) {
return <Widget>[
SliverToBoxAdapter(
child: Container(
height: 500,
color: Colors.red,
)),
SliverAppBar(
pinned: true,
bottom: TabBar(
tabs: <Widget>[
Tab(
text: "Home",
icon: Icon(Icons.home),
),
Tab(
text: "Example page",
icon: Icon(Icons.help),
)
],
controller: _tabController,
)),
];
},
body: TabBarView(
children: <Widget>[
PageExample(),
PageExample(),
],
controller: _tabController,
),
),
);
It does the trick, my problem is I'd like to hide/remove this SliverAppBar that wrapped my TabBar:
I needed to set expandedHeight to 0:
return Scaffold(
appBar: AppBar(
title: Text('Title'),
),
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool boxIsScrolled) {
return <Widget>[
SliverToBoxAdapter(
child: Container(
height: 500,
color: Colors.red,
)),
SliverAppBar(
expandedHeight: 0,
pinned: true,
bottom: TabBar(
tabs: <Widget>[
Tab(
text: "Home",
icon: Icon(Icons.home),
),
Tab(
text: "Example page",
icon: Icon(Icons.help),
)
],
controller: _tabController,
)),
];
},
body: TabBarView(
children: <Widget>[
PageExample(),
PageExample(),
],
controller: _tabController,
),
),
);
Related
I have a TabBarView(), and i need to show a MessageDialog (showDialog) asking if really want to leave this tab. is there any way to do it?
this is the body of my code
child: Scaffold(
drawer: SideMenu(),
appBar: AppBar(
title: Text("Antecedentes"),
bottom: TabBar(
isScrollable: true,
controller: _controller,
tabs: [
Tab(text: 'Tab # 1'),
Tab(text: 'Tab # 2'),
Tab(text: 'Tab # 3'),
),
),
body: TabBarView(
controller: _controller,
children: [
TabOnePage(),
TabTwoPage(),
TabThreePage(),
],
),
),
Im using version 2.2.3 of Flutter.
I appreciate help on this .
Show AlertDialog on Tabbar onTap action, and set TabBar index to previous one(this will allow TabBar to stay on the same tab):
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Tabbar Demo"),
bottom: TabBar(
onTap: (index) {
print(index);
showAlert(tabController.index);
tabController.index = tabController.previousIndex;
},
unselectedLabelColor: Colors.white,
labelColor: Colors.black,
tabs: [
Tab(icon: Icon(Icons.chat)),
Tab(icon: Icon(Icons.drafts)),
Tab(icon: Icon(Icons.ac_unit))
],
controller: tabController,
indicatorColor: Colors.black,
indicatorSize: TabBarIndicatorSize.tab,
),
),
body: TabBarView(
children: <Widget>[
CustomView("First"),
CustomView("Second"),
CustomView("Third"),
],
controller: tabController,
));
}
And upon selection of the "OK" button in the AlertDialog, set Tabbar index to the new one.
void showAlert(int newIndex) {
AlertDialog alertDialog = new AlertDialog(
content: new Container(
height: 80.0,
child: new Column(
children: <Widget>[
new Text("Want to leave this tab?"),
new RaisedButton(
child: new Text("OK"),
onPressed: () {
tabController.index = newIndex;
Navigator.pop(context);
},
)
],
),
),
);
showDialog(context: context, child: alertDialog);
}
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()],
),
),
),
),
);
}
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
So i would like to have a page with first some content, then two tabs with two different lists. The Tab bar should behave like a PersistentHeader (sticking to the top). One of these Lists within the TabBarView also contains SliverPersistentHeaders but these do not stack up on the TabBar PersistentHeader but scroll behind instead. How can one achieve such behaviour?
Here's a gif that shows the current behaviour:
This is what i've tried so far:
#override
Widget build(BuildContext context) {
return NestedScrollView(
controller: _scrollViewController,
headerSliverBuilder:
(BuildContext context, bool boxIsScrolled) {
return <Widget>[
SliverList(
delegate: SliverChildListDelegate([
_buildTopImage(),
Padding(
padding: EdgeInsets.symmetric(
horizontal: defaultPagePadding),
child: _buildTopText(),
),
]),
),
SliverAppBar(
pinned: true,
floating: true,
expandedHeight: 0,
backgroundColor: Colors.white,
bottom: TabBar(
labelColor: Theme.of(context).primaryColor,
unselectedLabelColor: Colors.grey,
tabs: <Widget>[
Tab(
text: "Tab 1",
),
Tab(
text: "Tab 2",
)
],
controller: _tabController,
),
)
];
},
body: TabBarView(
children: <Widget>[
_buildList1(),
_buildList2(),
],
controller: _tabController,
),
),
}
Widget _buildList2() {
return CustomScrollView(slivers: <Widget>[
SliverAppBar(
pinned: true,
title: Text('Test'),
),
SliverAppBar(
pinned: true,
title: Text('Test'),
),
SliverAppBar(
pinned: true,
title: Text('Test'),
),
]);
}
I'm new to Flutter so i would really appreciate any help from you guys!
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"),
],
),
),
),
);
}