Gradient in SliverAppBar (Flutter)? - flutter

Has anyone used Gradient in SliverAppBar? I can do it in FlexibleSpace when it's expanded, but when it's collapsed it gets a solid color. Is it possible to treat this?

Wrap a new Container Widget in FlexibleSpaceBar, then add BoxDecoration, LinearGradient under the container.

Unless I'm missing something, this should do what you're asking.
Before it's scrolled it looks like this:
And after scrolling like this:
import 'package:flutter/material.dart';
void main() => runApp(GradientAppBar());
class GradientAppBar extends StatefulWidget {
#override
_GradientAppBarState createState() => _GradientAppBarState();
}
class _GradientAppBarState extends State<GradientAppBar> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: 100,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black,
Colors.white,
],
),
),
),
title: Text("This app bar has a gradient!"),
),
SliverList(
delegate: SliverChildListDelegate(
[
Container(
color: Colors.blue,
height: 500,
),
Divider(),
Container(
color: Colors.black12,
height: 500,
),
Divider(),
Container(
color: Colors.lightBlue,
height: 500,
),
Divider(),
Container(
color: Colors.lightGreen,
height: 500,
),
Divider(),
],
),
),
],
),
),
);
}
}

show this from Flutter documention:
CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('Demo'),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('grid item $index'),
);
},
childCount: 20,
),
),
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'),
);
},
),
),
],
)

Related

not overlap SliverToBoxAdapter with a transparent SliverAppBar

