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

如何在Flutter中使用Model类在ListView中展示嵌套API数据

问题:如何在Flutter ListView中展示解析后的学科数据

你已经成功获取并解析了JSON数据,能打印指定学科名称,接下来我们一步步实现把这些数据展示在ListView里。


先回顾你已有的内容

1. JSON响应

[
  {
    "success": 1,
    "subject": [
      {
        "subject_id": "5e32874c714fa",
        "subject_name": "Account",
        "image": "upload/subject/Account.png",
        "active": "1",
        "standard_id": "5d1594e283e1a",
        "medium_id": "5d15938aa1344"
      },
      {
        "subject_id": "5da9ff659fb7c",
        "subject_name": "Biology",
        "image": "upload/subject/03_logo-1164x484.png",
        "active": "1",
        "standard_id": "5d1594e283e1a",
        "medium_id": "5d15938aa1344"
      },
      {
        "subject_id": "5da9ff990b1c6",
        "subject_name": "Chemisty",
        "image": "upload/subject/02_logo-1168x490.png",
        "active": "1",
        "standard_id": "5d1594e283e1a",
        "medium_id": "5d15938aa1344"
      },
      {
        "subject_id": "5de76afbd064e",
        "subject_name": "Computer",
        "image": "upload/subject/07_logo-1169x486.png",
        "active": "1",
        "standard_id": "5d1594e283e1a",
        "medium_id": "5d15938aa1344"
      },
      {
        "subject_id": "5d788906c431b",
        "subject_name": "Devsatya Paperset March 2020",
        "image": "upload/subject/04_logo-1174x491.png",
        "active": "1",
        "standard_id": "5d1594e283e1a",
        "medium_id": "5d15938aa1344"
      }
    ]
  }
]

2. 已定义的Model类

// To parse this JSON data, do
//
// final subjectByUser = subjectByUserFromJson(jsonString);
import 'dart:convert';

List<SubjectByUser> subjectByUserFromJson(String str) => List<SubjectByUser>.from(
    json.decode(str).map((x) => SubjectByUser.fromJson(x)));

String subjectByUserToJson(List<SubjectByUser> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class SubjectByUser {
  SubjectByUser({
    required this.success,
    required this.subject,
  });

  int success;
  List<Subject> subject;

  factory SubjectByUser.fromJson(Map<String, dynamic> json) => SubjectByUser(
        success: json["success"],
        subject: List<Subject>.from(json["subject"].map((x) => Subject.fromJson(x))),
      );

  Map<String, dynamic> toJson() => {
        "success": success,
        "subject": List<dynamic>.from(subject.map((x) => x.toJson())),
      };
}

class Subject {
  Subject({
    required this.subjectId,
    required this.subjectName,
    required this.image,
    required this.active,
    required this.standardId,
    required this.mediumId,
  });

  String subjectId;
  String subjectName;
  String image;
  String active;
  String standardId;
  String mediumId;

  factory Subject.fromJson(Map<String, dynamic> json) => Subject(
        subjectId: json["subject_id"],
        subjectName: json["subject_name"],
        image: json["image"],
        active: json["active"],
        standardId: json["standard_id"],
        mediumId: json["medium_id"],
      );

  Map<String, dynamic> toJson() => {
        "subject_id": subjectId,
        "subject_name": subjectName,
        "image": image,
        "active": active,
        "standard_id": standardId,
        "medium_id": mediumId,
      };
}

3. 当前请求代码

Future getUserSubject(List subject) async {
  final subjectUrl = "$baseUrl/subject/get_by_user_plan?user_id=609cab2cd5b6c&order_id=1620889722609cd07a601af469889697609cab2cd5b6c&standard_id=5d1594e283e1a&medium_id=5d15938aa1344";
  final response = await http.get(Uri.parse(subjectUrl));
  final subjects = response.body;
  final decodedData = jsonDecode(subjects);
  SubjectByUserModel subjectByUserModel = SubjectByUserModel.fromJson(decodedData);
  print(subjectByUserModel.subject?[2].subjectName);
}

解决方案:实现ListView展示数据

第一步:修正请求方法,返回学科列表

首先,你的请求方法需要返回解析后的学科数据,并且要注意JSON是数组格式,需要取第一个元素解析:

import 'package:http/http.dart' as http;
import 'dart:convert';
// 导入你的Model类
import 'path/to/your/subject_model.dart';

Future<List<Subject>?> getUserSubject() async {
  final subjectUrl = "$baseUrl/subject/get_by_user_plan?user_id=609cab2cd5b6c&order_id=1620889722609cd07a601af469889697609cab2cd5b6c&standard_id=5d1594e283e1a&medium_id=5d15938aa1344";
  
  try {
    final response = await http.get(Uri.parse(subjectUrl));
    
    if (response.statusCode == 200) {
      final decodedData = jsonDecode(response.body);
      // JSON是数组,取第一个元素解析成SubjectByUser
      SubjectByUser subjectByUser = SubjectByUser.fromJson(decodedData[0]);
      return subjectByUser.subject;
    } else {
      print("请求失败,状态码:${response.statusCode}");
      return null;
    }
  } catch (e) {
    print("请求出错:$e");
    return null;
  }
}

第二步:在视图层用FutureBuilder构建ListView

在你的页面State类中,使用FutureBuilder来异步获取数据并渲染列表:

import 'package:flutter/material.dart';
// 导入请求方法和Model类
import 'path/to/your/api_service.dart';
import 'path/to/your/subject_model.dart';

class SubjectListPage extends StatefulWidget {
  const SubjectListPage({super.key});

  @override
  State<SubjectListPage> createState() => _SubjectListPageState();
}

class _SubjectListPageState extends State<SubjectListPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("我的学科"),
      ),
      body: FutureBuilder<List<Subject>?>(
        future: getUserSubject(),
        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("暂无学科数据"));
          }
          // 数据加载成功,构建ListView
          else {
            final subjects = snapshot.data!;
            return ListView.builder(
              padding: const EdgeInsets.all(8),
              itemCount: subjects.length,
              itemBuilder: (context, index) {
                final subject = subjects[index];
                return Card(
                  margin: const EdgeInsets.symmetric(vertical: 4),
                  child: ListTile(
                    leading: Image.network(
                      // 拼接完整的图片URL,因为返回的是相对路径
                      "$baseUrl/${subject.image}",
                      width: 50,
                      height: 50,
                      fit: BoxFit.cover,
                      // 图片加载失败时显示默认图标
                      errorBuilder: (context, error, stackTrace) {
                        return const Icon(Icons.book, size: 50);
                      },
                    ),
                    title: Text(subject.subjectName),
                    subtitle: Text("学科ID:${subject.subjectId}"),
                    // 可以添加点击事件
                    onTap: () {
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(content: Text("点击了${subject.subjectName}")),
                      );
                    },
                  ),
                );
              },
            );
          }
        },
      ),
    );
  }
}

关键注意事项

  1. 图片URL拼接:返回的image字段是相对路径,必须和你的baseUrl拼接才能正确加载图片,比如"$baseUrl/${subject.image}"
  2. 异常处理:添加了try-catch和状态码判断,避免请求出错导致崩溃
  3. 状态处理:覆盖了加载中、错误、无数据三种状态,提升用户体验
  4. JSON解析修正:你的原始JSON是数组格式,所以要取decodedData[0]来解析成SubjectByUser对象

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 07:07:34