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

如何创建通用Stall页面实现多摊位数据动态切换?

通用摊位页面实现方案(避免重复创建页面)

核心思路

用单一通用页面+数据传递替代多个重复页面,通过向StallPage传入不同的Stall对象,实现页面内容动态切换,彻底告别stall1_page、stall2_page这类冗余代码。

具体改造步骤

1. 重构Stall类:统一管理所有摊位数据

把所有摊位的生成逻辑整合到一处,方便后续统一调用和维护:

class Stall {
  String name;
  String label;
  String logoUrl;
  String desc;
  num score;
  Map<String, List<Food>> menu;

  Stall(
    this.name,
    this.label,
    this.logoUrl,
    this.desc,
    this.score,
    this.menu,
  );

  // 生成所有摊位的完整数据列表
  static List<Stall> generateAllStalls() {
    return [
      Stall(
        'Tray blazers',
        'Restaurant',
        'assets/images/tray_blazers.jpg',
        'Tray Blazers by Chef Tracy',
        4.5,
        {
          'Recommended': Food.generateRecommendedFoods1(),
          'Popular': Food.generatePopularFoods1(),
          'Smoothie': [],
          'Rice': [],
        },
      ),
      Stall(
        'Papa Rimz',
        'Restaurant',
        'assets/images/papa_rimz.png',
        '',
        4.3,
        {
          'Recommended': [], // 填入Papa Rimz的实际菜单数据
          'Popular': [],
        },
      ),
      Stall(
        'W SAUCE',
        'Restaurant',
        'assets/images/w_sauce-removebg.png',
        '',
        4.7,
        {
          'Recommended': [], // 填入W SAUCE的实际菜单数据
          'Popular': [],
        },
      ),
      // 其他摊位按相同格式依次添加
    ];
  }

  // 根据摊位名称快速查找对应数据
  static Stall getStallByName(String name) {
    return generateAllStalls().firstWhere((stall) => stall.name == name);
  }
}

2. 修改Item类:关联摊位数据而非固定路由

去掉冗余的view字段,改为存储对应摊位的名称,用于后续匹配数据:

class Item {
  String title;
  String subtitle;
  String event;
  String img;
  String stallName; // 关联对应摊位的名称

  Item({
    required this.title,
    required this.subtitle,
    required this.event,
    required this.img,
    required this.stallName,
  });
}

3. 更新GridDashboard:动态生成Item并传递数据到通用页面

自动从Stall数据生成列表项,点击时导航到通用StallPage并传入对应摊位数据:

class GridDashboard extends StatelessWidget {
  final List<Stall> allStalls = Stall.generateAllStalls();

  // 从Stall数据自动生成Item列表
  List<Item> get myList => allStalls.map((stall) {
        return Item(
          title: stall.name,
          subtitle: 'Open',
          event: stall.name == 'Tray blazers' ? 'by Chef Tracy' : '',
          img: stall.logoUrl, // 若首页图标和摊位logo不同,可单独设置img路径
          stallName: stall.name,
        );
      }).toList();

  @override
  Widget build(BuildContext context) {
    return Flexible(
      child: GridView.count(
        childAspectRatio: 1.0,
        padding: const EdgeInsets.only(left: 16, right: 16),
        crossAxisCount: 2,
        crossAxisSpacing: 18,
        mainAxisSpacing: 18,
        children: myList.map((data) {
          return Container(
            decoration: BoxDecoration(
              color: const Color(0xff453658),
              borderRadius: BorderRadius.circular(10),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                GestureDetector(
                  onTap: () {
                    // 获取当前Item对应的摊位数据
                    final targetStall = Stall.getStallByName(data.stallName);
                    // 导航到通用StallPage并传入数据
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => StallPage(stall: targetStall),
                      ),
                    );
                  },
                  child: Image.asset(data.img, width: 90),
                ),
                const SizedBox(height: 14),
                Text(
                  data.title,
                  style: const TextStyle(
                    fontWeight: FontWeight.w600,
                    fontSize: 13,
                    color: Colors.white,
                  ),
                ),
                const SizedBox(height: 8),
                Text(
                  data.subtitle,
                  style: const TextStyle(
                    fontWeight: FontWeight.w600,
                    fontSize: 10,
                    color: Colors.white38,
                  ),
                ),
              ],
            ),
          );
        }).toList(),
      ),
    );
  }
}