Is it possible for a SliverToBoxAdapter to not be overlaped with a transparentSliverAppBar when scrolled upon?
Or give up on using SliverAppBar and just use AppBar instead?
Widget build(BuildContext context) {
return SafeArea(
child: Stack(
children: [
Image(
image: bkgImage,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
alignment: Alignment.centerLeft,
),
Scaffold(
backgroundColor: Colors.transparent,
body: CustomScrollView(
slivers: [
SliverAppBar(
actions: <Widget>[
IconButton(
icon: const Icon(Icons.search),
onPressed: () {},
),
],
pinned: _pinned,
snap: _snap,
floating: _floating,
expandedHeight: 160,
backgroundColor: Colors.transparent,
elevation: 0.0,
flexibleSpace: FlexibleSpaceBar(
expandedTitleScale: 1.6,
titlePadding: EdgeInsets.all(18),
title: Text('Panahon'),
),
),
SliverList(
delegate: SliverChildListDelegate([
SizedBox(
height: 200,
child: Card(),
),
SizedBox(
height: 200,
child: Card(),
),
SizedBox(
height: 200,
child: Card(),
),
SizedBox(
height: 200,
child: Card(),
),
SizedBox(
height: 200,
child: Card(),
),
]),
)
],
),
),
],
),
);
}
The goal is this:
Precious view:
To get actual effect, we can use SliverPersistentHeaderDelegate. Concept is we need to pass image itself [both image must be screen size].
class MySliverHeaderDelegate extends SliverPersistentHeaderDelegate {
final double _maxExtent = 160;
final VoidCallback onActionTap;
final Image imageWidget;
MySliverHeaderDelegate({
required this.onActionTap,
required this.imageWidget,
});
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
debugPrint(shrinkOffset.toString());
return Stack(
children: [
Positioned(
top: 0,
left: 0,
right: 0,
child: imageWidget,
),
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
'Panahon',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Colors.white,
),
),
),
),
// here provide actions
Positioned(
top: 0,
right: 0,
child: IconButton(
icon: const Icon(
Icons.search,
color: Colors.white,
),
onPressed: onActionTap,
),
),
],
);
}
#override
double get maxExtent => _maxExtent;
#override
double get minExtent => kToolbarHeight;
#override
bool shouldRebuild(covariant MySliverHeaderDelegate oldDelegate) {
return oldDelegate != this;
}
}
And use
Widget build(BuildContext context) {
final imageWidget = Image.asset(
"assets/images/image01.png",
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
alignment: Alignment.centerLeft,
);
return SafeArea(
child: Stack(
children: [
imageWidget,
Scaffold(
backgroundColor: Colors.transparent,
body: CustomScrollView(
slivers: [
SliverPersistentHeader(
pinned: true,
delegate: MySliverHeaderDelegate(
imageWidget: imageWidget,
onActionTap: () {
debugPrint("on Tap");
}),
),
//.....
],
),
),
],
),
);
}
Others way to handle this situation
Being SliverAppBar's backgroundColor:Colors.transparent you are able to see though appbar.
If you provide your preferable color, that will fix the issue in this case, but you will not get background effect on appBar.
SliverAppBar(
backgroundColor: Colors.green, // the one you like
For your case, you can use CupertinoSliverNavigationBar instead of using SliverAppBar. It will still block the background on scroll and it won't get that much height.
CupertinoSliverNavigationBar(
backgroundColor: Colors.transparent,
largeTitle: const Align(
alignment: Alignment.bottomLeft,
child: SizedBox(
child: Text('Panahon'),
),
),
trailing: IconButton(
icon: const Icon(Icons.search),
onPressed: () {},
),
),

How to scroll with NestedScrollView to use stack Circle Avatar over SliverAppBar?

I am dealing with a Flutter project recently and I have such a problem.
See photo 1: here's my code: the photo and text should be between the TabBar widget and the red background like in design (blue).
Here you can see my actual code:
class Main
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
PortfolioSliverAppBar(_pages[_tabController.index].item1),
SliverPersistentHeader(
delegate: SliverPersistentHeaderDelegateImpl(
tabBar: TabBar(
padding: EdgeInsets.only(top: 15.0),
labelColor: Colors.black,
indicatorColor: Colors.black,
indicator: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(18)),
color: Colors.blue),
controller: _tabController,
tabs: _pages
.map<Tab>((Tuple3 page) => Tab(text: page.item1))
.toList(),
),
),
),
];
},
body: Container(
margin: EdgeInsets.only(top: 20.0),
child: TabBarView(
controller: _tabController,
children: _pages.map<Widget>((Tuple3 page) => page.item2).toList(),
),
),
class Silver
class SliverPersistentHeaderDelegateImpl extends SliverPersistentHeaderDelegate {
final TabBar tabBar;
final Color color;
const SliverPersistentHeaderDelegateImpl({
Color color = Colors.transparent,
#required this.tabBar,
}) : this.color = color;
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: color,
child: tabBar,
);
}
See photo 2: here's the given design:
my view
the actual UI
Thanks a lot!
Please refer to below code
class NestedScrollWithTabs extends StatefulWidget {
const NestedScrollWithTabs({Key key}) : super(key: key);
#override
_NestedScrollWithTabsState createState() => _NestedScrollWithTabsState();
}
class _NestedScrollWithTabsState extends State<NestedScrollWithTabs>
with TickerProviderStateMixin {
var animation;
var controller;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: DefaultTabController(
length: 2,
child: NestedScrollView(
physics: NeverScrollableScrollPhysics(),
headerSliverBuilder: (headerCtx, innnerBoxIsScrolled) {
if (innnerBoxIsScrolled) {
/* Animation */
controller = AnimationController(
vsync: this,
duration: Duration(
seconds: 1,
),
);
animation = Tween(
begin: 0.0,
end: 1.0,
).animate(controller);
/* Animation */
controller.forward();
}
return <Widget>[
SliverAppBar(
expandedHeight: ScreenUtil().setHeight(185.0),
floating: false,
pinned: true,
backgroundColor: Colors.white,
automaticallyImplyLeading: false,
titleSpacing: 0.0,
centerTitle: true,
elevation: 0.0,
leadingWidth: 0.0,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
if (innnerBoxIsScrolled != null &&
innnerBoxIsScrolled == true)
FadeTransition(
opacity: animation,
child: Text(
"Title",
style: TextStyle(
color: Colors.black,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
),
],
),
flexibleSpace: FlexibleSpaceBar(
background: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
alignment: Alignment.center,
clipBehavior: Clip.none,
children: [
Image.network(
"https://images.pexels.com/photos/10181294/pexels-photo-10181294.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" ??
"",
fit: BoxFit.fitWidth,
height: ScreenUtil().setHeight(126.0),
width: ScreenUtil().screenWidth,
filterQuality: FilterQuality.low,
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent loadingProgress) {
if (loadingProgress == null) return child;
return Container(
height: ScreenUtil().setHeight(126.0),
width: ScreenUtil().screenWidth,
color: Colors.grey,
);
},
errorBuilder: (context, error, stackTrace) {
return SizedBox(
height: ScreenUtil().setHeight(126.0),
width: ScreenUtil().screenWidth,
child: Container(
width: ScreenUtil().screenWidth,
),
);
},
),
Positioned(
top: ScreenUtil().setHeight(92.0),
// left: ScreenUtil().setWidth(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircleAvatar(
backgroundColor: Colors.transparent,
radius: 30.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(
45.0,
),
child: Image.network(
"https://images.pexels.com/photos/10181294/pexels-photo-10181294.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" ??
"",
fit: BoxFit.fill,
height: ScreenUtil().setHeight(72.0),
width: ScreenUtil().screenWidth,
filterQuality: FilterQuality.low,
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent loadingProgress) {
if (loadingProgress == null)
return child;
return Container(
height:
ScreenUtil().setHeight(72.0),
width: ScreenUtil().screenWidth,
color: Colors.grey,
);
},
errorBuilder:
(context, error, stackTrace) {
return SizedBox(
height:
ScreenUtil().setHeight(72.0),
width: ScreenUtil().screenWidth,
child: Container(
width: ScreenUtil().screenWidth,
),
);
},
),
),
),
Text("Name"),
Text("Place"),
],
),
),
],
),
],
),
),
),
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
headerCtx),
sliver: SliverPersistentHeader(
delegate: SliverAppBarDelegate(TabBar(
labelColor: Colors.blue,
unselectedLabelColor: Colors.black,
labelStyle: TextStyle(
fontSize: 15.0,
),
unselectedLabelStyle: TextStyle(
fontSize: 15.0,
),
labelPadding: EdgeInsets.zero,
indicatorColor: Colors.blue,
indicatorPadding: EdgeInsets.zero,
physics: NeverScrollableScrollPhysics(),
tabs: [
Tab(
text: "Tab 1",
),
Tab(
text: "Tab 2",
),
],
)),
pinned: false,
),
),
];
},
body: TabBarView(
children: [
/* Tab 1 */
Container(
color: Colors.white,
child: ListView.builder(
itemCount: 100,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: Text("Index value: $index"),
);
},
),
),
/* Tab 2 */
Container(
color: Colors.white,
child: ListView.builder(
itemCount: 10,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: Text("Index value of Tab 2: $index"),
);
},
),
),
],
),
),
),
),
);
}
}
class SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
SliverAppBarDelegate(this.tabBars);
final TabBar tabBars;
#override
double get minExtent => 60.0;
#override
double get maxExtent => 60.0;
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
// shinkOffsetPerValue.value = shrinkOffset;
return new Container(
color: Colors.white,
child: Column(
children: [
tabBars,
],
),
);
}
#override
bool shouldRebuild(SliverAppBarDelegate oldDelegate) {
return false;
}
}

