🚀 Flutter 鸿蒙新手实战:网络请求 + 列表展示,一键跑通鸿蒙虚拟机

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrosplatform.csdn.net
哈喽各位~ 我是计算机专业大一新生,这是我的第一个鸿蒙 Flutter 实战作业📒,全程新手友好、代码可直接复制、能在鸿蒙虚拟机完美运行!
本文带大家从零搞定「dio 网络库集成 + JSON 数据解析 + 列表渲染 + 鸿蒙适配」全流程,没有冗余废话,跟着做就能出成果✨

⚙️ 前置环境准备

DevEco Studio 4.0+(内置鸿蒙 SDK、模拟器)
适配 OpenHarmony 的 Flutter SDK 3.16+
已启动的虚拟鸿蒙主机(OpenHarmony 3.2+)
能正常联网的开发环境

📂 极简项目结构

分层清晰,新手不混乱,完全符合 Flutter 开发规范:

demo1/
├── ohos/                # 鸿蒙平台专属配置
├── lib/
│   ├── main.dart        # 应用入口+页面UI
│   ├── models/post.dart # 数据模型(JSON解析)
│   └── services/api_service.dart # 网络请求封装
└── pubspec.yaml         # 项目依赖配置

💻 核心功能一步步实现

  1. 📦 集成 dio 三方网络库
    dio 是 Flutter 生态顶流网络库,已完美适配鸿蒙系统,也是本次征文要求的「三方库鸿蒙化实践」核心内容~
    打开项目根目录的pubspec.yaml,添加依赖:
dependencies:
  flutter:
    sdk: flutter
  dio: ^5.4.0 # 鸿蒙适配稳定版

保存后,终端执行 flutter pub get 拉取依赖,一步搞定✅

  1. 🌐 封装网络请求服务
    在lib/services/api_service.dart中,封装统一的网络请求,单例模式 + 全局配置,新手直接复制就能用:
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

class ApiService {
  // 单例模式,全局唯一实例
  static final ApiService _instance = ApiService._internal();
  factory ApiService() => _instance;
  ApiService._internal();

  static Dio? _dio;
  // 懒加载初始化dio
  static Dio get dio {
    _dio ??= _initDio();
    return _dio!;
  }

  // 全局dio配置
  static Dio _initDio() {
    final dio = Dio(
      BaseOptions(
        baseUrl: "https://jsonplaceholder.typicode.com/",
        connectTimeout: const Duration(seconds: 10),
        receiveTimeout: const Duration(seconds: 10),
      ),
    );
    // 日志拦截器,开发调试超方便
    dio.interceptors.add(LogInterceptor(responseBody: true));
    return dio;
  }

  // 获取帖子列表接口
  static Future<List<dynamic>> getPostList() async {
    final response = await dio.get("/posts");
    return response.data;
  }
}
  1. 🧩 定义数据模型,JSON 一键解析
    把接口返回的无结构 JSON,转成强类型的 Dart 对象,避免写错字段导致翻车!
    在lib/models/post.dart中写入:
class Post {
  final int id;
  final String title;
  final String body;

  Post({required this.id, required this.title, required this.body});

  // JSON转Post对象
  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      id: json['id'] ?? 0,
      title: json['title'] ?? '无标题',
      body: json['body'] ?? '无内容',
    );
  }

  // 批量解析列表数据
  static List<Post> fromJsonList(List<dynamic> jsonList) {
    return jsonList.map((json) => Post.fromJson(json)).toList();
  }
}
  1. 🎨 主页面实现:列表 + 全状态管理
    修改lib/main.dart,完整实现加载中 / 加载失败 / 加载成功三种状态,卡片式列表美观又好懂:
import 'package:flutter/material.dart';
import 'models/post.dart';
import 'services/api_service.dart';

// 应用入口
void main() => runApp(const MyApp());

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '鸿蒙Flutter实战',
      theme: ThemeData(primarySwatch: Colors.purple),
      home: const PostListPage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

// 帖子列表页面
class PostListPage extends StatefulWidget {
  const PostListPage({super.key});

  
  State<PostListPage> createState() => _PostListPageState();
}

