Files
plan_flutter/lib/page/plan/detail/widgets/avatar_card.dart
zhutao 193d29b0ce 1
2025-09-05 18:00:26 +08:00

226 lines
6.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:remixicon/remixicon.dart';
import '../viewmodel/plan_detail_store.dart';
class AvatarCard extends StatefulWidget {
const AvatarCard({super.key});
@override
State<AvatarCard> createState() => _AvatarCardState();
}
class _AvatarCardState extends State<AvatarCard> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
///对话框值
String _dialog = "";
@override
void initState() {
super.initState();
//初始化动画
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 300),
);
_animation = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
),
);
_initDialog();
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
///初始化是否显示对话
void _initDialog() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final store = context.read<PlanDetailStore>();
void listener() {
if (store.planDetail.dialog != null && !store.showRoleTalk) {
setState(() {
_dialog = store.planDetail.dialog!;
});
_toggleShow();
store.removeListener(listener);
}
}
store.addListener(listener);
});
}
///切换显示show
void _toggleShow() {
var store = context.read<PlanDetailStore>();
if (store.planDetail.dialog == null) {
return;
}
setState(() {
store.showRoleTalk = !store.showRoleTalk;
if (store.showRoleTalk) {
_controller.forward();
} else {
_controller.reverse();
}
});
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Transform.translate(
offset: Offset(0, 50),
child: Column(
children: [
BothSizeTransition(
animation: _animation,
offset: Offset(50, 50),
child: Container(
padding: EdgeInsets.only(bottom: 10),
child: InkWell(
onTap: _toggleShow,
child: Stack(
children: [
Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
border: Border.all(color: Colors.black, width: 1),
),
child: Text(_dialog, style: TextStyle(fontSize: 12)),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: CustomPaint(
painter: BubblePainter(),
),
),
],
),
),
),
),
SizedBox(
width: double.infinity,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
Image.asset("assets/image/xiaozhi.png", height: 100),
Positioned(
top: 20,
child: Transform.translate(
offset: Offset(40, -10),
child: GestureDetector(
onTap: _toggleShow,
child: BothSizeTransition(
animation: ReverseAnimation(_animation),
offset: Offset(-50, -50),
child: Icon(RemixIcons.message_2_line),
),
),
),
),
],
),
),
],
),
),
);
}
}
///聊天气泡三角
class BubblePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
var bottomWidth = 10;
//点坐标
var start = Offset((size.width - bottomWidth) / 2, 0);
//底线
final bottomLinePaint = Paint()
..color = Colors.white
..strokeWidth = 2
..style = PaintingStyle.stroke;
final bottomLinePath = Path()
..moveTo(start.dx, 0)
..lineTo(start.dx + bottomWidth, 0);
canvas.drawPath(bottomLinePath, bottomLinePaint);
//边线
final sideLinePaint = Paint()
..color = Colors.black
..strokeWidth = 1
..style = bottomLinePaint.style;
final sideLinePath = Path()
..moveTo(start.dx, 0)
..lineTo(start.dx + bottomWidth / 2, 5)
..lineTo(start.dx + bottomWidth, 0);
canvas.drawPath(sideLinePath, sideLinePaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
///缩放动画
class BothSizeTransition extends StatelessWidget {
final Animation<double> animation;
final Widget child;
final Offset offset;
const BothSizeTransition({
super.key,
required this.animation,
this.offset = Offset.zero,
required this.child,
});
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (_, child) {
return Align(
alignment: Alignment(0.7, 1),
child: Opacity(
opacity: animation.value,
child: Transform(
alignment: Alignment(0.5, 1),
transform: Matrix4.identity()
..translate(
offset.dx * (1 - animation.value),
offset.dy * (1 - animation.value),
)
..scale(animation.value, 1.0),
child: SizeTransition(
sizeFactor: animation,
axis: Axis.vertical,
child: child,
),
),
),
);
},
child: child,
);
}
}