Pinned SliverPersistentHeader overlaps with Android StatusBar - flutter

I'm working with Slivers. I have an SliverAppBar, then a SliverPersistentHeader and finally a SliverList.
The behavior I want to achieve is that the SliverAppBar scrolls off the screen but the SliverPersistentHeader to remain pinned at the top of the screen.
I am able to do that but the SliverPersistentHeader overlaps with the android status bar. Any idea on how can I fix this?
Finally this is the code
class ExampleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text('SliverAppBar'),
pinned: false,
floating: true,
snap: true,
elevation: 0.0,
),
SliverPersistentHeader(
pinned: true,
delegate: _SliverAppBarDelegate(
child: PreferredSize(
preferredSize: Size.fromHeight(40.0),
child: Container(
color: Theme.of(context).primaryColor,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('SliverPersistentHeader', style: TextStyle(color: Colors.white, fontSize: 20.0))
],
),
),
),
)
),
),
SliverFixedExtentList(
itemExtent: 150.0,
delegate: SliverChildListDelegate(
[
Container(color: Colors.red),
Container(color: Colors.purple),
Container(color: Colors.green),
Container(color: Colors.orange),
Container(color: Colors.yellow),
Container(color: Colors.pink),
],
),
),
],
);
}
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final PreferredSize child;
_SliverAppBarDelegate({ this.child });
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
// TODO: implement build
return child;
}
#override
// TODO: implement maxExtent
double get maxExtent => child.preferredSize.height;
#override
// TODO: implement minExtent
double get minExtent => child.preferredSize.height;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
// TODO: implement shouldRebuild
return false;
}
}

Just wrap your CustomScrollView into SafeArea :
return SafeArea(
child: CustomScrollView(
...

Additionally, you may need to hold your scroll widgets with an scaffold:
return Scaffold(
body: SafeArea(
child: CustomScrollView...

Related

Content over appbar in flutter?

I just want to make UI like content over the appbar, but I couldn't. Here is my code that I got from web. how to make the listview top of appbar and remove the card.
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Sample2(),
),
);
}
class Sample2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Material(
child: CustomScrollView(
slivers: [
SliverPersistentHeader(
delegate: MySliverAppBar(expandedHeight: 200),
pinned: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => ListTile(
title: Text("Index: $index"),
),
),
)
],
),
),
);
}
}
class MySliverAppBar extends SliverPersistentHeaderDelegate {
final double expandedHeight;
MySliverAppBar({#required this.expandedHeight});
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Stack(
fit: StackFit.expand,
overflow: Overflow.visible,
children: [
Image.network(
"https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
fit: BoxFit.cover,
),
Center(
child: Opacity(
opacity: shrinkOffset / expandedHeight,
child: Text(
"MySliverAppBar",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 23,
),
),
),
),
Positioned(
top: expandedHeight / 2 - shrinkOffset,
left: MediaQuery.of(context).size.width / 4,
child: Opacity(
opacity: (1 - shrinkOffset / expandedHeight),
child: Card(
elevation: 10,
child: SizedBox(
height: expandedHeight,
width: MediaQuery.of(context).size.width / 2,
child: FlutterLogo(),
),
),
),
),
],
);
}
#override
double get maxExtent => expandedHeight;
#override
double get minExtent => kToolbarHeight;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
Using stack is not the best way I think. May be I am wrong. but I think customScrollView and sliverappbar can do this. but I didn't get any tutorial or youtube videos. Most of the examples are listview below the appbar. I just want content over the appbar at first time, when it scrolls it should go below the appbar.
This will help you to hide appbar on scroll
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
title: Text('Sameple text'),
pinned: true,
floating: true,
forceElevated: innerBoxIsScrolled,
bottom: TabBar(
tabs: <Tab>[
Tab(text: 'ONE'),
Tab(text: 'TWO'),
],
controller: _tabController,
),
),
];
},
body: TabBarView(
controller: _tabController,
children: <Widget>[
Center(
child: Text(
"xyz",
style: TextStyle(fontSize: 60),
),
),
Text("xyz"),
],
),
),
);
}

How to stack Circle Avatar over SliverAppBar and SliverList?

