在 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,
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            )
    )