| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- import 'package:flutter/material.dart';
- import 'package:flutter_screenutil/flutter_screenutil.dart';
- import 'package:get/get.dart';
- import 'package:nomo/app/widgets/click_opacity.dart';
- import '../../config/translations/strings_enum.dart';
- import '../widgets/submit_btn.dart';
- import 'all_dialog.dart';
- /// 反馈弹窗底部弹出框
- class FeedbackBottomSheet extends StatefulWidget {
- const FeedbackBottomSheet({super.key});
- /// 显示反馈弹窗
- static Future<void> show() {
- return Get.bottomSheet(
- const FeedbackBottomSheet(),
- backgroundColor: Colors.transparent,
- isDismissible: true,
- enableDrag: true,
- isScrollControlled: true,
- );
- }
- @override
- State<FeedbackBottomSheet> createState() => _FeedbackBottomSheetState();
- }
- class _FeedbackBottomSheetState extends State<FeedbackBottomSheet>
- with SingleTickerProviderStateMixin {
- // 选中的表情索引(0-4)
- int? selectedEmojiIndex;
- // 选中的问题标签
- final Set<String> selectedIssues = {};
- // 表情列表
- final List<String> emojis = ['😡', '😥', '🤭', '😏', '🥰'];
- // 每个表情对应的问题标签
- Map<int, List<String>> get emojiIssues => {
- 0: [
- // 差评 😡
- Strings.vpnConnectionFailed.tr,
- Strings.internetTooSlow.tr,
- Strings.keepsDisconnecting.tr,
- Strings.appCrashes.tr,
- Strings.otherIssues.tr,
- ],
- 1: [
- // 一般偏差 😥
- Strings.connectionUnstable.tr,
- Strings.speedNotExpected.tr,
- Strings.hardToUse.tr,
- Strings.otherIssues.tr,
- ],
- 2: [
- // 中等 🤭
- Strings.worksFineNotFast.tr,
- Strings.limitedFreeServers.tr,
- Strings.appCouldBeSimpler.tr,
- Strings.sometimesDisconnects.tr,
- Strings.nothingSpecial.tr,
- ],
- 3: [
- // 好 😏
- Strings.easyToUse.tr,
- Strings.fastConnection.tr,
- Strings.stablePerformance.tr,
- Strings.usefulFreeVersion.tr,
- Strings.satisfiedOverall.tr,
- ],
- 4: [
- // 很好 🥰
- Strings.fastAndStable.tr,
- Strings.greatUserExperience.tr,
- Strings.excellentPremiumFeatures.tr,
- Strings.worthRecommending.tr,
- Strings.loveTheDesign.tr,
- ],
- };
- @override
- Widget build(BuildContext context) {
- return Container(
- decoration: BoxDecoration(
- color: Get.theme.highlightColor,
- borderRadius: BorderRadius.only(
- topLeft: Radius.circular(16.r),
- topRight: Radius.circular(16.r),
- ),
- ),
- child: SafeArea(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- // 关闭按钮
- _buildCloseButton(),
- // 标题
- _buildTitle(),
- 10.verticalSpaceFromWidth,
- // 副标题
- _buildSubtitle(),
- 10.verticalSpaceFromWidth,
- // 表情选择器
- _buildEmojiSelector(),
- // 问题标签区域 - 使用 AnimatedSize 实现平滑高度过渡
- AnimatedSize(
- duration: const Duration(milliseconds: 300),
- curve: Curves.easeInOut,
- child: selectedEmojiIndex != null
- ? Column(
- mainAxisSize: MainAxisSize.min,
- children: [24.verticalSpaceFromWidth, _buildIssueTags()],
- )
- : const SizedBox.shrink(),
- ),
- // 发送按钮 - 使用 AnimatedSize 实现平滑显示/隐藏
- AnimatedSize(
- duration: const Duration(milliseconds: 300),
- curve: Curves.easeInOut,
- child: selectedEmojiIndex != null
- ? _buildSendButton()
- : 24.verticalSpaceFromWidth,
- ),
- ],
- ),
- ),
- );
- }
- /// 构建关闭按钮
- Widget _buildCloseButton() {
- return Align(
- alignment: Alignment.centerRight,
- child: ClickOpacity(
- onTap: () => Navigator.of(Get.context!).pop(),
- child: Container(
- width: 24.w,
- height: 24.w,
- margin: EdgeInsets.only(right: 8.w, top: 8.w),
- padding: EdgeInsets.all(4.w),
- decoration: BoxDecoration(
- color: Get.theme.cardColor,
- shape: BoxShape.circle,
- ),
- child: Icon(
- Icons.close_rounded,
- size: 16.w,
- color: Get.theme.textTheme.bodyLarge!.color,
- ),
- ),
- ),
- );
- }
- /// 构建标题
- Widget _buildTitle() {
- return Text(
- Strings.howExperience.tr,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 22.sp,
- fontWeight: FontWeight.w500,
- color: Get.theme.textTheme.bodyLarge!.color,
- height: 1.3,
- ),
- );
- }
- /// 构建副标题
- Widget _buildSubtitle() {
- return Text(
- Strings.wedLoveToKnow.tr,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 16.sp,
- height: 1.4,
- color: Get.theme.hintColor,
- ),
- );
- }
- /// 构建表情选择器
- Widget _buildEmojiSelector() {
- return Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: List.generate(
- emojis.length,
- (index) => _buildEmojiButton(index),
- ),
- );
- }
- /// 构建单个表情按钮
- Widget _buildEmojiButton(int index) {
- final isSelected = selectedEmojiIndex == index;
- return ClickOpacity(
- onTap: () {
- setState(() {
- if (selectedEmojiIndex == index) {
- // 如果点击已选中的表情,取消选择
- selectedEmojiIndex = null;
- } else {
- // 选择新的表情
- selectedEmojiIndex = index;
- }
- // 切换表情时清空已选的问题标签
- selectedIssues.clear();
- });
- },
- child: Container(
- width: 56.w,
- height: 56.w,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: isSelected ? Get.theme.cardColor : Colors.transparent,
- border: Border.all(
- color: isSelected ? Get.theme.dividerColor : Colors.transparent,
- width: 1.w,
- ),
- ),
- alignment: Alignment.center,
- child: Text(emojis[index], style: TextStyle(fontSize: 32.sp)),
- ),
- );
- }
- /// 构建问题标签
- Widget _buildIssueTags() {
- if (selectedEmojiIndex == null) return const SizedBox.shrink();
- final issues = emojiIssues[selectedEmojiIndex!] ?? [];
- return Padding(
- padding: EdgeInsets.symmetric(horizontal: 24.w),
- child: Wrap(
- spacing: 10.w,
- runSpacing: 10.h,
- alignment: WrapAlignment.center,
- children: issues.map((issue) => _buildIssueTag(issue)).toList(),
- ),
- );
- }
- /// 构建单个问题标签
- Widget _buildIssueTag(String issue) {
- final isSelected = selectedIssues.contains(issue);
- return ClickOpacity(
- onTap: () {
- setState(() {
- if (isSelected) {
- selectedIssues.remove(issue);
- } else {
- selectedIssues.add(issue);
- }
- });
- },
- child: AnimatedContainer(
- duration: const Duration(milliseconds: 200),
- padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 8.h),
- decoration: BoxDecoration(
- color: isSelected ? Get.theme.primaryColor : Get.theme.cardColor,
- borderRadius: BorderRadius.circular(24.r),
- border: Border.all(
- color: isSelected ? Get.theme.primaryColor : Get.theme.dividerColor,
- width: 1.w,
- ),
- ),
- child: Text(
- issue,
- style: TextStyle(
- fontSize: 14.sp,
- color: isSelected
- ? Get.theme.textTheme.bodyLarge!.color
- : Get.theme.hintColor,
- ),
- ),
- ),
- );
- }
- /// 构建发送按钮
- Widget _buildSendButton() {
- // 必须选中表情并且选中至少一个问题标签才能发送
- final canSend = selectedEmojiIndex != null && selectedIssues.isNotEmpty;
- return Container(
- margin: EdgeInsets.only(left: 24.w, right: 24.w, top: 24.h, bottom: 10.w),
- child: SubmitButton(
- text: Strings.send.tr,
- enabled: canSend,
- onPressed: canSend ? _handleSend : null,
- ),
- );
- }
- /// 处理发送
- void _handleSend() {
- Navigator.of(Get.context!).pop();
- // 显示成功提示
- AllDialog.showFeedback();
- }
- }
|