SliverGrid grid cells are rectangular and not squares and am not sure why

I have a Gridview within a SliverGrid, and am setting width and height to 200 via maxCrossAxisExtent, mainAxisExtent. However, the grids are more rectangles than squares and do not know what I am missing.
return Scaffold(
appBar: AppBar(
title: Text("ListView"),
),
body: Container(
child: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(
[
Container(
height: 100.0,
color: Colors.white,
alignment: Alignment.center,
child: Center(
child: Text('welcome to Inference.link'),
),
),
],
),
),
// here is the grid view
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisExtent: 200.0,
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
childAspectRatio: 1,
),
delegate: SliverChildListDelegate(_buildTiles()),
),
],
),
),
);
}
// making the grid tiles
List<Widget> _buildTiles() {
final List<Widget> tiles = <Widget>[];
for (int i = 0; i < models.length; i++) {
tiles.add(new GridTile(
child: new InkResponse(
enableFeedback: true,
child: Container(
margin: const EdgeInsets.all(4.0),
decoration: BoxDecoration(color: colors[(new Random()).nextInt(3)]),
child: Center(
child: Text(
models[i],
),
),
),
onTap: () => didSelectModel != null ? didSelectModel!(models[i]) : null,
)));
}
return tiles;
}
below are the rectangular grids. any suggestion is appreciated.
because the number of columns is not defined which is supposed to be interpreted as a number of columns in a table which I believe it causes the problem.
so we need to specify width according to screen.
try this it work for me well.
final mediaQuery = MediaQuery.of(context);
return Scaffold(
appBar: AppBar(
title: Text("ListView"),
),
body: Container(
child: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(
[
Container(
height: 100.0,
color: Colors.white,
alignment: Alignment.center,
child: Center(
child: Text('welcome to Inference.link'),
),
),
],
),
),
// here is the grid view
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: mediaQuery.size.width / 3,,
mainAxisExtent: mediaQuery.size.width / 3,
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
childAspectRatio: 1,
),
delegate: SliverChildListDelegate(_buildTiles()),
),
],
),
),
);
}
// making the grid tiles
List<Widget> _buildTiles() {
final List<Widget> tiles = <Widget>[];
for (int i = 0; i < models.length; i++) {
tiles.add(new GridTile(
child: new InkResponse(
enableFeedback: true,
child: Container(
margin: const EdgeInsets.all(4.0),
decoration: BoxDecoration(color: colors[(new Random()).nextInt(3)]),
child: Center(
child: Text(
models[i],
),
),
),
onTap: () => didSelectModel != null ? didSelectModel!(models[i]) : null,
)));
}
return tiles;
}

