
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
const TestPage({super.key});
@override
State<StatefulWidget> createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> with SingleTickerProviderStateMixin{
@override
Widget build(BuildContext context) {
return Material(
color: Colors.white,
child: Center(
child: Stack(
children: [
SectorWidget(180,15,items: [
Container(
width: 100,
height: 20,
alignment: Alignment.center,
child: const Text("我是第一个",style: TextStyle(
fontSize: 10,
color: Colors.white
),),
),
Container(
width: 100,
height: 20,
alignment: Alignment.center,
child: const Text("--------",style: TextStyle(
fontSize: 10,
color: Colors.white
),),
),
Container(
height: 20,
width: 100,
alignment: Alignment.center,
child: const Text("我是第而个",style: TextStyle(
fontSize: 10,
color: Colors.white
),),
),
Container(
width: 100,
height: 20,
alignment: Alignment.center,
child: const Text("--------",style: TextStyle(
fontSize: 10,
color: Colors.white
),),
),
Container(
width: 100,
height: 20,
alignment: Alignment.center,
child: const Text("我是第三个",style: TextStyle(
fontSize: 10,
color: Colors.white
),),
),
Container(
width: 100,
height: 20,
alignment: Alignment.center,
child: const Text("--------",style: TextStyle(
fontSize: 10,
color: Colors.white
),),
),
Container(
width: 100,
height: 20,
alignment: Alignment.center,
child: const Text("我是第四个",style: TextStyle(
fontSize: 10,
color: Colors.white
),),
)
],)
],
),
),
);
}
}
class SectorWidget extends StatefulWidget {
final double size;
final double smallRadius;
final List<Widget> items;
const SectorWidget(this.size,this.smallRadius,{required this.items,super.key});
@override
State<StatefulWidget> createState() => _SectorWidgetState();
}
class _SectorWidgetState extends State<SectorWidget> with SingleTickerProviderStateMixin{
late AnimationController controller;
late Animation<double> animation;
ValueNotifier<bool> isExpand = ValueNotifier(false);
@override
void initState() {
super.initState();
controller = AnimationController(vsync: this,duration: const Duration(milliseconds: 300));
animation = Tween(begin: 0.0, end: 1.0).animate(controller);
controller.addListener(() {
setState(() {
});
});
controller.addStatusListener((status) {
if(status == AnimationStatus.completed){
}else if(status == AnimationStatus.dismissed){
isExpand.value = false;
}
});
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: isExpand,
builder: (context,value,child){
return SizedBox(
width: widget.size,
height: widget.size,
child: Stack(
children: [
if(isExpand.value)SizedBox(
width: widget.size,
height: widget.size,
child: CustomPaint(
painter: SectorCustomPainter(animation,widget.smallRadius),
),
),
if(isExpand.value) Flow(delegate: CusFlowDelegate(animation.value,widget.smallRadius + 70),children: widget.items),
Positioned(bottom: 7,left: 7,child: GestureDetector(
onTap: (){
if(controller.isAnimating)return;
if(isExpand.value){
controller.reverse();
}else {
isExpand.value = true;
controller.forward();
}
},
child: Icon(Icons.add,size: 50,),
),),
],
));
},
);
}
}
class CusFlowDelegate extends FlowDelegate {
double percent;
double itemTrans;
CusFlowDelegate(this.percent,this.itemTrans);
@override
void paintChildren(FlowPaintingContext context) {
var size = context.size;
var childCount = context.childCount;
var eachSweep = (pi / 2)/ (childCount + 1);
for(int i= 0;i<childCount;i++){
var childSize = context.getChildSize(i)!;
var _paintTransform = Matrix4.identity()
..translate(0.0,size.height - childSize.height/2)
..rotateZ(- (i + 1) * eachSweep * percent )
..translate(itemTrans)
;
context.paintChild(i,transform: _paintTransform);
}
}
@override
bool shouldRepaint(covariant CusFlowDelegate oldDelegate) {
return percent != oldDelegate.percent;
}
}
class SectorCustomPainter extends CustomPainter {
Animation<double> animation;
double smallRadius;
SectorCustomPainter(this.animation,this.smallRadius):super(repaint: animation);
@override
void paint(Canvas canvas, Size size) {
var clipPath = Path();
clipPath.moveTo(0, 0);
clipPath.addArc(Rect.fromLTRB(-size.width, 0, size.width, size.height * 2), 0, - pi /2 * animation.value);
clipPath.lineTo(0, size.height);
canvas.clipPath(clipPath);
var path = Path();
path.moveTo(0, 0);
path.arcTo(Rect.fromLTRB(-size.width,0.0,size.width,size.height * 2), -pi/2, pi/2, true);
path.lineTo(smallRadius, size.height);
path.arcToPoint(Offset(0, size.height - smallRadius ),radius: Radius.circular(smallRadius),clockwise: true,largeArc: false);
canvas.drawPath(path, Paint()
..shader = const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.red, Colors.orange],
).createShader(Rect.fromLTWH(0.0, 0.0, size.width, size.height)));
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}