Flutter登录
FlutterLogin
是一个现成的登录/注册组件,具有多种动画效果,展示了Flutter的强大功能。
安装
请按照这里的安装说明进行操作。
参考
属性 | 类型 | 描述 |
---|
onSignup | AuthCallback | 当用户在注册模式下点击提交按钮时调用。它接收一个SignupData 对象,包含姓名、密码,如果additionalSignUpFields 不为空,还包含用户填写的额外字段,以Map<String,String> 形式提供。 |
onConfirmSignup | ConfirmSignupCallback | 当用户在确认注册时点击提交按钮时调用。如果未指定,用户将不需要确认注册。 |
confirmSignupRequired | ConfirmSignupRequiredCallback | 用于在运行时决定是否需要确认的附加选项。如果未指定,当onConfirmSignup被指定时,用户将需要确认注册。 |
confirmSignupKeyboardType | TextInputType | 确认注册字段的键盘类型 |
onResendCode | AuthCallback | 当用户在确认注册时点击重新发送代码按钮时调用。仅在提供onConfirmSignup时需要。 |
onLogin | AuthCallback | 当用户在登录模式下点击提交按钮时调用 |
onRecoverPassword | RecoverCallback | 当用户在找回密码模式下点击提交按钮时调用 |
onConfirmRecover | ConfirmRecoverCallback | 当用户在找回密码模式下提交确认码并设置新密码时调用。如果未指定,将不使用确认码来找回密码。 |
title | String | 登录[Card]上方的大文本,通常是应用或公司名称。如果不需要标题,将字符串留空或设为null。 |
logo | ImageProvider或String | 要显示的logo图像的图像提供者或资源路径字符串 |
messages | LoginMessages | 描述所有标签、文本提示、按钮文本和其他认证描述 |
theme | LoginTheme | FlutterLogin的主题。如果未指定,将使用默认主题,如演示gif所示,并使用最近的Theme 小部件中的配色方案 |
userType | LoginUserType | FlutterLogin的用户类型。如果未指定,将使用默认的电子邮件用户类型 |
userValidator | FormFieldValidator<String> | 用户字段验证逻辑,在此添加自定义验证。默认为电子邮件验证逻辑。预期返回验证失败时显示的错误消息[String],或验证成功时返回[null] |
validateUserImmediately | bool | 是在失去焦点后立即验证电子邮件[true]还是在表单提交后验证[false]。默认:[false] |
passwordValidator | FormFieldValidator<String> | 与userValidator 相同,但用于密码 |
onSubmitAnimationCompleted | Function | 在提交动画完成后调用。在此处放置路由转换逻辑 |
logoTag | String | logo图像的Hero 标签。如果未指定,在更改路由时它将简单地淡出 |
titleTag | String | 标题文本的Hero 标签。如果想要在hero动画前后使用不同的字体大小,需要指定LoginTheme.beforeHeroFontSize 和LoginTheme.afterHeroFontSize |
showDebugButtons | bool | 显示调试按钮以快速前进/后退登录动画。在发布模式下,无论传入的值如何,此项都将被覆盖为false |
hideForgotPasswordButton | bool | 如果设置为true,则隐藏"忘记密码"按钮 |
hideProvidersTitle | bool | 如果设置为true,则隐藏登录提供商上方的标题。如果提供商列表为空,这将无效,因为标题无论如何都会被隐藏。默认为false |
disableCustomPageTransformer | bool | 禁用自定义转换,以解决RenderBox未布局错误。有关更多信息,请参见#97。 |
additionalSignUpFields | Map<String, UserFormField> | 用于指定额外的表单字段;该表单在注册后立即显示。最多可以提供6个额外字段。 |
onSwitchToAdditionalFields | AdditionalFieldsCallback | 当用户切换到额外字段时调用。 |
navigateBackAfterRecovery | bool | 成功找回密码后导航回登录页面。 |
savedEmail | String | 用户字段的预填值(例如,通过其他方式从上一个会话保存,如通过SharedPreferences) |
savedPassword | String | 密码字段的预填值(例如,通过其他方式从上一个会话保存,如通过SharedPreferences)。也会在Auth类中设置确认密码 |
termsOfService | TermOfService | 注册期间要列出的服务条款列表。在onSignup回调中,LoginData包含TermOfServiceResult 列表 |
children | [Widget ] | 可以添加到登录屏幕堆栈的小部件列表。可用于显示自定义横幅或logo。 |
scrollable | bool | 当设置为true时,登录卡片在需要时会变为可滚动而不是调整大小。 |
headerWidget | Widget | 可以放置在登录卡片顶部的小部件。 |
注意: 建议在两个地方使用相同的子部件作为 Hero 部件的子部件。对于标题的hero动画,在下一个界面中使用 LoginThemeHelper.loginTextStyle 来获取登录界面中精确文本部件的样式。可以通过添加以下行来访问 LoginThemeHelper : | | |
import 'package:flutter_login/theme.dart';
LoginMessages
属性 | 类型 | 描述 |
---|
userHint | String | 用户字段[TextField]的提示文本(注:用户字段可以是名称、邮箱或电话。更多信息请查看:LoginUserType ) |
passwordHint | String | 密码[TextField]的提示文本 |
confirmPasswordHint | String | 确认密码[TextField]的提示文本 |
forgotPasswordButton | String | 忘记密码按钮的标签 |
loginButton | String | 登录按钮的标签 |
signupButton | String | 注册按钮的标签 |
recoverPasswordButton | String | 找回密码按钮的标签 |
recoverPasswordIntro | String | 密码找回表单的介绍文本 |
recoverPasswordDescription | String | 密码找回表单的描述,当未提供onConfirmRecover回调时显示 |
recoverCodePasswordDescription | String | 密码找回表单的描述,当提供onConfirmRecover回调时显示 |
goBackButton | String | 返回按钮的标签。用于从密码找回表单返回到登录/注册表单 |
confirmPasswordError | String | 当确认密码与原始密码不匹配时显示的错误消息 |
recoverPasswordSuccess | String | 提交密码找回后显示的成功消息 |
confirmSignupIntro | String | 确认注册卡片的介绍文本 |
confirmationCodeHint | String | 确认码[TextField]的提示文本 |
confirmationCodeValidationError | String | 当确认码为空时显示的错误消息 |
resendCodeButton | String | 重新发送验证码按钮的标签 |
resendCodeSuccess | String | 重新发送确认码后显示的成功消息 |
confirmSignupButton | String | 确认注册按钮的标签 |
confirmSignupSuccess | String | 确认注册后显示的成功消息 |
confirmRecoverIntro | String | 确认找回密码卡片的介绍文本 |
recoveryCodeHint | String | 恢复码[TextField]的提示文本 |
recoveryCodeValidationError | String | 当恢复码为空时显示的错误消息 |
setPasswordButton | String | 密码找回时设置密码按钮的标签 |
confirmRecoverSuccess | String | 确认找回密码后显示的成功消息 |
flushbarTitleError | String | 错误情况下Flushbar的标题 |
flushbarTitleSuccess | String | 成功情况下Flushbar的标题 |
providersTitle | String | 显示在登录提供商上方的字符串,默认为"或通过以下方式登录" |
LoginTheme
属性 | 类型 | 描述 |
---|
primaryColor | Color | 部件主要部分的背景色,如登录界面和按钮 |
accentColor | Color | 次要颜色,用于标题文本颜色、加载图标等。应与[primaryColor]形成对比 |
errorColor | Color | 用于[TextField]输入验证错误的颜色 |
cardTheme | CardTheme | 用于渲染认证[Card]的颜色和样式 |
inputTheme | InputDecorationTheme | 定义所有[TextField]的外观 |
buttonTheme | LoginButtonTheme | 用于自定义提交按钮的形状、高度和颜色的主题 |
titleStyle | TextStyle | 大标题的文本样式 |
bodyStyle | TextStyle | 小文本的文本样式,如找回密码描述 |
textFieldStyle | TextStyle | [TextField]输入文本的文本样式 |
buttonStyle | TextStyle | 按钮文本的文本样式 |
beforeHeroFontSize | double | 定义登录界面中标题的字体大小(hero转换前) |
afterHeroFontSize | double | 定义登录界面后的界面中标题的字体大小(hero转换后) |
pageColorLight | Color | 登录界面的可选浅色背景色;如果提供,用于浅色渐变而不是primaryColor |
pageColorDark | Color | 登录界面的可选深色背景色;如果提供,用于深色渐变而不是primaryColor |
footerBottomPadding | double | 页脚底部内边距;如果未提供,默认为0 |
switchAuthTextColor | Color | 切换认证文本的可选颜色,如果未指定则使用[primaryColor] |
logoWidth | double | logo的宽度,其中1为登录卡片的全宽。如果未提供,默认为0.75 |
primaryColorAsInputLabel | bool | 如果你想使用主色作为输入标签,设置为true。默认为false |
LoginUserType
枚举 | 描述 |
---|
EMAIL | 用户字段将设置为电子邮件 |
NAME | 用户字段将设置为用户名 |
FIRSTNAME | 用户字段将设置为名字 |
LASTNAME | 用户字段将设置为姓氏 |
PHONE | 用户字段将设置为电话号码 |
INTLPHONE | 用户字段将设置为带国家代码选择的电话号码 |
TEXT | 用户字段将设置为文本 |
[LoginUserType]将改变用户字段[TextField]的行为。自动填充和键盘类型将根据你传递的用户类型自动调整。
UserFormField
属性 | 类型 | 描述 |
---|
keyName | String | 字段的标识符,它将作为返回映射中的键。请确保这是唯一的,否则将抛出错误 |
displayName | String | 表单上显示的字段名称。如果未给出,默认为keyName |
defaultValue | String | 字段的默认值,如果给出,字段将预填充此值 |
fieldValidator | FormFieldValidator<String> | 验证字段的函数。成功时应返回null,或返回一个包含错误说明的字符串 |
icon | Icon? | 字段左侧显示的图标。未提供时默认为用户图标 |
userType | LoginUserType | 表单的LoginUserType。将根据此类型显示相应的键盘和建议。默认为LoginUserType.user |
tooltip | InlineSpan | 该字段的附加描述 |
LoginProvider
属性 | 类型 | 描述 |
---|
button | Widget | 用于 [LoginProvider] 的按钮 - 参见示例使用 [SignInButton] 包 |
icon | IconData | [LoginProvider] 按钮使用的图标 |
label | String | 提供商下方显示的标签 |
callback | ProviderAuthCallback | 当按下提供商按钮时调用的函数。成功时必须返回 null,失败时返回描述错误的 String 。 |
providerNeedsSignUpCallback | ProviderNeedsSignUpCallback? | 可选。要求将 additionalSignUpFields 参数传递给 FlutterLogin 。如果给出,此回调必须返回 Future<bool> 。如果评估为 true ,则在评估 callback 后立即显示包含额外注册字段的卡片。如果未给出,默认行为是不显示注册卡片。 |
注意: [button] 和 [icon] 都可以添加到 [LoginProvider],但 [button] 优先于 [icon]
TermOfService
属性 | 类型 | 描述 |
---|
id | String | 仅在注册回调中用于标识单个可选的服务条款。 |
mandatory | bool | 如果设置为 true,且在提交表单验证时未勾选该条款,将显示验证错误消息。 |
text | String | 显示的服务条款名称。 |
linkUrl | String | 指向附加服务条款信息的网页链接。 |
validationErrorMessage | String | 要显示的验证错误消息。 |
initialValue | bool | 指定复选框是否初始化为已选中状态。 |
TermOfServiceResult
示例
你可以在 示例项目 中查看完整示例,该示例产生了上面的 gif 效果
基本示例
import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login.dart';
import 'dashboard_screen.dart';
const users = {
'dribbble@gmail.com': '12345',
'hunter@gmail.com': 'hunter',
};
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
Duration get loginTime => const Duration(milliseconds: 2250);
Future<String?> _authUser(LoginData data) {
debugPrint('Name: ${data.name}, Password: ${data.password}');
return Future.delayed(loginTime).then((_) {
if (!users.containsKey(data.name)) {
return 'User not exists';
}
if (users[data.name] != data.password) {
return 'Password does not match';
}
return null;
});
}
Future<String?> _signupUser(SignupData data) {
debugPrint('Signup Name: ${data.name}, Password: ${data.password}');
return Future.delayed(loginTime).then((_) {
return null;
});
}
Future<String> _recoverPassword(String name) {
debugPrint('Name: $name');
return Future.delayed(loginTime).then((_) {
if (!users.containsKey(name)) {
return 'User not exists';
}
return null;
});
}
@override
Widget build(BuildContext context) {
return FlutterLogin(
title: 'ECORP',
logo: const AssetImage('assets/images/ecorp-lightblue.png'),
onLogin: _authUser,
onSignup: _signupUser,
onSubmitAnimationCompleted: () {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const DashboardScreen(),
));
},
onRecoverPassword: _recoverPassword,
);
}
}
带登录提供商的基本示例
import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login.dart';
import 'dashboard_screen.dart';
const users = {
'dribbble@gmail.com': '12345',
'hunter@gmail.com': 'hunter',
};
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
Duration get loginTime => const Duration(milliseconds: 2250);
Future<String?> _authUser(LoginData data) {
debugPrint('Name: ${data.name}, Password: ${data.password}');
return Future.delayed(loginTime).then((_) {
if (!users.containsKey(data.name)) {
return 'User not exists';
}
if (users[data.name] != data.password) {
return 'Password does not match';
}
return null;
});
}
Future<String?> _signupUser(SignupData data) {
debugPrint('Signup Name: ${data.name}, Password: ${data.password}');
return Future.delayed(loginTime).then((_) {
return null;
});
}
Future<String> _recoverPassword(String name) {
debugPrint('Name: $name');
return Future.delayed(loginTime).then((_) {
if (!users.containsKey(name)) {
return 'User not exists';
}
return null;
});
}
@override
Widget build(BuildContext context) {
return FlutterLogin(
title: 'ECORP',
logo: const AssetImage('assets/images/ecorp-lightblue.png'),
onLogin: _authUser,
onSignup: _signupUser,
loginProviders: <LoginProvider>[
LoginProvider(
icon: FontAwesomeIcons.google,
label: 'Google',
callback: () async {
debugPrint('start google sign in');
await Future.delayed(loginTime);
debugPrint('stop google sign in');
return null;
},
),
LoginProvider(
icon: FontAwesomeIcons.facebookF,
label: 'Facebook',
callback: () async {
debugPrint('start facebook sign in');
await Future.delayed(loginTime);
debugPrint('stop facebook sign in');
return null;
},
),
LoginProvider(
icon: FontAwesomeIcons.linkedinIn,
callback: () async {
debugPrint('start linkdin sign in');
await Future.delayed(loginTime);
debugPrint('stop linkdin sign in');
return null;
},
),
LoginProvider(
icon: FontAwesomeIcons.githubAlt,
callback: () async {
debugPrint('start github sign in');
await Future.delayed(loginTime);
debugPrint('stop github sign in');
return null;
},
),
],
onSubmitAnimationCompleted: () {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const DashboardScreen(),
));
},
onRecoverPassword: _recoverPassword,
);
}
}
通过 ThemeData
进行主题定制
登录主题可以通过使用 ThemeData
间接定制,如下所示
// main.dart
import 'package:flutter/material.dart';
import 'login_screen.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '登录演示',
theme: ThemeData(
primarySwatch: Colors.deepPurple,
accentColor: Colors.orange,
cursorColor: Colors.orange,
textTheme: const TextTheme(
headline3: TextStyle(
fontFamily: 'OpenSans',
fontSize: 45.0,
color: Colors.orange,
),
button: TextStyle(
fontFamily: 'OpenSans',
),
subtitle1: TextStyle(fontFamily: 'NotoSans'),
bodyText2: TextStyle(fontFamily: 'NotoSans'),
),
),
home: LoginScreen(),
);
}
}
// login_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login.dart';
import 'dashboard_screen.dart';
class LoginScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FlutterLogin(
title: 'ECORP',
logo: const AssetImage('assets/images/ecorp.png'),
onLogin: (_) => Future(null),
onSignup: (_) => Future(null),
onSubmitAnimationCompleted: () {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const DashboardScreen(),
));
},
onRecoverPassword: (_) => Future(null),
);
}
}
自定义标签
import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login.dart';
import 'dashboard_screen.dart';
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
@override
Widget build(BuildContext context) {
return FlutterLogin(
title: 'ECORP',
logo: const AssetImage('assets/images/ecorp.png'),
onLogin: (_) => Future(null),
onSignup: (_) => Future(null),
onSubmitAnimationCompleted: () {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const DashboardScreen(),
));
},
onRecoverPassword: (_) => Future(null),
messages: LoginMessages(
userHint: '用户',
passwordHint: '密码',
confirmPasswordHint: '确认',
loginButton: '登录',
signupButton: '注册',
forgotPasswordButton: '忘记密码?',
recoverPasswordButton: '帮助',
goBackButton: '返回',
confirmPasswordError: '不匹配!',
recoverPasswordDescription:
'Lorem Ipsum是印刷和排版行业的虚拟文本',
recoverPasswordSuccess: '密码成功找回',
),
);
}
}
登录/注册 | 密码找回 |
---|
| |
主题自定义
import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login.dart';
import 'dashboard_screen.dart';
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
@override
Widget build(BuildContext context) {
const inputBorder = BorderRadius.vertical(
bottom: Radius.circular(10.0),
top: Radius.circular(20.0),
);
return FlutterLogin(
title: 'ECORP',
logo: const AssetImage('assets/images/ecorp-lightgreen.png'),
onLogin: (_) => Future(null),
onSignup: (_) => Future(null),
onSubmitAnimationCompleted: () {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const DashboardScreen(),
));
},
onRecoverPassword: (_) => Future(null),
theme: LoginTheme(
primaryColor: Colors.teal,
accentColor: Colors.yellow,
errorColor: Colors.deepOrange,
titleStyle: const TextStyle(
color: Colors.greenAccent,
fontFamily: 'Quicksand',
letterSpacing: 4,
),
bodyStyle: const TextStyle(
fontStyle: FontStyle.italic,
decoration: TextDecoration.underline,
),
textFieldStyle: const TextStyle(
color: Colors.orange,
shadows: [Shadow(color: Colors.yellow, blurRadius: 2)],
),
buttonStyle: const TextStyle(
fontWeight: FontWeight.w800,
color: Colors.yellow,
),
cardTheme: CardTheme(
color: Colors.yellow.shade100,
elevation: 5,
margin: const EdgeInsets.only(top: 15),
shape: ContinuousRectangleBorder(
borderRadius: BorderRadius.circular(100.0)),
),
inputTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.purple.withOpacity(.1),
contentPadding: EdgeInsets.zero,
errorStyle: const TextStyle(
backgroundColor: Colors.orange,
color: Colors.white,
),
labelStyle: const TextStyle(fontSize: 12),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue.shade700, width: 4),
borderRadius: inputBorder,
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue.shade400, width: 5),
borderRadius: inputBorder,
),
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red.shade700, width: 7),
borderRadius: inputBorder,
),
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red.shade400, width: 8),
borderRadius: inputBorder,
),
disabledBorder: const UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 5),
borderRadius: inputBorder,
),
),
buttonTheme: LoginButtonTheme(
splashColor: Colors.purple,
backgroundColor: Colors.pinkAccent,
highlightColor: Colors.lightGreen,
elevation: 9.0,
highlightElevation: 6.0,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
// shape: CircleBorder(side: BorderSide(color: Colors.green)),
// shape: ContinuousRectangleBorder(borderRadius: BorderRadius.circular(55.0)),
),
),
);
}
}
灵感来源
许可证