Add a border radius to SliverGrid background in flutter

I'm building an app with flutter and get stuck while working on building a grid of items using SliverGrid inside CustomScrollView and I'm not able to add border-radius to its background. Note, I can add a radius to the individual grid item.
This is what I tried
Scaffold(
backgroundColor: Colors.orange,
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
floating: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
background: Image.asset(
'assets/images/000.png',
fit: BoxFit.cover,
),
title: Text('Hello there')),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.symmetric(
horizontal: 15,
vertical: 15,
),
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('grid item $index'),
);
},
childCount: 30,
),
),
],
),
);
This picture below is what I got with the above code.
And what I need now is to add a circular border-radius to topLeft and topRight of the orange part.
you could add a shape to the sliverAppBar
return Scaffold(
backgroundColor: Colors.orange,
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
floating: true,
expandedHeight: 250.0,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.vertical(bottom: Radius.circular(16))), //like this
flexibleSpace: FlexibleSpaceBar(
background: Container(color: Colors.blueAccent), //changed it because I dont have the image asset
title: Text('Hello there')),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.symmetric(
horizontal: 15,
vertical: 15,
),
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('grid item $index'),
);
},
childCount: 100,
),
),
],
),
);
}
If you want it the other way around maybe you should create a custom ShapeBorder and override the getOuterPath to get outside of the shape and make it look like the orange side is the one with the shape. Let me know if you wanted it that way to try and update the answer
UPDATE
I believe you are looking for something like this
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.orange,
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
floating: true,
expandedHeight: 250.0,
shape: _CustomShape(), //like this
flexibleSpace: FlexibleSpaceBar(
background: Image.network(
"https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&h=350",
fit: BoxFit.cover,
),
title: Text('Hello there'),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.symmetric(
horizontal: 15,
vertical: 15,
),
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('grid item $index'),
);
},
childCount: 100,
),
),
],
),
);
}
}
class _CustomShape extends ShapeBorder {
const _CustomShape({
this.side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
}) : assert(side != null),
assert(borderRadius != null);
final BorderRadiusGeometry borderRadius;
/// The style of this border.
final BorderSide side;
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.all(side.width);
#override
ShapeBorder scale(double t) {
return _CustomShape(
side: side.scale(t),
borderRadius: borderRadius * t,
);
}
#override
ShapeBorder lerpFrom(ShapeBorder a, double t) {
assert(t != null);
if (a is ContinuousRectangleBorder) {
return ContinuousRectangleBorder(
side: BorderSide.lerp(a.side, side, t),
borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t),
);
}
return super.lerpFrom(a, t);
}
#override
ShapeBorder lerpTo(ShapeBorder b, double t) {
assert(t != null);
if (b is ContinuousRectangleBorder) {
return ContinuousRectangleBorder(
side: BorderSide.lerp(side, b.side, t),
borderRadius: BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t),
);
}
return super.lerpTo(b, t);
}
#override
Path getInnerPath(Rect rect, { TextDirection textDirection }) {
double length = 16;
return Path()
..lineTo(0, rect.height - length)
..lineTo(rect.width, rect.height - length)
..lineTo(rect.width, 0)
..close();
}
#override
Path getOuterPath(rect, {TextDirection textDirection}) {
double length = 16; //its just a random number I came up with to test the border
return Path()
..lineTo(0, rect.height)
..quadraticBezierTo(length / 4, rect.height - length, length, rect.height - length)
..lineTo(rect.width - length, rect.height - length)
..quadraticBezierTo(rect.width - (length / 4), rect.height - length, rect.width, rect.height)
..lineTo(rect.width, 0)
..close();
}
#override
void paint(Canvas canvas, Rect rect, { TextDirection textDirection }) {
if (rect.isEmpty)
return;
switch (side.style) {
case BorderStyle.none:
break;
case BorderStyle.solid:
final Path path = getOuterPath(rect, textDirection: textDirection);
final Paint paint = side.toPaint();
canvas.drawPath(path, paint);
break;
}
}
#override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ContinuousRectangleBorder
&& other.side == side
&& other.borderRadius == borderRadius;
}
#override
int get hashCode => hashValues(side, borderRadius);
}
What I did was create a custom ShapeBorder based on ContinuousRectangleBorder but changing the getOuterPath and getInnerPath to a constant value to make it look like this (this was for the example, if you want a custom class that can use in more than one situation maybe changing some other values in the constructor).
I still used in the SliverAppBar because thats the widget that allows me to change the shape with the shape attribute, but with the getOuterPath I painted the curves going from the max height of the widget to maxHeight - 16 (just a random number I came up, its like the former example when I added BorderRadius.vertical(bottom: Radius.circular(16))). If you didn't have the Sliver AppBar and instead an AppBar in the Scaffold you could just wrap the CustomScrollView in a Card with no margin and a shape of RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16))) for a similar effect
UPDATE WITH PACKAGE
add flutter_group_sliver: ^0.0.2 to your pubspec.yaml dependencies
dependencies:
flutter:
sdk: flutter
flutter_group_sliver: ^0.0.2
import it to your project and use the new class SliverGroupBuilder() inside CustomScrollView, it's basically a Container made into a Sliver so you can use the margin, decoration, padding option inside a Sliver
import 'package:flutter_group_sliver/flutter_group_sliver.dart';
Scaffold(
backgroundColor: Colors.pink[100],
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
floating: true,
expandedHeight: 250.0,
elevation: 0.0,
forceElevated: false,
flexibleSpace: FlexibleSpaceBar(
background: Image.network(
"https://happypixelmedia.com/wp-content/uploads/2019/10/Web-design-ideas-to-look-forward-to-in-2020-cover-1.jpg",
fit: BoxFit.cover,
),
title: Text('Hello there'),
),
),
SliverGroupBuilder(
margin: EdgeInsets.zero,
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.symmetric(
horizontal: 15,
vertical: 15,
),
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('grid item $index'),
);
},
childCount: 100,
),
),
)
],
),
)
The Scaffold background is pink and the SliverGroupBuilder is orange with a BorderRadius.vertical(top: Radius.circular(16)),
Closed SliverAppBar
This approach gives you what you want, but you have to be careful with the color of the Scaffold backgound to make it different so you can see the border radius
check https://pub.dev/packages/flutter_group_sliver#-readme-tab- for more info of the package
This is my approach
SliverAppBar(
elevation: 0,//remove elevetion
backgroundColor: Colors.white, // match the color of sliver grid/list
leading: SizedBox(), // // hide arrow icon
leadingWidth: 0.0, // hide arrow icon
expandedHeight: 200,
stretch: true,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin, // to make radius remain if scrolled
title: _myTitle,
titlePadding: EdgeInsets.all(30),
centerTitle: true,
stretchModes: [
StretchMode.zoomBackground, // zoom effect
StretchMode.fadeTitle, // fade effect
],
background: Container(
color: Colors.white,
child: Stack(
fit: StackFit.expand, // expand stack
children: [
ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.5),
BlendMode.srcOver,
),
child: Container(
child: Image.network(
"$imageLink",
fit: BoxFit.cover,
),
),
),
Positioned(
child: Container(
height: 30,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(50),
),
border: Border.all(
color: Colors.white,
width: 0,
)),
),
bottom: -1,
left: 0,
right: 0,
),
Positioned(
bottom: 0, // to bottom
right: 45, // to right 45
child: ClipRRect(
borderRadius: BorderRadius.circular(120),
child: Container(
color: darkBlue,
width: 60,
height: 60,
child: Icon(
LineIcons.store,
size: 26,
color: Colors.white,
),
),
),
),
],
),
),
),
)
)

how to corner radius on SliverGrid / SliverList on Flutter

Is it possible to borderRadius to SliverList / SliverGrid? If yes, how to radius topRight and topLeft
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
),
),
SliverGrid(
gridDelegate:
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
color: Colors.teal[100 * (index % 9)],
alignment: Alignment.center,
child: Text('grid item $index'),
);
},
childCount: 120,
),
),
],
),
You might not be able to make a SliverList rounded (even in some part), but you can tweak your way through by putting the children of the list in a Column and wrapping the Column in a Container then setting the borderRadius of the Container.
And the Scrolling still works perfectly as a normal CustomScrollView
The whole idea is to make the Column work for the SliverList. So you have to set childCount to 1 and allow the Column handle everything about the children
You can try the same for SliverGrid
I wrote an example with SliverList
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text("Hey"),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, __){
return Container(
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10))
),
child: Column(
children: List.generate(10, (index) => Container(child: ListTile(title: Text("$index nothing")))),
),
);
},
childCount: 1
),
)
],
),
);
}
}
I hope this helps you