这是CaiYouHui前端,一个关于flutter的安卓app,前端使用flutter实现

profile_help_screen.dart 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. import 'package:flutter/material.dart';
  2. import 'package:url_launcher/url_launcher.dart';
  3. class HelpCenterScreen extends StatefulWidget {
  4. const HelpCenterScreen({super.key});
  5. @override
  6. State<HelpCenterScreen> createState() => _HelpCenterScreenState();
  7. }
  8. class _HelpCenterScreenState extends State<HelpCenterScreen> {
  9. final TextEditingController _searchController = TextEditingController();
  10. final List<FAQItem> _allFAQs = [];
  11. List<FAQItem> _filteredFAQs = [];
  12. List<FAQCategory> _categories = [];
  13. bool _isSearching = false;
  14. String _selectedCategoryId = 'all';
  15. @override
  16. void initState() {
  17. super.initState();
  18. _initializeData();
  19. _searchController.addListener(_onSearchChanged);
  20. }
  21. @override
  22. void dispose() {
  23. _searchController.dispose();
  24. super.dispose();
  25. }
  26. void _initializeData() {
  27. // 初始化分类数据
  28. _categories = [
  29. FAQCategory(id: 'all', name: '全部', icon: Icons.all_inclusive, color: Colors.blue),
  30. FAQCategory(id: 'account', name: '账户问题', icon: Icons.person, color: Colors.purple),
  31. FAQCategory(id: 'payment', name: '支付相关', icon: Icons.payment, color: Colors.green),
  32. FAQCategory(id: 'order', name: '订单问题', icon: Icons.shopping_cart, color: Colors.orange),
  33. FAQCategory(id: 'app', name: '应用使用', icon: Icons.phone_iphone, color: Colors.red),
  34. FAQCategory(id: 'privacy', name: '隐私安全', icon: Icons.security, color: Colors.teal),
  35. FAQCategory(id: 'other', name: '其他问题', icon: Icons.help_outline, color: Colors.grey),
  36. ];
  37. // 初始化FAQ数据
  38. _allFAQs.addAll([
  39. FAQItem(
  40. id: '1',
  41. question: '如何修改个人资料?',
  42. answer: '您可以在"我的"页面点击头像或个人信息进入编辑页面进行修改。修改完成后记得点击保存按钮。',
  43. categoryId: 'account',
  44. isExpanded: false,
  45. ),
  46. FAQItem(
  47. id: '2',
  48. question: '忘记密码怎么办?',
  49. answer: '在登录页面点击"忘记密码",按照提示输入注册邮箱或手机号,系统将发送重置密码链接到您的邮箱或手机。',
  50. categoryId: 'account',
  51. isExpanded: false,
  52. ),
  53. FAQItem(
  54. id: '3',
  55. question: '如何绑定/解绑第三方登录?',
  56. answer: '进入"设置" -> "账户与安全" -> "第三方账号绑定",在这里可以绑定或解绑微信、Apple ID等第三方账号。',
  57. categoryId: 'account',
  58. isExpanded: false,
  59. ),
  60. FAQItem(
  61. id: '4',
  62. question: '支付方式有哪些?',
  63. answer: '我们支持微信支付、支付宝、银联卡等多种支付方式。在支付时可以选择您方便的支付方式进行付款。',
  64. categoryId: 'payment',
  65. isExpanded: false,
  66. ),
  67. FAQItem(
  68. id: '5',
  69. question: '支付遇到问题怎么办?',
  70. answer: '1. 检查网络连接\n2. 确认支付账户余额充足\n3. 检查银行卡是否过期\n4. 如仍无法解决,请联系客服提供订单号',
  71. categoryId: 'payment',
  72. isExpanded: false,
  73. ),
  74. FAQItem(
  75. id: '6',
  76. question: '如何查看订单状态?',
  77. answer: '进入"我的订单"页面,可以查看所有订单的详细状态,包括待支付、进行中、已完成、已取消等状态。',
  78. categoryId: 'order',
  79. isExpanded: false,
  80. ),
  81. FAQItem(
  82. id: '7',
  83. question: '如何取消订单?',
  84. answer: '在订单详情页面,如果订单状态允许取消,会出现"取消订单"按钮。请注意,部分订单在特定状态下不可取消。',
  85. categoryId: 'order',
  86. isExpanded: false,
  87. ),
  88. FAQItem(
  89. id: '8',
  90. question: '应用出现闪退怎么办?',
  91. answer: '1. 尝试重启应用\n2. 检查应用是否为最新版本\n3. 清理手机缓存\n4. 重启手机设备\n5. 如问题持续,请反馈给我们',
  92. categoryId: 'app',
  93. isExpanded: false,
  94. ),
  95. FAQItem(
  96. id: '9',
  97. question: '如何更新应用到最新版本?',
  98. answer: 'iOS用户:前往App Store搜索应用并更新\nAndroid用户:前往应用商店或应用内检查更新',
  99. categoryId: 'app',
  100. isExpanded: false,
  101. ),
  102. FAQItem(
  103. id: '10',
  104. question: '如何保护我的隐私信息?',
  105. answer: '我们采用加密技术保护您的数据,不会向第三方泄露您的个人信息。您可以在隐私设置中管理数据权限。',
  106. categoryId: 'privacy',
  107. isExpanded: false,
  108. ),
  109. FAQItem(
  110. id: '11',
  111. question: '如何联系客服?',
  112. answer: '1. 拨打电话:400-123-4567(工作日 9:00-18:00)\n2. 发送邮件:support@example.com\n3. 在线客服:应用内"我的客服"',
  113. categoryId: 'other',
  114. isExpanded: false,
  115. ),
  116. FAQItem(
  117. id: '12',
  118. question: '服务时间是什么时候?',
  119. answer: '在线客服:每天 8:00-22:00\n电话客服:工作日 9:00-18:00\n节假日服务时间可能调整',
  120. categoryId: 'other',
  121. isExpanded: false,
  122. ),
  123. ]);
  124. _filteredFAQs = List.from(_allFAQs);
  125. }
  126. void _onSearchChanged() {
  127. final query = _searchController.text.trim();
  128. setState(() {
  129. _isSearching = query.isNotEmpty;
  130. _filteredFAQs = _allFAQs.where((faq) {
  131. final matchesSearch = faq.question.toLowerCase().contains(query.toLowerCase()) ||
  132. (faq.answer?.toLowerCase().contains(query.toLowerCase()) ?? false);
  133. final matchesCategory = _selectedCategoryId == 'all' ||
  134. faq.categoryId == _selectedCategoryId;
  135. return matchesSearch && matchesCategory;
  136. }).toList();
  137. });
  138. }
  139. void _selectCategory(String categoryId) {
  140. setState(() {
  141. _selectedCategoryId = categoryId;
  142. _filteredFAQs = _allFAQs.where((faq) {
  143. final matchesSearch = _isSearching
  144. ? (faq.question.toLowerCase().contains(_searchController.text.toLowerCase()) ||
  145. (faq.answer?.toLowerCase().contains(_searchController.text.toLowerCase()) ?? false))
  146. : true;
  147. final matchesCategory = categoryId == 'all' || faq.categoryId == categoryId;
  148. return matchesSearch && matchesCategory;
  149. }).toList();
  150. });
  151. }
  152. void _toggleFAQ(int index) {
  153. setState(() {
  154. _filteredFAQs[index].isExpanded = !_filteredFAQs[index].isExpanded;
  155. });
  156. }
  157. Future<void> _contactSupport(String method) async {
  158. switch (method) {
  159. case 'phone':
  160. final Uri telUri = Uri.parse('tel:4001234567');
  161. if (await canLaunchUrl(telUri)) {
  162. await launchUrl(telUri);
  163. }
  164. break;
  165. case 'email':
  166. final Uri emailUri = Uri(
  167. scheme: 'mailto',
  168. path: 'support@example.com',
  169. queryParameters: {'subject': '用户咨询'},
  170. );
  171. if (await canLaunchUrl(emailUri)) {
  172. await launchUrl(emailUri);
  173. }
  174. break;
  175. }
  176. }
  177. void _showFeedbackDialog(BuildContext context) {
  178. showDialog(
  179. context: context,
  180. builder: (context) => AlertDialog(
  181. title: const Text('问题反馈'),
  182. content: const Text('您可以将遇到的问题详细描述并发送给我们,我们会尽快处理。'),
  183. actions: [
  184. TextButton(
  185. onPressed: () => Navigator.of(context).pop(),
  186. child: const Text('取消'),
  187. ),
  188. TextButton(
  189. onPressed: () {
  190. Navigator.of(context).pop();
  191. _contactSupport('email');
  192. },
  193. child: const Text('去反馈'),
  194. ),
  195. ],
  196. ),
  197. );
  198. }
  199. @override
  200. Widget build(BuildContext context) {
  201. return Scaffold(
  202. appBar: AppBar(
  203. title: const Text('帮助中心'),
  204. actions: [
  205. IconButton(
  206. icon: const Icon(Icons.feedback_outlined),
  207. tooltip: '问题反馈',
  208. onPressed: () => _showFeedbackDialog(context),
  209. ),
  210. ],
  211. ),
  212. body: Column(
  213. children: [
  214. // 搜索栏
  215. Padding(
  216. padding: const EdgeInsets.all(16),
  217. child: Container(
  218. decoration: BoxDecoration(
  219. color: Colors.grey[100],
  220. borderRadius: BorderRadius.circular(12),
  221. border: Border.all(color: Colors.grey[300]!),
  222. ),
  223. child: TextField(
  224. controller: _searchController,
  225. decoration: InputDecoration(
  226. hintText: '搜索问题或关键词...',
  227. prefixIcon: const Icon(Icons.search, color: Colors.grey),
  228. border: InputBorder.none,
  229. suffixIcon: _searchController.text.isNotEmpty
  230. ? IconButton(
  231. icon: const Icon(Icons.clear, color: Colors.grey),
  232. onPressed: () {
  233. _searchController.clear();
  234. _onSearchChanged();
  235. },
  236. )
  237. : null,
  238. contentPadding: const EdgeInsets.symmetric(vertical: 14),
  239. ),
  240. ),
  241. ),
  242. ),
  243. // 分类导航
  244. SizedBox(
  245. height: 100,
  246. child: ListView.builder(
  247. scrollDirection: Axis.horizontal,
  248. padding: const EdgeInsets.symmetric(horizontal: 16),
  249. itemCount: _categories.length,
  250. itemBuilder: (context, index) {
  251. final category = _categories[index];
  252. final isSelected = _selectedCategoryId == category.id;
  253. return Padding(
  254. padding: const EdgeInsets.only(right: 12),
  255. child: Column(
  256. children: [
  257. GestureDetector(
  258. onTap: () => _selectCategory(category.id),
  259. child: Container(
  260. width: 70,
  261. height: 70,
  262. decoration: BoxDecoration(
  263. color: isSelected
  264. ? category.color.withOpacity(0.2)
  265. : Colors.grey[50],
  266. borderRadius: BorderRadius.circular(16),
  267. border: Border.all(
  268. color: isSelected
  269. ? category.color
  270. : Colors.transparent,
  271. width: 2,
  272. ),
  273. ),
  274. child: Column(
  275. mainAxisAlignment: MainAxisAlignment.center,
  276. children: [
  277. Icon(category.icon, color: category.color, size: 28),
  278. const SizedBox(height: 4),
  279. ],
  280. ),
  281. ),
  282. ),
  283. const SizedBox(height: 4),
  284. Text(
  285. category.name,
  286. style: TextStyle(
  287. fontSize: 12,
  288. fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
  289. color: isSelected ? category.color : Colors.grey[700],
  290. ),
  291. ),
  292. ],
  293. ),
  294. );
  295. },
  296. ),
  297. ),
  298. // 问题列表
  299. Expanded(
  300. child: _filteredFAQs.isEmpty
  301. ? Center(
  302. child: Column(
  303. mainAxisAlignment: MainAxisAlignment.center,
  304. children: [
  305. Icon(
  306. Icons.search_off,
  307. size: 60,
  308. color: Colors.grey[400],
  309. ),
  310. const SizedBox(height: 16),
  311. Text(
  312. _isSearching
  313. ? '没有找到相关问题的解答'
  314. : '暂无问题',
  315. style: TextStyle(
  316. color: Colors.grey[600],
  317. fontSize: 16,
  318. ),
  319. ),
  320. if (_isSearching) ...[
  321. const SizedBox(height: 8),
  322. TextButton(
  323. onPressed: () => _showFeedbackDialog(context),
  324. child: const Text('反馈问题给我们'),
  325. ),
  326. ],
  327. ],
  328. ),
  329. )
  330. : ListView.builder(
  331. padding: const EdgeInsets.all(16),
  332. itemCount: _filteredFAQs.length,
  333. itemBuilder: (context, index) {
  334. final faq = _filteredFAQs[index];
  335. final category = _categories.firstWhere(
  336. (cat) => cat.id == faq.categoryId,
  337. orElse: () => _categories.last,
  338. );
  339. return Card(
  340. margin: const EdgeInsets.only(bottom: 12),
  341. elevation: 1,
  342. shape: RoundedRectangleBorder(
  343. borderRadius: BorderRadius.circular(12),
  344. side: BorderSide(color: Colors.grey[200]!),
  345. ),
  346. child: ExpansionTile(
  347. key: Key(faq.id),
  348. initiallyExpanded: faq.isExpanded,
  349. onExpansionChanged: (_) => _toggleFAQ(index),
  350. leading: Container(
  351. width: 36,
  352. height: 36,
  353. decoration: BoxDecoration(
  354. color: category.color.withOpacity(0.1),
  355. borderRadius: BorderRadius.circular(8),
  356. ),
  357. child: Icon(
  358. category.icon,
  359. color: category.color,
  360. size: 20,
  361. ),
  362. ),
  363. title: Text(
  364. faq.question,
  365. style: const TextStyle(
  366. fontWeight: FontWeight.w500,
  367. fontSize: 15,
  368. ),
  369. ),
  370. children: [
  371. Padding(
  372. padding: const EdgeInsets.fromLTRB(68, 8, 16, 16),
  373. child: Column(
  374. crossAxisAlignment: CrossAxisAlignment.start,
  375. children: [
  376. Text(
  377. faq.answer ?? '',
  378. style: TextStyle(
  379. color: Colors.grey[700],
  380. fontSize: 14,
  381. height: 1.5,
  382. ),
  383. ),
  384. if (faq.id == '11') ...[
  385. const SizedBox(height: 16),
  386. const Text(
  387. '快速联系:',
  388. style: TextStyle(
  389. fontWeight: FontWeight.bold,
  390. fontSize: 14,
  391. ),
  392. ),
  393. const SizedBox(height: 8),
  394. Row(
  395. children: [
  396. ElevatedButton.icon(
  397. onPressed: () => _contactSupport('phone'),
  398. icon: const Icon(Icons.phone, size: 16),
  399. label: const Text('电话联系'),
  400. style: ElevatedButton.styleFrom(
  401. backgroundColor: Colors.green[50],
  402. foregroundColor: Colors.green,
  403. elevation: 0,
  404. ),
  405. ),
  406. const SizedBox(width: 12),
  407. ElevatedButton.icon(
  408. onPressed: () => _contactSupport('email'),
  409. icon: const Icon(Icons.email, size: 16),
  410. label: const Text('发送邮件'),
  411. style: ElevatedButton.styleFrom(
  412. backgroundColor: Colors.blue[50],
  413. foregroundColor: Colors.blue,
  414. elevation: 0,
  415. ),
  416. ),
  417. ],
  418. ),
  419. ],
  420. ],
  421. ),
  422. ),
  423. ],
  424. ),
  425. );
  426. },
  427. ),
  428. ),
  429. // 底部联系栏
  430. Container(
  431. padding: const EdgeInsets.all(16),
  432. decoration: BoxDecoration(
  433. color: Colors.grey[50],
  434. border: Border(top: BorderSide(color: Colors.grey[300]!)),
  435. ),
  436. child: Column(
  437. children: [
  438. const Text(
  439. '没有找到您需要的答案?',
  440. style: TextStyle(
  441. fontWeight: FontWeight.w600,
  442. fontSize: 15,
  443. ),
  444. ),
  445. const SizedBox(height: 8),
  446. const Text(
  447. '我们的客服团队随时为您提供帮助',
  448. style: TextStyle(color: Colors.grey, fontSize: 13),
  449. ),
  450. const SizedBox(height: 16),
  451. Row(
  452. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  453. children: [
  454. Expanded(
  455. child: OutlinedButton.icon(
  456. onPressed: () => _contactSupport('phone'),
  457. icon: const Icon(Icons.phone, size: 18),
  458. label: const Text('电话客服'),
  459. style: OutlinedButton.styleFrom(
  460. padding: const EdgeInsets.symmetric(vertical: 12),
  461. ),
  462. ),
  463. ),
  464. const SizedBox(width: 12),
  465. Expanded(
  466. child: ElevatedButton.icon(
  467. onPressed: () => _showFeedbackDialog(context),
  468. icon: const Icon(Icons.chat, size: 18),
  469. label: const Text('在线反馈'),
  470. style: ElevatedButton.styleFrom(
  471. padding: const EdgeInsets.symmetric(vertical: 12),
  472. ),
  473. ),
  474. ),
  475. ],
  476. ),
  477. ],
  478. ),
  479. ),
  480. ],
  481. ),
  482. );
  483. }
  484. }
  485. // 数据模型
  486. class FAQItem {
  487. final String id;
  488. final String question;
  489. final String? answer;
  490. final String categoryId;
  491. bool isExpanded;
  492. FAQItem({
  493. required this.id,
  494. required this.question,
  495. this.answer,
  496. required this.categoryId,
  497. required this.isExpanded,
  498. });
  499. }
  500. class FAQCategory {
  501. final String id;
  502. final String name;
  503. final IconData icon;
  504. final Color color;
  505. FAQCategory({
  506. required this.id,
  507. required this.name,
  508. required this.icon,
  509. required this.color,
  510. });
  511. }