Flutter鸿蒙开发指南(三):使用dio库实现网络请求
本文介绍了在Flutter中使用Dio进行网络请求的方法。主要内容包括:(1)项目初始化与代码托管流程,使用AtomGit创建项目并配置访问令牌;(2)通过猫咪图片API示例,详细讲解了数据模型定义、API服务封装和UI渲染实现;(3)演示了如何在鸿蒙和安卓平台运行项目,展示网络请求结果。文章还预告了后续将开发商城项目,并提供了开源鸿蒙跨平台社区链接。
前言
最近比较忙,没时间更新,今天更新新一期Flutter学习笔记。本篇讲述Flutter是如何进行网络请求的,毕竟没有网络请求就只能看见自己手机本地的数据了,无法查看别人的。以后教程采用Android Studio编写,感觉Vscode不如Android Studio好用。
Android Studio安装教程可查看这篇:
【2025 最新】下载安装配置最新版 Android Studio 最详细教程(带图展示)_android studio安装教程-CSDN博客
一、项目初始化与代码管理
1.1 仓库初始化
本教程采用AtomGit的代码托管网站进行托管。首先进入此网站,点击创建项目,链接在下方。
幻影(6540542857dbf456ecbe217f) - AtomGit | GitCode
https://atomgit.com/Deng666

起好自己的项目名称,设置公开项目或者私有项目。README文件可以后续再添加,点击创建项目即可。

1.2 配置访问令牌
AtomGit不同于Github、Gitee等网站,AtomGit需要先配置访问令牌才可以进行代码的上传。

记得先复制下来自己的自己的访问令牌,保存好。因为只会展示这一次,后续无法找回,只能新建。


1.3 使用Android Studio创建项目
1.3.1 前置准备
在创建项目之前大家需要先下载好两个插件:Dart和Flutter

如何查看并下载?
鼠标悬浮在框住的这个地方并且点击该图标并且选择Settings,选中Plugins。Marketplace是插件市场,Installed是已经下载的。下载插件选择Marketplace




1.3.2 创建项目
创建好项目会出现一个New flutter Project的选项,点击该选项。然后会弹出让你选择SDK路径,这个SDK需要选择已经适配了鸿蒙的SDK。


这里起项目名,项目名尽量见名知意。选择需要开发的平台,我这里就全部勾选了。点击Create即可创建项目完成。(这里没有鸿蒙项目是正常的,我们创建完在终端创建即可。)
打开终端后,先输入"flutter --version"查看是否是适配了鸿蒙的Flutter SDK,如果不是需要切换至鸿蒙的SDK


在终端输入"flutter create --platform ohos ."创建鸿蒙的项目

1.3.3 项目分包
选中lib目录选择New再选择Directory,该操作为新建文件夹。

新建以下的目录,每个目录的作用如图所示:
1.3.4 使用Git命令托管代码
git init 作用:初始化仓库
git add . 作用:提交本地仓库到暂存区
git commit -m "初始化仓库" 作用:" "的内容可以改,说明本次提交代码完成了什么任务。
git remote add origin master <远程仓库地址> 作用:添加远程仓库。(<远程仓库地址>改成自己实际代码仓库,<>是不需要写的,这里这是为了方便读者看)
git push -u origin master 作用:推送主分支到远程
git log 作用:查看推送代码的日志


复制好自己仓库的地址,然后粘贴到终端,再复制令牌,先执行配置仓库密钥的git命令。
git remote set-url origin https://Deng666:wKRPhLaASqWJUs-Bag2h7U8B@gitlab.com:8080/MyProject.git
最后执行推送仓库的命令
git push -u origin master
-
用户名:
Deng666 -
令牌:
wKRPhLaASqWJUs-Bag2h7U8B -
地址格式:
https://用户名:令牌@仓库地址


可以看到项目已经推送成功。
二、使用Dio实现网络请求
由于我后续打算做一个商城的项目,这里的代码我就不使用git上传了,而且我打算在下篇能顺便演示git的回撤操作。在pubspec.yaml添加dio的库,dio的库为:^5.5.0+1。
2.1 简单总结
dio: ^5.5.0+1:都会安装最新的5.5.x版本,5.5.0+1约等于^5.5.0
provider: ^6.1.2:安装6.1.2或者更新的6.x版本,但别装7.0

