【学习打卡】Day02-基于Flutter开发鸿蒙应用_gitcode口袋工具箱
老规矩,lib下创建pages文件夹 然后创建文件 lib/pages是文件夹 user_search_page.dart就是我们要创建的名称,后续不再做此类解释。这里cd表示进入目录oh_gitcode也就是进入到我们的oh_gitcode项目目录实际上根据自己的创建为主,后续此类解释不再过多讲解。这个就表示没有报错,代码正常编译完成,只需要进入开发工具进行相关签名即可完成,然后就可以运行代码到
通过本教程,你将学会:
✅ 1. 学会 GitCode API 的使用方式
GET、Token 鉴权、错误码判断等
✅ 2. 学会在 Flutter(鸿蒙)中发起网络请求
使用 Dio 或 http(推荐 Dio)
✅ 3. 完成 GitCode 口袋工具核心功能:
-
输入用户名查询 GitCode 用户
-
可选 Token → 查询更多私有信息
-
处理各种状态码(401、404、429…)
-
网络异常处理
-
实时显示查询结果(头像、信息、仓库数量…)
✅ 4. 完成一个可运行的 UI 页面(Flutter)
🎯 教程结构
本教程分为 4 步,每一步都有完整代码:
-
创建 Flutter 项目(鸿蒙/HarmonyOS)
-
实现 GitCode API 网络模块(带 Token)
-
创建搜索 UI 界面
-
请求 GitCode API 并显示用户信息
第一阶段:创建 Flutter 鸿蒙项目
你需要在 CMD 中执行:
flutter create --platforms ohos 项目文件名
这里的项目文件名需要自定义,不建议使用中文和特殊符号 : 建议命名方式两种 oh_gitcode ohgitcode
然后进入项目目录:
cd oh_gitcode
这里cd 表示进入目录 oh_gitcode也就是进入到我们的oh_gitcode项目目录实际上根据自己的创建为主,后续此类解释不再过多讲解
🧩 第二阶段:安装网络库 Dio(推荐)
在 pubspec.yaml 添加:
dependencies:
dio: ^5.7.0
flutter_easyloading: ^3.0.5
用vscode打开创建的oh_gitcode项目目录,在根目录下打开 pubspec.yaml 文件,
找到 dependencies:
在下面添加 :
dio: ^5.7.0
flutter_easyloading: ^3.0.5
注意 yaml格式 是换行后 需要加入两个空格 再写依赖和版本号 否则会报错
然后在CMD执行:
flutter pub get
🧩 第三阶段:封装 GitCode API(核心)
这里是你“口袋工具箱”的真正核心模块
支持:匿名访问 / Token 鉴权 / 状态码处理
创建文件:
lib/api/gitcode_api.dart
在项目目录下打开lib文件夹在下面创建 api文件夹 并新建dart文件
import 'package:dio/dio.dart';
// 导入 Dio,用于进行 HTTP 网络请求(比 http 更强大)
class GitCodeApi {
// GitCode API 的基础 URL,所有请求都从这里开始
static const String baseUrl = "https://api.gitcode.com/api/v5";
final Dio _dio; // Dio 实例,用于执行请求
// 构造函数(可传入 Token)
GitCodeApi({String? token})
: _dio = Dio(
BaseOptions(
baseUrl: baseUrl, // 设置基础 URL
connectTimeout: const Duration(seconds: 5), // 连接超时时间
receiveTimeout: const Duration(seconds: 5), // 接收超时时间
headers: token != null // 如果 token 不为空,则加到请求头
? {
"Authorization": "Bearer $token", // Bearer Token 认证方式
}
: {}, // 否则请求头为空(匿名访问)
),
);
/// 查询 GitCode 用户信息接口
/// @param username GitCode 用户名
/// @return Map 包含 success、data、status
Future<Map<String, dynamic>> getUser(String username) async {
try {
// 发起 GET 请求: /users/{username}
final response = await _dio.get("/users/$username");
// 请求成功,返回数据
return {
"success": true,
"data": response.data, // 服务器返回的 JSON 数据
"status": response.statusCode, // 状态码(200、201 等)
};
} on DioException catch (e) {
// 捕获 Dio 异常(网络异常、状态码错误等)
return _handleError(e);
}
}
/// 错误统一处理(捕获 DioException)
Map<String, dynamic> _handleError(DioException e) {
// 如果服务器有返回内容(如 401 / 404)
if (e.response != null) {
return {
"success": false,
"status": e.response?.statusCode, // 状态码
"message": _statusMessage(e.response?.statusCode ?? 0), // 自定义错误文案
};
}
// 无服务器响应,多为断网、超时等
return {
"success": false,
"status": -1,
"message": "网络连接异常",
};
}
/// 根据 GitCode API 文档定义的状态码返回对应报错提示
String _statusMessage(int code) {
switch (code) {
case 401:
return "401 未授权(Token 无效或缺失)";
case 404:
return "404 用户不存在";
case 429:
return "429 请求频率超限,请稍后重试";
case 500:
return "服务器内部错误,请稍后重试";
default:
return "请求失败:$code";
}
}
}
✔ 支持匿名
✔ 支持 Token
✔ 支持状态码处理
✔ 支持网络异常
这就是后端逻辑 核心模块。
🧩 第四阶段:创建 UI 页面(Flutter)
创建文件:
lib/pages/user_search_page.dart
老规矩,lib下创建pages文件夹 然后创建文件 lib/pages是文件夹 user_search_page.dart就是我们要创建的名称,后续不再做此类解释
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import '../api/gitcode_api.dart';
class UserSearchPage extends StatefulWidget {
@override
_UserSearchPageState createState() => _UserSearchPageState();
}
class _UserSearchPageState extends State<UserSearchPage> {
final TextEditingController _usernameCtrl = TextEditingController();
final TextEditingController _tokenCtrl = TextEditingController();
Map<String, dynamic>? result;
bool loading = false;
bool _obscureToken = true;
void _search() async {
String username = _usernameCtrl.text.trim();
String token = _tokenCtrl.text.trim();
if (username.isEmpty) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("请输入用户名")));
return;
}
setState(() => loading = true);
EasyLoading.show(status: "查询中...");
final api = GitCodeApi(token: token.isEmpty ? null : token);
final res = await api.getUser(username);
setState(() {
result = res;
loading = false;
});
EasyLoading.dismiss();
if (res["success"] == true) {
EasyLoading.showSuccess("查询成功");
} else {
EasyLoading.showError(res["message"]);
}
}
void _showAboutDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text("关于 GitCode 口袋工具"),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("版本: 1.0.0"),
SizedBox(height: 8),
Text(
"GitCode 口袋工具是用于查询 GitCode 用户信息的小型应用"),
SizedBox(height: 8),
Text(
"支持带 Token 查询私有数据,也可匿名查询公共信息"),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("确定"))
],
));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("GitCode 用户查询工具"),
actions: [
IconButton(
icon: Icon(Icons.info_outline),
onPressed: _showAboutDialog,
),
],
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(
controller: _usernameCtrl,
decoration: InputDecoration(
labelText: "GitCode 用户名",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
),
SizedBox(height: 12),
TextField(
controller: _tokenCtrl,
obscureText: _obscureToken,
decoration: InputDecoration(
labelText: "访问 Token(可选)",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.security),
suffixIcon: IconButton(
icon: Icon(_obscureToken
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(() {
_obscureToken = !_obscureToken;
});
},
),
),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: loading ? null : _search,
child: loading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
))
: Text("查询用户"),
),
SizedBox(height: 20),
Expanded(child: _buildResult())
],
),
),
);
}
Widget _buildResult() {
if (result == null) return Container();
if (result!["success"] == false) {
return Center(
child: Text(
result!["message"],
style: TextStyle(fontSize: 16, color: Colors.red),
),
);
}
final user = result!["data"];
return ListView(
children: [
Center(
child: CircleAvatar(
radius: 40,
backgroundImage: NetworkImage(user["avatar_url"] ?? ""),
),
),
SizedBox(height: 16),
Center(
child: Text(
user["username"] ?? "未知用户名",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
Center(
child: Text(user["name"] ?? "无昵称"),
),
SizedBox(height: 12),
Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("公开仓库:${user["public_repos_count"] ?? 0}"),
Text("粉丝:${user["followers"] ?? 0}"),
Text("关注:${user["following"] ?? 0}"),
Text("个人简介:${user["bio"] ?? '无'}"),
Text("博客:${user["blog"] ?? '无'}"),
Text("类型:${user["type"] ?? '未知'}"),
],
),
),
)
],
);
}
}
🧩 第五阶段:在 main.dart 加入入口
修改:
lib/main.dart
在项目目录的Lib目录下已经有一个main.dart文件了,只需要修改文件名即可
import 'package:flutter/material.dart'; // 引入 Flutter 的 UI 库
import 'pages/user_search_page.dart'; // 引入我们写的用户查询页面
// Flutter 程序入口,所有 Dart 程序都从 main() 开始执行
void main() {
runApp(MyApp()); // 启动整个 Flutter 应用,加载 MyApp 小部件
}
// 定义一个无状态组件(UI 不随内部变量改变)
// MyApp 是整个应用的根节点
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GitCode口袋工具', // 应用名称(在任务管理器中显示)
theme: ThemeData(primarySwatch: Colors.blue), // 设置主题颜色为蓝色
home: UserSearchPage(), // 设置首页为用户查询页面
);
}
}
写完代码以后打开powershell
执行指令
flutter build app --release
注:如果出现
请通过DevEco Studio打开ohos工程后配置调试签名(File -> Project Structure -> Signing Configs 勾选Automatically generate signature)
这个就表示没有报错,代码正常编译完成,只需要进入开发工具进行相关签名即可完成,然后就可以运行代码到虚拟机查看效果了
拓展:
界面风格多样化,只需要替换页面代码即可更换风格新风格代码如下:
// 1. 导入dart:ui包:核心用于获取ImageFilter类,实现背景模糊(毛玻璃效果)
// 毛玻璃是现代UI设计常用风格,能提升界面层次感和美观度
import 'dart:ui';
// 2. 导入Flutter核心Material包:包含所有基础UI组件(如Text、TextField、Scaffold等)
// 开发Flutter应用必须导入,提供了Material Design风格的全套组件库
import 'package:flutter/material.dart';
// 3. 导入自定义的GitCode API工具类:封装了调用GitCode用户查询接口的逻辑
// 分离API请求与UI代码,符合单一职责原则,便于维护和复用
import '../api/gitcode_api.dart';
// 4. 定义页面主组件:使用StatefulWidget(有状态组件)
// 原因:页面需要维护输入框文本、加载状态、查询结果等动态数据,StatelessWidget无法管理状态
class UserSearchPage extends StatefulWidget {
// 5. 重写createState方法:创建对应的状态管理类
// 这是StatefulWidget的强制要求,状态类将负责维护页面数据和构建UI
@override
_UserSearchPageState createState() => _UserSearchPageState();
}
// 6. 定义状态管理类:继承自State<UserSearchPage>
// 作用:存储页面动态状态,提供setState方法更新UI,与主组件一一对应
class _UserSearchPageState extends State<UserSearchPage> {
// 7. 创建用户名输入框控制器:TextEditingController用于监听/控制输入框文本
// 原因:需要获取用户输入的用户名,控制器能方便地获取、修改输入框内容
final TextEditingController _usernameCtrl = TextEditingController();
// 8. 创建Token输入框控制器:同理用于控制Token输入框
// 原因:Token为可选输入,需要单独获取,便于后续传递给API
final TextEditingController _tokenCtrl = TextEditingController();
// 9. 存储查询结果:Map类型适配API返回的键值对数据
// 原因:API返回的用户信息是多字段数据(头像、用户名、仓库数等),Map能灵活存储
Map<String, dynamic>? result;
// 10. 加载状态标记:bool类型控制按钮状态和加载弹窗
// 原因:防止用户重复点击查询按钮,避免并发网络请求,提升用户体验
bool loading = false;
// 11. Token隐藏状态:控制Token输入框是否显示明文
// 原因:Token是敏感信息,默认隐藏明文,保护用户隐私
bool _obscureToken = true;
// 12. 核心查询方法:async标记为异步方法(因包含网络请求)
// 原因:网络请求是耗时操作,必须用异步处理,避免阻塞UI线程
void _search() async {
// 13. 获取用户名输入:trim()去除首尾空格
// 原因:避免用户输入空字符或仅输入空格导致的无效查询
String username = _usernameCtrl.text.trim();
// 14. 获取Token输入:同理去除首尾空格
// 原因:Token可能包含意外空格,影响API请求有效性
String token = _tokenCtrl.text.trim();
// 15. 表单验证:检查用户名是否为空
// 原因:用户名是查询的必填项,为空时直接提示用户,避免无效网络请求
if (username.isEmpty) {
// 16. 显示提示 SnackBar:轻量级反馈组件,不打断用户操作
// 原因:告知用户缺少必填项,SnackBar比弹窗更友好,自动消失
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("请输入用户名")),
);
// 17. 终止方法执行:避免后续无效逻辑
return;
}
// 18. 更新加载状态:设置loading为true
// 原因:通过setState触发UI重建,让按钮禁用并显示加载指示器
setState(() => loading = true);
// 19. 显示加载弹窗:barrierDismissible设为false(不可点击背景关闭)
// 原因:告知用户正在查询中,禁止背景点击避免操作干扰,提升体验
showDialog(
context: context,
barrierDismissible: false,
// 20. 加载指示器:CircularProgressIndicator是Flutter默认加载组件
// 原因:直观展示加载状态,Center组件让指示器居中显示
builder: (_) => const Center(
child: CircularProgressIndicator(),
),
);
// 21. 创建API实例:Token为空时传null(API内部处理匿名查询)
// 原因:适配两种查询模式(匿名查公共信息/Token查私有数据),灵活兼容
final api = GitCodeApi(token: token.isEmpty ? null : token);
// 22. 调用API查询用户:await等待异步请求结果
// 原因:网络请求是异步的,必须用await获取结果后再执行后续逻辑
final res = await api.getUser(username);
// 23. 关闭加载弹窗:rootNavigator: true确保关闭最顶层弹窗
// 原因:避免因弹窗层级问题导致无法关闭,确保查询完成后立即隐藏加载状态
Navigator.of(context, rootNavigator: true).pop();
// 24. 更新查询结果和加载状态:setState触发UI重建
// 原因:将API返回结果存入result,加载状态重置为false,让UI显示结果/恢复按钮状态
setState(() {
result = res;
loading = false;
});
// 25. 显示查询结果提示:根据success字段判断提示内容
// 原因:告知用户查询结果(成功/失败),失败时显示具体原因,提升交互体验
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
res["success"] == true ? "查询成功" : (res["message"] ?? "查询失败"),
),
),
);
}
// 26. 显示关于对话框:独立方法封装弹窗逻辑
// 原因:将弹窗逻辑与build方法分离,代码结构更清晰,便于复用和维护
void _showAboutDialog() {
// 27. 显示AlertDialog:系统默认弹窗组件,包含标题、内容、按钮
// 原因:AlertDialog适合展示基础信息,用户操作明确(仅确定按钮)
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text("关于 GitCode 口袋工具"), // 弹窗标题
// 28. 弹窗内容:Column设置mainAxisSize为min(最小高度)
// 原因:避免Column占满屏幕高度,仅包裹子元素,布局更紧凑
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, // 文本左对齐
children: const [
Text("版本: 1.0.0"), // 应用版本信息
SizedBox(height: 8), // 垂直间距:分隔不同信息,提升可读性
Text("GitCode 口袋工具是用于查询 GitCode 用户信息的小型应用"), // 应用描述
SizedBox(height: 8),
Text("支持带 Token 查询私有数据,也可匿名查询公共信息"), // 核心功能说明
],
),
// 29. 弹窗操作按钮:TextButton为文本按钮,风格简洁
// 原因:仅需"确定"按钮关闭弹窗,TextButton比ElevatedButton更轻便
actions: [
TextButton(
onPressed: () => Navigator.pop(context), // 关闭弹窗
child: const Text("确定"),
)
],
),
);
}
// 30. 重写build方法:构建页面UI的核心方法
// 原因:State类必须重写build方法,返回当前状态对应的Widget树
@override
Widget build(BuildContext context) {
// 31. Scaffold:页面基础容器,提供导航栏、主体内容等结构
// 原因:Flutter页面的标准布局容器,包含了页面所需的基础结构,必须使用
return Scaffold(
// 32. 页面背景容器:设置渐变背景
// 原因:使用LinearGradient实现深色渐变背景,提升UI美观度,符合工具类应用风格
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF1C1C2E), Color(0xFF2E2E3F)], // 深色渐变配色
begin: Alignment.topLeft, // 渐变起始点:左上
end: Alignment.bottomRight, // 渐变结束点:右下
),
),
// 33. SafeArea:适配系统安全区域(避免内容被状态栏/导航栏遮挡)
// 原因:不同设备的状态栏高度不同,SafeArea能自动留出安全间距,提升兼容性
child: SafeArea(
// 34. Padding:给子元素添加内边距
// 原因:避免内容紧贴屏幕边缘,提升UI美观度和可读性
child: Padding(
padding: const EdgeInsets.all(16), // 四周16像素内边距
// 35. Column:垂直排列组件(标题区、输入区、结果区)
// 原因:页面布局为垂直方向,Column是Flutter垂直布局的核心组件
child: Column(
children: [
// 36. 标题和信息按钮行:Row实现水平布局
// 原因:标题居左、信息按钮居右,需要水平排列
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // 两端对齐
children: [
// 37. 页面标题文本:设置字体大小、颜色、粗细
// 原因:突出页面主题,白色粗体字在深色背景下更醒目
const Text(
"GitCode 查询",
style: TextStyle(
fontSize: 22, // 字体大小:突出标题
color: Colors.white, // 白色文字适配深色背景
fontWeight: FontWeight.bold, // 加粗:增强视觉权重
),
),
// 38. 信息按钮:IconButton是可点击的图标组件
// 原因:用info_outline图标直观表示"关于"功能,白色图标适配深色背景
IconButton(
icon: const Icon(Icons.info_outline, color: Colors.white),
onPressed: _showAboutDialog, // 点击触发关于弹窗
),
],
),
const SizedBox(height: 20), // 垂直间距:分隔标题区和输入区
// 39. 输入框和查询按钮容器:使用自定义毛玻璃组件
// 原因:统一输入区样式,毛玻璃效果提升UI质感
_glassContainer(
child: Column(
children: [
// 40. 用户名输入框:TextField用于文本输入
// 原因:获取用户输入的GitCode用户名,是查询的核心输入项
TextField(
controller: _usernameCtrl, // 绑定控制器:获取输入文本
style: const TextStyle(color: Colors.white), // 白色文字适配深色背景
decoration: const InputDecoration(
labelText: "GitCode 用户名", // 输入提示文本
labelStyle: TextStyle(color: Colors.white70), // 提示文本浅色:不抢焦点
prefixIcon: Icon(Icons.person, color: Colors.white70), // 人形图标:直观表示用户
border: InputBorder.none, // 取消默认边框:毛玻璃容器已有边框
),
),
const SizedBox(height: 12), // 垂直间距:分隔两个输入框
// 41. Token输入框:支持隐藏/显示文本
// 原因:Token是敏感信息,默认隐藏,同时提供显示功能方便用户核对
TextField(
controller: _tokenCtrl, // 绑定控制器:获取Token输入
obscureText: _obscureToken, // 控制是否隐藏文本
style: const TextStyle(color: Colors.white), // 白色文字适配深色背景
decoration: InputDecoration(
labelText: "访问 Token(可选)", // 提示文本:说明Token是可选的
labelStyle: const TextStyle(color: Colors.white70), // 浅色提示文本
prefixIcon: const Icon(Icons.security, color: Colors.white70), // 安全图标:暗示敏感信息
// 42. 显示/隐藏Token按钮:IconButton切换_obscureToken状态
// 原因:方便用户查看输入的Token是否正确,提升易用性
suffixIcon: IconButton(
icon: Icon(
_obscureToken ? Icons.visibility : Icons.visibility_off,
color: Colors.white70,
),
onPressed: () {
// 43. 更新Token隐藏状态:setState触发UI重建
// 原因:切换_obscureToken的值,让输入框显示/隐藏文本
setState(() {
_obscureToken = !_obscureToken;
});
},
),
border: InputBorder.none, // 取消默认边框:与用户名输入框风格统一
),
),
const SizedBox(height: 16), // 垂直间距:分隔输入框和按钮
// 44. 查询按钮:ElevatedButton带阴影的按钮
// 原因:触发查询操作,loading状态下禁用,避免重复请求
ElevatedButton(
onPressed: loading ? null : _search, // loading时禁用按钮
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white24, // 半透明白色:适配毛玻璃风格
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), // 圆角:提升美观度
),
),
// 45. 按钮内容:根据loading状态显示加载指示器或文字
// 原因:loading时显示加载中,告知用户正在处理,提升体验
child: loading
? const SizedBox(
width: 20,
height: 20,
// 46. 小型加载指示器:颜色白色,线宽2像素
// 原因:与按钮文字颜色一致,小巧不占空间
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2),
)
: const Text("查询用户", style: TextStyle(color: Colors.white)), // 正常状态文字
),
],
),
),
const SizedBox(height: 20), // 垂直间距:分隔输入区和结果区
// 47. 查询结果区:Expanded占满剩余空间
// 原因:让结果区自适应屏幕剩余高度,避免内容溢出
Expanded(
// 48. 条件渲染:result为null时显示空容器,否则显示结果
// 原因:查询前不显示结果区,查询后显示内容,避免空状态下的无效UI
child: result == null ? Container() : _buildResultGlass(result!),
),
],
),
),
),
),
);
}
// 49. 自定义毛玻璃容器:封装通用毛玻璃样式,便于复用
// 原因:输入区和结果区都需要毛玻璃效果,封装后减少重复代码,统一风格
Widget _glassContainer({required Widget child}) {
return ClipRRect(
// 50. 圆角裁剪:设置20像素圆角
// 原因:毛玻璃容器通常搭配圆角,提升视觉柔和度
borderRadius: BorderRadius.circular(20),
child: BackdropFilter(
// 51. 背景模糊滤镜:sigmaX/Y控制模糊程度(值越大越模糊)
// 原因:实现毛玻璃核心效果,20像素模糊度适中,不影响内容可读性
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(
padding: const EdgeInsets.all(16), // 内边距:让子内容不紧贴容器边缘
decoration: BoxDecoration(
// 52. 半透明白色背景:opacity 0.1 透明度适中
// 原因:毛玻璃需要半透明底色,增强模糊效果的层次感
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(20), // 与ClipRRect圆角一致,避免裁剪异常
// 53. 白色边框:opacity 0.2 浅色边框
// 原因:勾勒容器轮廓,增强毛玻璃的质感和边界感
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: child, // 传入子组件:实现容器复用
),
),
);
}
// 54. 结果显示毛玻璃组件:专门用于展示查询结果
// 原因:分离结果显示逻辑,让build方法更简洁,专注于结果展示
Widget _buildResultGlass(Map<String, dynamic> res) {
// 55. 失败状态处理:显示错误信息
// 原因:查询失败时告知用户具体原因,红色文字突出错误
if (res["success"] == false) {
return Center(
child: Text(
res["message"] ?? "查询失败", // 优先显示API返回的错误信息,无则显示默认值
style: const TextStyle(color: Colors.red, fontSize: 16), // 红色文字:直观提示错误
),
);
}
// 56. 成功状态:提取用户数据
// 原因:API返回的data字段包含用户信息,单独提取便于使用
final user = res["data"];
// 57. 结果列表:ListView支持滚动,避免内容过多溢出
// 原因:用户信息可能较多(如简介、博客等),ListView确保内容可滚动查看
return ListView(
children: [
// 58. 用户头像:CircleAvatar圆形头像组件
// 原因:展示用户头像,圆形是头像的常用样式,radius控制大小
Center(
child: CircleAvatar(
radius: 40, // 头像半径:40像素大小适中
// 59. 网络图片加载:NetworkImage用于加载网络图片
// 原因:用户头像URL来自API返回,通过NetworkImage加载
backgroundImage: NetworkImage(user["avatar_url"] ?? ""),
),
),
const SizedBox(height: 16), // 垂直间距:分隔头像和用户名
// 60. 用户名:加粗显示,突出重要信息
Center(
child: Text(
user["username"] ?? "未知用户名", // 无数据时显示默认值,避免空指针
style: const TextStyle(
fontSize: 18, // 字体较大:突出用户名
fontWeight: FontWeight.bold, // 加粗:增强视觉权重
color: Colors.white, // 白色文字适配深色背景
),
),
),
// 61. 用户昵称:浅色显示,作为补充信息
Center(
child: Text(
user["name"] ?? "无昵称", // 无昵称时显示默认文本
style: const TextStyle(color: Colors.white70), // 浅色:不抢用户名的焦点
),
),
const SizedBox(height: 12), // 垂直间距:分隔昵称和详情区
// 62. 用户详情容器:使用毛玻璃组件,保持风格统一
_glassContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, // 文本左对齐,结构清晰
children: [
// 63. 公开仓库数:显示用户公开仓库数量
Text("公开仓库:${user["public_repos_count"] ?? 0}", style: const TextStyle(color: Colors.white)),
// 64. 粉丝数:显示用户粉丝数量
Text("粉丝:${user["followers"] ?? 0}", style: const TextStyle(color: Colors.white)),
// 65. 关注数:显示用户关注数量
Text("关注:${user["following"] ?? 0}", style: const TextStyle(color: Colors.white)),
// 66. 个人简介:无简介时显示"无"
Text("个人简介:${user["bio"] ?? '无'}", style: const TextStyle(color: Colors.white)),
// 67. 博客地址:无博客时显示"无"
Text("博客:${user["blog"] ?? '无'}", style: const TextStyle(color: Colors.white)),
// 68. 用户类型:区分个人/组织账号
Text("类型:${user["type"] ?? '未知'}", style: const TextStyle(color: Colors.white)),
],
),
),
],
);
}
}
目前一个简单的工具箱就写完成了,代码具体意思可以自行ai
其中会遇到的Bug,
1.虚拟机无网络
重启电脑就可以解决
2.pubspec.yaml 报错
添加依赖组件的时候换行 然后呢删除到最前面 空格两个
每个组件为一行,每个组件前面需要两个空格
如图:
好了,本次学习笔记完成,有更多报错可以留言,
本课程开发过程中未解决Bug
编译后运行完成后再次flutter编译会报签名错误 目前暂时没时间去研究解决,知道的同学朋友可以评论区解答目前操作方式是重新创建一个项目解决
报错内容:
> hvigor WARN: The project has not explicitly set the 'targetSdkVersion' version at build-profile.json5. It is
recommended to configure it. Reference:
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-hvigor-build-profile-app#section45865492619
Flutter assets will be downloaded from https://storage.flutter-io.cn. Make sure you trust this source!
> hvigor WARN: WARN: ArkTS:WARN File:
E:/Java/oh_code/oh_test_gitcode/ohos/oh_modules/.ohpm/@ohos+flutter_ohos@om4d5v8+rovmfrwedbpni+qru+qkbo5i0gbcrt+48xg=/oh
_modules/@ohos/flutter_ohos/src/main/ets/plugin/common/Any.ets:7:20
Usage of 'ESObject' type is restricted (arkts-limited-esobj)
WARN: ArkTS:WARN: For details about ArkTS syntax errors, see FAQs
WARN: ArkTS:WARN File:
E:/Java/oh_code/oh_test_gitcode/ohos/oh_modules/.ohpm/@ohos+flutter_ohos@om4d5v8+rovmfrwedbpni+qru+qkbo5i0gbcrt+48xg=/oh
_modules/@ohos/flutter_ohos/src/main/ets/util/StringUtils.ets:7:21
Currently module for 'libflutter.so' is not verified. If you're importing napi, its verification will be enabled in
later SDK version. Please make sure the corresponding .d.ts file is provided and the napis are correctly declared.
WARN: ArkTS:WARN File:
E:/Java/oh_code/oh_test_gitcode/ohos/oh_modules/.ohpm/@ohos+flutter_ohos@om4d5v8+rovmfrwedbpni+qru+qkbo5i0gbcrt+48xg=/oh
_modules/@ohos/flutter_ohos/src/main/ets/embedding/engine/dart/DartMessenger.ets:277:5
Function may throw exceptions. Special handling is required.
WARN: ArkTS:WARN File:
E:/Java/oh_code/oh_test_gitcode/ohos/oh_modules/.ohpm/@ohos+flutter_ohos@om4d5v8+rovmfrwedbpni+qru+qkbo5i0gbcrt+48xg=/oh
_modules/@ohos/flutter_ohos/src/main/ets/embedding/engine/dart/DartMessenger.ets:306:5
Function may throw exceptions. Special handling is required.
WARN: ArkTS:WARN File:
E:/Java/oh_code/oh_test_gitcode/ohos/oh_modules/.ohpm/@ohos+flutter_ohos@om4d5v8+rovmfrwedbpni+qru+qkbo5i0gbcrt+48xg=/oh
_modules/@ohos/flutter_ohos/src/main/ets/embedding/engine/dart/DartMessenger.ets:316:20
Function may throw exceptions. Special handling is required.
WARN: ArkTS:WARN File:
E:/Java/oh_code/oh_test_gitcode/ohos/oh_modules/.ohpm/@ohos+flutter_ohos@om4d5v8+rovmfrwedbpni+qru+qkbo5i0gbcrt+48xg=/oh
_modules/@ohos/flutter_ohos/src/main/ets/plugin/common/SendableJSONMessageCodec.ets:77:7
Function may throw exceptions. Special handling is required.
WARN: ArkTS:WARN File:
E:/Java/oh_code/oh_test_gitcode/ohos/oh_modules/.ohpm/@ohos+flutter_ohos@om4d5v8+rovmfrwedbpni+qru+qkbo5i0gbcrt+48xg=/oh
_modules/@ohos/flutter_ohos/src/main/ets/util/PathUtils.ets:27:10
Function may throw exceptions. Special handling is required.
WARN: ArkTS:WARN File: E:/Java/oh_code/oh_test_gitcode/ohos/entry/src/main/ets/pages/Index.ets:5:28
'getShared' has been deprecated.
WARN: ArkTS:WARN File: E:/Java/oh_code/oh_test_gitcode/ohos/entry/src/main/ets/pages/Index.ets:11:21
'getContext' has been deprecated.
> hvigor ERROR: Failed :entry:default@SignHap...
> hvigor ERROR: Tools execution failed.
11-25 02:49:56.103 ERROR -
ERROR: 11014003 Init keystore failed
Error Message: Integrity check failed: java.security.NoSuchAlgorithmException: Algorithm HmacPBESHA256 not available
* Try the following:
> The key store file does not exist, please check the key store file path.
> Incorrect keystore password, please input the correct plaintext password.
> The keystore was created by a newer JDK version, please use the same JDK version
Detail: Please check the message from tools.
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --debug option to get more log output.
> hvigor ERROR: BUILD FAILED in 35 s 670 ms
Running Hvigor task assembleApp... /
Oops; flutter has exited unexpectedly: "ProcessException: The command failed with exit code 1
Command: hvigorw assembleApp -p product=default -p buildMode=release --no-daemon -p FLUTTER_TARGET=lib\main.dart -p
TARGET_PLATFORM=ohos-arm64 -p DART_OBFUSCATION=false -p TRACK_WIDGET_CREATION=true -p TREE_SHAKE_ICONS=true -p
PACKAGE_CONFIG=E:\Java\oh_code\oh_test_gitcode\.dart_tool\package_config.json".
A crash report has been written to E:\Java\oh_code\oh_test_gitcode\flutter_02.log
This crash may already be reported. Check GitHub for similar crashes.
总结:
本工具箱利用ai完全可以做出来,需要掌握部分基础知识,相对来说结合ai入行鸿蒙应用开发是比较容易的,预留作业,用户名显示未知用户名 看看评论区有人能找到答案吗?美化后的代码基本上全部有解释,不利用ai的情况下是可以找到并修正的
修正后如图:
更多推荐

所有评论(0)