menu_list.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import 'package:flutter/material.dart' hide Banner;
  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 '../../../data/models/banner/banner_list.dart';
  6. import '../../../widgets/ix_image.dart';
  7. import '../controllers/home_controller.dart';
  8. /// 菜单列表组件
  9. /// 支持放在 SliverFillRemaining 中
  10. /// 排列规则:
  11. /// - 9个:3排,每排3个
  12. /// - 6个:2排,每排3个
  13. /// - 5个:2排,第一排3个,第二排2个
  14. /// - 4个:2排,每排2个
  15. /// - 3个:1排,每排3个
  16. /// - 2个:1排,每排2个
  17. /// - 1个:1排,占满
  18. class MenuList extends StatelessWidget {
  19. const MenuList({super.key});
  20. /// 根据数量计算每排的布局
  21. /// 返回一个二维列表,每个子列表表示一排的 Banner
  22. List<List<Banner>> _calculateLayout(List<Banner> items) {
  23. final count = items.length.clamp(0, 9);
  24. if (count == 0) return [];
  25. switch (count) {
  26. case 1:
  27. return [items.sublist(0, 1)];
  28. case 2:
  29. return [items.sublist(0, 2)];
  30. case 3:
  31. return [items.sublist(0, 3)];
  32. case 4:
  33. return [items.sublist(0, 2), items.sublist(2, 4)];
  34. case 5:
  35. return [items.sublist(0, 3), items.sublist(3, 5)];
  36. case 6:
  37. return [items.sublist(0, 3), items.sublist(3, 6)];
  38. case 7:
  39. return [items.sublist(0, 3), items.sublist(3, 6), items.sublist(6, 7)];
  40. case 8:
  41. return [items.sublist(0, 3), items.sublist(3, 6), items.sublist(6, 8)];
  42. case 9:
  43. default:
  44. return [items.sublist(0, 3), items.sublist(3, 6), items.sublist(6, 9)];
  45. }
  46. }
  47. @override
  48. Widget build(BuildContext context) {
  49. final controller = Get.find<HomeController>();
  50. return Obx(() {
  51. if (controller.nineBannerList.isEmpty) {
  52. return const SizedBox.shrink();
  53. }
  54. final layout = _calculateLayout(controller.nineBannerList);
  55. return Column(
  56. mainAxisSize: MainAxisSize.min,
  57. children: layout.asMap().entries.map((entry) {
  58. final rowIndex = entry.key;
  59. final rowItems = entry.value;
  60. return Padding(
  61. padding: EdgeInsets.only(top: rowIndex > 0 ? 8.w : 0),
  62. child: _buildRow(rowItems, controller),
  63. );
  64. }).toList(),
  65. );
  66. });
  67. }
  68. /// 构建一排菜单项
  69. Widget _buildRow(List<Banner> items, HomeController controller) {
  70. return Row(
  71. children: items.asMap().entries.map((entry) {
  72. final index = entry.key;
  73. final banner = entry.value;
  74. return Expanded(
  75. child: Padding(
  76. padding: EdgeInsets.only(left: index > 0 ? 8.w : 0),
  77. child: _buildMenuItem(banner, controller),
  78. ),
  79. );
  80. }).toList(),
  81. );
  82. }
  83. /// 构建单个菜单项
  84. Widget _buildMenuItem(Banner banner, HomeController controller) {
  85. return GestureDetector(
  86. onTap: () => controller.onBannerTap(banner),
  87. child: Container(
  88. height: 56.w,
  89. decoration: BoxDecoration(
  90. color: Get.reactiveTheme.cardColor,
  91. borderRadius: BorderRadius.circular(10.r),
  92. ),
  93. child: Column(
  94. mainAxisAlignment: MainAxisAlignment.center,
  95. children: [
  96. // 图标(网络图片)
  97. IXImage(
  98. source: banner.img ?? '',
  99. width: 20.w,
  100. height: 20.w,
  101. sourceType: ImageSourceType.network,
  102. ),
  103. 4.verticalSpaceFromWidth,
  104. // 标题
  105. Text(
  106. banner.title ?? '',
  107. textAlign: TextAlign.center,
  108. maxLines: 1,
  109. overflow: TextOverflow.ellipsis,
  110. style: TextStyle(
  111. fontSize: 13.sp,
  112. height: 1.4,
  113. color: Get.theme.hintColor,
  114. fontWeight: FontWeight.w500,
  115. ),
  116. ),
  117. ],
  118. ),
  119. ),
  120. );
  121. }
  122. }