Add a border radius to SliverGrid background in flutter - 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,
),
),
),
),
],
),
),
),
)
)

Related

Is there any way to make the first element standing alone in Gridview.builder()?

I've been trying to make the first item look like this. And make them all scrollable.
But only the items which are located in two groups are scrolling. The image at the top stays where it is. I want it scrolls too. Anyone can help?
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomBar(),
body: Column(
children: [
Container(
width: 550,
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
// color: Colors.amber,
image: const DecorationImage(
fit: BoxFit.cover,
// I want his part to be scrolled.
image: AssetImage("assetss/fortuneteller.jpg"))),
),
SizedBox(
height: 30,
),
Padding(
padding: const EdgeInsets.only(
left: 15,
right: 15,
),
This is the part where items that are able to scroll are located.
child: Container(
width: screenWidth / 1,
height: screenlHeight / 1.6,
child: GridView.builder(
itemBuilder: (BuildContext context, int index) {
return CardForFortuneTellers();
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
mainAxisExtent: 500,
mainAxisSpacing: 0, //screenlHeight / 20,
crossAxisCount: 2,
),
itemCount: 10,
),
),
)
],
),
);
}
}

how to Make whole page scrollable instead to GridView Builder in flutter?

I am new to flutter and creating a screen with following code:-
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wallpaper/ui/widgets/card_wallpaper.dart';
import '../../providers/anime_provider.dart';
import '../../models/wallpaper.dart';
import '../../providers/wallpaper_provider.dart';
class AnimeDetail extends StatelessWidget {
#override
Widget build(BuildContext context) {
final String id = ModalRoute.of(context).settings.arguments;
final selectedAnime = Provider.of<AnimeProvider>(context).findById(id);
final selectedWallPaper =Provider.of<WallpaperProvider>(context).getByAnime(id);
final appBar = AppBar(
leading: BackButton(
color: Theme.of(context).primaryColor,
),
elevation: 0,
backgroundColor: Colors.transparent,
title: Text(
selectedAnime.title,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontFamily: 'Righteous',
),
),
);
final mediaQuery = MediaQuery.of(context);
final double totalHeight = mediaQuery.size.height -appBar.preferredSize.height -mediaQuery.padding.top -335;
return Scaffold(
appBar: appBar,
body: SingleChildScrollView(
child: Column(
children: [
Container(
width: double.infinity,
height: 300,
child: Card(
elevation: 3.1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: Stack(
children: [
Container(
height: 300,
width: double.infinity,
child: ClipRRect(
child: Image.asset(
selectedAnime.imageUrl,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
),
),
],
),
),
),
SizedBox(height: 10),
Row(
children: [
Container(
height: 25,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 0),
child: Text(
'WallPapers from ${selectedAnime.title}',
style: TextStyle(
fontFamily: 'Righteous',
fontSize: 16,
color: Theme.of(context).primaryColor,
),
),
),
),
],
),
Container(
width: double.infinity,
height: totalHeight,
child: GridView.builder(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.5,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: selectedWallPaper[i],
child: CardWallpaper(),
),
padding: const EdgeInsets.all(8.0),
itemCount: selectedWallPaper.length,
),
),
],
),
));
}
}
When I run this app, the gridview is scrollable but the image at the top of it does not scroll,but, I want to scroll whole page but only the grid scrolls even though I am using singlechildscrollview. I tried using expanded on gridview builder but it produces error.How can I make whole page scroll instead of just gridview.builder.
![See the screen here]:https://i.stack.imgur.com/ClDZy.jpg
As you can see when you scroll the page only gridview gets scrolled while top image remains there. I want to scroll the page as whole. Is there any way to determine the height of gridtile?
In your gridview, set the parameter scrollable: NeverScrollablePhysics().

Get the current sliver that is visible/scrolled inside a Flutter CustomScrollView

