How to Give BorderRadius to SliverList

Viewed 5802

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 :

enter image description here

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...

enter image description here

can suggest any other approach that i can take to achieve this kind of design...

7 Answers

I achieved this design using SliverToBoxAdapter my code like this.

enter image description here

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.

  1. first I create a blue (should be Appbar color) container for the corner edge.
  2. 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:

enter image description here 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

enter image description here

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

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

enter image description here

Related