Skip to content

Instantly share code, notes, and snippets.

@rydmike
Last active June 3, 2020 13:27
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save rydmike/dc8a915f169455463e4e2016619e00bb to your computer and use it in GitHub Desktop.
Color wheel example that does work on Flutter WEB together with the one that crashes
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;
}
}
@rydmike
Copy link
Author

rydmike commented May 21, 2020

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

@rydmike
Copy link
Author

rydmike commented Jun 3, 2020

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