Flutter实战:手把手打造高颜值登录页面
渐变色调试使用在线生成渐变配色,替换colors数组即可输入框焦点效果添加响应式安全区域必须使用SafeArea避免刘海屏遮挡关键内容加载状态优化通过0 : 6实现平滑过渡效果表单验证增强使用RegExp实现更精准的邮箱/密码验证。
一、为什么选择Flutter做登录页?
在移动开发中,登录页是用户接触产品的第一印象。Flutter凭借以下优势成为UI开发的利器:
- 🚀 高性能渲染:Skia引擎直接绘制,60fps流畅动画
- 🎨 跨平台一致性:一套代码同时适配iOS/Android/Web
- 🔥 热重载调试:UI调整实时可见,效率提升50%+
- 📦 丰富组件库:Material/Cupertino双风格支持
本文基于Flutter 3.19稳定版开发,所有代码均可直接运行
二、核心功能拆解
| 功能模块 | 技术要点 | 实现效果 |
|---|---|---|
| 渐变背景 | LinearGradient + BoxDecoration |
动态色彩过渡 |
| 智能表单 | Form + TextFormField |
实时验证/错误提示 |
| 状态切换 | StatefulWidget + isLoading |
登录中加载动画 |
| 响应式布局 | MediaQuery + Padding |
适配不同屏幕尺寸 |
三、代码实战(附详细注释)
1. 创建基础项目
flutter create flutter_login_demo
cd flutter_login_demo
2. 核心文件:lib/main.dart
✅ 步骤1:搭建渐变背景框架
Container(
decoration: BoxDecoration(
// 核心:线性渐变背景
gradient: LinearGradient(
begin: Alignment.topLeft, // 起点:左上
end: Alignment.bottomRight, // 终点:右下
colors: [
Color(0xFF4776E6), // 蓝色起点
Color(0xFF8E54E9) // 紫色终点
],
),
),
child: SafeArea( // 避免刘海屏遮挡
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 后续添加内容
],
),
),
),
)
https://img-blog.csdnimg.cn/direct/3a7b3a6e4b0d4f9c8e0a0b3e3d0c3e3d.png
💡 技巧:使用
Color(0xFF...)十六进制写法比Colors.blue更精准控制色彩
✅ 步骤2:实现动态表单验证
Form(
key: _formKey, // 表单唯一标识
child: Column(
children: [
// 邮箱输入框
_buildInputField(
icon: Icons.email,
hintText: "邮箱地址",
validator: (value) {
if (value == null || value.isEmpty) {
return "邮箱不能为空";
}
if (!RegExp(r"^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(value)) {
return "邮箱格式错误";
}
return null;
},
),
SizedBox(height: 16),
// 密码输入框
_buildInputField(
icon: Icons.lock,
hintText: "密码",
isPassword: true,
validator: (value) {
if (value == null || value.length < 6) {
return "密码至少6位";
}
return null;
},
),
],
),
)
自定义输入框组件(关键样式封装):
Widget _buildInputField({
IconData icon,
String hintText,
bool isPassword = false,
FormFieldValidator<String> validator,
}) {
return TextFormField(
obscureText: isPassword, // 密码掩码
decoration: InputDecoration(
prefixIcon: Icon(icon, color: Colors.blue[300]),
hintText: hintText,
hintStyle: TextStyle(color: Colors.grey[300]),
// 核心:圆角边框+半透明背景
filled: true,
fillColor: Colors.white.withOpacity(0.2),
border: InputBorder.none, // 去掉默认边框
contentPadding: EdgeInsets.symmetric(vertical: 18, horizontal: 20),
),
style: TextStyle(color: Colors.white),
validator: validator,
);
}
https://img-blog.csdnimg.cn/direct/5c7b3a6e4b0d4f9c8e0a0b3e3d0c3e3d.png
⚠️ 注意:
obscureText必须配合isPassword参数使用,避免密码明文显示
✅ 步骤3:实现智能登录按钮
ElevatedButton(
onPressed: _isLoading ? null : _submitForm, // 加载中禁用点击
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Color(0xFF4776E6),
padding: EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), // 圆角按钮
),
elevation: _isLoading ? 0 : 6, // 加载时去除阴影
),
child: _isLoading
? SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF4776E6)),
),
)
: Text(
"登录",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
)
https://img-blog.csdnimg.cn/direct/7a7b3a6e4b0d4f9c8e0a0b3e3d0c3e3d.png
✅ 步骤4:添加社交登录选项
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildSocialButton(Icons.facebook, Colors.blue[800]),
SizedBox(width: 20),
_buildSocialButton(Icons.g_mobiledata, Colors.red),
],
)
// 社交按钮组件
Widget _buildSocialButton(IconData icon, Color color) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.5), width: 1),
),
child: IconButton(
icon: Icon(icon, color: Colors.white, size: 28),
onPressed: () => print("点击${icon.toString()}登录"),
),
);
}
https://img-blog.csdnimg.cn/direct/9a7b3a6e4b0d4f9c8e0a0b3e3d0c3e3d.png
四、完整源码(可直接运行)
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 登录演示',
theme: ThemeData(useMaterial3: true),
home: const LoginPage(),
debugShowCheckedModeBanner: false,
);
}
}
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
void _submitForm() {
if (_formKey.currentState!.validate()) {
setState(() => _isLoading = true);
// 模拟网络请求
Future.delayed(const Duration(seconds: 2), () {
setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("登录成功!")),
);
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFF4776E6), Color(0xFF8E54E9)],
),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 顶部Logo
const Icon(Icons.lock, size: 100, color: Colors.white),
const SizedBox(height: 10),
const Text(
"欢迎登录",
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 40),
// 邮箱输入
_buildInputField(
icon: Icons.email,
hintText: "邮箱地址",
validator: (value) {
if (value == null || value.isEmpty) {
return "邮箱不能为空";
}
if (!RegExp(r"^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(value)) {
return "邮箱格式错误";
}
return null;
},
),
const SizedBox(height: 16),
// 密码输入
_buildInputField(
icon: Icons.lock,
hintText: "密码",
isPassword: true,
validator: (value) {
if (value == null || value.length < 6) {
return "密码至少6位";
}
return null;
},
),
const SizedBox(height: 24),
// 登录按钮
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _isLoading ? null : _submitForm,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: const Color(0xFF4776E6),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: _isLoading ? 0 : 6,
),
child: _isLoading
? SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: const AlwaysStoppedAnimation<Color>(
Color(0xFF4776E6)),
),
)
: const Text(
"登录",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
),
),
const SizedBox(height: 20),
// 社交登录
const Text(
"第三方登录",
style: TextStyle(color: Colors.white70),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildSocialButton(Icons.facebook, Colors.blue[800]!),
const SizedBox(width: 20),
_buildSocialButton(Icons.g_mobiledata, Colors.red),
],
),
],
),
),
),
),
),
);
}
Widget _buildInputField({
required IconData icon,
required String hintText,
bool isPassword = false,
required FormFieldValidator<String> validator,
}) {
return TextFormField(
obscureText: isPassword,
decoration: InputDecoration(
prefixIcon: Icon(icon, color: Colors.blue[300]),
hintText: hintText,
hintStyle: const TextStyle(color: Colors.grey),
filled: true,
fillColor: Colors.white.withOpacity(0.2),
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 18, horizontal: 20),
),
style: const TextStyle(color: Colors.white),
validator: validator,
);
}
Widget _buildSocialButton(IconData icon, Color color) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.5), width: 1),
),
child: IconButton(
icon: Icon(icon, color: Colors.white, size: 28),
onPressed: () => print("点击${icon.toString()}登录"),
),
);
}
}
五、关键技巧总结
-
渐变色调试
使用Adobe Color在线生成渐变配色,替换colors数组即可 -
输入框焦点效果
添加onEditingComplete回调实现回车切换焦点:onEditingComplete: () => FocusScope.of(context).nextFocus(), -
响应式安全区域
必须使用SafeArea避免刘海屏遮挡关键内容 -
加载状态优化
通过elevation: _isLoading ? 0 : 6实现平滑过渡效果 -
表单验证增强
使用RegExp实现更精准的邮箱/密码验证
六、拓展学习建议
- 进阶动画:添加
Hero动画实现页面跳转过渡 - 状态管理:集成
Provider管理全局登录状态 - 主题定制:使用
ThemeData统一管理颜色/字体 - 网络请求:通过
dio实现真实登录API对接
源码获取:点击此处下载完整项目(记得给个Star哦✨)
最后思考:
你觉得登录页还应该包含哪些实用功能?
欢迎在评论区留言讨论,点赞过100将更新注册页+密码找回完整实现!
https://img-blog.csdnimg.cn/direct/1a7b3a6e4b0d4f9c8e0a0b3e3d0c3e3d.png
本文已同步至GitHub精选项目 🌟
关注公众号【Flutter实验室】回复"登录"获取高清设计稿+动效资源
下期预告:《Flutter动画三剑客:Hero/AnimatedContainer/AnimationController实战》
更多推荐


所有评论(0)