Flutter: How to achieve a Radial Widget like this in flutter? Circular breadcrumbs/ step progress indicator
Solution 1:
Use stack and Postioned widget :
calculate your area :
top right corner is Offset zero (x=0, y = 0), you can pass the calculated data to Positioned Widget
here the simple code for your reference :
Widget build(BuildContext context) {
double areaRadius = MediaQuery.of(context).size.width;
double bigCircleRadius = areaRadius * 0.8;
double smallCircleRadius = areaRadius * 0.15;
double imageRadius = areaRadius * 0.6;
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
),
body: SizedBox(
height: areaRadius,
width: areaRadius,
child: Stack(
children: [
///Bottom Layer --> BIG Circle
Positioned(
top: areaRadius * 0.1,
left: areaRadius * 0.1,
child: Container(
width: bigCircleRadius,
height: bigCircleRadius,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.black.withOpacity(0.1),
width: 4.0
)
),
),
),
Positioned(
top: areaRadius * 0.2,
left: areaRadius * 0.2,
child: Container(
width: imageRadius,
height: imageRadius,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/burger.png"),
fit: BoxFit.contain
),
shape: BoxShape.circle
),
),
),
///you can use trigonometry for this <---- Create a Offset(x,y) left is X and top is Y
Positioned(
left: (areaRadius * 0.5) - (smallCircleRadius /2),
top: (areaRadius * 0.1) - (smallCircleRadius /2),
child: Container(
width: smallCircleRadius,
height: smallCircleRadius,
decoration: const BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle
),
child: const Center(
child: Text("1", style: TextStyle(color: Colors.white, fontSize: 20.0),),
),
),
),
Positioned(
left: (areaRadius * 0.25) - (smallCircleRadius/2),
top: (areaRadius * 0.8) - (smallCircleRadius /2),
child: Container(
width: smallCircleRadius,
height: smallCircleRadius,
decoration: const BoxDecoration(
color: Colors.grey,
shape: BoxShape.circle
),
child: const Center(
child: Text("2", style: TextStyle(color: Colors.black, fontSize: 20.0),),
),
),
),
Positioned(
left: (areaRadius * 0.75) - (smallCircleRadius/2),
top: (areaRadius * 0.8) - (smallCircleRadius /2),
child: Container(
width: smallCircleRadius,
height: smallCircleRadius,
decoration: const BoxDecoration(
color: Colors.grey,
shape: BoxShape.circle
),
child: const Center(
child: Text("3", style: TextStyle(color: Colors.black, fontSize: 20.0),),
),
),
),
]
),
),
);
}
and here the result :
Can you comment my answer? I don't have notification for your response, for dynamic you can make a logic with radians of course, and for misalign on your widget that because you not calculated small circle size, you can use fractional offset from that.
Here is the code :
import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'package:sekolah_app/tema.dart';
class TestingPage extends StatefulWidget {
const TestingPage({Key? key}) : super(key: key);
@override
_TestingPageState createState() => _TestingPageState();
}
class _TestingPageState extends State<TestingPage> {
double areaRadius = 100;
int count = 4;
@override
Widget build(BuildContext context) {
var smallCircleRadius = areaRadius > 0 ? areaRadius*0.25 : 0.0;
double anchorPointX = lebarLayar(context)*0.5 - (areaRadius/2);
double anchorPointY = lebarLayar(context)*0.5 - (areaRadius/2);
return Scaffold(
body: Column(
children: [
Container(
width: lebarLayar(context),
height: lebarLayar(context),
color: Colors.yellow.withOpacity(0.1),
child: Stack(
alignment: Alignment.center,
children:
_bigCircle(areaRadius, anchorPointX, anchorPointY) +
_buildChildren(context, areaRadius, smallCircleRadius, count, anchorPointX, anchorPointY),
),
),
_controller(value: areaRadius, onChanged: (radius){
setState(() {
areaRadius = radius;
});
}),
_controller(value: count.toDouble(), min: 0, max: 8, onChanged: (itemCount){
setState(() {
count = itemCount.toInt();
});
})
],
),
);
}
_buildChildren(context,aR,sCR,c, apX, apY) {
final children = <Widget>[];
var angle = 360 / c;
var middlePoint = aR/2;
for (var i = 1; i <= c; i++) {
var r = aR / 2;
var radians = (angle * math.pi / 180) * (i-1);
double x = r * (math.sin(radians));
double y = r * (math.cos(radians));
debugPrint("x:${x.roundToDouble()}: ${y.roundToDouble()}");
children.add(_customChild(
sCR,
middlePoint + x + apX,
middlePoint - y + apY,
i
));
}
return children;
}
_customChild(smallCircleRadius,x,y,i){
return Positioned(
left: x,
top: y,
child: FractionalTranslation(
translation: const Offset(-0.5,-0.5),
child: Container(
width: smallCircleRadius,
height: smallCircleRadius,
decoration: const BoxDecoration(
color: Colors.cyan,
shape: BoxShape.circle
),
child: Center(
child: Text(i.toString(), style: TextStyle(color: Colors.white, fontSize: 20.0),),
),
),
),
);
}
List<Widget>_bigCircle (double radius, apx, apy){
return [
Positioned(
left: apx,
top: apy,
child: Container(
height: radius,
width: radius,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey
),
),
)
];
}
Widget _controller({required double value, required void Function(double)? onChanged, double? min, double? max}){
return Slider(
value: value,
onChanged: onChanged,
min: min??0.0,
max: max??320,
);
}
}
the update result :