Search code examples
flutterdartscrollcontainersshadow

How to expand a shadow box beyond the container


I am creating a horizontal scroll with cards that cast a shadow.

I do not understand the reason why if I have 3 cards the shadow gets "clipped" but if I have 2 it doesn't.

This is how it should look: The shadow behaves as expected

This is how it looks when I have 3 or more elements (I suspect is more elements than those that fit in the screen). The shadow get unintentionally cropped

How can I avoid this behaviour?

I have a Column inside a SingleChildScrollView. Inside that column, there are SingleChildScrollViews with Rows of cards.

This is the code inside the scaffold:

SingleChildScrollView(
    physics: const BouncingScrollPhysics(),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const SectionTitlePadded("Dependency"),
        SingleChildScrollView(
          physics: const BouncingScrollPhysics(),
          scrollDirection: Axis.horizontal,
          child: Row(children: [
            SizedBox(width: CustomTheme.standardHorizontalPadding.left * 0.7),
            TaskRejectionReasonCard(onButtonPressed: () => rejectWithReason(Reason.Location), buttonText: "Location", explanation: "A specific location is required", image: Image.asset("assets/images/location.png")),
            TaskRejectionReasonCard(onButtonPressed: () => rejectWithReason(Reason.Collaborator), buttonText: "Collaborator", explanation: "Somebody else is required", image: Image.asset("assets/images/friends.png")),
            TaskRejectionReasonCard(onButtonPressed: () => rejectWithReason(Reason.TaskDependency), buttonText: "Task", explanation: "Another task must be completed", image: Image.asset("assets/images/list.png")),
            SizedBox(width: CustomTheme.standardHorizontalPadding.right * 0.7),
          ]),
        ),
        const SectionTitlePadded("Time"),
        SingleChildScrollView(
          physics: const BouncingScrollPhysics(),
          scrollDirection: Axis.horizontal,
          child: Row(children: [
            SizedBox(width: CustomTheme.standardHorizontalPadding.left * 0.7),
            TaskRejectionReasonCard(onButtonPressed: () => rejectWithReason(Reason.LackOfTime), buttonText: "Not enough", explanation: "There is not enough time right now", image: Image.asset("assets/images/hourglass.png")),
            TaskRejectionReasonCard(onButtonPressed: () => rejectWithReason(Reason.Schedule), buttonText: "Not now", explanation: "Right now is not the proper time", image: Image.asset("assets/images/schedule.png")),
            SizedBox(width: CustomTheme.standardHorizontalPadding.right * 0.7),
          ]),
        ),
        const SectionTitlePadded("Task Properties"),
        SingleChildScrollView(
          physics: const BouncingScrollPhysics(),
          scrollDirection: Axis.horizontal,
          child: Row(children: [
            SizedBox(width: CustomTheme.standardHorizontalPadding.left * 0.7),
            TaskRejectionReasonCard(onButtonPressed: () => rejectWithReason(Reason.Priority), buttonText: "Priority", explanation: "The task is not as high a priority", image: Image.asset("assets/images/bring.png")),
            TaskRejectionReasonCard(onButtonPressed: () => rejectWithReason(Reason.Delete), buttonText: "Delete", explanation: "This task is no longer required", image: Image.asset("assets/images/delete.png")),
            SizedBox(width: CustomTheme.standardHorizontalPadding.right * 0.7),
          ]),
        ),
        const SizedBox(
          height: CustomTheme.standardSeparation,
        ),
        Align(
          alignment: Alignment.center,
          child: TextButton(
            onPressed: () {},
            child: const Text("Other"),
          ),
        ),
        const SizedBox(
          height: CustomTheme.standardSeparation * 2,
        ),
      ],
    ),
  )

And this is the code of the card (what emits the shadow):

class CardCustom extends StatelessWidget {
Widget? child;
EdgeInsetsGeometry? innerPadding;
EdgeInsetsGeometry? outerPadding;

CardCustom({Key? key, this.child, this.innerPadding, this.outerPadding}) : super(key: key);

@override
Widget build(BuildContext context) {
  return Padding(
    padding: outerPadding ?? EdgeInsets.fromLTRB(CustomTheme.standardHorizontalPadding.left, 0, CustomTheme.standardHorizontalPadding.right, CustomTheme.standardSeparation),
    child: Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: CustomTheme.borderRadius,
        boxShadow: [BoxShadow(
          color: const Color(0xFF887AA6).withAlpha(25),
          spreadRadius: 0,
          blurRadius: 40,
          offset: const Offset(0, 4),
        )],
      ),
      child: Padding(
        padding: innerPadding ?? EdgeInsets.fromLTRB(CustomTheme.standardHorizontalPadding.left, CustomTheme.standardSeparation, CustomTheme.standardHorizontalPadding.right, CustomTheme.standardSeparation),
        child: child,
      ),
    ),
  );
}
}

Solution

  • I ended up solving it using clipBehavior: Clip.none from this post: How to fix cropped shadow in a Horizontal List View in Flutter