Flutter基础(前端教程⑦-Http和卡片)

1. 假设后端返回的数据格式

复制代码
{
  "code": 200,
  "data": [
    {
      "name": "张三",
      "age": 25,
      "email": "zhangsan@example.com",
      "avatar": "https://picsum.photos/200/200?random=1",
      "status": "在线"
    },
    {
      "name": "李四",
      "age": 30,
      "email": "lisi@example.com",
      "avatar": "https://picsum.photos/200/200?random=2",
      "status": "离线"
    },
    {
      "name": "王五",
      "age": 22,
      "email": "wangwu@example.com",
      "avatar": "https://picsum.photos/200/200?random=3",
      "status": "忙碌"
    },
    {
      "name": "赵六",
      "age": 28,
      "email": "zhaoliu@example.com",
      "avatar": "https://picsum.photos/200/200?random=4",
      "status": "在线"
    }
  ]
}

2. 修改后代码实现

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('多人数据展示')),
        body: const UserListPage(),
      ),
    );
  }
}

// 数据模型(保持不变)
class User {
  final String name;
  final int age;
  final String email;
  final String avatar;
  final String status;

  User({
    required this.name,
    required this.age,
    required this.email,
    required this.avatar,
    required this.status,
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'],
      age: json['age'],
      email: json['email'],
      avatar: json['avatar'],
      status: json['status'],
    );
  }
}

// 主页面
class UserListPage extends StatefulWidget {
  const UserListPage({super.key});

  @override
  _UserListPageState createState() => _UserListPageState();
}

class _UserListPageState extends State<UserListPage> {
  List<User> _users = []; // 存储多个用户数据
  bool _isLoading = true;
  String? _errorMessage;

  @override
  void initState() {
    super.initState();
    _fetchUsersData();
  }

  // 调用API获取多用户数据
  void _fetchUsersData() async {
    try {
      final response = await http.get(
        Uri.parse('https://api.example.com/users'), // 替换为实际API地址
      );

      if (response.statusCode == 200) {
        final Map<String, dynamic> jsonData = json.decode(response.body);
        
        if (jsonData['code'] == 200) {
          // 解析JSON数组为User列表
          final List<dynamic> userList = jsonData['data'];
          setState(() {
            _users = userList.map((item) => User.fromJson(item)).toList();
            _isLoading = false;
          });
        } else {
          setState(() {
            _errorMessage = '获取数据失败:${jsonData['message']}';
            _isLoading = false;
          });
        }
      } else {
        setState(() {
          _errorMessage = '请求失败:状态码 ${response.statusCode}';
          _isLoading = false;
        });
      }
    } catch (e) {
      setState(() {
        _errorMessage = '网络错误:${e.toString()}';
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    if (_errorMessage != null) {
      return Center(
        child: Text(
          _errorMessage!,
          style: const TextStyle(color: Colors.red, fontSize: 16),
        ),
      );
    }

    // 数据为空的情况
    if (_users.isEmpty) {
      return const Center(child: Text('暂无用户数据'));
    }

    // 使用ListView展示多个用户卡片
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      itemCount: _users.length,
      itemBuilder: (context, index) {
        return Padding(
          padding: const EdgeInsets.only(bottom: 16),
          child: _buildUserCard(_users[index]), // 为每个用户构建卡片
        );
      },
    );
  }

  // 构建单个用户卡片(保持不变)
  Widget _buildUserCard(User user) {
    return Card(
      elevation: 4,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(12),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            ClipRRect(
              borderRadius: BorderRadius.circular(8),
              child: Image.network(
                user.avatar,
                width: 80,
                height: 80,
                fit: BoxFit.cover,
              ),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(
                        user.name,
                        style: const TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      Container(
                        padding: const EdgeInsets.symmetric(
                          horizontal: 8,
                          vertical: 2,
                        ),
                        decoration: BoxDecoration(
                          color: user.status == '在线' 
                              ? Colors.green[100] 
                              : Colors.grey[200],
                          borderRadius: BorderRadius.circular(4),
                        ),
                        child: Text(
                          user.status,
                          style: TextStyle(
                            color: user.status == '在线' 
                                ? Colors.green 
                                : Colors.grey[600],
                            fontSize: 12,
                          ),
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  Text(
                    '年龄:${user.age}岁',
                    style: const TextStyle(color: Colors.grey),
                  ),
                  const SizedBox(height: 4),
                  Text(
                    '邮箱:${user.email}',
                    style: const TextStyle(color: Colors.grey),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

123

相关推荐
中微子7 分钟前
闭包面试宝典:高频考点与实战解析
前端·javascript
brzhang7 分钟前
前端死在了 Python 朋友的嘴里?他用 Python 写了个交互式数据看板,着实秀了我一把,没碰一行 JavaScript
前端·后端·架构
G等你下课35 分钟前
告别刷新就丢数据!localStorage 全面指南
前端·javascript
该用户已不存在36 分钟前
不知道这些工具,难怪的你的Python开发那么慢丨Python 开发必备的6大工具
前端·后端·python
爱编程的喵39 分钟前
JavaScript闭包实战:从类封装到防抖函数的深度解析
前端·javascript
LovelyAqaurius39 分钟前
Unity URP管线着色器库攻略part1
前端
Xy91042 分钟前
开发者视角:App Trace 一键拉起(Deep Linking)技术详解
java·前端·后端
lalalalalalalala44 分钟前
开箱即用的 Vue3 无限平滑滚动组件
前端·vue.js
前端Hardy44 分钟前
8个你必须掌握的「Vue」实用技巧
前端·javascript·vue.js
snakeshe10101 小时前
深入理解 React 中 useEffect 的 cleanUp 机制
前端