Добавьте радиус границы к фону SliverGrid во флаттере

Я создаю приложение с флаттером и застреваю при построении сетки элементов, используя SliverGrid внутри CustomScrollViewи я не могу добавить к его фону радиус границы. Обратите внимание: я могу добавить радиус к отдельному элементу сетки.

Это то, что я пробовал

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 к topLeft и topRight оранжевой части.

2 ответа

Решение

Вы можете добавить фигуру к slverAppBar

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);

}

Что я сделал, так это создал настраиваемый ShapeBorder на основе ContinuousRectangleBorder, но изменил getOuterPath и getInnerPath на постоянное значение, чтобы он выглядел так (это было для примера, если вам нужен настраиваемый класс, который можно использовать более чем в одной ситуации, возможно, изменение некоторые другие значения в конструкторе).

Я все еще использовал в SliverAppBar, потому что это виджет, который позволяет мне изменять форму с помощью атрибута shape, но с помощью getOuterPath я нарисовал кривые, идущие от максимальной высоты виджета до maxHeight - 16 (просто случайное число, которое я придумал, это похоже на предыдущий пример, когда я добавил BorderRadius.vertical(bottom: Radius.circular(16))). Если у вас не было Sliver AppBar, а вместо него AppBar в Scaffold, вы могли бы просто обернуть CustomScrollView в Card без полей и в формеRoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16))) для аналогичного эффекта

ОБНОВЛЕНИЕ ПАКЕТОМ

Добавить flutter_group_sliver: ^0.0.2 в зависимости от pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  flutter_group_sliver: ^0.0.2

импортируйте его в свой проект и используйте новый класс SliverGroupBuilder() внутри CustomScrollView, это в основном Контейнер, превращенный в 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 для получения дополнительной информации о пакете

Это мой подход

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

Другие вопросы по тегам