class _PostListPageState extends State<PostListPage> {
  List<Post> postList = [];
  bool isLoading = true;
  String? errorMsg;

  
  void initState() {
    super.initState();
    // 页面启动时加载数据
    loadData();
  }

  // 核心:加载网络数据
  Future<void> loadData() async {
    setState(() {
      isLoading = true;
      errorMsg = null;
    });
    try {
      final jsonData = await ApiService.getPostList();
      setState(() {
        postList = Post.fromJsonList(jsonData);
        isLoading = false;
      });
    } catch (e) {
      setState(() {
        errorMsg = e.toString();
        isLoading = false;
      });
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("开发者社区"), centerTitle: true),
      body: buildBody(),
      // 悬浮刷新按钮
      floatingActionButton: FloatingActionButton(
        onPressed: loadData,
        child: const Icon(Icons.refresh),
      ),
    );
  }

  // 动态渲染页面状态
  Widget buildBody() {
    // 1. 加载中状态⏳
    if (isLoading) {
      return const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircularProgressIndicator(),
            SizedBox(height: 16),
            Text("数据加载中...", style: TextStyle(color: Colors.grey)),
          ],
        ),
      );
    }

    // 2. 加载失败状态❌
    if (errorMsg != null) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.error_outline, color: Colors.red, size: 48),
            const SizedBox(height: 16),
            Text(errorMsg!, textAlign: TextAlign.center),
            const SizedBox(height: 20),
            ElevatedButton(onPressed: loadData, child: const Text("点击重试")),
          ],
        ),
      );
    }

    // 3. 加载成功状态✅ 列表展示
    return ListView.builder(
      padding: const EdgeInsets.all(12),
      itemCount: postList.length,
      itemBuilder: (context, index) {
        final post = postList[index];
        return Padding(
          padding: const EdgeInsets.only(bottom: 12),
          child: Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    post.title,
                    style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                    maxLines: 2,
                  ),
                  const SizedBox(height: 8),
                  Text(
                    post.body,
                    style: const TextStyle(color: Colors.grey),
                    maxLines: 3,
                    overflow: TextOverflow.ellipsis,
                  ),
                  const SizedBox(height: 8),
                  Align(
                    alignment: Alignment.bottomRight,
                    child: Text("帖子ID:${post.id}", style: const TextStyle(fontSize: 12, color: Colors.grey)),
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}

🔧 鸿蒙专属适配(必做!不然网络直接翻车)
鸿蒙系统对应用权限管控超严格,想要访问网络,必须配置网络权限,否则所有请求都会被系统拦截!
打开 ohos/entry/src/main/module.json5,在requestPermissions数组中添加以下配置:

{
  "name": "ohos.permission.INTERNET",
  "reason": "应用需要访问网络获取帖子数据",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "inuse"
  }
}

保存文件,权限配置就搞定啦✅
📱 一键运行到鸿蒙虚拟机

  1. 构建鸿蒙 HAP 安装包
    HAP 是鸿蒙应用的安装包格式,终端进入项目的ohos目录,执行构建命令:
hvigorw assembleHap -p product=default -p buildMode=debug

终端出现BUILD SUCCESS,就是构建成功啦🎉
2. 安装应用到虚拟鸿蒙主机

hdc install -r entry/build/default/outputs/default/entry-default-unsigned.hap
  1. 启动应用
hdc shell aa start -a EntryAbility -b com.example.demo1

搞定!现在你的虚拟鸿蒙主机上,已经能看到应用完美运行,列表正常加载、刷新功能丝滑可用✨

运行效果截图

运行效果截图

⚠️ 新手必看踩坑指南
新手必看踩坑指南

✨ 学习总结
通过这次实战,我这个大一新生从零搞定了:
✅ Flutter 三方库的集成与鸿蒙适配
✅ dio 网络请求封装与 JSON 数据解析
✅ 鸿蒙应用权限配置与 HAP 包构建
✅ 列表页面的状态管理与 UI 渲染
✅ 应用在鸿蒙虚拟机的全流程运行

全程没有复杂晦涩的概念,新手跟着做就能复现,也完美完成了老师布置的作业~ 后续我也会继续分享鸿蒙 Flutter 的新手实战内容,和大家一起学习进步💪

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