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

Flutter餐厅菜单应用如何传参复用单页动态加载不同JSON数据

实现方案

你只需要调整首页传参逻辑、复用列表页代码做一个通用页即可,无需为每个分类单独写页面:

1. 新建通用分类展示页面

创建新文件menu_category_page.dart,代码如下,保留你原有渲染逻辑的同时新增JSON路径参数:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:localize_and_translate/localize_and_translate.dart';

// 新增构造参数接收JSON路径
class MenuCategoryPage extends StatelessWidget {
  const MenuCategoryPage({super.key, required this.jsonPath});
  // 外部传入的对应分类JSON文件路径
  final String jsonPath;

  @override
  Widget build(BuildContext context) {
    var screenHeight = MediaQuery.of(context).size.height;
    var screenWidth = MediaQuery.of(context).size.width;

    Future<List<Widget>> createList() async {
      List<Widget> items = <Widget>[];
      // 这里改用传入的jsonPath加载对应文件
      String dataString = await rootBundle.loadString(translator.translate(jsonPath));
      List<dynamic> dataJSON = jsonDecode(dataString);

      dataJSON.forEach((object) {
        String finalString = "";
        List<dynamic> dataList = object["placeItems"];
        dataList.forEach((item) {
          finalString = finalString + item + " | ";
        });

        items.add(Padding(
          padding: EdgeInsets.all(2.0),
          child: Container(
            decoration: BoxDecoration(
                color: Color.fromRGBO(255, 255, 255, 0.7),
                borderRadius: BorderRadius.all(Radius.circular(10.0)),
                boxShadow: [
                  BoxShadow(
                      color: Colors.black12,
                      spreadRadius: 2.0,
                      blurRadius: 5.0),
                ]),
            margin: EdgeInsets.all(5.0),
            child: Column(
              mainAxisSize: MainAxisSize.max,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                ClipRRect(
                  child: Image.asset(
                    object["placeImage"],
                    fit: BoxFit.cover,
                    width: 280,
                    height: 180,
                  ),
                ),
                SizedBox(
                  width: 250,
                  child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Text(
                          translator.translate(object["placeName"]),
                          style: GoogleFonts.elMessiri(
                              textStyle: TextStyle(
                                  fontSize: 15.0, color: Colors.black54)),
                        ),
                        Text(
                          translator.translate(" ${object["minOrder"]} IQD"),
                          style:
                          TextStyle(fontSize: 12.0, color: Colors.black54),
                        )
                      ],
                    ),
                  ),
                )
              ],
            ),
          ),
        ));
      });

      return items;
    }

    return Scaffold(
      appBar: AppBar(
        backgroundColor: HexColor("#242424"),
        leading: IconButton(
          icon: Icon(Icons.arrow_back_ios),
          iconSize: 20.0,
          onPressed: () {
            Navigator.pop(context);
          },
        ),
        title: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Image.asset(
              "assets/images/logo.png",
              fit: BoxFit.contain,
              height: 40,
            ),
          ],
        ),
      ),
      body: SafeArea(
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                Container(
                  decoration: BoxDecoration(
                    image: DecorationImage(
                      image: AssetImage("assets/images/backg.png"),
                      fit: BoxFit.cover,
                    ),
                  ),
                  height: 3000,
                  width: screenWidth,
                  child: FutureBuilder<List<Widget>>(
                      initialData: [Text("")],
                      future: createList(),
                      builder: (context, snapshot) {
                        if (snapshot.connectionState == ConnectionState.waiting)
                          return Text("Loading");

                        if (snapshot.hasError) {
                          return Text("Error ${snapshot.error}");
                        }
                        if (snapshot.hasData) {
                          return Padding(
                              padding: EdgeInsets.all(8.0),
                              child: GridView.count(
                                childAspectRatio: 1,
                                crossAxisCount: 2,
                                shrinkWrap: true,
                                physics: NeverScrollableScrollPhysics(),
                                children: [
                                  ...?snapshot.data,
                                ],
                              ));
                        } else {
                          return CircularProgressIndicator();
                        }
                      }),
                )
              ],
            ),
          )),
      floatingActionButton: FloatingActionButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => About()),
            );
          },
          backgroundColor: Colors.black,
          child: Icon(
            MdiIcons.information,
            color: Colors.white,
          )),
    );
  }
}

2. 修改首页跳转逻辑

调整首页中点击分类的跳转代码,传递对应分类的JSON路径即可:
首先在首页顶部引入通用页,删掉原有冗余页面的引入:

import 'package:menu_app/menu_category_page.dart';
// 删除原来的hotDrinks.dart、coldDrinks.dart引入

然后修改两个分类的点击事件:

// 热饮分类点击事件
onTap: () {
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => const MenuCategoryPage(jsonPath: "assets/hotDrinks.json")),
  );
},

// 冷饮分类点击事件
onTap: () {
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => const MenuCategoryPage(jsonPath: "assets/coldDrinks.json")),
  );
},

注意事项

  • 后续新增甜点、餐食等品类,只需要在首页新增对应入口,传递对应JSON路径即可,无需重复创建页面
  • 确保所有JSON文件路径都已在pubspec.yaml的assets节点中注册
  • 如果需要为不同分类定制标题、背景等样式,只需在通用页的构造函数中新增对应参数传递即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 13:36:03