You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter实现:结合静态文本与可滚动数组的ListView数据展示

Flutter 动态分类+餐厅列表实现方案

你的场景完全不需要嵌套两个ListView.builder,反而容易引发滚动冲突和不必要的性能消耗。根据常见的UI设计,给你两种适配不同需求的实现思路:

场景1:每个分类下餐厅横向滚动(主流外卖APP样式)

外层用SingleChildScrollView实现横向滚动的分类模块,每个模块内用SingleChildScrollView+Row渲染横向餐厅列表,避免嵌套可滚动组件:

// 先定义对应的数据结构(根据你的实际数据调整)
class Category {
  final String title;
  final List<Restaurant> rests;

  Category({required this.title, required this.rests});
}

class Restaurant {
  final String name;
  final String coverUrl;

  Restaurant({required this.name, required this.coverUrl});
}

// 页面实现
class DynamicRestaurantPage extends StatelessWidget {
  // 动态传入的页面标题、副标题、分类数据
  final String pageTitle;
  final String pageSubtitle;
  final List<Category> categories;

  const DynamicRestaurantPage({
    super.key,
    required this.pageTitle,
    required this.pageSubtitle,
    required this.categories,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 顶部动态标题区域
          Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(pageTitle, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
                const SizedBox(height: 8),
                Text(pageSubtitle, style: const TextStyle(fontSize: 16, color: Colors.grey)),
              ],
            ),
          ),
          // 横向滚动的分类模块区域
          Expanded(
            child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Row(
                children: categories.map((category) {
                  // 每个分类模块占屏幕90%宽度,保留左右边距的视觉效果
                  return SizedBox(
                    width: MediaQuery.of(context).size.width * 0.9,
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        // 分类标题
                        Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 16),
                          child: Text(
                            category.title,
                            style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w600),
                          ),
                        ),
                        const SizedBox(height: 12),
                        // 横向滚动的餐厅列表
                        SizedBox(
                          height: 200, // 固定餐厅卡片高度,避免布局混乱
                          child: SingleChildScrollView(
                            scrollDirection: Axis.horizontal,
                            padding: const EdgeInsets.symmetric(horizontal: 16),
                            child: Row(
                              children: category.rests.map((restaurant) {
                                return Container(
                                  width: 150,
                                  margin: const EdgeInsets.only(right: 12),
                                  decoration: BoxDecoration(
                                    borderRadius: BorderRadius.circular(12),
                                    image: DecorationImage(
                                      image: NetworkImage(restaurant.coverUrl),
                                      fit: BoxFit.cover,
                                    ),
                                  ),
                                  child: Align(
                                    alignment: Alignment.bottomCenter,
                                    child: Container(
                                      width: double.infinity,
                                      padding: const EdgeInsets.all(8),
                                      decoration: const BoxDecoration(
                                        color: Colors.black54,
                                        borderRadius: BorderRadius.only(
                                          bottomLeft: Radius.circular(12),
                                          bottomRight: Radius.circular(12),
                                        ),
                                      ),
                                      child: Text(
                                        restaurant.name,
                                        style: const TextStyle(color: Colors.white, fontSize: 14),
                                      ),
                                    ),
                                  ),
                                );
                              }).toList(),
                            ),
                          ),
                        ),
                      ],
                    ),
                  );
                }).toList(),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

场景2:每个分类下餐厅纵向滚动

如果需要分类模块横向切换,每个模块内餐厅纵向滚动,可以用ListView.builder但必须禁用自身滚动,让外层横向滚动控制整体:

// 仅修改分类模块内的餐厅列表部分
Expanded(
  child: ListView.builder(
    shrinkWrap: true,
    physics: const NeverScrollableScrollPhysics(), // 禁用自身滚动,避免冲突
    padding: const EdgeInsets.symmetric(horizontal: 16),
    itemCount: category.rests.length,
    itemBuilder: (context, index) {
      final restaurant = category.rests[index];
      return Container(
        height: 100,
        margin: const EdgeInsets.only(bottom: 12),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(12),
          color: Colors.grey[200],
        ),
        child: Row(
          children: [
            Container(
              width: 100,
              decoration: BoxDecoration(
                borderRadius: const BorderRadius.only(
                  topLeft: Radius.circular(12),
                  bottomLeft: Radius.circular(12),
                ),
                image: DecorationImage(
                  image: NetworkImage(restaurant.coverUrl),
                  fit: BoxFit.cover,
                ),
              ),
            ),
            Expanded(
              child: Padding(
                padding: const EdgeInsets.all(12),
                child: Text(restaurant.name, style: const TextStyle(fontSize: 16)),
              ),
            ),
          ],
        ),
      );
    },
  ),
)

核心要点

  1. 动态内容处理:页面标题、副标题、分类及餐厅数据全部通过参数传入,从接口或状态管理中获取后传递即可。
  2. 避免嵌套滚动:优先用SingleChildScrollView+Row/Column替代嵌套ListView.builder,减少滚动冲突和性能开销。
  3. 性能优化:如果餐厅数据量极大,建议用ListView.builder配合shrinkWrap: true,但必须设置physics: NeverScrollableScrollPhysics()避免滚动冲突。

内容的提问来源于stack exchange,提问作者Azhagappan Kathiresan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:10:39