I want to implement a float AppBar with a pinned TabBar at the bottom. The following is the test code (dartPad):
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: Colors.yellow,
title: Text(
"WhatsApp type sliver appbar",
),
centerTitle: true,
pinned: true,
floating: true,
bottom: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: Container(
color: Colors.orange,
alignment: Alignment.topLeft,
child: TabBar(
isScrollable: true,
indicatorColor: Colors.white,
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
labelPadding: EdgeInsets.only(
top: 13, bottom: 13, left: 16, right: 16),
tabs: [
Text(
"TAB A",
),
Text(
"TAB B",
),
]),
),
),
),
];
},
body: TabBarView(
controller: _tabController,
children: [
TabA(),
const Center(
child: Text('Display Tab 2',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
),
],
),
),
);
}
I find that it will have a top-padding on the TabBar when scrolling down. Is there any way to remove that space? I have set the SliverAppBar's toolbarheight, but that space will keep there even I lower the height.
scroll up (show appbar):
scroll down (hide appbar, the top yellow is not hidden):
Just set property pinned: false
See documentation
pinned → bool
Whether the app bar should remain visible at the start of the scroll view. [...]
final
Also remove tabBar from bottom: and add it above tabbarview inside a column
Thank you for the help.
Finally, I have another solution that may also take consider. I post here for others to ref.
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: Colors.yellow,
title: Text(
"WhatsApp type sliver appbar",
),
elevation: 0.0,
forceElevated: false,
pinned: false,
floating: true,
primary: false,
centerTitle: false,
titleSpacing: NavigationToolbar.kMiddleSpacing,
automaticallyImplyLeading: false,
),
SliverAppBar(
backgroundColor: Colors.orange,
pinned: true,
primary: false,
centerTitle: false,
titleSpacing: 0,
toolbarHeight: 48,
automaticallyImplyLeading: false,
forceElevated: true,
title: Align(
alignment: Alignment.topLeft,
child: TabBar(
isScrollable: true,
indicatorColor: Colors.white,
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
labelPadding: EdgeInsets.only(
top: 13, bottom: 13, left: 16, right: 16),
tabs: [
Text(
"TAB A",
),
Text(
"TAB B",
),
]),
),
),
];
},
body: TabBarView(
controller: _tabController,
children: [
TabA(),
const Center(
child: Text('Display Tab 2',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
),
],
),
),
);
}
Basically, what i do is separate 2 SliverAppBar, one is not pinned and floating; another is pinned. This makes the upper appbar disappear when scroll down and display when scroll up while the tabbar will keep pinning there.
İf you use listview add this line
ListView.builder(
padding: EdgeInsets.zero,
Related
I am developing a flutter app, in home screen i have tabs( each tab is showing a category) and by click one of them the category products will appear.
Just like that
The problem is when i click home first i must have a general products from all categories and no category should be selected first.
After the user see's the general products he can then choose category he wants to see.
So i must customize the tabs and i don't know how.
Any idea?
Here is a demo widget using DefaultTabController, you can archive it easily.
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverAppBar(
snap: true,
floating: true,
toolbarHeight: 80,
backgroundColor: Colors.white,
title: Container(
width: double.infinity,
color: Colors.redAccent,
child: Text("search here"),
),
centerTitle: true,
bottom: PreferredSize(
preferredSize: Size(0.0, 48.0),
child: Column(
children: [
TabBar(
isScrollable: true,
indicatorWeight: 1,
labelColor: Colors.green,
unselectedLabelColor: Colors.grey,
// indicatorSize: TabBarIndicatorSize.label,
labelStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
tabs: [
Tab(text: "screen1"),
Tab(text: "screen2"),
Tab(text: "screen3"),
Tab(text: "screen4"),
],
),
],
),
),
)
],
body: TabBarView(
children: [
Container(
child: Text("screen1"),
),
Container(
child: Text("screen2"),
),
Container(
child: Text("screen3"),
),
Container(
child: Text("screen4"),
),
],
)),
),
);
}
}
I wanna make flutter app.
How to make a moving title on appbar when scrolloing??
I attached app's status on GIF file.
Where do I need to set moving the title text?
I just make app like Uber app
#override
Widget build(BuildContext context) {
double sliderValue = 0.0;
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
child: SliverAppBar(
forceElevated: innerBoxIsScrolled ,
backgroundColor: Colors.transparent,
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.arrow_back),
color: Colors.black,
),
elevation: 0.0,
expandedHeight: 100.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.only(left: 15.0),
collapseMode: CollapseMode.none,
title: Text("Notification",
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
)),
),
),
),
];
},
body: Center(
child: Text("Sample Text"),
),
),
);
}
Have to use CustomScrollView widget
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text('Title'),
pinned: true,
expandedHeight: 48,
),
SliverFillRemaining(
child: Center(
child: Text('sliver'),
)),
],
);
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!
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,
),
),
);
I want my appbar to act as a fixed appbar when it's scrolled down, or user's searching something.
SliverAppBar(
title: new TextField(
style: Theme.of(context).primaryTextTheme.title,
decoration: InputDecoration(
hintText: '검색',
),
),
),
But I want to draw it as a flexible appbar when it's scrolled up and user's not searching.
flexibleSpace: new FlexibleSpaceBar(
centerTitle: false,
title: new TextField(
style: Theme.of(context).primaryTextTheme.title,
decoration: InputDecoration(
hintText: '검색',
),
),
background: Stack(
fit: StackFit.expand,
children: <Widget>[
SizedBox(
height: 256.0,
child: Container(
child: Padding(
padding: const EdgeInsets.only(top: 24.0),
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: FlutterLogo(
size: 64.0,
),
),
Padding(padding: const EdgeInsets.only(bottom: 24.0)),
ListTile(
title: Text('Some Text'),
),
],
),
),
),
),
// This gradient ensures that the toolbar icons are distinct
// against the background image.
],
),
),
Search field is transformed to top-right little bit when scrolled up with second approach.
The effect can be achieved by moving title content to another SliverList.
Remove flexibleSpace from SliverAppBar, and move contents of flexibleSpace.background to SliverList before the SliverAppBar.
Example:
#override
Widget build(BuildContext context) {
return new CustomScrollView(
slivers: <Widget>[
new SliverList(
delegate: new SliverChildListDelegate(<Widget>[
Align(
alignment: Alignment.topLeft,
child: FlutterLogo(size: 64.0),
),
ListTile(
title: Text('Some Text'),
),
ListTile(),
])),
new SliverAppBar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
elevation: 0.0,
automaticallyImplyLeading: false,
pinned: true,
floating: false,
title: new TextField(
focusNode: _searchFocusNode,
style: Theme.of(context).primaryTextTheme.title,
decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
suffixIcon: Icon(Icons.search),
hintText: '검색',
),
),
),
new SliverList(
delegate: new SliverChildListDelegate(List.generate(
100,
(i) => ListTile(
title: Text('Scroll'),
)).toList()),
),
],
);
}
I've written a getMaterialSerach(); method in this gist which has the exact material search view you need. just add getMaterialSearch() from this in your appBar: widget like below.
here is the gist for getMaterialSearch();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: getMaterialSearchBar(),
body: Center(
child: Container(),
),
);
}
Here's my code with a TextField and Tabs in a SliverAppBar:
NestedScrollView(
controller: model.mainScrollController,
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
/// https://github.com/flutter/flutter/issues/54059
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
automaticallyImplyLeading: false,
pinned: true,
floating: true,
snap: true,
expandedHeight: 100,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
children: [
BackButton(
color: Colors.white,
),
Flexible(
child: TextField(
controller: model.searchController,
decoration: InputDecoration(
focusColor: Colors.blueAccent,
hoverColor: Colors.blueAccent,
fillColor: Colors.white,
filled: true,
isDense: true,
prefixIconConstraints: BoxConstraints(maxHeight: 24, maxWidth: 48),
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Icon(Icons.search_outlined),
),
hintText: 'Search...'),
),
),
IconButton(
icon: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
ExtendedNavigator.named('topNav').push(Routes.newExerciseView);
},
tooltip: 'Create Exercise',
),
],
),
),
),
bottom: TabBar(
labelPadding: EdgeInsets.only(bottom: 8),
indicatorWeight: 3,
indicatorSize: TabBarIndicatorSize.label,
tabs: [
Text('All'),
Text('Custom'),
Text('Favorites'),
],
),
),
),
];
},
body: TabBarView(
children: [
AllExercises(),
CustomExercises(),
FavoriteExercises(),
]),
),
There's a few key pieces that make this work right, the first being that you need to use the SliverAppBar's flexibleSpace property to add your search widget. If you try adding it in the title property, the TextField just get's squished but never goes off screen.
Second, make sure that the collapseMode of the FlexibleSpaceBar is set to CollapseMode.pin. This makes it so that the contents of the flexibleSpace scroll off screen and don't change size.
And finally, set pinned on the sliverAppBar to true so the TabBar sticks.