09-Flutter鸿蒙网络请求入门:HTTP数据请求和JSON模型封装

09. Flutter鸿蒙网络请求入门:HTTP数据请求和JSON模型封装

网络请求是业务项目最常见的能力之一。Flutter 鸿蒙项目中,普通 HTTP 请求仍然可以在 Flutter 层完成:添加依赖、封装模型、写请求服务、处理加载状态和错误状态即可。

添加依赖

yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  http: ^1.2.2

执行:

bash 复制代码
flutter pub get

定义模型

dart 复制代码
class User {
  final int id;
  final String name;
  final String phone;

  const User({
    required this.id,
    required this.name,
    required this.phone,
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int? ?? 0,
      name: json['name'] as String? ?? '',
      phone: json['phone'] as String? ?? '',
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'phone': phone,
    };
  }
}

封装服务

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

class ApiService {
  ApiService({http.Client? client}) : _client = client ?? http.Client();

  final http.Client _client;
  static const String baseUrl = 'https://example.com/api';

  Future<List<User>> fetchUsers() async {
    final uri = Uri.parse('$baseUrl/users');
    final response = await _client.get(uri);

    if (response.statusCode != 200) {
      throw Exception('请求失败:${response.statusCode}');
    }

    final data = jsonDecode(response.body);
    if (data is! List) {
      throw Exception('接口返回格式不是数组');
    }

    return data
        .map((item) => User.fromJson(item as Map<String, dynamic>))
        .toList();
  }

  Future<User> createUser(String name, String phone) async {
    final uri = Uri.parse('$baseUrl/users');
    final response = await _client.post(
      uri,
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({'name': name, 'phone': phone}),
    );

    if (response.statusCode != 200 && response.statusCode != 201) {
      throw Exception('新增失败:${response.statusCode}');
    }

    return User.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
  }
}

页面状态

dart 复制代码
class UserListPage extends StatefulWidget {
  const UserListPage({super.key});

  @override
  State<UserListPage> createState() => _UserListPageState();
}

class _UserListPageState extends State<UserListPage> {
  late Future<List<User>> _future;

  @override
  void initState() {
    super.initState();
    _future = ApiService().fetchUsers();
  }

  Future<void> _reload() async {
    setState(() {
      _future = ApiService().fetchUsers();
    });
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<User>>(
      future: _future,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Center(child: CircularProgressIndicator());
        }
        if (snapshot.hasError) {
          return Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Text('加载失败:${snapshot.error}'),
                const SizedBox(height: 12),
                FilledButton(onPressed: _reload, child: const Text('重试')),
              ],
            ),
          );
        }
        final users = snapshot.data ?? [];
        if (users.isEmpty) {
          return const Center(child: Text('暂无数据'));
        }
        return RefreshIndicator(
          onRefresh: _reload,
          child: ListView.builder(
            itemCount: users.length,
            itemBuilder: (_, index) {
              final user = users[index];
              return ListTile(
                title: Text(user.name),
                subtitle: Text(user.phone),
              );
            },
          ),
        );
      },
    );
  }
}

排错

网络问题常见排查:

text 复制代码
1. 接口地址是否能在浏览器或 Postman 打开
2. 设备是否有网络权限和网络连接
3. HTTP/HTTPS 是否符合服务端要求
4. JSON 顶层结构是对象还是数组
5. statusCode 是否被正确处理
6. 页面是否处理 loading、empty、error 三种状态

请求层不要直接写在页面里

页面里直接写 http.get 能跑,但项目变大后很难维护。更好的做法是页面只关心状态,网络细节放到 service:

text 复制代码
lib
├─ models
│  └─ user.dart
├─ services
│  └─ api_service.dart
└─ pages
   └─ user_list_page.dart

这样接口地址变化、请求头变化、错误处理变化时,不需要翻每个页面。

调试接口返回

开发阶段可以临时打印响应:

dart 复制代码
debugPrint('statusCode: ${response.statusCode}');
debugPrint('body: ${response.body}');

正式提交前不要在日志里输出敏感信息,比如 token、手机号、身份证、详细地址。网络日志要能辅助排错,但不能泄露用户数据。

不需要翻每个页面。

相关推荐
玖玥拾2 小时前
C# 语言进阶(十四)Unity UI界面开发 + 网络消息联动
服务器·开发语言·网络·ui·unity·c#·游戏引擎
~kiss~2 小时前
LLM 的 层归一化(稳定训练) - Layer Normalization & RMSNorm
学习
xd1855785552 小时前
鸿蒙PC平台下基于Flutter框架的AI历史对话应用开发实践
人工智能·华为·harmonyos·鸿蒙
MartinYeung52 小时前
[论文学习]LLM-based AI Agent 安全威胁与防御系统性综述
人工智能·学习·安全
星释3 小时前
鸿蒙智能体开发实战:41.智能体上架审核规范与合规指南
华为·ai·harmonyos·鸿蒙
不羁的木木3 小时前
HarmonyOS APP实战-画图APP - 第9篇:图像效果应用(智能取色)
华为·harmonyos
hbugs0013 小时前
PNETLab vs EVE-NG Pro 流量洞察功能底层架构技术白皮书
网络·架构·eve-ng·bpf·流量洞察
ZZZMMM.zip3 小时前
基于鸿蒙PC与鸿蒙Flutter框架的AI紧急求助应用开发实践
人工智能·flutter·华为·harmonyos·鸿蒙
不才难以繁此生3 小时前
NavPathStack 返回错页怎么排查:中式美食搜索、收藏和推荐入口怎么带来源
harmonyos·arkts·arkui·中式美食·navpathstack·路由状态
解局易否结局3 小时前
ArkUI-X 跨平台开发实战:从工程搭建到平台差异化适配
华为·harmonyos