Flutter使用scrollable_list_tabview实现分类菜单及动态高度适配求助
解决方案
1. 食品数据按分类分组实现
先把接口返回的JSON数据转换为Dart模型类(Food类和Category类),再对食品数据按category_id做分组,同时去重得到所有唯一分类作为tab的数据源:
// 提前定义的分类模型 class Category { final int id; final String name; Category({required this.id, required this.name}); } // 提前定义的食品模型 class Food { final int id; final String name; final double price; final int categoryId; final Category category; Food({required this.id, required this.name, required this.price, required this.categoryId, required this.category}); } // 页面内定义的变量 // 原接口返回的所有食品数组,替换为你实际的数据源 List<Food> _allFoods = []; // 分类映射:key为category_id,value为对应分类下的所有食品列表 Map<int, List<Food>> _foodsByCategory = {}; // 去重后的分类列表,用于生成tab List<Category> _categories = []; // 数据分组方法,在接口返回食品数据后调用 void _groupFoodData() { _foodsByCategory.clear(); _categories.clear(); Set<int> addedCategoryIds = {}; for (var food in _allFoods) { if (!_foodsByCategory.containsKey(food.categoryId)) { _foodsByCategory[food.categoryId] = []; // 分类去重 if (!addedCategoryIds.contains(food.category.id)) { addedCategoryIds.add(food.category.id); _categories.add(food.category); } } _foodsByCategory[food.categoryId]!.add(food); } // 分组完成后计算自适应高度 _calculateExpandedHeight(); }
2. SliverAppBar高度自适应实现
因为餐饮菜单的列表项布局统一高度固定,直接累加所有列表项高度加上tab栏高度即可得到动态的expandedHeight:
// 页面内变量定义 // 你设置的tab栏高度 final double _tabHeight = 50; // 单个食品列表项的高度,ListTile默认高度为56,自定义布局请替换为实际高度 final double _foodItemHeight = 56; // 计算得到的自适应展开高度 double _expandedHeight = 0; // 高度计算方法 void _calculateExpandedHeight() { double totalListHeight = 0; // 累加所有分类下所有食品项的总高度 for (var foodList in _foodsByCategory.values) { totalListHeight += foodList.length * _foodItemHeight; } // 总高度 = tab栏高度 + 所有食品列表总高度 _expandedHeight = _tabHeight + totalListHeight; if(mounted) setState(() {}); }
修改后的SliverAppBar代码,动态生成tabs并使用自适应高度:
SliverAppBar( backgroundColor: Colors.white, automaticallyImplyLeading: false, elevation: 0.0, // 替换为计算得到的动态高度 expandedHeight: _expandedHeight, flexibleSpace: ScrollableListTabView( tabHeight: _tabHeight, bodyAnimationDuration: const Duration(milliseconds: 150), tabAnimationCurve: Curves.easeOut, tabAnimationDuration: const Duration(milliseconds: 200), // 动态生成所有分类对应的tab tabs: _categories.map((category) { List<Food> currentCategoryFoods = _foodsByCategory[category.id] ?? []; return ScrollableListTab( tab: ListTab( label: Text(category.name), icon: const Icon(Icons.fastfood), showIconOnList: false, ), body: ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: currentCategoryFoods.length, itemBuilder: (context, index) { Food food = currentCategoryFoods[index]; return ListTile( leading: Container( height: 40, width: 40, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.grey, ), alignment: Alignment.center, child: Text(food.id.toString()), ), title: Text(food.name), subtitle: Text('¥${food.price.toStringAsFixed(2)}'), ); }, ), ); }).toList(), ), ),
注意事项
- 如果你自定义了食品列表项的布局,请把
_foodItemHeight替换为你实际的每行高度值,保证高度计算准确 - 如果列表项高度不固定,可使用尺寸测量类第三方库动态测量每个item的高度再累加,常规餐饮菜单用固定高度计算的方案即可满足需求
- 请在接口加载完食品数据后再调用
_groupFoodData()方法,避免空数据导致高度计算错误
内容的提问来源于stack exchange,提问作者Ismail Muhammad
相关产品推荐
相关产品推荐

