【开源鸿蒙跨平台开发先锋训练营】DAY15~DAY19为开源鸿蒙跨平台应用全面集成添加核心场景-注册页面
本文介绍了Flutter注册页面的开发实现,包含手机号、密码和验证码输入功能。页面采用白色背景,包含标题区、输入表单(带图标和样式)、验证码发送按钮(含倒计时功能)、操作按钮(返回登录和立即注册)以及隐私政策同意选项。通过状态管理控制密码可见性、验证码倒计时和隐私政策勾选状态,并实现了表单验证逻辑。注册成功后跳转至主页面,失败则显示错误提示。整体采用绿色(#4CAF50)作为主题色,搭配灰阶文本和
有了登录页面 怎么可能没有注册页面呢 所以它来了
1.注册页面的开发先看截图效果

该页面包含手机号、密码、验证码输入,短信验证码发送与倒计时,表单验证,隐私政策同意与否。
2.功能部分
注册页面主要包含以下几个部分:
标题区域:用户注册标题
手机号输入框:带图标的手机号输入
密码输入框:支持显示/隐藏密码
验证码输入框:验证码输入 + 获取验证码按钮(带倒计时)
短信帮助链接:解决验证码问题的帮助信息
操作按钮:返回登录 + 立即注册
隐私政策同意:复选框 + 隐私政策页面
3.状态
class RegisterPage extends StatefulWidget {
const RegisterPage({super.key});
@override
State<RegisterPage> createState() => _RegisterPageState();
}
class _RegisterPageState extends State<RegisterPage> {
// 输入控制器
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _codeController = TextEditingController();
// 状态变量
bool _obscurePassword = true; // 密码是否隐藏
bool _agreedToPrivacy = false; // 是否同意隐私政策
int _countdown = 0; // 验证码倒计时
@override
void dispose() {
_phoneController.dispose();
_passwordController.dispose();
_codeController.dispose();
super.dispose();
}
}
代码解释
三个TextEditingController分别为手机号、密码、验证码输入
_countdown是验证码按钮的倒计时状态
在 dispose中释放资源
4.页面整体代码结构
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 60),
// 标题
const Text(
'用户注册',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Color(0xFF1F2937),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 60),
_buildPhoneInput(),
const SizedBox(height: 24),
_buildPasswordInput(),
const SizedBox(height: 24),
_buildCodeInput(),
const SizedBox(height: 8),
_buildSmsHelpLink(),
const SizedBox(height: 24),
_buildActionButtons(),
const SizedBox(height: 40),
_buildPrivacyAgreement(),
],
),
),
),
);
}
5.手机号输入框
Widget _buildPhoneInput() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.phone_outlined, color: Colors.grey[600], size: 20),
const SizedBox(width: 8),
Text(
'手机号',
style: TextStyle(
fontSize: 16,
color: Colors.grey[700],
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 12),
TextField(
controller: _phoneController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
hintText: '请输入手机号',
hintStyle: TextStyle(color: Colors.grey[400]),
filled: true,
fillColor: const Color(0xFFF9FAFB),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
),
),
),
],
);
}
代码解释
1.与登录页面保持一致的设计风格
2.使用数字键盘类型TextInputType.phone
3.浅灰色背景
6.密码输入框
Widget _buildPasswordInput() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.lock_outline, color: Colors.grey[600], size: 20),
const SizedBox(width: 8),
Text(
'密码',
style: TextStyle(
fontSize: 16,
color: Colors.grey[700],
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 12),
TextField(
controller: _passwordController,
obscureText: _obscurePassword,
decoration: InputDecoration(
hintText: '请输入密码',
hintStyle: TextStyle(color: Colors.grey[400]),
filled: true,
fillColor: const Color(0xFFF9FAFB),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword ? Icons.visibility_off : Icons.visibility,
color: Colors.grey[400],
size: 20,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
),
),
],
);
}
7.验证码输入框
Widget _buildCodeInput() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.shield_outlined, color: Colors.grey[600], size: 20),
const SizedBox(width: 8),
Text(
'验证码',
style: TextStyle(
fontSize: 16,
color: Colors.grey[700],
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
// 验证码输入框
Expanded(
child: TextField(
controller: _codeController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: '请输入验证码',
hintStyle: TextStyle(color: Colors.grey[400]),
filled: true,
fillColor: const Color(0xFFF9FAFB),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
),
),
),
),
const SizedBox(width: 12),
// 获取验证码按钮
ElevatedButton(
onPressed: _countdown > 0 ? null : _sendVerificationCode,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF4CAF50),
foregroundColor: Colors.white,
disabledBackgroundColor: Colors.grey[300],
disabledForegroundColor: Colors.grey[600],
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 14,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 0,
),
child: Text(
_countdown > 0 ? '${_countdown}s' : '获取验证码',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
],
),
],
);
}
代码解释一下
输入框和按钮并排布局
按钮根据倒计时状态动态显示文字
倒计时期间按钮禁用(onPressed: null)
禁用状态使用灰色
8.短信帮助功能
这个我要额外说一下 由于本人是个人开发者 所以懂得都懂 只能借鉴第3方 这个时候 这个功能我觉得是非常重要的
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: _showSmsHelpDialog,
child: const Text(
'短信遇到问题?',
style: TextStyle(
color: Color(0xFF9CA3AF),
fontSize: 13,
decoration: TextDecoration.underline,
decorationColor: Color(0xFF9CA3AF),
),
),
),
)
9.返回登录和立即注册功能
Row(
children: [
// 返回登录按钮
Expanded(
child: OutlinedButton(
onPressed: () {
Navigator.pop(context);
},
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
side: const BorderSide(color: Color(0xFFE5E7EB)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.arrow_back, size: 18, color: Color(0xFF6B7280)),
SizedBox(width: 8),
Text(
'返回登录',
style: TextStyle(
fontSize: 15,
color: Color(0xFF6B7280),
fontWeight: FontWeight.w500,
),
),
],
),
),
),
const SizedBox(width: 12),
// 立即注册按钮
Expanded(
flex: 2,
child: ElevatedButton(
onPressed: _handleRegister,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF4CAF50),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 0,
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.person_add, size: 18),
SizedBox(width: 8),
Text(
'立即注册',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
],
)
10.校验是否为空
void _handleRegister() async {
// 验证手机号
if (_phoneController.text.isEmpty) {
_showMessage('请输入手机号');
return;
}
// 验证密码
if (_passwordController.text.isEmpty) {
_showMessage('请输入密码');
return;
}
// 验证验证码
if (_codeController.text.isEmpty) {
_showMessage('请输入验证码');
return;
}
// 验证隐私政策
if (!_agreedToPrivacy) {
_showMessage('请先阅读并同意隐私政策');
return;
}
}
10.调用注册的API
final result = await UserApi.register(
phone: _phoneController.text,
password: _passwordController.text,
code: _codeController.text,
);
11.注册成功之后的处理逻辑
if (result != null && result['success'] == true) {
// 注册成功,跳转到主页面,其实我觉得调回登录页蛮好的 ,后续改改吧
if (mounted) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const MainPage()),
(route) => false,
);
}
} else {
// 注册失败,显示错误信息
final message = result?['message'] ?? '注册失败,请重试';
_showMessage(message);
}
12.颜色推荐
| 颜色用途 | 颜色值 | 说明 |
| 主题色 | 0xFF4CAF50 | 按钮,复选框 |
| 主文本 | 0xFF1F2937 | 标题文字 |
| 次要文本 | Colors.grey[700] | |
| 提示文本 | Colors.grey[400] | 输入框占位符 |
| 帮助文本 | 0xFF9CA3AF | |
| 内容文本 | 0xFF4B5563 | 对话框内容 |
| 输入框背景 | 0xFFF9FAFB | 浅灰色背景 |
| 边框颜色 | 0xFFE5E7EB | 按钮边框 |
| 禁用背景 | Colors.grey[300] | 禁用按钮背景 |
13.最后
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)