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

Flutter解析PHP接口JSON 实现blockBustorDealList动态展示

Flutter对接PHP接口动态渲染列表实现方案

适配你当前Dart 2.7.0 ~ 3.0.0的版本环境,全程保留原有UI样式,仅替换数据加载逻辑,满足final修饰列表变量的要求。

前置配置(必做,否则网络请求会被系统拦截)

因为你用的是HTTP明文协议的本地接口,需要先给平台开网络权限:

  • 安卓端:找到项目路径下android/app/src/main/AndroidManifest.xml文件
    1. 在manifest根节点添加网络权限:
    <uses-permission android:name="android.permission.INTERNET" />
    
    1. 在<application>标签上添加明文流量允许配置:
    <application
        android:usesCleartextTraffic="true"
        <!-- 其他原有配置保持不动 -->
    >
    
  • iOS端:找到ios/Runner/Info.plist文件,添加HTTP访问配置:
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    

代码修改步骤

  1. 删掉原来全局定义的硬编码blockBustorDealList静态列表
  2. 将原来的OfferGrid从无状态组件(StatelessWidget)改成有状态组件(StatefulWidget),支持异步数据加载后的UI刷新
  3. 定义final修饰的列表变量,添加网络请求、JSON解析逻辑,增加加载中、请求失败的兜底状态

修改后完整可运行代码

直接替换你原来的BlockBusterDeals、OfferGrid相关代码即可:

import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

// 原有自定义导入保持不变
import 'package:go_kart/pages/category/top_offers.dart';
import 'package:http/http.dart' as http;

class BlockBusterDeals extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      child: Column(
        children: <Widget>[
          OfferGrid(),
        ],
      ),
    );
  }
}

class OfferGrid extends StatefulWidget {
  @override
  _OfferGridState createState() => _OfferGridState();
}

class _OfferGridState extends State<OfferGrid> {
  // final修饰的列表变量,符合要求,仅可修改内部内容不可重新赋值
  final List<Map<String, dynamic>> blockBustorDealList = [];
  // 加载状态标记
  bool isLoading = true;
  // 错误信息
  String errorMsg = '';
  // 接口地址
  final String apiUrl = "http://192.168.1.4/flutter/get_products.php";

  @override
  void initState() {
    super.initState();
    // 页面初始化时拉取接口数据
    _fetchDealData();
  }

  // 接口请求与JSON解析方法
  Future<void> _fetchDealData() async {
    setState(() {
      isLoading = true;
      errorMsg = '';
    });
    try {
      final response = await http.get(Uri.parse(apiUrl)).timeout(Duration(seconds: 10));
      if (response.statusCode == 200) {
        // 解析JSON数据,根据接口实际返回结构调整取值逻辑
        // 如果接口直接返回数组就用下面这行
        final dynamic jsonData = jsonDecode(response.body);
        // 如果接口返回结构是{code:200, data:[...]} 就改成 final dynamic jsonData = jsonDecode(response.body)['data'];
        blockBustorDealList.clear();
        jsonData.forEach((item) {
          blockBustorDealList.add({
            // 这里如果接口字段名和下面不一致,自行替换成接口返回的实际字段名
            'image': item['image'],
            'title': item['title'],
            'offer': item['offer']
          });
        });
      } else {
        errorMsg = '接口请求失败,状态码:${response.statusCode}';
      }
    } catch (e) {
      errorMsg = '网络异常:${e.toString()}';
    } finally {
      if (mounted) {
        setState(() {
          isLoading = false;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;

    InkWell getStructuredGridCell(blockBustorDeal) {
      final item = blockBustorDeal;
      return InkWell(
        child: Container(
          margin: EdgeInsets.all(2.0),
          decoration: BoxDecoration(
            color: Color.fromARGB(255, 15, 13, 13),
            borderRadius: BorderRadius.circular(10.0),
          ),
          child: Column(
            children: <Widget>[
              Container(
                margin: const EdgeInsets.only(top: 10.0, bottom: 10.0),
                height: 170.0,
                child: Image.network(
                  item['image'],
                  fit: BoxFit.fitWidth,
                ),
              ),
              Container(
                child: Column(
                  children: <Widget>[
                    Text(
                      '${item['title']}',
                      style: TextStyle(fontSize: 12.0, color: Colors.white),
                    ),
                    Text(
                      '${item['offer']}',
                      style: TextStyle(
                          color: const Color(0xFF67A86B), fontSize: 16.0),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
        onTap: () {
          Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => TopOffers(title: '${item['title']}')),
          );
        },
      );
    }

    return Column(
      children: <Widget>[
        SizedBox(
          width: width,
          height: 479.0,
          child: Stack(
            children: <Widget>[
              Container(
                width: width,
                height: 460.0,
                decoration: BoxDecoration(
                  color: Colors.transparent,
                ),
              ),
              Container(
                decoration: BoxDecoration(
                  color: Colors.transparent,
                  borderRadius: BorderRadius.circular(5.0),
                ),
                padding: EdgeInsets.only(top: 5.0, right: 5.0, left: 5.0),
                width: width - 20.0,
                margin: EdgeInsets.only(right: 10.0, left: 10.0, top: 10.0),
                // 根据加载状态显示不同内容
                child: isLoading
                    ? Center(child: CircularProgressIndicator())
                    : errorMsg.isNotEmpty
                        ? Center(
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                Text(errorMsg, style: TextStyle(color: Colors.white), textAlign: TextAlign.center),
                                SizedBox(height: 10),
                                ElevatedButton(
                                  onPressed: _fetchDealData,
                                  child: Text('重新加载'),
                                )
                              ],
                            ),
                          )
                        : blockBustorDealList.isEmpty
                            ? Center(child: Text('暂无优惠数据', style: TextStyle(color: Colors.white)))
                            : GridView.count(
                                primary: false,
                                crossAxisSpacing: 0,
                                mainAxisSpacing: 0,
                                crossAxisCount: 2,
                                childAspectRatio: ((width) / 500),
                                children: List.generate(blockBustorDealList.length, (index) {
                                  return getStructuredGridCell(blockBustorDealList[index]);
                                }),
                              ),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

注意事项

  • 测试时确保运行APP的设备和部署PHP接口的电脑在同一局域网下,手机浏览器直接访问http://192.168.1.4/flutter/get_products.php能正常拿到JSON数据再调试APP
  • 如果接口返回的JSON字段名和代码中写的image/title/offer不一致,直接修改解析部分的字段映射即可
  • final修饰的列表变量没有重新赋值,仅在请求成功后清空原有测试数据、写入接口返回数据,完全符合语法要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 12:48:16