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、手机号、身份证、详细地址。网络日志要能辅助排错,但不能泄露用户数据。

不需要翻每个页面。

相关推荐
小白说大模型41 分钟前
AI驱动的个性化学习路径:知识图谱与知识点关联的存储与推理
大数据·人工智能·学习·mysql·机器学习·prompt·知识图谱
吃好睡好便好1 小时前
三伏天结束了
学习·生活·毕淑敏·心理游戏
m0_749690231 小时前
【寻迹校园 HarmonyOS NEXT 实战 16】Preferences 还是 RelationalStore:HarmonyOS 本地存储选型实战
华为·harmonyos·arkts·preferences·relationalstore
格尔曼Noah1 小时前
iPhone共享网络给电脑的方式
网络·电脑·iphone
2501_931819702 小时前
方言标注标准化模板迭代优化诉求 泸州话标注公司学习信实翻译模板迭代方法论
学习
好评1242 小时前
【Linux】Socket编程TCP
linux·网络·tcp/ip
tju新生代魔迷2 小时前
Python学习日记5
学习
夜来小雨3 小时前
异常Wi-Fi信号问题处理
网络
小唔w3 小时前
武汉外籍人员子女学校择校观察:枫叶教育信息整理
学习
for_ever_love__3 小时前
python基础语法学习: 装饰器
python·学习·装饰器·装饰器模式