Created
September 14, 2021 03:31
-
-
Save roipeker/43afe157bf4282558d7772ea9e0bd769 to your computer and use it in GitHub Desktop.
GraphX sunburst chart concept.
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
/// 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(), | |
), | |
), | |
), | |
), | |
), | |
); | |
} | |
} |
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'; | |
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