4. 改造StallPage:接收外部传入的摊位数据

将StallPage改为可接收外部Stall参数的组件,彻底摆脱固定数据:

class StallPage extends StatefulWidget {
  final Stall stall; // 接收外部传入的摊位数据

  const StallPage({super.key, required this.stall});

  @override
  State<StallPage> createState() => _StallPageState();
}

class _StallPageState extends State<StallPage> {
  var selected = 0;
  final pageController = PageController();

  @override
  Widget build(BuildContext context) {
    // 使用widget.stall获取传入的摊位数据
    final stall = widget.stall;

    return Scaffold(
      backgroundColor: const Color(0xff392850),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          CustomAppBar(
            Icons.arrow_back_ios_outlined,
            Icons.search_outlined,
            leftCallback: () => Navigator.of(context).pop(),
          ),
          // 向StallInfo传递摊位数据
          StallInfo(stall: stall),
          FoodList(
            selected,
            (int index) {
              setState(() {
                selected = index;
              });
              pageController.jumpToPage(index);
            },
            stall,
          ),
          Expanded(
            child: FoodListView(
              selected,
              (int index) {
                setState(() {
                  selected = index;
                });
              },
              pageController,
              stall,
            ),
          ),
          Container(
            padding: EdgeInsets.symmetric(horizontal: 25),
            height: 60,
            child: SmoothPageIndicator(
              controller: pageController,
              count: stall.menu.length,
              effect: CustomizableEffect(
                dotDecoration: DotDecoration(
                  width: 8,
                  height: 8,
                  color: Colors.grey.withOpacity(0.5),
                  borderRadius: BorderRadius.circular(8),
                ),
                activeDotDecoration: DotDecoration(
                  width: 10,
                  height: 10,
                  color: const Color(0xff392850),
                  borderRadius: BorderRadius.circular(10),
                  dotBorder: const DotBorder(
                    color: Colors.orange, // 替换为你的kPrimaryColor
                    padding: 2,
                    width: 2,
                  ),
                ),
              ),
              onDotClicked: (index) => pageController.jumpToPage(index),
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        backgroundColor: Colors.orange, // 替换为你的kPrimaryColor
        elevation: 2,
        child: const Icon(
          Icons.shopping_cart_outlined,
          color: Colors.black,
          size: 30,
        ),
      ),
    );
  }
}

5. 改造StallInfo:接收外部传入的摊位数据

去掉内部固定生成的stall,改为接收外部参数:

class StallInfo extends StatelessWidget {
  final Stall stall; // 接收外部传入的摊位数据

  const StallInfo({super.key, required this.stall});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(top: 40),
      padding: const EdgeInsets.symmetric(horizontal: 25),
      child: Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    stall.name,
                    style: const TextStyle(
                      fontSize: 25,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 10),
                  Row(
                    children: [
                      Container(
                          padding: const EdgeInsets.all(5),
                          decoration: BoxDecoration(
                            color: Colors.blueGrey.withOpacity(0.4),
                            borderRadius: BorderRadius.circular(5),
                          ),
                          child: Text(
                            stall.label,
                            style: const TextStyle(
                              color: Colors.white,
                            ),
                          )),
                      const SizedBox(width: 10),
                    ],
                  )
                ],
              ),
              ClipRRect(
                borderRadius: BorderRadius.circular(50),
                child: Image.asset(
                  stall.logoUrl,
                  width: 80,
                ),
              ),
            ],
          ),
          const SizedBox(height: 5),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                stall.desc,
                style: const TextStyle(fontSize: 16),
              ),
              Row(
                children: [
                  const Icon(
                    Icons.star_outline,
                    color: Colors.amber,
                  ),
                  Text(
                    '${stall.score}',
                    style: const TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(width: 15),
                ],
              )
            ],
          )
        ],
      ),
    );
  }
}

额外优化建议

  • 可将摊位数据迁移到JSON文件,通过解析JSON生成Stall列表,进一步降低代码耦合度
  • 若使用命名路由,可通过arguments传递Stall对象:
    // 路由注册
    routes: {
      '/stall': (context) => StallPage(),
    },
    // 导航时传递参数
    Navigator.pushNamed(
      context,
      '/stall',
      arguments: targetStall,
    );
    // 在StallPage中获取参数
    final stall = ModalRoute.of(context)!.settings.arguments as Stall;
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 09:25:23