How do I get the current sliver item that is visible/scrolled inside a Flutter CustomScrollView?
My CustomScrollView has multiple SliverGrid and SliverToBoxAdapter widgets inside. I would like to know which `SliverGrid' is currently visible (scrolled to).
child: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(),
SliverGrid(),
SliverToBoxAdapter(),
SliverGrid(),
...
],
)
I would also like to know the scroll position inside the currently visible SliverGrid.
you can try this to get the current visible widget, inview_notifier_list:
import 'package:flutter/material.dart';
import 'package:inview_notifier_list/inview_notifier_list.dart';
import 'box.dart';
class CSVExample extends StatelessWidget {
final ScrollController? scrollController;
final IsInViewPortCondition? inViewPortCondition;
const CSVExample({Key? key, this.scrollController, this.inViewPortCondition})
: super(key: key);
#override
Widget build(BuildContext context) {
final IsInViewPortCondition condition = inViewPortCondition ??
(double deltaTop, double deltaBottom, double vpHeight) {
return deltaTop < (0.5 * vpHeight) && deltaBottom > (0.5 * vpHeight);
};
return Stack(
fit: StackFit.expand,
children: <Widget>[
InViewNotifierCustomScrollView(
initialInViewIds: ['grid3', 'grid4', 'grid5'],
isInViewPortCondition: condition,
contextCacheCount: 35,
controller: scrollController,
slivers: <Widget>[
SliverPadding(
padding: EdgeInsets.only(top: 50.0, bottom: 50.0),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 1.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Box(
id: 'grid$index',
height: 200.0,
);
},
childCount: 20,
),
),
),
SliverPadding(
padding: EdgeInsets.only(top: 50.0, bottom: 50.0),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
width: double.infinity,
height: 300.0,
color: Colors.blueGrey,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(vertical: 50.0),
child: Box(id: 'list$index'),
);
},
childCount: 3,
),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 1.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Box(
id: '2nd-grid$index',
height: 200.0,
);
},
childCount: 10,
),
),
],
),
IgnorePointer(
ignoring: true,
child: Align(
alignment: Alignment.center,
child: Container(
// height: 200.0,
// color: Colors.redAccent.withOpacity(0.2),
height: 1.0,
color: Colors.redAccent,
),
),
),
],
);
}
}

How to Give BorderRadius to SliverList

I am using SliverAppBar and SliverListView in my project.
I need BorderRadius to my SliverList that is coming bottom of my SliverAppBar.
Here is screenshot what I need :
And here is my code:
Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
backgroundColor: Colors.transparent,
brightness: Brightness.dark,
actions: <Widget>[
IconButton(icon: Icon(Icons.favorite), onPressed: () {}),
IconButton(icon: Icon(Icons.share), onPressed: () {})
],
floating: false,
pinned: false,
//title: Text("Flexible space title"),
expandedHeight: getHeight(context) - MediaQuery.of(context).padding.top,
flexibleSpace: Container(
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage("assets/images/Rectangle-image.png")
)
),
),
bottom: _bottomWidget(context),
),
SliverList(
delegate: SliverChildListDelegate(listview),
),
],
),
)
So, with this code the UI is coming like this...
can suggest any other approach that i can take to achieve this kind of design...
I achieved this design using SliverToBoxAdapter my code like this.
final sliver = CustomScrollView(
slivers: <Widget>[
SliverAppBar(),
SliverToBoxAdapter(
child: Container(
color: Color(0xff5c63f1),
height: 20,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
height: 20,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(20.0),
topRight: const Radius.circular(20.0),
),
),
),
],
),
),
),
SliverList(),
],
);
I used 2 containers inside SliverToBoxAdapter.
SliverToBoxAdapter is between the Sliver Appbar and the Sliver List.
first I create a blue (should be Appbar color) container for the corner edge.
then I create the same height white container with border-radius inside the blue container for list view.
Preview on dartpad
Solution
At the time of writing, there is no widget that would support this functionality. The way to do it is with Stack widget and with your own SliveWidget
Before:
Here is your default code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flexible space title',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
backgroundColor: Colors.transparent,
brightness: Brightness.dark,
actions: <Widget>[IconButton(icon: Icon(Icons.favorite), onPressed: () {}), IconButton(icon: Icon(Icons.share), onPressed: () {})],
floating: false,
pinned: false,
expandedHeight: 250 - MediaQuery.of(context).padding.top,
flexibleSpace: Container(
height: 550,
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'https://images.unsplash.com/photo-1561752888-21eb3b67eb4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=967&q=80'))),
),
//bottom: _bottomWidget(context),
),
SliverList(
delegate: SliverChildListDelegate(_listview(50)),
),
],
),
),
);
}
}
List _listview(int count) {
List<Widget> listItems = List();
listItems.add(Container(
color: Colors.black,
height: 50,
child: TabBar(
tabs: [FlutterLogo(), FlutterLogo()],
),
));
for (int i = 0; i < count; i++) {
listItems.add(new Padding(padding: new EdgeInsets.all(20.0), child: new Text('Item ${i.toString()}', style: new TextStyle(fontSize: 25.0))));
}
return listItems;
}
After
And here is your code done with Stack and SliveWidget widgets:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flexible space title',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
body: Stack(
children: [
Container(
height: 550,
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'https://images.unsplash.com/photo-1561752888-21eb3b67eb4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=967&q=80'))),
),
CustomScrollView(
anchor: 0.4,
slivers: <Widget>[
SliverWidget(
child: Container(
width: double.infinity,
height: 100,
decoration: BoxDecoration(
color: Colors.yellow, borderRadius: BorderRadius.only(topLeft: Radius.circular(30), topRight: Radius.circular(30))),
child: TabBar(
tabs: [FlutterLogo(), FlutterLogo()],
),
),
),
SliverList(
delegate: SliverChildListDelegate(_listview(50)),
),
],
),
],
),
),
);
}
}
List _listview(int count) {
List<Widget> listItems = List();
for (int i = 0; i < count; i++) {
listItems.add(
Container( //NOTE: workaround to prevent antialiasing according to: https://github.com/flutter/flutter/issues/25009
decoration: BoxDecoration(
color: Colors.white, //the color of the main container
border: Border.all(
//apply border to only that side where the line is appearing i.e. top | bottom | right | left.
width: 2.0, //depends on the width of the unintended line
color: Colors.white)),
child: Container(
padding: EdgeInsets.all(20),
color: Colors.white,
child: new Text(
'Item ${i.toString()}',
style: new TextStyle(fontSize: 25.0),
),
),
),
);
}
return listItems;
}
class SliverWidget extends SingleChildRenderObjectWidget {
SliverWidget({Widget child, Key key}) : super(child: child, key: key);
#override
RenderObject createRenderObject(BuildContext context) {
// TODO: implement createRenderObject
return RenderSliverWidget();
}
}
class RenderSliverWidget extends RenderSliverToBoxAdapter {
RenderSliverWidget({
RenderBox child,
}) : super(child: child);
#override
void performResize() {}
#override
void performLayout() {
if (child == null) {
geometry = SliverGeometry.zero;
return;
}
final SliverConstraints constraints = this.constraints;
child.layout(constraints.asBoxConstraints(/* crossAxisExtent: double.infinity */), parentUsesSize: true);
double childExtent;
switch (constraints.axis) {
case Axis.horizontal:
childExtent = child.size.width;
break;
case Axis.vertical:
childExtent = child.size.height;
break;
}
assert(childExtent != null);
final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: childExtent);
final double cacheExtent = calculateCacheOffset(constraints, from: 0.0, to: childExtent);
assert(paintedChildSize.isFinite);
assert(paintedChildSize >= 0.0);
geometry = SliverGeometry(
scrollExtent: childExtent,
paintExtent: 100,
paintOrigin: constraints.scrollOffset,
cacheExtent: cacheExtent,
maxPaintExtent: childExtent,
hitTestExtent: paintedChildSize,
);
setChildParentData(child, constraints, geometry);
}
}
Use Stack. It's the best and smooth way I found and used.
Preview
import 'dart:math';
import 'package:agro_prep/views/structure/constant.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CropDetailsPage extends StatelessWidget {
const CropDetailsPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
backgroundColor: Colors.white,
actions: <Widget>[
IconButton(icon: Icon(Icons.share), onPressed: () {})
],
floating: false,
pinned: false,
//title: Text("Flexible space title"),
expandedHeight: 281.h,
flexibleSpace: Stack(
children: [
const Positioned.fill(
child: FadeInImage(
image: NetworkImage(tempImage),
placeholder: const NetworkImage(tempImage),
// imageErrorBuilder: (context, error, stackTrace) {
// return Image.asset('assets/images/background.jpg',
// fit: BoxFit.cover);
// },
fit: BoxFit.cover,
),
),
Positioned(
child: Container(
height: 33.h,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(40),
),
),
),
bottom: -7,
left: 0,
right: 0,
)
],
),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return ListTile(
tileColor: whiteColor,
title: Text(Random().nextInt(100).toString()),
);
}, childCount: 15))
],
),
);
}
}
Worked for me!
SliverAppBar(
pinned: true,
floating: false,
centerTitle: true,
title: TextWidget(detail.title,
weight: FontWeight.bold
),
expandedHeight: MediaQuery.of(context).size.height/2.5,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
collapseMode: CollapseMode.parallax,
background: Stack(
children: [
// Carousel images
Swiper(
itemWidth: MediaQuery.of(context).size.width,
itemHeight: MediaQuery.of(context).size.height /3.5,
itemCount: 2,
pagination: SwiperPagination.dots,
loop: detail.banners.length > 1,
itemBuilder: (BuildContext context, int index) {
return Image.network(
'https://image.com?image=123.png',
fit: BoxFit.cover
);
}
),
//Border radius
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.transparent,
height: 20,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
height: 10,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(10),
topRight: const Radius.circular(10),
),
),
),
],
),
),
)
],
),
),
)
So the best way to achieve your result is to use "bottom" poperty inside SliverAppBar. This will add your rounded container to bottom of appbar / start of sliverlist
bottom: PreferredSize(
preferredSize: const Size.fromHeight(24),
child: Container(
width: double.infinity,
decoration: const BoxDecoration(
borderRadius: BorderRadius.vertical(
top: Radius.circular(12),
),
color: Colors.white,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(2),
),
),
),
],
),
),
),
The idea is good but it looks odd in some cases.
You could give a borderRadius to your first element in your list
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(index == 0 ? 15 : 0),
topLeft: Radius.circular(index == 0 ? 15 : 0),
),
),
)
Hope this helps someone
Try This, It's a Simple Solution
import 'package:flutter/material.dart';
class SliveR extends StatelessWidget {
const SliveR({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
SizedBox(
width: double.infinity,
child: Image.network(
'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2700&q=80',
fit: BoxFit.cover,
height: MediaQuery.of(context).size.height * 0.35,
),
),
Align(
alignment: Alignment.topCenter,
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30)),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(30)),
child: CustomScrollView(
anchor: 0.3,
slivers: [
SliverToBoxAdapter(
child: Container(
height: 900,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(40.0),
topRight: Radius.circular(40.0),
),
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: Offset(0.0, 1.0), //(x,y)
blurRadius: 16.0,
),
],
),
child: const Center(
child: Text(
'Hello',
style: TextStyle(color: Colors.grey),
),
),
),
)
],
),
),
),
),
],
),
);
}
}
FlexibleSpaceBar(
title: CustomText(
text: "Renaissance Concourse Hotel",
textSize: kSubtitle3FontSize,
fontWeight: kBold),
centerTitle: true,
collapseMode: CollapseMode.pin,
background: Stack(
children: [
CachedNetworkImage(
imageUrl:
"url",
width: DeviceUtils.getWidth(context),
fit: BoxFit.cover,
placeholder: (context, url) => const Center(
child: CircularProgressIndicator(),
),
errorWidget: (context, url, error) =>
const Icon(Icons.error_rounded),
),
Positioned(
bottom: 50,
right: 0,
left: 0,
child: ContainerPlus(
color: kWhiteColor,
child: const SizedBox(
height: 20,
),
radius: RadiusPlus.only(
topLeft: kBorderRadiusValue10,
topRight: kBorderRadiusValue10,
),
),
)
],
))

Gradient in SliverAppBar (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'),
);
},
),
),
],
)