Reputation: 487
I want to create a top app bar similar to the one in the Apple News app.
It's a bit difficult to see the blur in the screenshot because the bar is so thin, but you get the idea.
I want the bar to expand and contract by scrolling and be pinned at the top when contracted, just like in the screenshot, the SliverAppBar does all that, except that I can't wrap it in ClipRect, BackdropFilter and Opacity to create the frosted glass effect because CustomScrollView only takes RenderSliver child classes.
My test code:
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text('SliverAppBar'),
elevation: 0,
floating: true,
pinned: true,
backgroundColor: Colors.grey[50],
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
background: Image.network("https://i.imgur.com/cFzxleh.jpg", fit: BoxFit.cover)
),
)
,
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,
child: Image.network("https://i.imgur.com/cFzxleh.jpg", fit: BoxFit.cover)
),
],
),
),
],
);
}
Is there a way to achieve what I want?
Upvotes: 3
Views: 4099
Reputation: 487
I managed to get it working by wrapping an AppBar
inside of a SliverPersistentHeader
(this is basically what SliverAppBar
does).
Ignore the un-blurred edges it's an iOS simulator bug.
Here is a proof of concept code example:
class TranslucentSliverAppBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverPersistentHeader(
floating: true,
pinned: true,
delegate: _TranslucentSliverAppBarDelegate(
MediaQuery.of(context).padding,
)
);
}
}
class _TranslucentSliverAppBarDelegate extends SliverPersistentHeaderDelegate {
/// This is required to calculate the height of the bar
final EdgeInsets safeAreaPadding;
_TranslucentSliverAppBarDelegate(this.safeAreaPadding);
@override
double get minExtent => safeAreaPadding.top;
@override
double get maxExtent => minExtent + kToolbarHeight;
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return ClipRect(child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 16, sigmaY: 16),
child: Opacity(
opacity: 0.93,
child: Container(
// Don't wrap this in any SafeArea widgets, use padding instead
padding: EdgeInsets.only(top: safeAreaPadding.top),
height: maxExtent,
color: Colors.white,
// Use Stack and Positioned to create the toolbar slide up effect when scrolled up
child: Stack(
overflow: Overflow.clip,
children: <Widget>[
Positioned(
bottom: 0, left: 0, right: 0,
child: AppBar(
primary: false,
elevation: 0,
backgroundColor: Colors.transparent,
title: Text("Translucent App Bar"),
),
)
],
)
)
)
));
}
@override
bool shouldRebuild(_TranslucentSliverAppBarDelegate old) {
return maxExtent != old.maxExtent || minExtent != old.minExtent ||
safeAreaPadding != old.safeAreaPadding;
}
}
Upvotes: 6