如何创建通用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
相关产品推荐
相关产品推荐