I want to achieve the above design
I did somewhat like this using slivers
import 'package:flutter/material.dart';
class ProfilePage extends StatefulWidget {
#override
_ProfilePageState createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: DefaultTabController(
length: 4,
child: NestedScrollView(
body: TabBarView(children: [
//pages of tabBar
]),
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverAppBar(
//for the pinned appBar
elevation: 0.0,
backgroundColor: Colors.red,
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(backgroundColor: Colors.red.withOpacity(0.4),
child: Icon(Icons.arrow_back,color: Colors.white,),
),
),
pinned: true,
flexibleSpace: FlexibleSpaceBar(
background: Image.network(
"https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&h=350",
fit: BoxFit.cover,
),
),
expandedHeight: 150,
),
SliverList(delegate: SliverChildListDelegate([
// for bio and other stuff
])),
SliverAppBar(
toolbarHeight: 0.0,
primary: false,
pinned: true,
bottom: TabBar(tabs: [
// tab bars
],),
)
];
},
),
),
),
);
}
}
I tried adding stack inside my sliver list with positioned but Circle Avatar gets display behind the app bar.
How can we achieve such a design or what could be an alternative way to achieve it.

Custom Scroll View getting scrolled under Sliver Persistent Header

DefaultTabController(
length: _subCategory.tabLength,
initialIndex: 0,
child:
NestedScrollView(
physics: BouncingScrollPhysics(),
headerSliverBuilder: (headerCtx, innnerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 200.0,
backgroundColor: _productColor.backgroundColor,
pinned: true,
elevation: 0,
forceElevated: innnerBoxIsScrolled,
flexibleSpace: FlexibleSpaceBar(
title: Text("${_subCategory.currentSubCategoryName()}"),
background: Container(
margin: const EdgeInsets.only(
top: 4,
bottom: 50.0,
),
child: Hero(
tag: _subCategory.currentSubCategoryId(),
child: Image.asset(
'asset/images/grocery.jpeg',
),
),
),
),
),
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(headerCtx),
sliver: SliverPersistentHeader(
pinned: true,
delegate: _ProductTabSliver(
TabBar(
labelColor: Colors.white,
unselectedLabelColor: Colors.black87,
tabs: [
..._subCategory.currentTab().map(
(tabValue) {
return Tab(text: "${tabValue.fullName}");
},
).toList()
],
),
),
),
),
];
},
body:CustomScrollView(
physics: BouncingScrollPhysics(),
slivers: <Widget>[
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(ctx, pdIndex) {
final heightVisible =
_subCategory.advanceCompanyProductCount(pdIndex);
return ProductLayout();
},
childCount: _subCategory.differentProductCount(),
),
),
],
);,
));
CustomScrollList getting scrolled under sliverPersistentHeader Tab.
DefaultTabController
NestedScrollView
SliverAppBar
SliverPersistentHeader
-body: CustomScrollView
- slivers: SliverChildBuilderDelegate
SliverPersistentHeader had all the tabs displayed on the top(TabBar)
Body of Nested ScrollView is CustomScrollView which has SliverChildBuilderDelegate has a child.
On scrolling the list, my list scroll behind the tabs of sliver persistent header. Seems like sliverPersistentHeader is transparent and list scrolls can be seen behind.
To solve this problem, I had tried SliverOverlapInjector and SliverOverlapAbsorber, but that didn't help.
CustomScrollView scroll problem image is 4th for better understanding. Sunflower oil card on scrolling reaches behind the tab bar.
Images:
Sliver Overlap Absorber
Sliver Overlap Injector
Custom Scroll View
Overlapping Problem
class ProductAppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return NestedScrollView(
physics: BouncingScrollPhysics(),
headerSliverBuilder: (headerCtx, innnerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 200.0,
backgroundColor: _productColor.backgroundColor,
pinned: true,
elevation: 0,
forceElevated: innnerBoxIsScrolled,
flexibleSpace: FlexibleSpaceBar(
title: Text("${_subCategory.currentSubCategoryName()}"),
background: Container(
margin: const EdgeInsets.only(
top: 4,
bottom: 50.0,
),
child: Hero(
tag: _subCategory.currentSubCategoryId(),
child: Image.asset(
'asset/images/grocery.jpeg',
),
),
),
),
),
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(headerCtx),
sliver: SliverPersistentHeader(
pinned: true,
delegate: _ProductTabSliver(
TabBar(
onTap: (index) {
_subCategory.updateTabIndex(index);
},
labelColor: Colors.white,
unselectedLabelColor: Colors.black87,
tabs: [
..._subCategory.currentTab().map(
(tabValue) {
return Tab(text: "${tabValue.fullName}");
},
).toList()
],
),
),
),
),
];
},
body: TabBarView(
children: _subCategory.currentTab().map((tabElement) {
return ProductScreenLayout();
}).toList(),
),
);
}
}
class _ProductTabSliver extends SliverPersistentHeaderDelegate {
final TabBar _tabBar;
_ProductTabSliver(this._tabBar);
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final _productColor =
Provider.of<ColorConfig>(context, listen: false).randomProductColor();
return Container(
decoration: BoxDecoration(
color: _productColor.backgroundColor,
),
child: _tabBar);
}
#override
double get maxExtent => _tabBar.preferredSize.height;
#override
double get minExtent => _tabBar.preferredSize.height;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}
Instead of returning just TabBar Widget from the SliverPersistentHeaderDelegate wrapping it with Container and setting backgroundColor solve my problem.
Inside class _ProductTabSliver build method I had wrapped the Container

