Skip to content

Instantly share code, notes, and snippets.

@roipeker
Created September 14, 2021 03:31
Show Gist options
  • Save roipeker/43afe157bf4282558d7772ea9e0bd769 to your computer and use it in GitHub Desktop.
Save roipeker/43afe157bf4282558d7772ea9e0bd769 to your computer and use it in GitHub Desktop.
GraphX sunburst chart concept.
/// made by roipeker 2021.
/// demo: https://graphx-sunburst-chart.surge.sh
import 'package:flutter/material.dart';
import 'package:graphx/graphx.dart';
import 'sunburst.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Sunburst chart graphx'),
),
backgroundColor: Colors.grey.shade100,
body: Center(
child: SizedBox(
width: 400,
height: 400,
child: SceneBuilderWidget(
builder: () => SceneController(
back: SunburstScene(),
),
),
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:graphx/graphx.dart';
class SunburstScene extends GSprite {
int maxLevels = 5;
double get sw => stage?.stageWidth ?? 0.0;
double get sh => stage?.stageHeight ?? 0.0;
late List<Color> slicesColors;
late GSprite container;
late double ringThickness, innerRadius;
@override
void addedToStage() {
super.addedToStage();
stage!.showBoundsRect = false;
stage!.maskBounds = true;
// stage!.color = Colors.grey.shade100;
ringThickness = 30;
innerRadius = 30;
container = GSprite();
addChild(container);
container.setPosition(sw / 2, sh / 2);
slicesColors = [
const Color(0xffF1C961),
const Color(0xffEBA453),
const Color(0xffE17B49),
];
drawInnerRings(
0,
Math.PI_2,
innerRadius,
innerRadius + ringThickness,
3,
0,
);
}
void drawInnerRings(
double parentAngle,
double maxAng,
double r1,
double r2,
int len,
int level,
) {
if (level == maxLevels) return;
/// random assign portions
var percents = randomPercent(
amount: len,
min: 0.1,
max: 0.2,
);
var prevAngle = 0.0;
for (var i = 0; i < len; ++i) {
final percent = percents[i];
final sliceAngle = percent * maxAng;
var color = Math.randomList(slicesColors);
if (Math.random() < .1) {
color = Colors.grey.shade300;
// color = const Color(0xffE6E6E6);
}
final slice = buildSlice(r1, r2, 0, sliceAngle, color);
slice.rotation = parentAngle + prevAngle;
container.addChildAt(slice, 0);
slice.onMouseOver.add((event) {
slice.tween(
duration: .15,
colorize: Colors.blue,
scale: 1.04,
);
// slice.colorize = Colors.red;
});
slice.onMouseOut.add((event) {
slice.tween(
duration: .6,
colorize: Colors.blue.withAlpha(0),
scale: 1,
);
// slice.colorize = null;
});
var numChildren = Math.randomRangeInt(2, 5);
if (level == maxLevels - 2) {
numChildren = Math.randomRangeInt(0, 2);
}
if (numChildren > 0) {
drawInnerRings(
slice.rotation,
sliceAngle,
r2,
r2 + ringThickness,
numChildren,
level + 1,
);
}
prevAngle += sliceAngle;
}
}
GShape buildSlice(
double innerRadius,
double outerRadius,
double fromAngle,
double toAngle,
Color color,
) {
final shape = GShape();
shape.mouseUseShape = true;
final g = shape.graphics;
g.beginFill(color);
g.lineStyle(0.5, Colors.white);
g.arc(0, 0, outerRadius, fromAngle, toAngle);
g.arc(0, 0, innerRadius, fromAngle + toAngle, -toAngle);
g.closePath();
g.endFill();
/// 2nd path (to get hit proper tests if needed).
// g.lineStyle(1, Colors.blue.withAlpha(80));
// g.moveTo(0, 0);
// g.arc(0, 0, outerRadius, fromAngle, toAngle);
// g.lineTo(0, 0);
return shape;
}
}
List<double> randomPercent({
required int amount,
double min = 0.1,
double max = 0.3,
}) {
final list = List.generate(
amount,
(_) => Math.randomRange(min, max),
growable: false,
);
var totalSum = list.reduce((v, e) => v + e);
for (var i = 0; i < amount; ++i) {
list[i] /= totalSum;
}
return list;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment