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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 18:57:17