2.2 接口说明
本篇先使用网上找的一个比较简单的接口进行接口的请求示例。后续再全部更换成商城的。
这里使用的是猫咪图片请求接口
基本说明:
| 接口地址:https://api.thecatapi.com/v1/images/search |
|---|
| 返回格式:json |
| 请求方式:get |
| 请求示例:https://api.thecatapi.com/v1/images/search?limit=1 |
请求参数说明:
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | int | 选填 | 要返回的图片数 |
| page | int | 选填 | 页码 |
| order | int | 选填 | 按照上传日期排序 |
| has_breeds | int | 选填 | 是否包含猫品种信息 |
| breed_ids | string | 选填 | 品种的ID |
| category_ids | string | 选填 | 品种的ID |
| sub_id | int | 选填 | 过滤具有上传时使用的值的图像sub_id |
返回参数说明:
| 名称 | 类型 | 说明 |
|---|---|---|
| url | string | 图片链接 |
| width | int | 宽度 |
| height | int | 图片高度 |
JSON返回示例:
[{
"id": "MTgxNTAxOA",
"url": "https://cdn2.thecatapi.com/images/MTgxNTAxOA.jpg",
"width": 560,
"height": 443
}]
2.3 使用Dio进行网络请求并渲染UI
2.3.1 猫咪图片数据模型
文件路径:lib/viewmodels/cat_model.dart
作用:定义API返回的数据结构
cat_model.dart代码
class CatModel {
final String id;
final String url;
final int width;
final int height;
CatModel({
required this.id,
required this.url,
required this.width,
required this.height,
});
factory CatModel.fromJson(Map<String, dynamic> json) {
return CatModel(
id: json['id'] ?? '',
url: json['url'] ?? '',
width: json['width'] ?? 0,
height: json['height'] ?? 0,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'url': url,
'width': width,
'height': height,
};
}
2.3.2 猫咪API服务
文件路径:lib/api/cat_api.dart
作用:封装具体的网络请求方法
cat_api.dart代码
import 'package:qing_mall/utils/http_util.dart';
import 'package:qing_mall/viewmodels/cat_model.dart';
import 'package:qing_mall/contents/api_constants.dart';
class CatApi {
static final HttpUtil _http = HttpUtil();
// 获取猫咪图片列表(基础方法)
static Future<List<CatModel>> getCatImages({
int limit = 1, // 默认只获取1张
int? page,
String? order,
bool? hasBreeds,
String? breedIds,
String? categoryIds,
int? subId,
}) async {
try {
final Map<String, dynamic> queryParams = {
'limit': limit,
};
// 添加可选参数
if (page != null) queryParams['page'] = page;
if (order != null) queryParams['order'] = order;
if (hasBreeds != null) queryParams['has_breeds'] = hasBreeds ? 1 : 0;
if (breedIds != null) queryParams['breed_ids'] = breedIds;
if (categoryIds != null) queryParams['category_ids'] = categoryIds;
if (subId != null) queryParams['sub_id'] = subId;
final data = await _http.get(
'${ApiConstants.catBaseUrl}${ApiConstants.catImagesEndpoint}',
queryParams: queryParams,
);
if (data is List) {
return data.map((json) => CatModel.fromJson(json)).toList();
} else {
throw Exception('返回数据格式错误');
}
} catch (e) {
rethrow;
}
}
// 获取单张猫咪图片(简化方法)
static Future<CatModel> getSingleCat() async {
final cats = await getCatImages(limit: 1); // ✅ 调用上面定义的方法
return cats.first;
}
// 可选:获取多张猫咪图片
static Future<List<CatModel>> getMultipleCats(int count) async {
return await getCatImages(limit: count);
}
}
2.3.3 猫咪页面调用
文件:lib/pages/Cats/index.dart
作用:界面调用API并显示数据
index.dart代码
import 'package:flutter/material.dart';
import 'package:qing_mall/api/cat_api.dart';
import 'package:qing_mall/viewmodels/cat_model.dart';
import 'package:qing_mall/components/cat_card.dart';
class CatsPage extends StatefulWidget {
const CatsPage({super.key});
@override
State<CatsPage> createState() => _CatsPageState();
}
class _CatsPageState extends State<CatsPage> {
CatModel? _cat; // ✅ 只保存一张图片
bool _isLoading = false;
String? _error;
@override
void initState() {
super.initState();
_fetchCat(); // ✅ 只获取一张
}
Future<void> _fetchCat() async {
setState(() {
_isLoading = true;
_error = null;
});
try {
final cat = await CatApi.getSingleCat(); // ✅ 调用获取单张的方法
setState(() {
_cat = cat;
});
} catch (e) {
setState(() {
_error = e.toString();
});
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('随机猫咪 🐱'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _fetchCat, // ✅ 刷新时也只获取一张
),
],
),
body: _buildBody(),
);
}
Widget _buildBody() {
if (_isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (_error != null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 64, color: Colors.red),
const SizedBox(height: 16),
Text('加载失败: $_error'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _fetchCat,
child: const Text('重新获取'),
),
],
),
);
}
if (_cat == null) {
return const Center(child: Text('暂无猫咪图片'));
}
// ✅ 只显示一张图片,居中显示
return SingleChildScrollView(
child: Center(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CatCard(cat: _cat!),
const SizedBox(height: 20),
// 添加一些操作按钮
Wrap(
spacing: 12,
children: [
ElevatedButton.icon(
onPressed: _fetchCat,
icon: const Icon(Icons.refresh),
label: const Text('换一张'),
),
OutlinedButton.icon(
onPressed: () {
// 可以添加保存或分享功能
},
icon: const Icon(Icons.download),
label: const Text('保存'),
),
],
),
const SizedBox(height: 20),
// 显示详细信息
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'图片信息',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text('ID: ${_cat!.id}'),
Text('宽度: ${_cat!.width} 像素'),
Text('高度: ${_cat!.height} 像素'),
Text('URL: ${_cat!.url}'),
],
),
),
],
),
),
),
);
}
}
2.3.4 猫咪卡片组件
文件:lib/components/cat_card.dart
作用:专门显示猫咪图片的UI组件
cat_card.dart代码
import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/cat_model.dart';
class CatCard extends StatelessWidget {
final CatModel cat;
const CatCard({
super.key,
required this.cat,
});
@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 2,
child: Column(
children: [
// 猫咪图片
ClipRRect(
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12),
),
child: SizedBox(
width: double.infinity,
height: 200,
child: Image.network(
cat.url,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
errorBuilder: (context, error, stackTrace) {
return Container(
color: Colors.grey[100],
child: const Center(
child: Icon(
Icons.image_not_supported,
size: 50,
color: Colors.grey,
),
),
);
},
),
),
),
// 猫咪信息
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'尺寸: ${cat.width} × ${cat.height}',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
Text(
'ID: ${cat.id}', // ✅ 修复:直接显示完整ID,不截取
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
),
],
),
);
}
}
2.3.5 路由配置文件
文件:lib/routes/index.dart
作用:管理页面之间的跳转关系
index.dart代码
import 'package:flutter/material.dart';
import 'package:qing_mall/pages/Login/index.dart';
import 'package:qing_mall/pages/Main/index.dart';
import 'package:qing_mall/pages/Cats/index.dart';
// 管理路由
import 'package:flutter/material.dart';
import 'package:qing_mall/pages/Login/index.dart';
import 'package:qing_mall/pages/Main/index.dart';
import 'package:qing_mall/pages/Cats/index.dart';
// 返回App根组件
Widget getRootWidget() {
return MaterialApp(
// 命名路由
initialRoute: '/', // 默认首页
routes: getRootRoutes(),
);
}
// 返回该App的路由配置
Map<String, Widget Function(BuildContext)> getRootRoutes() {
return {
'/': (context) => MainPage(), // 主页路由
'/login': (context) => LoginPage(), // 登录路由
'/cats': (context) => CatsPage(), // 猫咪图库路由
};
}
2.3.6 猫咪API常量配置
文件:lib/contents/api_constants.dart
作用:集中管理API配置,避免硬编码,便于统一维护。
api_constants.dart代码
class ApiConstants {
// 猫咪API
static const String catBaseUrl = 'https://api.thecatapi.com/v1';
static const String catImagesEndpoint = '/images/search';
// 默认配置
static const int defaultPageSize = 10;
static const int defaultTimeout = 15; // 秒
}
2.3.7 封装Dio网络请求
文件:lib/utils/http_util.dart
作用:Dio网络请求工具类,封装单例和错误处理
http_util.dart代码
import 'package:dio/dio.dart';
class HttpUtil {
static final HttpUtil _instance = HttpUtil._internal();
late Dio _dio;
factory HttpUtil() => _instance;
HttpUtil._internal() {
_dio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 15),
receiveTimeout: const Duration(seconds: 15),
));
}
Dio get dio => _dio;
// 通用GET请求
Future<dynamic> get(String endpoint, {Map<String, dynamic>? queryParams}) async {
try {
final response = await _dio.get(endpoint, queryParameters: queryParams);
return response.data;
} on DioException catch (e) {
throw _handleError(e);
}
}
String _handleError(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
case DioExceptionType.sendTimeout:
case DioExceptionType.receiveTimeout:
return '网络连接超时,请检查网络';
case DioExceptionType.badResponse:
return '服务器错误: ${e.response?.statusCode}';
case DioExceptionType.cancel:
return '请求已取消';
default:
return '网络错误: ${e.message}';
}
}
}
2.3.8 状态管理
文件:lib/stores/cat_store.dart
作用:负责获取、管理和更新猫咪图片数据
cat_store.dart代码
import 'package:flutter/material.dart';
import 'package:qing_mall/api/cat_api.dart';
import 'package:qing_mall/viewmodels/cat_model.dart';
class CatStore extends ChangeNotifier {
List<CatModel> _cats = [];
bool _isLoading = false;
String? _error;
int _currentPage = 1;
List<CatModel> get cats => _cats;
bool get isLoading => _isLoading;
String? get error => _error;
int get currentPage => _currentPage;
// 获取猫咪图片
Future<void> fetchCats({
int limit = 10,
bool hasBreeds = false,
bool loadMore = false,
}) async {
if (!loadMore) {
_currentPage = 1;
}
_isLoading = true;
_error = null;
notifyListeners();
try {
final newCats = await CatApi.getCatImages(
limit: limit,
page: _currentPage,
hasBreeds: hasBreeds,
);
if (loadMore) {
_cats.addAll(newCats);
} else {
_cats = newCats;
}
_currentPage++;
} catch (e) {
_error = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
// 刷新数据
Future<void> refresh() async {
await fetchCats(limit: _cats.length);
}
// 清空数据
void clear() {
_cats = [];
_error = null;
_currentPage = 1;
notifyListeners();
}
}
2.3.9 App入口主页
文件:lib/pages/Main/index.dart
作用:App的入口主页,用户从这里选择进入猫咪图库
index.dart代码
import 'package:flutter/material.dart';
class MainPage extends StatelessWidget {
const MainPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Qing Mall'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'欢迎使用 Qing Mall',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 40),
// 猫咪图库入口
SizedBox(
width: 200,
child: ElevatedButton.icon(
onPressed: () {
Navigator.pushNamed(context, '/cats');
},
icon: const Icon(Icons.pets),
label: const Text('猫咪图库'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
),
),
const SizedBox(height: 16),
// 登录入口
SizedBox(
width: 200,
child: OutlinedButton(
onPressed: () {
Navigator.pushNamed(context, '/login');
},
child: const Text('用户登录'),
),
),
],
),
),
);
}
}
2.3.10 登录页面
文件:lib/pages/Login/index.dart
作用:登录页面的空壳子,有页面结构但需要填充具体登录功能
index.dart代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('登录'),),
body: Center(child: Text('登录页面'),),
);
}
}
2.3.11 主程序入口文件
文件:lib/main.dart
作用:应用启动入口,初始化
main.dart代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:qing_mall/routes/index.dart';
import 'package:qing_mall/stores/cat_store.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CatStore()),
// 可以添加更多Provider
],
child: getRootWidget(),
),
);
}
2.4 运行项目
2.4.1 鸿蒙端
打开ohos的项目并且点击右上角启动项目即可。点击猫咪图库,即可看到可爱的小猫~



2.4.2 安卓端
选择任意一个安卓模拟器,打开后会显示出来。这里已经显示出来了Pixel 6(mobile),点击绿色三角形进行运行即可。程序运行完点击猫咪图库,即可看到小猫咪。





三、结语
本篇文章到此结束,感谢各位阅读。如果本篇文章对你有帮助,那就请点个赞吧。后续计划打算做一个商城项目,目前仍在学习,希望能早日做出并且发布在CSDN上帮助大家。
最后,欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)