Scaling content on drag down in Flutter

I want to achieve the following behavior in Flutter.
Do you know if there is a built-in widget in Flutter that provides that functionality out of the box?
try this CustomScrollView:
LayoutBuilder(
builder: (context, constraints) {
return CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
pinned: true,
delegate: Delegate(),
),
SliverToBoxAdapter(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30)),
border: Border.all(
width: 2.0,
color: Colors.deepPurple,
),
),
height: constraints.biggest.height,
child: Center(
child: Text('Drag me down (or up) in order to see (or hide) the red icon on the top',
textAlign: TextAlign.center,
textScaleFactor: 5.0,
),
),
),
),
],
);
}
),
the "delegate" class is as follows:
class Delegate extends SliverPersistentHeaderDelegate {
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
// print(shrinkOffset);
return Opacity(
opacity: 1 - shrinkOffset / maxExtent,
child: FittedBox(child: Icon(Icons.alarm, color: Colors.red,), fit: BoxFit.contain),
);
}
#override double get maxExtent => 300;
#override double get minExtent => 100;
#override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
now try to drag the text up and down
EDIT: and this is a little modification to be similar like your example:
LayoutBuilder(
builder: (context, constraints) {
return CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
pinned: true,
delegate: Delegate(),
),
SliverToBoxAdapter(
child: Container(
padding: EdgeInsets.only(top: 75),
height: constraints.biggest.height,
child: Text('Drag me down (or up) in order to make the red icon bigger (or smaller)',
textAlign: TextAlign.center,
textScaleFactor: 5.0,
),
),
),
],
);
}
),
and the modified delegate class:
class Delegate extends SliverPersistentHeaderDelegate {
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
// print(shrinkOffset);
return OverflowBox(
maxHeight: 400,
alignment: Alignment.topCenter,
child: Container(
height: 400 - shrinkOffset,
child: FittedBox(child: Icon(Icons.alarm, color: Colors.red,), fit: BoxFit.contain),
),
);
}
#override double get maxExtent => 300;
#override double get minExtent => 1;
#override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
SliverAppBar now provides this feature out of the box using the stretch property:
SliverAppBar(
stretch: true, // <- this
flexibleSpace: FlexibleSpaceBar(
background: Image.network('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg', fit: BoxFit.cover),
title: Text('Hello again, SliverAppBar'),
stretchModes: <StretchMode>[
StretchMode.zoomBackground, // <- and this
],
),
);
Ref:
DartPad Example
Youtube Video

Flutter CustomScrollView scrolls when there are not enough items

Having an issue with scrolling when using slivers and a sliver appbar. How can I prevent the scrollview from scrolling when no scroll is needed like in the video. And if there are enough items for scrolling it should scroll (That works perfectly)
I followed this medium post. And you can see that he has the same problem.
https://medium.com/#diegoveloper/flutter-collapsing-toolbar-sliver-app-bar-14b858e87abe
https://youtu.be/l1EwM9GAfxw
class HomeScreen extends StatelessWidget{
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverAppBar(
brightness: Brightness.dark,
backgroundColor: Colors.amber.withOpacity(0.5),
expandedHeight: 166,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 80,
width: 80,
child: Placeholder(),
),
Row(
children: [
Text(
'sdalkf',
),
],
),
],
),
),
),
),
SliverPersistentHeader(
delegate: SliverAppBarDelegate(
MediaQuery.of(context).padding.top,
Container(
color: Colors.amber.withOpacity(0.5),
child: SafeArea(
bottom: false,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('sdfaklladsjfkladslkf\nsadjflkasjklfs\nsdkjlfjlkadslfjk'),
],
),
),
),
),
pinned: true,
),
],
body: GridView.builder(
itemCount: 3,
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 0.68),
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Container(
color: Colors.black54,
),
),
),
),
);
}
}
class SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final Widget child;
final double topSafeArea;
_SliverAppBarDelegate(this.topSafeArea, this.child);
#override
double get minExtent => 105 + topSafeArea;
#override
double get maxExtent => 105 + topSafeArea;
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
child: child,
);
}
#override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return false;
}
}
Related to:
Flutter SliverAppBar with Tabs overlays content
The above link is not a sollution but a temp fix
Set physics behavior of ScrollView to AlwaysScrollableScrollPhysics
from documentation:
/// Scroll physics that always lets the user scroll.
///
/// This overrides the default behavior which is to disable scrolling
/// when there is no content to scroll. It does not override the
/// handling of overscrolling.