'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 looks when I have 3 or more elements (I suspect is more elements than those that fit in the screen).

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 1:[1]
I ended up solving it using clipBehavior: Clip.none from this post: How to fix cropped shadow in a Horizontal List View in Flutter
Solution 2:[2]
This happens because with 2 elements, the SingleChildScrollView is treated as a Row as there is no scroll needed. The widget is made scrollable only when the 3rd element is added to list which makes it overflowed.
One of the workaround could be adding EdgeInsets.symmetric(vertical: value) in the padding parameter of SingleChildScrollView.
Another workaround could be removing Outer Padding Widget and adding EdgeInsets.symmetric(vertical: value) in the margin parameter of Container in CardCustom.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Guillem Poy |
| Solution 2 | Muhammad Talha |

