548 lines
21 KiB
Dart
548 lines
21 KiB
Dart
import 'dart:ui' as ui;
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
import 'package:aslan/app_localizations.dart';
|
|
import 'package:aslan/chatvibe_ui/components/text/at_text.dart';
|
|
import 'package:aslan/chatvibe_core/constants/at_global_config.dart';
|
|
import 'package:aslan/main.dart';
|
|
import 'package:aslan/chatvibe_managers/app_general_manager.dart';
|
|
import 'package:aslan/chatvibe_managers/user_profile_manager.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:aslan/chatvibe_ui/components/at_compontent.dart';
|
|
import 'package:aslan/chatvibe_core/constants/at_screen.dart';
|
|
import 'package:aslan/chatvibe_core/routes/at_fluro_navigator.dart';
|
|
import 'package:aslan/chatvibe_data/sources/repositories/room_repository_imp.dart';
|
|
import 'package:aslan/chatvibe_domain/models/res/gift_res.dart';
|
|
import 'package:aslan/chatvibe_features/wallet/wallet_route.dart';
|
|
import 'package:aslan/chatvibe_features/gift/gift_tab_page.dart';
|
|
|
|
class CommentGiftPage extends StatefulWidget {
|
|
String toUserId;
|
|
String dynamicContentId;
|
|
Function(int count)? sendGiftCallBack;
|
|
CommentGiftPage(this.toUserId, this.dynamicContentId, {super.key,this.sendGiftCallBack});
|
|
|
|
@override
|
|
_CommentGiftPageState createState() => _CommentGiftPageState();
|
|
}
|
|
|
|
class _CommentGiftPageState extends State<CommentGiftPage>
|
|
with TickerProviderStateMixin {
|
|
late TabController _tabController;
|
|
|
|
bool isAnimating = false;
|
|
final GlobalKey _sourceKey1 = GlobalKey();
|
|
final GlobalKey _targetKey1 = GlobalKey();
|
|
ChatVibeGiftRes? checkedGift;
|
|
final List<Widget> _pages = [];
|
|
final List<Widget> _tabs = [];
|
|
|
|
///数量的箭头是否朝上
|
|
bool isNumberUp = true;
|
|
|
|
///数量
|
|
int number = 1;
|
|
|
|
int giveType = 1;
|
|
|
|
int giftType = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
Provider.of<AppGeneralManager>(context, listen: false).giftList();
|
|
Provider.of<ChatVibeUserProfileManager>(context, listen: false).balance();
|
|
_pages.add(
|
|
GiftTabPage("ALL", (int checkedI) {
|
|
checkedGift = null;
|
|
var all =
|
|
Provider.of<AppGeneralManager>(
|
|
context,
|
|
listen: false,
|
|
).giftByTab["ALL"];
|
|
if (all != null) {
|
|
checkedGift = all[checkedI];
|
|
}
|
|
setState(() {
|
|
giftType = 0;
|
|
});
|
|
}, isDark: true),
|
|
);
|
|
_pages.add(
|
|
GiftTabPage("NATIONAL_FLAG", (int checkedI) {
|
|
checkedGift = null;
|
|
var flag =
|
|
Provider.of<AppGeneralManager>(
|
|
context,
|
|
listen: false,
|
|
).giftByTab["NATIONAL_FLAG"];
|
|
if (flag != null) {
|
|
checkedGift = flag[checkedI];
|
|
}
|
|
setState(() {
|
|
giftType = 0;
|
|
});
|
|
}),
|
|
);
|
|
_tabController = TabController(length: _pages.length, vsync: this);
|
|
_tabController.addListener(() {}); // 监听切换
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
_tabs.clear();
|
|
_tabs.add(Tab(text: ATAppLocalizations.of(context)!.gift));
|
|
_tabs.add(Tab(text: ATAppLocalizations.of(context)!.country));
|
|
return Consumer<AppGeneralManager>(
|
|
builder: (context, ref, child) {
|
|
return SafeArea(
|
|
top: false,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(width: 50.w, height: 50.w, key: _targetKey1),
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.only(
|
|
topLeft: Radius.circular(12.0),
|
|
topRight: Radius.circular(12.0),
|
|
),
|
|
child: Material(
|
|
color: Color(0xff232126),
|
|
child: Container(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
constraints: BoxConstraints(maxHeight: 350.w),
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
children: [
|
|
SizedBox(width: 5.w),
|
|
Expanded(
|
|
child: TabBar(
|
|
tabAlignment: TabAlignment.start,
|
|
labelPadding: EdgeInsets.symmetric(
|
|
horizontal: 8.w,
|
|
),
|
|
labelColor: Colors.white,
|
|
isScrollable: true,
|
|
indicator: BoxDecoration(),
|
|
unselectedLabelColor: Colors.white54,
|
|
labelStyle: TextStyle(
|
|
fontSize: 14.sp,
|
|
fontFamily: 'MyCustomFont',
|
|
),
|
|
unselectedLabelStyle: TextStyle(
|
|
fontSize: 12.sp,
|
|
fontFamily: 'MyCustomFont',
|
|
),
|
|
indicatorColor: Colors.transparent,
|
|
dividerColor: Colors.transparent,
|
|
controller: _tabController,
|
|
tabs: _tabs,
|
|
),
|
|
),
|
|
SizedBox(width: 5.w),
|
|
],
|
|
),
|
|
Expanded(
|
|
child: Stack(
|
|
children: [
|
|
TabBarView(
|
|
physics: NeverScrollableScrollPhysics(),
|
|
controller: _tabController,
|
|
children: _pages,
|
|
),
|
|
isAnimating
|
|
? GestureDetector(
|
|
child: Container(
|
|
color: Colors.transparent,
|
|
alignment: Alignment.center,
|
|
),
|
|
onTap: () {},
|
|
)
|
|
: Container(),
|
|
],
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
SizedBox(width: 15.w),
|
|
GestureDetector(
|
|
onTap: () {
|
|
SmartDialog.dismiss(
|
|
tag: "showGiftControl",
|
|
);
|
|
ATNavigatorUtils.push(
|
|
navigatorKey.currentState!.context,
|
|
WalletRoute.recharge,
|
|
replace: false,
|
|
);
|
|
},
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
vertical: 8.w,
|
|
horizontal: 8.w,
|
|
),
|
|
width: 120.w,
|
|
decoration: BoxDecoration(
|
|
color: Colors.white10,
|
|
borderRadius: BorderRadius.circular(18),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Image.asset(
|
|
"atu_images/general/at_icon_jb.png",
|
|
width: 14.w,
|
|
height: 14.w,
|
|
),
|
|
SizedBox(width: 5.w),
|
|
Consumer<ChatVibeUserProfileManager>(
|
|
builder: (context, ref, child) {
|
|
return Expanded(
|
|
child: text(
|
|
"${ref.myBalance}",
|
|
fontSize: 12.sp,
|
|
textColor: Colors.white,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
SizedBox(width: 5.w),
|
|
Icon(
|
|
Icons.arrow_forward_ios,
|
|
color: Colors.white,
|
|
size: 14.w,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Spacer(),
|
|
Builder(
|
|
builder: (ct) {
|
|
return GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () {
|
|
isNumberUp = false;
|
|
_showNumber(ct);
|
|
setState(() {});
|
|
},
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white10,
|
|
borderRadius: BorderRadius.circular(
|
|
18,
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
SizedBox(width: 10.w),
|
|
text("$number", fontSize: 12.sp),
|
|
Icon(
|
|
isNumberUp
|
|
? Icons.keyboard_arrow_up
|
|
: Icons.keyboard_arrow_down,
|
|
color: Colors.white,
|
|
size: 20.w,
|
|
),
|
|
SizedBox(width: 5.w),
|
|
GestureDetector(
|
|
onTap: () {
|
|
giveGifts();
|
|
},
|
|
child: Container(
|
|
key: _sourceKey1,
|
|
padding: EdgeInsets.symmetric(
|
|
vertical: 8.w,
|
|
horizontal: 20.w,
|
|
),
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
Color(0xffF5A8B7),
|
|
Color(0xffD61F3F),
|
|
],
|
|
begin:
|
|
Alignment.topCenter,
|
|
end:
|
|
Alignment
|
|
.bottomCenter,
|
|
),
|
|
borderRadius:
|
|
BorderRadius.circular(
|
|
18,
|
|
),
|
|
),
|
|
child: text(
|
|
ATAppLocalizations.of(
|
|
context,
|
|
)!.send,
|
|
fontSize: 14.sp,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
SizedBox(width: 15.w),
|
|
],
|
|
),
|
|
SizedBox(height: 15.w),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
///数量选项
|
|
void _showNumber(BuildContext ct) {
|
|
SmartDialog.showAttach(
|
|
tag: "showNumber",
|
|
targetContext: ct,
|
|
alignment: Alignment.topLeft,
|
|
animationType: SmartAnimationType.fade,
|
|
scalePointBuilder: (selfSize) => Offset(selfSize.width, 10),
|
|
builder: (_) {
|
|
return Transform.translate(
|
|
offset: Offset(ATGlobalConfig.lang == "ar" ? 20 : -20, -5),
|
|
child: CheckNumber(
|
|
onNumberChanged: (number) {
|
|
this.number = number;
|
|
isNumberUp = true;
|
|
setState(() {});
|
|
},
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
void giveGifts() {
|
|
ChatRoomRepository()
|
|
.giveGift(
|
|
[widget.toUserId],
|
|
checkedGift!.id ?? "",
|
|
number,
|
|
false,
|
|
dynamicContentId: widget.dynamicContentId,
|
|
)
|
|
.then((result) {
|
|
_startA1();
|
|
widget.sendGiftCallBack?.call(number);
|
|
Provider.of<ChatVibeUserProfileManager>(
|
|
context,
|
|
listen: false,
|
|
).updateBalance(result);
|
|
})
|
|
.catchError((e) {});
|
|
}
|
|
|
|
void _startA1() {
|
|
setState(() {
|
|
isAnimating = true;
|
|
});
|
|
// 确保上下文有效
|
|
if (_sourceKey1.currentContext == null) return;
|
|
AnimationController? _controllerX1;
|
|
Animation<double>? _scaleAnimationX1;
|
|
Animation<double>? _scaleAnimationX2;
|
|
Animation<Offset>? _positionAnimationX1;
|
|
OverlayEntry? _overlayEntryX1;
|
|
|
|
// 获取源控件位置和大小
|
|
final RenderBox sourceBox =
|
|
_sourceKey1.currentContext!.findRenderObject() as RenderBox;
|
|
final Offset sourcePosition = sourceBox.localToGlobal(Offset.zero);
|
|
final Size sourceSize = sourceBox.size;
|
|
|
|
// 计算源控件中心点
|
|
Offset sourceCenter = Offset(
|
|
sourcePosition.dx + sourceSize.width / 2,
|
|
sourcePosition.dy + sourceSize.height / 2,
|
|
);
|
|
|
|
// 获取目标控件位置和大小
|
|
if (_targetKey1?.currentContext == null) return;
|
|
final RenderBox targetBox =
|
|
_targetKey1!.currentContext!.findRenderObject() as RenderBox;
|
|
final Offset targetPosition = targetBox.localToGlobal(Offset.zero);
|
|
final Size targetSize = targetBox.size;
|
|
|
|
// 计算目标控件中心点
|
|
final Offset targetCenter = Offset(
|
|
targetPosition.dx + targetSize.width / 2,
|
|
targetPosition.dy + targetSize.height / 2,
|
|
);
|
|
|
|
// 计算图片中心点偏移(使图片中心对准控件中心)
|
|
final double imageOffsetX = 55.w / 2;
|
|
final double imageOffsetY = 55.w / 2;
|
|
|
|
_controllerX1 = AnimationController(
|
|
vsync: this,
|
|
duration: Duration(milliseconds: 350),
|
|
)..addStatusListener((status) {
|
|
if (status == AnimationStatus.completed) {
|
|
if (_overlayEntryX1 != null) {
|
|
_overlayEntryX1?.remove();
|
|
_overlayEntryX1 = null;
|
|
}
|
|
setState(() {
|
|
isAnimating = false;
|
|
});
|
|
}
|
|
});
|
|
|
|
// 创建位置动画:从起始位置到目标位置
|
|
_positionAnimationX1 = Tween<Offset>(
|
|
begin: Offset(
|
|
sourceCenter.dx - imageOffsetX,
|
|
sourceCenter.dy - imageOffsetY,
|
|
),
|
|
end: Offset(
|
|
targetCenter.dx - imageOffsetX,
|
|
targetCenter.dy - imageOffsetY,
|
|
),
|
|
).animate(
|
|
CurvedAnimation(
|
|
parent: _controllerX1!,
|
|
curve: const Interval(0.2, 0.4, curve: Curves.easeIn),
|
|
),
|
|
);
|
|
// 创建缩放动画
|
|
_scaleAnimationX1 = Tween<double>(
|
|
begin: 0.5,
|
|
end: 1.1, // 可以根据需要调整
|
|
).animate(
|
|
CurvedAnimation(
|
|
parent: _controllerX1!,
|
|
curve: const Interval(0, 0.2, curve: Curves.easeIn),
|
|
),
|
|
);
|
|
|
|
// 创建缩放动画
|
|
_scaleAnimationX2 = Tween<double>(
|
|
begin: 1,
|
|
end: 1.5, // 可以根据需要调整
|
|
).animate(
|
|
CurvedAnimation(
|
|
parent: _controllerX1!,
|
|
curve: const Interval(0.4, 0.7, curve: Curves.easeIn),
|
|
),
|
|
);
|
|
// 创建浮动控件
|
|
_overlayEntryX1 = OverlayEntry(
|
|
builder:
|
|
(context) => AnimatedBuilder(
|
|
animation: _controllerX1!,
|
|
builder: (context, child) {
|
|
return Positioned(
|
|
left: _positionAnimationX1!.value.dx,
|
|
top: _positionAnimationX1!.value.dy,
|
|
child: Transform.scale(
|
|
scale: _scaleAnimationX1!.value,
|
|
child: Transform.scale(
|
|
scale: _scaleAnimationX2!.value,
|
|
child: netImage(
|
|
url: checkedGift?.giftPhoto ?? "",
|
|
width: 55.w,
|
|
height: 55.w,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
|
|
// 插入Overlay并启动动画
|
|
Overlay.of(context).insert(_overlayEntryX1!);
|
|
_controllerX1.forward(from: 0);
|
|
}
|
|
}
|
|
|
|
class CheckNumber extends StatelessWidget {
|
|
final Function(int) onNumberChanged;
|
|
late BuildContext context;
|
|
|
|
CheckNumber({super.key, required this.onNumberChanged});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
this.context = context;
|
|
|
|
return Material(
|
|
color: Colors.transparent,
|
|
child: Container(
|
|
alignment: AlignmentDirectional.topEnd,
|
|
margin: EdgeInsets.only(right: width(22)),
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.black87,
|
|
borderRadius: BorderRadius.all(Radius.circular(height(10))),
|
|
),
|
|
width: width(80),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: <Widget>[
|
|
SizedBox(height: height(10)),
|
|
_item(1),
|
|
_item(7),
|
|
_item(17),
|
|
_item(77),
|
|
_item(777),
|
|
_item(7777),
|
|
SizedBox(height: height(10)),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
_item(int number) {
|
|
return GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () {
|
|
SmartDialog.dismiss(tag: "showNumber");
|
|
onNumberChanged(number);
|
|
},
|
|
child: SizedBox(
|
|
height: height(24),
|
|
child: Row(
|
|
children: <Widget>[
|
|
SizedBox(width: width(10)),
|
|
Text(
|
|
"$number",
|
|
style: TextStyle(
|
|
fontSize: sp(14),
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w400,
|
|
decoration: TextDecoration.none,
|
|
),
|
|
),
|
|
SizedBox(width: width(10)),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|