Last active
July 27, 2021 17:06
-
-
Save rydmike/0c6a2412cb3222a02e25cfead9ba8d29 to your computer and use it in GitHub Desktop.
Issue demo: ClipRect over Container with BoxShadow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
) | |
], | |
), | |
); | |
} | |
} | |
} |
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
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:
In DartPad (WEB) after turning ClipRect on:
On a device, after turning ClipRect on:
On Windows Desktop, before turning ClipRect on:
On Windows Desktop, after turning ClipRect on: