在 flutter 中为 SliverGrid 背景添加边框半径
Add a border radius to SliverGrid background in flutter
我正在构建一个带有 flutter 的应用程序,但在使用 CustomScrollView
中的 SliverGrid
构建项目网格时卡住了,我无法在其背景中添加 border-radius .请注意,我可以为单个网格项添加半径。
这是我试过的
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,
),
),
],
),
);
下图是我用上面的代码得到的。
而我现在需要的是在橙色部分的左上角和右上角添加一个圆形的border-radius。
您可以向 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,
),
),
],
),
);
}
如果您想要相反的方式,也许您应该创建自定义 ShapeBorder 并覆盖 getOuterPath 以超出形状并使其看起来像橙色的一面是具有形状的一面。让我知道您是否希望以这种方式尝试更新答案
更新
我相信你正在寻找这样的东西
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);
}
我所做的是创建一个基于 ContinuousRectangleBorder 的自定义 ShapeBorder,但将 getOuterPath 和 getInnerPath 更改为常量值以使其看起来像这样(这是为了示例,如果您想要自定义 class可以在不止一种情况下使用,可能会在构造函数中更改一些其他值。
我仍然在 SliverAppBar 中使用,因为这是允许我使用 shape 属性更改形状的小部件,但是使用 getOuterPath 我绘制了从小部件的最大高度到 maxHeight - 16 的曲线(只是一个我想出的随机数,就像我添加 BorderRadius.vertical(bottom: Radius.circular(16))
时的前一个例子)。如果您没有 Sliver AppBar 而不是 Scaffold 中的 AppBar,您可以将 CustomScrollView 包装在没有边距且形状为 RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)))
的 Card 中以获得类似的效果
更新包
将 flutter_group_sliver: ^0.0.2
添加到您的 pubspec.yaml 依赖项中
dependencies:
flutter:
sdk: flutter
flutter_group_sliver: ^0.0.2
将其导入您的项目并在 CustomScrollView 中使用新的 class SliverGroupBuilder(),它基本上是一个制成 Sliver 的容器,因此您可以在 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,
),
),
)
],
),
)
Scaffold 背景为粉红色,SliverGroupBuilder 为橙色,带有 BorderRadius.vertical(top: Radius.circular(16))
、
已关闭 SliverAppBar
这种方法可以满足您的需求,但您必须注意脚手架背景的颜色,使其与众不同,这样您才能看到边框半径
查看 https://pub.dev/packages/flutter_group_sliver#-readme-tab- 了解更多包裹信息
这是我的方法
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,
),
),
),
),
],
),
),
),
)
)
我正在构建一个带有 flutter 的应用程序,但在使用 CustomScrollView
中的 SliverGrid
构建项目网格时卡住了,我无法在其背景中添加 border-radius .请注意,我可以为单个网格项添加半径。
这是我试过的
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,
),
),
],
),
);
下图是我用上面的代码得到的。 而我现在需要的是在橙色部分的左上角和右上角添加一个圆形的border-radius。
您可以向 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,
),
),
],
),
);
}
如果您想要相反的方式,也许您应该创建自定义 ShapeBorder 并覆盖 getOuterPath 以超出形状并使其看起来像橙色的一面是具有形状的一面。让我知道您是否希望以这种方式尝试更新答案
更新
我相信你正在寻找这样的东西
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);
}
我所做的是创建一个基于 ContinuousRectangleBorder 的自定义 ShapeBorder,但将 getOuterPath 和 getInnerPath 更改为常量值以使其看起来像这样(这是为了示例,如果您想要自定义 class可以在不止一种情况下使用,可能会在构造函数中更改一些其他值。
我仍然在 SliverAppBar 中使用,因为这是允许我使用 shape 属性更改形状的小部件,但是使用 getOuterPath 我绘制了从小部件的最大高度到 maxHeight - 16 的曲线(只是一个我想出的随机数,就像我添加 BorderRadius.vertical(bottom: Radius.circular(16))
时的前一个例子)。如果您没有 Sliver AppBar 而不是 Scaffold 中的 AppBar,您可以将 CustomScrollView 包装在没有边距且形状为 RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)))
的 Card 中以获得类似的效果
更新包
将 flutter_group_sliver: ^0.0.2
添加到您的 pubspec.yaml 依赖项中
dependencies:
flutter:
sdk: flutter
flutter_group_sliver: ^0.0.2
将其导入您的项目并在 CustomScrollView 中使用新的 class SliverGroupBuilder(),它基本上是一个制成 Sliver 的容器,因此您可以在 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,
),
),
)
],
),
)
Scaffold 背景为粉红色,SliverGroupBuilder 为橙色,带有 BorderRadius.vertical(top: Radius.circular(16))
、
已关闭 SliverAppBar
这种方法可以满足您的需求,但您必须注意脚手架背景的颜色,使其与众不同,这样您才能看到边框半径
查看 https://pub.dev/packages/flutter_group_sliver#-readme-tab- 了解更多包裹信息
这是我的方法
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,
),
),
),
),
],
),
),
),
)
)