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

相关推荐
liulian09164 小时前
Flutter for OpenHarmony 跨平台开发:颜色选择器功能实战指南
flutter
ZC跨境爬虫5 小时前
跟着 MDN 学 HTML day_9:(信件语义标记)
前端·css·笔记·ui·html
前端老石人5 小时前
HTML 字符引用完全指南
开发语言·前端·html
幼儿园技术家5 小时前
前端如何设计权限系统(RBAC / ABAC)?
前端
前端摸鱼匠7 小时前
Vue 3 的v-bind合并行为:讲解v-bind与普通属性合并的规则
前端·javascript·vue.js·前端框架·ecmascript
REDcker7 小时前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
liulian09169 小时前
Flutter for OpenHarmony 跨平台开发:BMI计算器功能实战指南
flutter·华为
donecoding9 小时前
一个 sudo 引发的血案:npm 全局包权限错乱彻底修复
前端·node.js·前端工程化
风骏时光牛马9 小时前
Raku正则匹配与数据批量处理实操案例
前端
nbwenren9 小时前
2026实测:Gemini 3 镜像站视觉能力实践——拍照原型图,一键生成 HTML+CSS 代码
前端·css·html