Flutter中如何获取Stream Builder返回的JSON数据并转为Widget?
解决方案:解析嵌套JSON并构建Flutter Widget
步骤1:解析JSON数据
首先将HTTP响应的body通过json.decode()转换为Dart的List/Map结构,这是访问嵌套字段的基础。
步骤2:创建数据模型(推荐)
直接操作Map容易出现拼写错误和类型问题,创建强类型模型类更规范易维护。假设你的JSON结构为列表嵌套Map:
[ {"id": 1, "name": "商品A", "info": {"price": 99, "stock": 100}}, {"id": 2, "name": "商品B", "info": {"price": 199, "stock": 50}} ]
对应的模型类代码:
class Product { final int id; final String name; final ProductInfo info; Product({required this.id, required this.name, required this.info}); // 从Map转模型 factory Product.fromJson(Map<String, dynamic> json) { return Product( id: json['id'], name: json['name'], info: ProductInfo.fromJson(json['info']), ); } } class ProductInfo { final int price; final int stock; ProductInfo({required this.price, required this.stock}); factory ProductInfo.fromJson(Map<String, dynamic> json) { return ProductInfo( price: json['price'], stock: json['stock'], ); } }
步骤3:改造Stream与StreamBuilder
更新Stream方法,直接返回解析后的模型列表,让StreamBuilder直接处理业务数据:
Stream<List<Product>> getProductStream() async* { yield* Stream.periodic(const Duration(seconds: 5), (_) { return http.get(Uri.parse("https://script.google.com/macros/s/AKfycbwhbpF4ZxuMUcTZZvObAqvE1pAbEfPt7gZHRV1vVp8PuKt39-ouOm-kQJ1U1LtlEwV-/exec")); }).asyncMap((responseFuture) async { final response = await responseFuture; if (response.statusCode == 200) { final List<dynamic> jsonList = json.decode(response.body); return jsonList.map((json) => Product.fromJson(json)).toList(); } else { throw Exception('加载数据失败'); } }); }
然后修改StreamBuilder,构建可展示的列表Widget:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('嵌套JSON数据展示')), body: StreamBuilder<List<Product>>( stream: getProductStream(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('错误:${snapshot.error}')); } else if (!snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text('暂无数据')); } else { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { final product = snapshot.data![index]; return ListTile( title: Text(product.name), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('ID:${product.id}'), Text('价格:${product.info.price}元'), Text('库存:${product.info.stock}件'), ], ), ); }, ); } }, ), ); }
完整代码
整合所有部分后的完整代码:
import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; void main() { runApp(const MaterialApp(home: PeriodicRequester())); } class Product { final int id; final String name; final ProductInfo info; Product({required this.id, required this.name, required this.info}); factory Product.fromJson(Map<String, dynamic> json) { return Product( id: json['id'], name: json['name'], info: ProductInfo.fromJson(json['info']), ); } } class ProductInfo { final int price; final int stock; ProductInfo({required this.price, required this.stock}); factory ProductInfo.fromJson(Map<String, dynamic> json) { return ProductInfo( price: json['price'], stock: json['stock'], ); } } class PeriodicRequester extends StatelessWidget { const PeriodicRequester({super.key}); Stream<List<Product>> getProductStream() async* { yield* Stream.periodic(const Duration(seconds: 5), (_) { return http.get(Uri.parse("https://script.google.com/macros/s/AKfycbwhbpF4ZxuMUcTZZvObAqvE1pAbEfPt7gZHRV1vVp8PuKt39-ouOm-kQJ1U1LtlEwV-/exec")); }).asyncMap((responseFuture) async { final response = await responseFuture; if (response.statusCode == 200) { final List<dynamic> jsonList = json.decode(response.body); return jsonList.map((json) => Product.fromJson(json)).toList(); } else { throw Exception('加载数据失败'); } }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('嵌套JSON数据展示')), body: StreamBuilder<List<Product>>( stream: getProductStream(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('错误:${snapshot.error}')); } else if (!snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text('暂无数据')); } else { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { final product = snapshot.data![index]; return ListTile( title: Text(product.name), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('ID:${product.id}'), Text('价格:${product.info.price}元'), Text('库存:${product.info.stock}件'), ], ), ); }, ); } }, ), ); } }
核心说明
- 强类型解析:通过模型类的
fromJson方法,将松散的JSON结构转换为强类型对象,避免直接操作Map时的类型风险。 - Stream优化:将Stream返回值从
http.Response改为业务模型列表,简化StreamBuilder的处理逻辑。 - UI构建:用
ListView.builder高效渲染列表,通过ListTile和Column组合展示嵌套字段,清晰呈现数据层级。
内容的提问来源于stack exchange,提问作者MOBILES RECOVERY
相关产品推荐
相关产品推荐

