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

相关推荐
知了清语3 分钟前
pnpm之monorepo项目, vite版本冲突, 导致vite.config.ts ts警告处理
前端
Lucifer晓23 分钟前
记录一次Flutter项目上传App Store Connect出现“Validation failed”错误的问题
flutter·ios
弗锐土豆25 分钟前
一个基于若依(ruoyi-vue3)的小项目部署记录
前端·vue.js·部署·springcloud·ruoyi·若依
Hilaku28 分钟前
我为什么放弃了“大厂梦”,去了一家“小公司”?
前端·javascript·面试
1undefined230 分钟前
element中的table改造成虚拟列表(不定高),并封装成hooks
前端·vue.js
浅墨momo33 分钟前
搭建第一个Shopify App
前端·程序员
然我38 分钟前
React 事件机制:从代码到原理,彻底搞懂合成事件的核心逻辑
前端·react.js·面试
Codebee38 分钟前
OneCode 组件服务通用协议栈:构建企业级低代码平台的技术基石
前端·前端框架·开源
Running_C38 分钟前
常见web攻击类型
前端·http