题记

上一篇,我们简单实现了一个列表与表单,接下来我们考虑将表单的数据存储到后端,这个时候就涉及到网络请求。
本篇主要任务是打通虚拟机到宿主机器之间的网络连接,本地启动一个服务,然后从鸿蒙的虚拟机请求本地服务,获取数据。
上次的篇幅有点长导致省略的很多的地方,这次步子迈小点(不扯蛋咱们开始)。
世界,开始!

本次任务

  1. 配置鸿蒙模拟器连接外网
  2. 配置本地node服务
  3. 使用flutter 开发一个 请求本地接口的demo
  4. app请求,并获取数据展示列表页。

配置本地接口

本地配置的网络环境,就选择自己最熟悉的语言就行。这里我选择了 nodejs fastify。
假定你已经配置好了nodejs的环境了(如果没有的话 还是推荐asdf ,我们第一篇已经提到了,如果你选择用它的话,这次正好你熟悉哈哈),我们这里就不讲nodejs环境配置了直接开始。

安装Fastify

mkdir fastify-s && cd fastify-s
yarn init -y 
# 一路enter
yarn add fastify

安装完依赖,我们简单写一个服务,返回一个数组。

// main.js
import Fastify from 'fastify'
const fastify = Fastify({
  logger: true
})

// Declare a route
fastify.get('/', async function handler(request, reply) {
  return [{ hello: 'world' }]
})
// 10.0.2.2:3000

// Run the server!
try {
  await fastify.listen({ port: 3000 })
} catch (err) {
  fastify.log.error(err)
  process.exit(1)
}

都是很简单的东西,这里我们启动并验证一下服务看看有没有问题。
在这里插入图片描述
这里也是提一下我的node版本是24.3.0,不用其它的包就支持热更新。
看图里 我们的服务也是正常的启动了。接下来我们验证一下请求看看是否正常。
ps: 这里还是需要提一下
在这里插入图片描述
如果出现这样的提示就是你忘记在package.json中设置module类型了,还有低版本的的node 并不能像我一样丝滑的直接启动esm代码哦。
在这里插入图片描述
OK!看来这里服务已经起来了。接下来我们就开始在flutter来获取hello world。

在harmony虚拟机中请求接口

安装请求库

这里直接安装比较火的dio作为默认的请求库,Dio也是目前 Flutter 开发者首选的 HTTP 请求库,它的地位相当于前端开发中的 Axios。
在这里插入图片描述
这里还是跟之前一样,为了方便理解观看,我们新建一个页面。
在这里插入图片描述
这里我们的思路是,在请求的时候添加一个loading,获取完成数据进行渲染,没有数据就展示空。为了方便调试,另外添加一个icon按钮,点击之后就进行更新数据。

import 'package:flutter/material.dart';
import 'package:dio/dio.dart';

class Internet extends StatefulWidget {
  const Internet({super.key});

  
  _InternetState createState() => _InternetState();
}

class _InternetState extends State<Internet> {
  final dio = Dio();

  // 状态变量
  List<dynamic> _data = [];
  bool _isLoading = true; // 是否正在加载
  String? _errorMessage; // 错误信息

  
  void initState() {
    super.initState();
    // 只有在初始化时请求一次,或者由用户手动触发刷新
    _fetchData();
  }

  // 获取数据函数
  Future<void> _fetchData() async {
    try {
      setState(() {
        _isLoading = true;
        _errorMessage = null; // 清除之前的错误
      });

      // 10.0.2.2 是 Android 模拟器访问电脑本地的地址
      final response = await dio.get('http://10.0.2.2:3000/');

      setState(() {
        // 假设返回的是一个 List,如果不是,需要根据具体 JSON 结构解析
        _data = response.data as List<dynamic>;
        _isLoading = false;
      });
    } on DioException catch (e) {
      setState(() {
        _isLoading = false;
        _errorMessage = "网络请求失败: ${e.message}";
      });
    } catch (e) {
      setState(() {
        _isLoading = false;
        _errorMessage = "发生未知错误: $e";
      });
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Internet Data'),
        actions: [
          IconButton(onPressed: _fetchData, icon: const Icon(Icons.refresh)),
        ],
      ),
      body: _buildBody(), // 抽离主体内容判断
    );
  }

  // 逻辑判断渲染主体内容
  Widget _buildBody() {
    // 1. 加载中状态
    if (_isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    // 2. 错误状态
    if (_errorMessage != null) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.error_outline, size: 48, color: Colors.red),
            const SizedBox(height: 16),
            Text(_errorMessage!),
            ElevatedButton(onPressed: _fetchData, child: const Text('重试')),
          ],
        ),
      );
    }

    // 3. 数据为空状态
    if (_data.isEmpty) {
      return const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.inbox, size: 48, color: Colors.grey),
            SizedBox(height: 16),
            Text('暂无数据'),
          ],
        ),
      );
    }

    // 4. 正常数据渲染
    return ListView.separated(
      padding: const EdgeInsets.all(8),
      itemCount: _data.length,
      itemBuilder: (context, index) {
        final item = _data[index];
        return ListTile(
          title: Text(item.toString()), // 根据你实际 JSON 的字段来,比如 item['name']
          subtitle: Text('ID: $index'),
          leading: const Icon(Icons.cloud_queue),
        );
      },
      separatorBuilder: (context, index) => const Divider(),
    );
  }
}

写完之后在路由配置中添加一下。

final List<ListPage> RouteList = [
  ListPage(
    path: '/home',
    title: '计数器',
    icon: Icons.home,
    builder: (context) => const MyHomePage(title: '计数器'),
  ),
  ListPage(
    path: '/user_list',
    title: '用户管理',
    icon: Icons.person,
    builder: (context) => const UserListPage(),
  ),
  ListPage(
    path: '/internet',
    title: '网络请求',
    icon: Icons.http,
    builder: (context) => const Internet(),
  ),
];

更新一下页面我门看看效果
在这里插入图片描述
点击进入网络请求页面,进入页面的时候会立即进行请求,这里我添加了一debug断点,我们看一下效果如愿以偿的获取到了 hello world
在这里插入图片描述
在这里插入图片描述
我们在后端新增一条数据,再次进行请求。
在这里插入图片描述
示例地址,点击跳转到gitcode

总结

本次任务圆满的完成
网络请求链路已打通,Flutter → OH模拟器 → 宿主机服务 的基本闭环验证通过。
项目源码已上传至:maolin319/harmony_sqllite_floor_demo(gitcode)。
主要是在鸿蒙文档中查到 ,在本地计算机上建立网络服务端,模拟器可以通过10.0.2.2:访问本地计算机服务端,其中10.0.2.2为模拟器的默认网关。,所以访问宿主机直接使用 10.0.2.2:3000。没毛病的老铁。
Flutter 在 OpenHarmony 上的网络子系统可行性已初步确认,无重大阻塞。

欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

Logo

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

更多推荐