Skip to content

Instantly share code, notes, and snippets.

@rydmike
Last active July 27, 2021 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rydmike/0c6a2412cb3222a02e25cfead9ba8d29 to your computer and use it in GitHub Desktop.
Save rydmike/0c6a2412cb3222a02e25cfead9ba8d29 to your computer and use it in GitHub Desktop.
Issue demo: ClipRect over Container with BoxShadow
import 'package:flutter/material.dart';
void main() {
runApp(IssueDemoApp());
}
class IssueDemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.indigo,
scaffoldBackgroundColor: Colors.grey[100],
buttonTheme: ButtonThemeData(
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.indigo),
textTheme: ButtonTextTheme.primary,
),
),
debugShowCheckedModeBanner: false,
home: ClipRectIssueDemo(),
);
}
}
class ClipRectIssueDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ClipRectIssueDemoState();
}
}
class _ClipRectIssueDemoState extends State<ClipRectIssueDemo> {
bool _clipRectOn = false;
double _width = 219.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ClipRect Issue Demo'),
centerTitle: true,
elevation: 0,
),
body: SingleChildScrollView(
child: Column(
children: [
// And then some gap space too
const SizedBox(height: 20),
Text('ClipRect over Container with BoxShadow',
style: Theme.of(context).textTheme.headline6),
const SizedBox(height: 20),
SizedBox(
width: 420,
height: 420,
child: Center(
child: SizedBox(
height: _width,
width: _width,
child: ClipRectDecoratedContainer(clipRectOn: _clipRectOn),
),
),
),
const SizedBox(height: 10),
Center(
child: SizedBox(
width: 450,
child: SwitchListTile(
title: const Text('ClipRect ON/OFF'),
subtitle: const Text(
'Turn on ClipRect to see edge remnants. \nIf you resize '
'window/media size or change container size, you can observe the edge '
'remnants appearing and dissapearing at different sizes.'),
value: _clipRectOn,
onChanged: (value) {
setState(() {
_clipRectOn = value;
});
},
),
),
),
const SizedBox(height: 10),
Center(
child: SizedBox(
width: 450,
child: ListTile(
title: const Text('Change Container size'),
subtitle: Slider(
min: 100.0,
max: 400.0,
divisions: (400 - 100).floor(),
label: _width.floor().toString(),
value: _width,
onChanged: (value) {
setState(() {
_width = value;
});
},
),
trailing: Padding(
padding: const EdgeInsets.only(right: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
const Text(
'Width',
style: TextStyle(fontSize: 11),
),
Text(
_width.floor().toString(),
style: const TextStyle(fontSize: 15),
),
],
),
),
),
),
),
],
),
),
);
}
}
class ClipRectDecoratedContainer extends StatelessWidget {
const ClipRectDecoratedContainer({
Key? key,
this.clipRectOn = false,
}) : super(key: key);
final bool clipRectOn;
@override
Widget build(BuildContext context) {
if (clipRectOn) {
return ClipRect(
clipBehavior: Clip.hardEdge,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.red[900]!,
blurRadius: 25.0,
)
],
),
),
);
} else {
return Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.red[900]!,
blurRadius: 25.0,
)
],
),
);
}
}
}
@rydmike
Copy link
Author

rydmike commented Jun 3, 2020

Example of ClipRect Issue

TLDR

ClipRect does not always clip correctly, there are often thin remnants left of what it should clip away.

Applies to

All Flutter versions (past ones too), all channels and all platforms.
The issue is not just an issue with CanvasKit, as far as I have noticed it applies to all platforms, using SKIA rendering as well as DomCanvas.

Steps to reproduce

This Gist: https://gist.github.com/rydmike/0c6a2412cb3222a02e25cfead9ba8d29 show an example of the ClipRect issue mentioned in flutter/flutter#58547

The example Gist can also be tested live in DartPad here:
https://dartpad.dartlang.org/0c6a2412cb3222a02e25cfead9ba8d29

Detailed Information

The above mentioned MaskFilter.blur(BlurStyle.outer, 10) case was where I first noticed it, while using it on CustomPaint objects to create a blur effect on a custom paint outer edges. This filtering also caused a blur on the paint container's (canvas) outer edges, that I then needed to cut away with with ClipRect. I then noticed that the blur effects outside the container did not always get clipped.

As shown in this code snippet, we can equally well demonstrate this by just putting a surrounding BoxShadow on a Container and try to clip it away with a ClipRect. The situation is the same as the more involved case where I noticed it. Normally one would probably not put an outer BoxShadow on a Container and the clip it away again directly with a ClipRect, seldom any point in doing that, but it is a simple way to demo the issue with ClipRect. As mentioned, a more real use case is when drawing something and using e.g. the outer blur style, or something else that would cause an effect outside the used container that you then want to clip away fully.

Example case

With the provided code snippet a Container with a BoxShadow is presented. When the switch is flipped, the same Container is wrapped with a ClipRect. At some Container sizes and/or media (window) sizes, we can observe very thin remnants of the box shadow effect on "random" edges, that ClipRect should have clipped away completely. The used clipBehavior does not have any major impact on the end result.

With this example the Slider can be used to interactively change the size of the Container and observe how the remnants appear and disappear at various edges as the size is changed. The same behavior can also be observed to some extent on Desktop or Web when changing window (media) size.

Screenshot examples

In DartPad (WEB), before turning ClipRect on:

image

In DartPad (WEB) after turning ClipRect on:

image

On a device, after turning ClipRect on:

image

On Windows Desktop, before turning ClipRect on:

image

On Windows Desktop, after turning ClipRect on:

image

@rydmike
Copy link
Author

rydmike commented Jul 27, 2021

Updated the GIST to null safe version. The issue still exists in Flutter 2.2.3 Dart SDK 2.13.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment