核心内容摘要
枫花恋:一场穿越时空的灵魂低语
【开源鸿蒙跨平台开发先锋训练营】DAY15DAY19 动效能力全场景集成 登录注册模拟实现目 录【开源鸿蒙跨平台开发先锋训练营】DAY15DAY19 动效能力全场景集成 登录注册模拟实现 摘 要 1 概述
1 开发背景
2 开发目标
3 核心技术栈️ 2 开发环境准备
1 环境配置
2 本地资源配置
3 我的页面目录结构 3 我的页面功能实现
1 主页面入口实现
2 核心组件实现
3 子页面实现
4 存储工具类 4 运行验证与效果展示
1 我的主页面
2 子页面效果 5
总结与拓展
1
总结
2 拓展方向 6 参考资料 摘 要本文基于开源鸿蒙跨平台开发先锋训练营实战项目记录在 Flutter 鸿蒙技术栈下完成底部选项卡我的页面中登录 / 注册功能的实现。
采用 Hive CE SharedPreferences 混合存储方法结合 GetX 路由管理封装了通用 UI 组件带密码切换的输入框、隐私协议勾选框实现从表单校验、本地存储到状态同步的完整登录注册闭环。
同时对我的页面进行了 UI 适配并将登录注册入口整合到设置页面。
1 概述
1 开发背景在开源鸿蒙跨平台开发场景下我的页面是应用的核心模块之一需支持登录 / 注册、用户信息展示、功能入口聚合、系统设置等能力。
本项目聚焦我的页面与登录、注册功能的模拟实现兼顾跨平台适配性与用户体验。
2 开发目标✅ 实现底部选项卡导航支持首页、美食、动态、推荐、我的页面切换✅ 实现完整的登录 / 注册功能表单校验、验证码倒计时、本地存储、状态同步✅ 将登录注册入口整合到设置页面形成账号管理闭环✅ 采用鸿蒙适配技术栈保证跨平台兼容性。
3 核心技术栈 Flutter跨平台 UI 框架 Hive CE / Hive CE Flutter本地对象存储 SharedPreferences轻量键值对存储存储 Token 等简单数据 GetX路由管理与状态管理 Fluttertoast轻量提示框组件。
️ 2 开发环境准备
1 环境配置
基础环境 Flutter环境安装 Flutter
3.
2
5版本配置flutter-OHOS-
1.
1鸿蒙适配分支分支兼容 OpenHarmony 鸿蒙环境安装DevEco Studio
6.
0 Release配置 OpenHarmony SDKAPI Version
206.
0.
47创建鸿蒙模拟器 项目初始化基于现有flutter_harmonyos工程调整目录结构安装依赖。
依赖安装通过 pubspec.yaml 声明并安装项目依赖核心依赖说明如下dependencies: flutter: sdk: flutter cupertino_icons: ^
1.
8 http: ^
1.
0 # 网络请求核心库鸿蒙兼容 cached_network_image: ^
3.
1 # 图片缓存解决鸿蒙设备图片重复加载卡顿 pull_to_refresh: ^
2.
0 # 下拉刷新适配鸿蒙触控交互 flutter_rating_bar: ^
4.
1 # 美食评分组件鸿蒙UI适配 dio:
5.
0 # 网络请求封装 json_annotation: ^
4.
0 # 数据解析 infinite_scroll_pagination:
4.
0 # 专注上拉分页适配鸿蒙异步逻辑 easy_refresh: ^
3.
0 # 轻量化刷新适配鸿蒙触控交互 carousel_slider: ^
5.
1 # 轮播图组件 # 存储相关核心 shared_preferences: ^
2.
3 # 用于本地存储历史记录存Token hive_ce: ^
2.
1
2 # 存用户信息 hive_ce_flutter: ^
2.
4 # Hive Flutter适配 # UI/工具 fluttertoast: ^
8.
2 # 提示框登录/注册结果 get: ^
4.
5 # 轻量状态管理简化页面跳转 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^
5.
0 json_serializable: ^
6.
5 build_runner: ^
2.
15 # 代码构建工具 hive_ce_generator: ^
1.
2 # Hive模型代码生成
2 本地资源配置在 pubspec.yaml 中声明本地图片资源适配我的页面头像、功能图标等场景assets: - assets/images/ - assets/images/foods/ # 美食卡片图片 - assets/images/food_show/ # 美食页相关图片 - assets/images/food_show/dynamic/ # 清单页动态图 - assets/images/food_show/star/ # 排行页达人头像 - assets/images/food_show/task/ # 任务中心图标 # 新增美食笔记动态模块的本地资源声明 - assets/images/food_note/ # 动态模块根资源 - assets/images/food_note/avatar/ # 用户头像资源 - assets/images/food_note/dynamic/ # 动态图片资源 - assets/images/eat_what/ # 推荐页面资源 - assets/images/mine/ # 我的页面资源
3 我的页面目录结构页面采用组件化 分层设计其中包含注册、登录所需文件目录清晰、职责明确lib/pages/mine/ ├── components/ # 通用UI组件 │ ├── auth_input.dart # 带密码切换的输入框 │ ├── privacy_checkbox.dart # 隐私协议勾选框 │ ├── user_info_widget.dart # 用户信息展示组件 │ └── mine_function_item.dart # 功能条目组件 ├── models/ # 数据模型 │ ├── user_model.dart # 适配Hive的用户信息模型 │ └── mine_function_model.dart # 我的页面功能条目模型 ├── login_page.dart # 登录页 ├── register_page.dart # 注册页 ├── mine_page.dart # 我的页面主页面 ├── settings_page.dart # 设置页面 └── my_collection_page.dart # 我的收藏子页面 3 我的页面功能实现
1 主页面入口实现在main.dart中完成全局初始化与路由配置是整个应用的入口// lib/pages/mine/mine_page.dart import package:flutter/material.dart; import components/user_info_widget.dart; import components/mine_section_widget.dart; import components/mine_function_item.dart; // 导入子页面 import my_collection_page.dart; import my_history_page.dart; import settings_page.dart; import feedback_page.dart; import my_publish_page.dart; class MinePage extends StatelessWidget { const MinePage({super.key}); override Widget build(BuildContext context) { return Scaffold( body: ListView( padding: EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom 50, ), children: [ //
顶部用户信息增加上边距 替换成本地头像 // 增加顶部间距 Padding( padding: const EdgeInsets.only(top:
, // 可根据需求调整数值如20/25 child: UserInfoWidget( userName: 美食爱好者, signature: 分享美食记录生活, // 替换为本地头像 avatarUrl: assets/images/mine/avatar_default.png, ), ), //
我的内容分组 const MineSectionWidget(title: 我的内容), Column( children: [ MineFunctionItem( icon: Icons.bookmark_border, title: 我的收藏, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const MyCollectionPage()), ), ), MineFunctionItem( icon: Icons.history, title: 浏览历史, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const MyHistoryPage()), ), ), MineFunctionItem( icon: Icons.edit_note, title: 我的发布, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const MyPublishPage()), ), ), ], ), //
系统设置分组 const MineSectionWidget(title: 系统设置), MineFunctionItem( icon: Icons.settings, title: 设置, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const SettingsPage()), ), ), MineFunctionItem( icon: Icons.help_outline, title: 帮助与反馈, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const FeedbackPage()), ), ), ], ), ); } }
2 核心组件实现1带密码切换的输入框 auth_input.dart封装支持手机号 / 密码 / 验证码输入的通用组件自动处理密码可见性切换// auth_input.dart # 带图标输入框 // lib/pages/mine/components/auth_input.dart import package:flutter/material.dart; class AuthInput extends StatefulWidget { final TextEditingController controller; final String hintText; final IconData icon; final bool isPassword; // 是否是密码框 const AuthInput({ super.key, required this.controller, required this.hintText, required this.icon, this.isPassword false, }); override StateAuthInput createState() _AuthInputState(); } class _AuthInputState extends StateAuthInput { // 控制密码可见性 bool _obscureText true; override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(vertical:
, decoration: BoxDecoration( border: Border.all(color: Colors.grey[300]!), borderRadius: BorderRadius.circular(
, ), child: TextField( controller: widget.controller, // 用状态变量控制密码是否隐藏 obscureText: widget.isPassword ? _obscureText : false, keyboardType: widget.isPassword ? TextInputType.text : TextInputType.phone, decoration: InputDecoration( prefixIcon: Icon(widget.icon, color: Colors.green), hintText: widget.hintText, border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(vertical:
, // 密码框添加显示/隐藏按钮 suffixIcon: widget.isPassword ? IconButton( icon: Icon( _obscureText ? Icons.visibility_off : Icons.visibility, color: Colors.grey[500], ), // 点击切换密码可见性 onPressed: () { setState(() { _obscureText !_obscureText; }); }, ) : null, ), ), ); } }2隐私协议勾选框 privacy_checkbox.dart实现富文本展示 状态回调满足隐私合规要求登录 / 注册页强制校验// privacy_checkbox.dart # 隐私勾选框 // lib/pages/mine/components/privacy_checkbox.dart import package:flutter/material.dart; class PrivacyCheckbox extends StatefulWidget { final Function(bool?) onChanged; // 勾选状态回调 const PrivacyCheckbox({super.key, required this.onChanged}); override StatePrivacyCheckbox createState() _PrivacyCheckboxState(); } class _PrivacyCheckboxState extends StatePrivacyCheckbox { bool _isChecked false; override Widget build(BuildContext context) { return Row( children: [ Checkbox( value: _isChecked, onChanged: (value) { setState(() _isChecked value!); widget.onChanged(value); }, activeColor: Colors.green, ), const Text.rich( TextSpan( text: 我已阅读并同意, style: TextStyle(fontSize: 12, color: Colors.grey), children: [ TextSpan( text: 《用户协议》, style: TextStyle(color: Colors.green), ), TextSpan(text: 和), TextSpan( text: 《隐私政策》, style: TextStyle(color: Colors.green), ), ], ), ), ], ); } }
3 子页面实现1登录页 login_page.dart完成表单校验、模拟登录、本地存储与路由跳转核心逻辑// login_page.dart # 登录页 // lib/pages/mine/login_page.dart import package:flutter/material.dart; import package:fluttertoast/fluttertoast.dart; import package:get/get.dart; import components/auth_input.dart; import components/privacy_checkbox.dart; import register_page.dart; // 导入用户模型相对路径根据你的目录结构 import models/user_model.dart; // 导入存储工具类相对路径根据你的目录结构 import package:flutter_harmonyos/utils/storage_util.dart; class LoginPage extends StatefulWidget { const LoginPage({super.key}); override StateLoginPage createState() _LoginPageState(); } class _LoginPageState extends StateLoginPage { final _phoneController TextEditingController(); final _pwdController TextEditingController(); bool _isPrivacyAgreed false; // 登录逻辑 void _handleLogin() { String phone _phoneController.text.trim(); String password _pwdController.text.trim(); // 简单校验 if (phone.isEmpty || phone.length !
{ Fluttertoast.showToast(msg: 请输入正确的手机号); return; } if (password.isEmpty || password.length
{ Fluttertoast.showToast(msg: 密码长度不能少于6位); return; } if (!_isPrivacyAgreed) { Fluttertoast.showToast(msg: 请同意用户协议和隐私政策); return; } // 模拟登录API请求后续替换为真实接口 Future.delayed(const Duration(seconds:
, () { // 用 fromLogin 工厂构造函数创建用户 UserModel user UserModel.fromLogin( phone: phone, token: mock_token_${DateTime.now().millisecondsSinceEpoch}, userName: 美食用户$phone, // 替换原来的 nickname ); // 存储用户信息 StorageUtil.saveUser(user); Fluttertoast.showToast(msg: 登录成功); // 清空路由栈并跳转到首页替换首页路由 Get.offAllNamed(/home); }); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(登录)), body: Padding( padding: const EdgeInsets.all(
16.
, child: Column( children: [ const SizedBox(height:
, // 手机号输入框 AuthInput( controller: _phoneController, hintText: 请输入手机号, icon: Icons.phone, ), // 密码输入框 AuthInput( controller: _pwdController, hintText: 请输入密码, icon: Icons.lock, isPassword: true, ), const SizedBox(height:
, // 隐私勾选框 PrivacyCheckbox( onChanged: (value) setState(() _isPrivacyAgreed value!), ), const SizedBox(height:
, // 登录按钮 SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _isPrivacyAgreed ? _handleLogin : null, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, padding: const EdgeInsets.symmetric(vertical:
, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(
, ), ), child: const Text( 登录, style: TextStyle(fontSize: 16, color: Colors.white), ), ), ), const SizedBox(height:
, // 前往注册 TextButton( onPressed: () Get.to(const RegisterPage()), child: const Text( 还没有账号前往注册, style: TextStyle(color: Colors.green), ), ), ], ), ), ); } }2注册页 register_page.dart新增验证码 60 秒倒计时功能复用登录页校验逻辑流程闭环// register_page.dart # 注册页 // lib/pages/mine/register_page.dart import dart:async; // 导入Timer所在的dart:async库 import package:flutter/material.dart; import package:fluttertoast/fluttertoast.dart; import package:get/get.dart; import components/auth_input.dart; import components/privacy_checkbox.dart; class RegisterPage extends StatefulWidget { const RegisterPage({super.key}); override StateRegisterPage createState() _RegisterPageState(); } class _RegisterPageState extends StateRegisterPage { final _phoneController TextEditingController(); final _codeController TextEditingController(); final _pwdController TextEditingController(); bool _isPrivacyAgreed false; bool _isCounting false; // 验证码倒计时 int _countdown 60; // 获取验证码 void _getCode() { String phone _phoneController.text.trim(); if (phone.isEmpty || phone.length !
{ Fluttertoast.showToast(msg: 请输入正确的手机号); return; } // 开始倒计时 setState(() _isCounting true); Timer.periodic(const Duration(seconds:
, (timer) { setState(() { _countdown--; if (_countdown
{ timer.cancel(); _isCounting false; _countdown 60; } }); }); // 模拟获取验证码API请求 Fluttertoast.showToast(msg: 验证码已发送); } // 注册逻辑 void _handleRegister() { String phone _phoneController.text.trim(); String code _codeController.text.trim(); String password _pwdController.text.trim(); // 简单校验 if (phone.isEmpty || phone.length !
{ Fluttertoast.showToast(msg: 请输入正确的手机号); return; } if (code.isEmpty || code.length !
{ Fluttertoast.showToast(msg: 请输入6位验证码); return; } if (password.isEmpty || password.length
{ Fluttertoast.showToast(msg: 密码长度不能少于6位); return; } if (!_isPrivacyAgreed) { Fluttertoast.showToast(msg: 请同意用户协议和隐私政策); return; } // 模拟注册API请求 Future.delayed(const Duration(seconds:
, () { Fluttertoast.showToast(msg: 注册成功); // 存储Token/用户信息后续对接存储层 // 跳转回登录页 Get.back(); }); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(注册)), body: Padding( padding: const EdgeInsets.all(
16.
, child: Column( children: [ const SizedBox(height:
, // 手机号输入框 AuthInput( controller: _phoneController, hintText: 请输入手机号, icon: Icons.phone, ), // 验证码输入框 获取验证码按钮 Row( children: [ Expanded( child: AuthInput( controller: _codeController, hintText: 请输入验证码, icon: Icons.code, ), ), const SizedBox(width:
, SizedBox( width: 120, child: ElevatedButton( onPressed: _isCounting ? null : _getCode, style: ElevatedButton.styleFrom( backgroundColor: _isCounting ? Colors.grey : Colors.green, ), child: Text( _isCounting ? ${_countdown}s : 获取验证码, style: const TextStyle(fontSize:
, ), ), ), ], ), // 密码输入框 AuthInput( controller: _pwdController, hintText: 请设置密码, icon: Icons.lock, isPassword: true, ), const SizedBox(height:
, // 隐私勾选框 PrivacyCheckbox( onChanged: (value) setState(() _isPrivacyAgreed value!), ), const SizedBox(height:
, // 注册按钮 SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _handleRegister, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, padding: const EdgeInsets.symmetric(vertical:
, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(
, ), ), child: const Text( 立即注册, style: TextStyle(fontSize: 16, color: Colors.white), ), ), ), ], ), ), ); } }3我的页面 mine_page.dart动态渲染登录状态适配底部安全区整合功能入口// lib/pages/mine/mine_page.dart import package:flutter/material.dart; import components/user_info_widget.dart; import components/mine_section_widget.dart; import components/mine_function_item.dart; // 导入子页面 import my_collection_page.dart; import my_history_page.dart; import settings_page.dart; import feedback_page.dart; import my_publish_page.dart; class MinePage extends StatelessWidget { const MinePage({super.key}); override Widget build(BuildContext context) { return Scaffold( body: ListView( padding: EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom 50, ), children: [ //
顶部用户信息增加上边距 替换成本地头像 // 增加顶部间距 Padding( padding: const EdgeInsets.only(top:
, // 可根据需求调整数值如20/25 child: UserInfoWidget( userName: 美食爱好者, signature: 分享美食记录生活, // 替换为本地头像 avatarUrl: assets/images/mine/avatar_default.png, ), ), //
我的内容分组 const MineSectionWidget(title: 我的内容), Column( children: [ MineFunctionItem( icon: Icons.bookmark_border, title: 我的收藏, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const MyCollectionPage()), ), ), MineFunctionItem( icon: Icons.history, title: 浏览历史, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const MyHistoryPage()), ), ), MineFunctionItem( icon: Icons.edit_note, title: 我的发布, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const MyPublishPage()), ), ), ], ), //
系统设置分组 const MineSectionWidget(title: 系统设置), MineFunctionItem( icon: Icons.settings, title: 设置, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const SettingsPage()), ), ), MineFunctionItem( icon: Icons.help_outline, title: 帮助与反馈, onTap: () Navigator.push( context, MaterialPageRoute(builder: (context) const FeedbackPage()), ), ), ], ), ); } }4设置页面 settings_page.dart整合账号安全与系统设置动态渲染登录状态// settings_page.dart # 设置页面 import package:flutter/material.dart; // 导入登录/注册页和存储工具 import login_page.dart; import register_page.dart; import package:flutter_harmonyos/utils/storage_util.dart; class SettingsPage extends StatefulWidget { const SettingsPage({super.key}); override StateSettingsPage createState() _SettingsPageState(); } class _SettingsPageState extends StateSettingsPage { // 登录状态标记 bool _isLogin false; // 主题颜色选择 ListColor themeColors [ const Color(0xFFFF
, const Color(0xFF4CAF
, const Color(0xFF2196F
, const Color(0xFF9C27B
, const Color(0xFFFFC
, Colors.black, ]; int selectedColorIndex 4; // 默认选中橙色 // 检查登录状态 void _checkLoginStatus() { setState(() { _isLogin StorageUtil.isLogin(); }); } override void initState() { super.initState(); _checkLoginStatus(); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(设置), centerTitle: true, ), body: ListView( children: [ // 账号与安全板块拆分登录/注册 const ListTile( title: Text( 账号与安全, style: TextStyle(fontSize: 14, color: Color(0xFF
), ), contentPadding: EdgeInsets.only(left: 16, top: 16, bottom:
, ), // 动态显示未登录→显示登录注册已登录→显示退出登录 if (!_isLogin) Column( children: [ // 登录入口 ListTile( leading: const Icon(Icons.login, color: Color(0xFFFC
), title: const Text(登录), trailing: const Icon(Icons.arrow_forward_ios, size:
, onTap: () async { // 跳转到登录页返回后刷新状态 await Navigator.push( context, MaterialPageRoute(builder: (c) const LoginPage()), ); _checkLoginStatus(); }, ), // 注册入口现在用到了register_page.dart警告消失 ListTile( leading: const Icon(Icons.app_registration, color: Color(0xFFFC
), title: const Text(注册), trailing: const Icon(Icons.arrow_forward_ios, size:
, onTap: () async { // 跳转到注册页注册成功后返回刷新状态 await Navigator.push( context, MaterialPageRoute(builder: (c) const RegisterPage()), ); _checkLoginStatus(); }, ), ], ) else // 已登录显示退出登录 ListTile( leading: const Icon(Icons.logout, color: Color(0xFFFC
), title: const Text(退出登录), trailing: const Icon(Icons.arrow_forward_ios, size:
, onTap: () async { // 执行退出登录 await StorageUtil.clearUser(); _checkLoginStatus(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text(退出登录成功)), ); }, ), const Divider(height:
, // 主题切换 const ListTile( title: Text(主题), leading: Icon(Icons.color_lens, color: Color(0xFFFC
), ), Padding( padding: const EdgeInsets.symmetric(horizontal:
, child: Wrap( spacing: 12, children: List.generate(themeColors.length, (index) { return GestureDetector( onTap: () { setState(() { selectedColorIndex index; }); }, child: Container( width: 40, height: 40, decoration: BoxDecoration( color: themeColors[index], borderRadius: BorderRadius.circular(
, border: selectedColorIndex index ? Border.all(color: Colors.white, width:
: null, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(
0.
, blurRadius: 4, ), ], ), ), ); }), ), ), const Divider(height:
, // 多语言 ListTile( title: const Text(多语言), leading: const Icon(Icons.language, color: Color(0xFFFC
), trailing: const Text(跟随系统), onTap: () { // 多语言选择逻辑 }, ), const Divider(height:
, // 清除缓存 ListTile( title: const Text(清除缓存), leading: const Icon(Icons.delete_sweep, color: Color(0xFFFC
), trailing: const Text(
3
66M), onTap: () { // 清除缓存逻辑 ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text(缓存已清除)), ); }, ), const Divider(height:
, // 关于我们 ListTile( title: const Text(关于我们), leading: const Icon(Icons.info_outline, color: Color(0xFFFC
), onTap: () { // 跳转关于页面 }, ), ], ), ); } }
4 存储工具类存储工具类 storage_util.dart采用 Hive CE SharedPreferences 混合存储方案封装用户信息与 Token 操作保证登录状态闭环// 开发存储工具类lib/utils/storage_util.dart // lib/utils/storage_util.dart import package:hive_ce_flutter/hive_ce_flutter.dart; import package:shared_preferences/shared_preferences.dart; // 导入用户模型 import package:flutter_harmonyos/pages/mine/models/user_model.dart; class StorageUtil { // SharedPreferences实例修正变量名统一为_prefs static SharedPreferences? _prefs; // Hive用户存储箱修正变量名统一为_userBox static BoxUserModel? _userBox; // 初始化存储带异常捕获移除adapters判断 static Futurevoid init() async { try { // 初始化Hive社区版 await Hive.initFlutter(); // 注册UserModel适配器hive_ce重复注册会自动忽略无需判断adapters Hive.registerAdapter(UserModelAdapter()); // 打开Hive存储箱 _userBox await Hive.openBoxUserModel(user_box); // 初始化SharedPreferences _prefs await SharedPreferences.getInstance(); print(StorageUtil初始化成功); } catch (e) { print(StorageUtil初始化失败: $e); } } // Token相关 static Futurevoid saveToken(String token) async { if (_prefs null) return; await _prefs!.setString(token, token); } static String? getToken() { return _prefs?.getString(token); } static Futurevoid clearToken() async { if (_prefs null) return; await _prefs!.remove(token); } // 用户信息相关 static Futurevoid saveUser(UserModel user) async { if (_userBox null) return; await _userBox!.put(current_user, user); } static UserModel? getCurrentUser() { return _userBox?.get(current_user); } static Futurevoid clearUser() async { if (_userBox null) return; await _userBox!.delete(current_user); await clearToken(); } // 检查是否已登录带空指针防护 static bool isLogin() { return _prefs ! null getToken() ! null getToken()!.isNotEmpty; } } 4 运行验证与效果展示
1 我的主页面显示用户头像、昵称、个性签名展示我的收藏 / 浏览历史 / 我的发布功能列表底部安全区适配完整无内容遮挡。
2 子页面效果登录 / 注册页输入框支持密码可见性切换隐私协议勾选后按钮可点击验证码倒计时状态反馈清晰设置页面未登录时显示「登录 / 注册」入口已登录时显示「退出登录」主题切换功能支持多颜色选择交互流畅。
5
总结与拓展
1
总结本阶段完成了底部选项卡导航、我的页面、登录 / 注册体系的完整实现核心亮点✅ 跨平台适配采用 Hive CE 等鸿蒙适配依赖保证在鸿蒙设备上稳定运行✅ 组件化开发封装通用 UI 组件登录 / 注册页复用率 100%降低代码冗余✅ 状态闭环管理登录状态动态渲染退出登录时自动清除存储保证数据一致性。
2 拓展方向完善登录/注册功能替换模拟登录 / 注册请求完善登录/注册功能主题切换优化将主题颜色存储到本地实现重启应用后主题状态保留隐私协议跳转为《用户协议》《隐私政策》添加跳转链接完善合规性。
6 参考资料 参考前文https://blog.csdn.net/m0_74451345/article/details/156915775?spm
1001.
2014.
3
5502https://blog.csdn.net/m0_74451345/article/details/157024056?spm
1001.
2014.
3
5502https://blog.csdn.net/m0_74451345/article/details/157032531?spm
1001.
2014.
3
5502https://blog.csdn.net/m0_74451345/article/details/157094159?spm
1001.
2014.
3
5502https://blog.csdn.net/m0_74451345/article/details/157464927?spm
1001.
2014.
3
5502https://blog.csdn.net/m0_74451345/article/details/157505773?spm
1001.
2014.
3
5502https://blog.csdn.net/m0_74451345/article/details/157542849?spm
1001.
2014.
3
5502https://blog.csdn.net/m0_74451345/article/details/157583805?spm
1001.
2014.
3
5501https://blog.csdn.net/m0_74451345/article/details/157657991?spm
1001.
2014.
3