menu_list.dart 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_screenutil/flutter_screenutil.dart';
  3. import 'package:get/get.dart';
  4. import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
  5. import '../../../../config/translations/strings_enum.dart';
  6. import '../../../../utils/system_helper.dart';
  7. import '../../../constants/iconfont/iconfont.dart';
  8. import '../../../dialog/all_dialog.dart';
  9. import '../../../routes/app_pages.dart';
  10. /// 菜单项数据模型
  11. class MenuItem {
  12. final IconData icon;
  13. final String title;
  14. final Color iconColor;
  15. final VoidCallback? onTap;
  16. MenuItem({
  17. required this.icon,
  18. required this.title,
  19. required this.iconColor,
  20. this.onTap,
  21. });
  22. }
  23. /// 菜单列表组件
  24. /// 支持放在 SliverFillRemaining 中
  25. /// 排列规则:
  26. /// - 6个:2排,每排3个
  27. /// - 5个:2排,第一排3个,第二排2个
  28. /// - 4个:2排,每排2个
  29. /// - 3个:1排,每排3个
  30. /// - 2个:1排,每排2个
  31. /// - 1个:1排,占满
  32. class MenuList extends StatelessWidget {
  33. const MenuList({super.key});
  34. // 获取菜单项列表(最多6个)
  35. List<MenuItem> _getMenuItems() {
  36. return [
  37. MenuItem(
  38. icon: IconFont.icon19,
  39. title: Strings.moviesAndTV.tr,
  40. iconColor: const Color(0xFFFF3B30),
  41. onTap: () {
  42. print('Movies&TV tapped');
  43. Get.toNamed(Routes.MEDIALOCATION);
  44. },
  45. ),
  46. MenuItem(
  47. icon: IconFont.icon26,
  48. title: Strings.social.tr,
  49. iconColor: const Color(0xFF007AFF),
  50. onTap: () {
  51. print('Social tapped');
  52. SystemHelper.openTestPage();
  53. },
  54. ),
  55. MenuItem(
  56. icon: IconFont.icon28,
  57. title: Strings.support.tr,
  58. iconColor: const Color(0xFF34C759),
  59. onTap: () {
  60. print('Support tapped');
  61. AllDialog.showUpdate(hasForceUpdate: true);
  62. },
  63. ),
  64. MenuItem(
  65. icon: IconFont.icon41,
  66. title: Strings.sport.tr,
  67. iconColor: const Color(0xFFFF9500),
  68. onTap: () {
  69. print('Sport tapped');
  70. },
  71. ),
  72. MenuItem(
  73. icon: IconFont.icon52,
  74. title: Strings.music.tr,
  75. iconColor: const Color(0xFF00C7BE),
  76. onTap: () {
  77. print('Music tapped');
  78. },
  79. ),
  80. MenuItem(
  81. icon: IconFont.icon53,
  82. title: Strings.game.tr,
  83. iconColor: const Color(0xFFAF52DE),
  84. onTap: () {
  85. print('Game tapped');
  86. },
  87. ),
  88. ];
  89. }
  90. /// 根据数量计算每排的布局
  91. /// 返回一个二维列表,每个子列表表示一排的菜单项
  92. List<List<MenuItem>> _calculateLayout(List<MenuItem> items) {
  93. final count = items.length.clamp(0, 6);
  94. if (count == 0) return [];
  95. switch (count) {
  96. case 1:
  97. // 1个:1排占满
  98. return [items.sublist(0, 1)];
  99. case 2:
  100. // 2个:1排,每排2个
  101. return [items.sublist(0, 2)];
  102. case 3:
  103. // 3个:1排,每排3个
  104. return [items.sublist(0, 3)];
  105. case 4:
  106. // 4个:2排,每排2个
  107. return [items.sublist(0, 2), items.sublist(2, 4)];
  108. case 5:
  109. // 5个:2排,第一排3个,第二排2个
  110. return [items.sublist(0, 3), items.sublist(3, 5)];
  111. case 6:
  112. default:
  113. // 6个:2排,每排3个
  114. return [items.sublist(0, 3), items.sublist(3, 6)];
  115. }
  116. }
  117. @override
  118. Widget build(BuildContext context) {
  119. final menuItems = _getMenuItems();
  120. final layout = _calculateLayout(menuItems);
  121. return Column(
  122. mainAxisSize: MainAxisSize.min,
  123. children: layout.asMap().entries.map((entry) {
  124. final rowIndex = entry.key;
  125. final rowItems = entry.value;
  126. return Padding(
  127. padding: EdgeInsets.only(top: rowIndex > 0 ? 8.w : 0),
  128. child: _buildRow(rowItems),
  129. );
  130. }).toList(),
  131. );
  132. }
  133. /// 构建一排菜单项
  134. Widget _buildRow(List<MenuItem> items) {
  135. return Row(
  136. children: items.asMap().entries.map((entry) {
  137. final index = entry.key;
  138. final item = entry.value;
  139. return Expanded(
  140. child: Padding(
  141. padding: EdgeInsets.only(left: index > 0 ? 8.w : 0),
  142. child: _buildMenuItem(item),
  143. ),
  144. );
  145. }).toList(),
  146. );
  147. }
  148. /// 构建单个菜单项
  149. Widget _buildMenuItem(MenuItem item) {
  150. return GestureDetector(
  151. onTap: item.onTap,
  152. child: Container(
  153. height: 56.w,
  154. decoration: BoxDecoration(
  155. color: Get.reactiveTheme.cardColor,
  156. borderRadius: BorderRadius.circular(10.r),
  157. ),
  158. child: Column(
  159. mainAxisAlignment: MainAxisAlignment.center,
  160. children: [
  161. // 图标
  162. Icon(item.icon, size: 20.w, color: item.iconColor),
  163. 4.verticalSpaceFromWidth,
  164. // 标题
  165. Text(
  166. item.title,
  167. textAlign: TextAlign.center,
  168. maxLines: 1,
  169. overflow: TextOverflow.ellipsis,
  170. style: TextStyle(
  171. fontSize: 13.sp,
  172. height: 1.4,
  173. color: Get.theme.hintColor,
  174. fontWeight: FontWeight.w500,
  175. ),
  176. ),
  177. ],
  178. ),
  179. ),
  180. );
  181. }
  182. }