help_center_screen.dart 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:go_router/go_router.dart';
  4. import 'package:shimmer/shimmer.dart';
  5. import '../../../core/l10n/app_localizations.dart';
  6. import '../../../core/theme/app_colors.dart';
  7. import '../../../providers/help_center_provider.dart';
  8. import '../../widgets/common/app_refresh_indicator.dart';
  9. class HelpCenterScreen extends ConsumerWidget {
  10. const HelpCenterScreen({super.key});
  11. @override
  12. Widget build(BuildContext context, WidgetRef ref) {
  13. final cs = Theme.of(context).colorScheme;
  14. final state = ref.watch(helpCenterProvider);
  15. final notifier = ref.read(helpCenterProvider.notifier);
  16. return Scaffold(
  17. appBar: AppBar(
  18. elevation: 0,
  19. leading: IconButton(
  20. icon: const Icon(Icons.chevron_left, size: 28),
  21. onPressed: () => context.pop(),
  22. ),
  23. title: Text(
  24. AppLocalizations.of(context)!.helpCenter,
  25. style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
  26. ),
  27. centerTitle: true,
  28. ),
  29. body: _buildBody(context, cs, state, notifier),
  30. );
  31. }
  32. Widget _buildBody(
  33. BuildContext context,
  34. ColorScheme cs,
  35. HelpCenterState state,
  36. HelpCenterNotifier notifier,
  37. ) {
  38. final isDark = Theme.of(context).brightness == Brightness.dark;
  39. // 首次加载 → shimmer 骨架屏
  40. if (state.isLoading && state.groups.isEmpty) {
  41. return _ShimmerGroups(cs: cs);
  42. }
  43. if (state.groups.isEmpty) {
  44. return Center(
  45. child: Text(
  46. AppLocalizations.of(context)!.noHelpContent,
  47. style: TextStyle(color: cs.onSurface.withAlpha(153)),
  48. ),
  49. );
  50. }
  51. return AppRefreshIndicator(
  52. onRefresh: notifier.refresh,
  53. child: ListView.builder(
  54. padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
  55. itemCount: state.groups.length,
  56. itemBuilder: (_, index) {
  57. final group = state.groups[index];
  58. final isExpanded = state.expandedIndices.contains(index);
  59. return Container(
  60. margin: const EdgeInsets.only(bottom: 12),
  61. decoration: BoxDecoration(
  62. color: isDark ? AppColors.darkBgSecondary : AppColors.lightBgSecondary,
  63. borderRadius: BorderRadius.circular(12),
  64. ),
  65. child: Column(
  66. children: [
  67. // 分组标题
  68. InkWell(
  69. onTap: () => notifier.toggleExpand(index),
  70. borderRadius: BorderRadius.circular(12),
  71. child: Padding(
  72. padding: const EdgeInsets.symmetric(
  73. horizontal: 16, vertical: 14),
  74. child: Row(
  75. children: [
  76. Expanded(
  77. child: Text(
  78. group.localizedGroupTitle,
  79. style: TextStyle(
  80. color: cs.onSurface,
  81. fontSize: 15,
  82. fontWeight: FontWeight.w600,
  83. ),
  84. ),
  85. ),
  86. Icon(
  87. isExpanded
  88. ? Icons.keyboard_arrow_up
  89. : Icons.keyboard_arrow_down,
  90. color: cs.onSurface.withAlpha(153),
  91. size: 22,
  92. ),
  93. ],
  94. ),
  95. ),
  96. ),
  97. // 子项列表
  98. if (isExpanded)
  99. ...group.items.map((item) {
  100. return Column(
  101. children: [
  102. Divider(height: 1, indent: 16, color: cs.outline),
  103. InkWell(
  104. onTap: () =>
  105. context.push('/user/help/${item.id}'),
  106. child: Padding(
  107. padding: const EdgeInsets.symmetric(
  108. horizontal: 16, vertical: 12),
  109. child: Row(
  110. children: [
  111. Expanded(
  112. child: Text(
  113. item.title,
  114. style: TextStyle(
  115. color: cs.onSurface,
  116. fontSize: 14,
  117. ),
  118. maxLines: 2,
  119. overflow: TextOverflow.ellipsis,
  120. ),
  121. ),
  122. Icon(
  123. Icons.chevron_right,
  124. size: 18,
  125. color: cs.onSurface.withAlpha(153),
  126. ),
  127. ],
  128. ),
  129. ),
  130. ),
  131. ],
  132. );
  133. }),
  134. ],
  135. ),
  136. );
  137. },
  138. ),
  139. );
  140. }
  141. }
  142. // ── Shimmer 骨架屏 ──────────────────────────────────────────
  143. class _ShimmerGroups extends StatelessWidget {
  144. const _ShimmerGroups({required this.cs});
  145. final ColorScheme cs;
  146. @override
  147. Widget build(BuildContext context) {
  148. return Shimmer.fromColors(
  149. baseColor: cs.onSurface.withAlpha(15),
  150. highlightColor: cs.onSurface.withAlpha(30),
  151. child: Padding(
  152. padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
  153. child: Column(
  154. children: List.generate(5, (i) {
  155. return Container(
  156. margin: const EdgeInsets.only(bottom: 12),
  157. padding:
  158. const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
  159. decoration: BoxDecoration(
  160. color: Colors.white,
  161. borderRadius: BorderRadius.circular(12),
  162. ),
  163. child: Row(
  164. children: [
  165. Expanded(
  166. child: Container(
  167. height: 16,
  168. decoration: BoxDecoration(
  169. color: Colors.white,
  170. borderRadius: BorderRadius.circular(4),
  171. ),
  172. ),
  173. ),
  174. const SizedBox(width: 40),
  175. Container(
  176. height: 20,
  177. width: 20,
  178. decoration: BoxDecoration(
  179. color: Colors.white,
  180. borderRadius: BorderRadius.circular(4),
  181. ),
  182. ),
  183. ],
  184. ),
  185. );
  186. }),
  187. ),
  188. ),
  189. );
  190. }
  191. }