Last active
June 3, 2020 13:27
Star
You must be signed in to star a gist
Color wheel example that does work on Flutter WEB together with the one that crashes
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 'dart:math' as math; | |
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(WebCrashDemo()); | |
} | |
class WebCrashDemo 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: HomePage(), | |
); | |
} | |
} | |
class HomePage extends StatefulWidget { | |
@override | |
State<StatefulWidget> createState() { | |
return _HomePageState(); | |
} | |
} | |
class _HomePageState extends State<HomePage> { | |
bool _crashMe = false; | |
double _width = 16.0; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Circle Gradient Crash on Web'), | |
centerTitle: true, | |
elevation: 0, | |
), | |
body: SingleChildScrollView( | |
child: Column( | |
children: [ | |
// And then some gap space too | |
const SizedBox(height: 20), | |
const Text('Draw with Arc OK on Web'), | |
const SizedBox(height: 20), | |
Center( | |
child: SizedBox( | |
width: 170, | |
height: 170, | |
child: CustomPaint( | |
painter: ColorWheelOK( | |
width: _width, | |
), | |
), | |
), | |
), | |
const SizedBox(height: 20), | |
const Text( | |
'Shader createShader, using a SweepGradient crashes on WEB'), | |
const SizedBox(height: 20), | |
Center( | |
child: SizedBox( | |
width: 170, | |
height: 170, | |
child: CustomPaint( | |
painter: ColorWheelCrash( | |
crashMe: _crashMe, | |
width: _width, | |
), | |
), | |
), | |
), | |
const SizedBox(height: 10), | |
Center( | |
child: SizedBox( | |
width: 450, | |
child: SwitchListTile( | |
title: const Text('Crash me on WEB'), | |
subtitle: | |
const Text('This option crashes on web, but only on WEB! ' | |
'Turn on only on none WEB! If you run this code ' | |
'on an emulator or desktop it works fine.'), | |
value: _crashMe, | |
onChanged: (value) { | |
setState(() { | |
_crashMe = value; | |
}); | |
}, | |
), | |
), | |
), | |
const SizedBox(height: 10), | |
Center( | |
child: SizedBox( | |
width: 450, | |
child: ListTile( | |
title: const Text('Width of the color wheel'), | |
subtitle: Slider( | |
min: 10.0, | |
max: 60.0, | |
divisions: (60 - 10).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 ColorWheelCrash extends CustomPainter { | |
ColorWheelCrash({ | |
this.crashMe = false, | |
this.width = 16, | |
}); | |
final bool crashMe; | |
final double width; | |
double radius(Size size) => | |
math.min(size.width, size.height).toDouble() / 2 - (width / 2); | |
@override | |
void paint(Canvas canvas, Size size) { | |
final Offset center = Offset(size.width / 2, size.height / 2); | |
final double radius = this.radius(size); | |
const SweepGradient colorWheelGradient = | |
SweepGradient(center: Alignment(1.5, 1.5), colors: [ | |
Color.fromARGB(255, 255, 0, 0), | |
Color.fromARGB(255, 255, 255, 0), | |
Color.fromARGB(255, 0, 255, 0), | |
Color.fromARGB(255, 0, 255, 255), | |
Color.fromARGB(255, 0, 0, 255), | |
Color.fromARGB(255, 255, 0, 255), | |
Color.fromARGB(255, 255, 0, 0), | |
]); | |
// If we create a shader from the above SweepGraident, we get | |
// a crash on web, but only on web. | |
final Shader sweepShader = crashMe | |
? colorWheelGradient.createShader(Rect.fromLTWH(0, 0, radius, radius)) | |
: null; | |
canvas.drawCircle( | |
center, | |
radius, | |
Paint() | |
..style = PaintingStyle.stroke | |
..strokeWidth = width | |
..shader = sweepShader); | |
} | |
@override | |
bool shouldRepaint(ColorWheelCrash other) => true; | |
} | |
// ***************************************************************************** | |
class ColorWheelOK extends CustomPainter { | |
const ColorWheelOK({ | |
this.ticks = 360, | |
this.width = 16, | |
}); | |
final int ticks; | |
final double width; | |
@override | |
void paint(Canvas canvas, Size size) { | |
const double rads = (2 * math.pi) / 360; | |
const double step = 1.0; | |
const double aliasing = 0.5; | |
for (int i = 0; i < ticks; i++) { | |
final double sRad = (i - aliasing) * rads; | |
final double eRad = (i + step) * rads; | |
final Rect rect = Rect.fromLTWH( | |
width / 2, width / 2, size.width - width, size.height - width); | |
final Paint segmentPaint = Paint() | |
..color = HSVColor.fromAHSV(1.0, i.toDouble(), 1.0, 1.0).toColor() | |
..style = PaintingStyle.stroke | |
..strokeWidth = width; | |
canvas.drawArc( | |
rect, | |
sRad, | |
sRad - eRad, | |
false, | |
segmentPaint, | |
); | |
} | |
} | |
@override | |
bool shouldRepaint(ColorWheelOK oldDelegate) { | |
return oldDelegate.ticks != ticks; | |
} | |
} |
Updated the GIST code for the example that works. The new updated version is much cleaner and simpler. The previous version used HSV/HSL color handling code that was borrowed/extracted from dart package TinyColor. Using TinyColor is however not needed for this use case, as the needed HSV/HSL color handling classes already exist in the Flutter SDK.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is newer version of the same issue that was demoed first in:
https://gist.github.com/rydmike/093461f7c8998aa03b70af4ee71622bc
This is also the Gist that was used when reporting the issue to the Flutter repo:
flutter/flutter#57752