Ruudy
Ruudy

Reputation: 271

Flutter shadow over other widgets

My base widget is a Column. The first element is a Container which has a BoxShadow. The second element is a ListView which builds several Card, depending on the context. If the scroll is 0 the shadow gets displayed. However when start scrolling, the card is "over" the shadow (higher z-index) and hides it.

unscrolled

Shadow is visible when not scrolling

scrolled enter image description here

The shadow should stay always on top, over the Cards. How is this done?

Upvotes: 9

Views: 3075

Answers (3)

Vettiyanakan
Vettiyanakan

Reputation: 8500

EDIT

if you don't want container shadow to disappear on scroll remove the ScrollNotification and NotificationListener

There is a widget for that 😉

You can use ScrollNotification with NotificationListener. Try this;

Happy coding! 🤓

class TestPage extends StatefulWidget {
  const TestPage({Key key}) : super(key: key);

  @override
  _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  double blurRadius = 10.0;
  double spreadRadius = 1.0;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.blueGrey,
        title: Text('Title'),
      ),
      body: Container(
        width: Get.width,
        height: Get.height,
        child: Stack(
          children: [
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16.0).copyWith(
                top: 62.0,
              ),
              child: NotificationListener<ScrollNotification>(
                // ignore: missing_return
                onNotification: (scrollNotification) {
                  if (scrollNotification is ScrollStartNotification) {
                    setState(() {
                      blurRadius = 0.0;
                      spreadRadius = 0.0;
                    });
                  } else if (scrollNotification is ScrollEndNotification) {
                    setState(() {
                      blurRadius = 10.0;
                      spreadRadius = 1.0;
                    });
                  }
                },
                child: ListView.builder(
                  // controller: _controller,
                  itemCount: 10,
                  itemBuilder: (context, index) {
                    return Card(
                      child: Container(
                          width: Get.width * .8,
                          height: 100.0,
                          child: Center(
                            child: Text('child @ index : $index'),
                          )),
                    );
                  },
                ),
              ),
            ),
            Positioned(
              top: 0,
              left: 0,
              right: 0,
              child: Container(
                width: Get.width,
                height: 60.0,
                margin: EdgeInsets.only(),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.only(
                    bottomLeft: Radius.circular(20.0),
                    bottomRight: Radius.circular(20.0),
                  ),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.grey,
                      blurRadius: blurRadius,
                      spreadRadius: spreadRadius,
                    ),
                  ],
                ),
                child: Center(
                  child: Text('TOP CONTAINER'),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Upvotes: 1

Md. Yeasin Sheikh
Md. Yeasin Sheikh

Reputation: 63669

According to your question, here is the concept of what is going wrong here.

The 1st child of Column is a Container with shadow. Shadow renders outside of the defined size. If you don't provide any spaces after this Container we won't be able to see the shadow. This space can be done by Container margin , SizedBox or wrapping our list with Padding. But now our main question is how we get shadow while index=0. I believe it is coming from ListChildren`. They contain upper spaces, that's why we can see only the 1st time.

On Ui render priority starts from bottom to Top.

How to solve this problem: We can provide space at the bottom of our container or assign margin(not padding), or use a SizedBox after the container providing the same amount of height as shadow.

  1. providing bottom margin on the container.
  2. adding a SizedBox with height of shadow.
  3. wrapping our list with Padding and providing top:.

In this image, our shadow: white, background:amber.

enter image description here

Demo code:

import 'package:flutter/material.dart';

class ColumnWithContainerShadow extends StatelessWidget {
  const ColumnWithContainerShadow({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.amber,
      body: Column(
        children: [
          Container(
            height: 50,
            width: double.infinity,
            ////* we dont need margin if we have padding on ListView
            // margin: EdgeInsets.only(bottom: 12),
            decoration: BoxDecoration(
              color: Colors.green,
              boxShadow: [
                BoxShadow(
                  offset: Offset(0, 12),
                  color: Colors.white,
                )
              ],
            ),
            child: Center(child: Text("Container A")),
          ),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.only(top: 12),
              child: ListView(
                children: [
                  ...List.generate(
                    333,
                    (index) => Container(
                      /// enable this margine and remove other spaces to see 1st child shadow.(shadow depend on children position)
                      // margin: EdgeInsets.only(top: 12),
                      height: 60,
                      color: Colors.deepPurple,
                      child: Text("$index"),
                    ),
                  )
                ],
              ),
            ),
          ),
          Container(
            height: 50,
            width: double.infinity,
            alignment: Alignment.center,
            decoration: BoxDecoration(
              color: Colors.green,
              boxShadow: [
                BoxShadow(
                  offset: Offset(0, 12),
                  color: Colors.white,
                )
              ],
            ),
            child: Text("Bottom Container"),
          ),
          // Comment to close shadow
          SizedBox(
            height: 20,
          )
        ],
      ),
    );
  }
}

Upvotes: 1

Rahul
Rahul

Reputation: 5049

Wrap this upper box in Material widget and provide an elevation.

Upvotes: 1

Related Questions