Flutter解析PHP接口JSON 实现blockBustorDealList动态展示
Flutter对接PHP接口动态渲染列表实现方案
适配你当前Dart 2.7.0 ~ 3.0.0的版本环境,全程保留原有UI样式,仅替换数据加载逻辑,满足final修饰列表变量的要求。
前置配置(必做,否则网络请求会被系统拦截)
因为你用的是HTTP明文协议的本地接口,需要先给平台开网络权限:
- 安卓端:找到项目路径下
android/app/src/main/AndroidManifest.xml文件- 在manifest根节点添加网络权限:
<uses-permission android:name="android.permission.INTERNET" />- 在
<application>标签上添加明文流量允许配置:
<application android:usesCleartextTraffic="true" <!-- 其他原有配置保持不动 --> > - iOS端:找到
ios/Runner/Info.plist文件,添加HTTP访问配置:<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
代码修改步骤
- 删掉原来全局定义的硬编码
blockBustorDealList静态列表 - 将原来的
OfferGrid从无状态组件(StatelessWidget)改成有状态组件(StatefulWidget),支持异步数据加载后的UI刷新 - 定义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
相关产品推荐
相关产品推荐

