hi-client/lib/app/widgets/kr_simple_loading.dart
speakeloudest f42a481452
Some checks failed
Build Android APK / 编译 libcore.aar (push) Has been cancelled
Build Android APK / 编译 Android APK (release) (push) Has been cancelled
Build Android APK / 创建 GitHub Release (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Android) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Windows) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (macOS) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Linux) (push) Has been cancelled
Build Multi-Platform / 构建 Android APK (push) Has been cancelled
Build Multi-Platform / 构建 Windows (push) Has been cancelled
Build Multi-Platform / 构建 macOS (push) Has been cancelled
Build Multi-Platform / 构建 Linux (push) Has been cancelled
Build Multi-Platform / 创建 Release (push) Has been cancelled
Build Windows / build (push) Has been cancelled
feat: 更新代码仓库全部修改
2025-10-30 04:47:53 -07:00

281 lines
7.5 KiB
Dart
Executable File

import 'package:flutter/material.dart';
/// ====================================================================
/// 1. KRSimpleLoading: 旋转和扇形加载动画 (支持暂停,已修复重合)
/// ====================================================================
class KRSimpleLoading extends StatefulWidget {
final Color? color;
final double size;
final Duration duration;
final bool isPaused; // 控制动画暂停/恢复
const KRSimpleLoading({
super.key,
this.color,
this.size = 30.0, // 环的宽高默认为 30.0 像素
this.duration = const Duration(milliseconds: 1000),
this.isPaused = false, // 默认不暂停
});
@override
State<KRSimpleLoading> createState() => _KRSimpleLoadingState();
}
class _KRSimpleLoadingState extends State<KRSimpleLoading>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _rotationAnimation;
// 环的宽度 5px
static const double _strokeWidth = 5.0;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.duration,
vsync: this,
);
_rotationAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.linear,
));
// 根据初始状态决定是重复播放还是停止
_setAnimationState(widget.isPaused);
}
void _setAnimationState(bool isPaused) {
if (isPaused) {
_controller.stop();
} else {
// 如果之前停止了,调用 repeat 重新开始
_controller.repeat();
}
}
@override
void didUpdateWidget(covariant KRSimpleLoading oldWidget) {
super.didUpdateWidget(oldWidget);
// 监听 isPaused 参数的变化,并更新动画状态
if (widget.isPaused != oldWidget.isPaused) {
_setAnimationState(widget.isPaused);
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final defaultColor = widget.color ?? Theme.of(context).primaryColor;
// 强制动画组件拥有固定的 30x30 尺寸
return SizedBox(
width: widget.size,
height: widget.size,
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.rotate(
angle: _rotationAnimation.value * 2 * 3.14159,
// 关键修复: 使用 Stack 和 Center+SizedBox 确保两个环完美重合
child: Stack(
children: [
// 1. 底环 (Container): 绘制 30x30 边界内的 5px 边框
Positioned.fill(
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: defaultColor.withOpacity(0.3),
width: _strokeWidth,
),
),
),
),
// 2. 顶扇形 (CircularProgressIndicator):
// 通过 Center+SizedBox 强制其绘制区域收缩,以消除渲染偏差。
Center(
child: SizedBox(
// 关键:将尺寸减去笔触宽度 (30 - 5 = 25)
width: widget.size - _strokeWidth,
height: widget.size - _strokeWidth,
child: CircularProgressIndicator(
value: 0.2, // 彩色扇形长度
strokeCap: StrokeCap.round, // 圆角
strokeWidth: _strokeWidth, // 宽度 5px
backgroundColor: Colors.transparent, // 必须透明
valueColor: AlwaysStoppedAnimation<Color>(
defaultColor,
),
),
),
),
],
),
);
},
),
);
}
}
class KRSimplePulse extends StatefulWidget {
final Color? color;
final double size;
final Duration duration;
const KRSimplePulse({
super.key,
this.color,
this.size = 40.0,
this.duration = const Duration(milliseconds: 1500),
});
@override
State<KRSimplePulse> createState() => _KRSimplePulseState();
}
class _KRSimplePulseState extends State<KRSimplePulse>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.duration,
vsync: this,
);
_animation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
_controller.repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Transform.scale(
scale: 0.5 + (_animation.value * 0.5),
child: Opacity(
opacity: 1.0 - _animation.value,
child: Container(
width: widget.size,
height: widget.size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: widget.color ?? Theme.of(context).primaryColor,
),
),
),
);
},
);
}
}
/// 波浪加载动画
class KRSimpleWave extends StatefulWidget {
final Color? color;
final double size;
final Duration duration;
const KRSimpleWave({
super.key,
this.color,
this.size = 40.0,
this.duration = const Duration(milliseconds: 1200),
});
@override
State<KRSimpleWave> createState() => _KRSimpleWaveState();
}
class _KRSimpleWaveState extends State<KRSimpleWave>
with TickerProviderStateMixin {
late List<AnimationController> _controllers;
late List<Animation<double>> _animations;
@override
void initState() {
super.initState();
_controllers = List.generate(3, (index) {
return AnimationController(
duration: widget.duration,
vsync: this,
);
});
_animations = _controllers.map((controller) {
return Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: controller,
curve: Curves.easeInOut,
));
}).toList();
for (int i = 0; i < _controllers.length; i++) {
Future.delayed(Duration(milliseconds: i * 200), () {
if (mounted) {
_controllers[i].repeat();
}
});
}
}
@override
void dispose() {
for (var controller in _controllers) {
controller.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(3, (index) {
return AnimatedBuilder(
animation: _animations[index],
builder: (context, child) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 2.0),
width: widget.size / 6,
height: widget.size * (0.3 + (_animations[index].value * 0.7)),
decoration: BoxDecoration(
color: widget.color ?? Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(2.0),
),
);
},
);
}),
);
}
}