Flutter自动登录插件autologin的使用
Flutter自动登录插件autologin的使用
简介
autologin
插件可以帮助用户快速登录。当用户在平台特定的密码管理器中保存了凭据时,这些凭据将通过简单的确认或账户选择(取决于平台)自动填充。您还可以通过简单的API调用来保存用户的凭据。在支持的平台上,这些凭据将在所有设备上同步。
在Android和Apple设备上,您还可以存储一个登录令牌,以便无缝登录而无需任何交互。因此,当您的用户首次在新设备上使用您的应用时,他们已经登录了。
您可以在 GitHub 页面上查看支持的浏览器中的Web示例应用。
功能和兼容性
特性 | Android | iOS | Linux | MacOS | Web | Windows |
---|---|---|---|---|---|---|
凭据 | ✅ | ✅ | ✅ | ✅* | ✅** | ✅*** |
登录令牌 | ✅ | ✅ | ✅ |
- *) 仅支持iOS上的保存功能
- **) 仅支持基于Chromium的浏览器
- ***) 文档中提到需要“Pro”版本
技术实现细节可以在相应的包中找到。
兼容性检查
如果您想检查当前使用的平台是否受支持,可以调用 AutologinPlugin.isPlatformSupported
。如果您需要所有功能的完整报告,可以调用 await AutologinPlugin.performCompatibilityChecks()
,这将返回 Compatibilities
对象,包含以下字段:
isPlatformSupported
canSafeSecrets
canEncryptSecrets
hasZeroTouchSupport
hasOneClickSupport
这样您就可以知道在当前平台上所需的功能是否可用。即使您调用了可能不受支持的API方法,它们也不会崩溃,而是什么也不做或可能返回 null
。
凭据
凭据可以通过 AutologinPlugin.saveCredentials(...)
保存,并通过 AutologinPlugin.requestCredentials()
请求。
登录令牌
登录令牌可以是一个刷新令牌或其他任何可以用于识别用户的信息。在认证用户之前,您应该验证该令牌是否仍然有效。根据您的使用情况,请求第二因素验证是一个好主意。
登录令牌可以通过 AutologinPlugin.requestLoginToken()
请求,并通过 AutologinPlugin.saveLoginToken('yourToken')
保存。
示例代码
以下是一个完整的示例代码,展示了如何使用 autologin
插件:
import 'dart:async';
import 'package:autologin/autologin.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const DemoFrame());
}
/// 这个框架只是为了确保 DemoPage 有一个可以显示 snackbar 的上下文
class DemoFrame extends StatelessWidget {
const DemoFrame({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Autologin-Plugin example app'),
),
body: const SingleChildScrollView(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 24),
child: Align(
child: SizedBox(
width: 400,
child: DemoPage(),
),
),
),
),
);
}
}
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
/// 大多数状态字段只是为了调试发生了什么
class _DemoPageState extends State<DemoPage> {
bool? isPlatformSupported;
bool obscurePassword = true;
final usernameController = TextEditingController();
final passwordController = TextEditingController();
String loginToken = 'Loading...';
@override
void initState() {
super.initState();
unawaited(initPlatformState());
AutologinPlugin.setup(
domain: 'rekire.github.io',
appId: 'eu.rekisoft.flutter.autologin',
appName: 'Autologin Demo',
);
AutologinPlugin.requestLoginToken().then((value) async {
if (value != null) {
setState(() => loginToken = value);
} else {
final hasZeroTouchSupport =
(await AutologinPlugin.performCompatibilityChecks())
.hasZeroTouchSupport;
setState(
() => loginToken = hasZeroTouchSupport
? 'null (this is the first app start or token was deleted)'
: '(Platform not supported)',
);
if (hasZeroTouchSupport) {
await AutologinPlugin.saveLoginToken('First start ${DateTime.now()}');
}
}
}).onError((error, stackTrace) {
setState(() => loginToken = error.toString());
});
}
// 平台消息是异步的,所以我们在一个异步方法中初始化
Future<void> initPlatformState() async {
final isSupported = await AutologinPlugin.isPlatformSupported;
setState(() => isPlatformSupported = isSupported);
}
Future<void> requestCredentials() async {
final credentials = await AutologinPlugin.requestCredentials();
if (mounted) {
updateCredentials(credentials?.username, credentials?.password);
}
}
Future<void> saveCredentials() async {
final success = await AutologinPlugin.saveCredentials(
Credential(
username: usernameController.text,
password: passwordController.text,
domain: 'rekire.github.io',
),
);
if (!success && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Failed to save credentials!'),
),
);
}
}
void updateCredentials(String? username, String? password) {
final usernameChanged = usernameController.text != username;
final passwordChanged = passwordController.text != password;
if (username == null || password == null) {
final fields = [
if (username == null) 'username',
if (password == null) 'password',
].join(' or ');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('The API returned no $fields.')),
);
} else if (!usernameChanged || !passwordChanged) {
final fields = [
if (!usernameChanged) 'username',
if (!passwordChanged) 'password',
].join(' and ');
final verb = usernameChanged == passwordChanged ? 'have' : 'has';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('The $fields $verb not changed.')),
);
}
usernameController.text = username ?? '';
passwordController.text = password ?? '';
}
@override
Widget build(BuildContext context) {
const target = kIsWeb ? 'browser' : 'platform';
return Column(
children: [
if (isPlatformSupported != true)
const Padding(
padding: EdgeInsets.only(bottom: 16),
child: Text(
'⚠️ This $target is not supported ⚠️',
),
),
const Text('This app shows the features of the autologin flutter package. You cannot sign in this app, you need to implement that in your own app.'),
const SizedBox(height: 16),
TextFormField(
controller: usernameController,
textInputAction: TextInputAction.next,
autofillHints: const [AutofillHints.username],
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Username',
),
onFieldSubmitted: (_) => saveCredentials(),
),
const SizedBox(height: 16),
TextFormField(
controller: passwordController,
obscureText: obscurePassword,
textInputAction: TextInputAction.send,
keyboardType: TextInputType.visiblePassword,
autofillHints: const [AutofillHints.password],
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: 'Password',
suffixIcon: IconButton(
icon: Icon(
obscurePassword ? Icons.visibility : Icons.visibility_off,
),
onPressed: () {
setState(() => obscurePassword = !obscurePassword);
},
tooltip: obscurePassword ? 'Show password' : 'Hide password',
),
),
onFieldSubmitted: (_) => saveCredentials(),
),
const SizedBox(height: 16),
FilledButton(
onPressed: isPlatformSupported == true ? saveCredentials : null,
child: const Text('Save credentials'),
),
const SizedBox(height: 8),
OutlinedButton(
onPressed: () =>
updateCredentials('Some-Username', r'Example-P@§$w0rd!'),
child: const Text('Enter sample data'),
),
const SizedBox(height: 8),
OutlinedButton(
onPressed: isPlatformSupported == true ? requestCredentials : null,
child: const Text('Request login data'),
),
const SizedBox(height: 8),
OutlinedButton(
onPressed: isPlatformSupported == true
? () {
AutologinPlugin.deleteLoginToken();
setState(() => loginToken = '(token deleted)');
}
: null,
child: const Text('Delete login token'),
),
const SizedBox(height: 8),
Text('Login-Token: $loginToken'),
],
);
}
}
这个示例代码展示了如何使用 autologin
插件来保存和请求凭据以及登录令牌。您可以根据自己的需求进行修改和扩展。
更多关于Flutter自动登录插件autologin的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter自动登录插件autologin的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用autologin
插件实现自动登录功能的代码示例。假设autologin
插件提供了一个简单的API来存储和检索登录凭证。请注意,实际插件的API可能会有所不同,这里是一个概念性的示例。
首先,确保你已经在pubspec.yaml
文件中添加了autologin
插件的依赖:
dependencies:
flutter:
sdk: flutter
autologin: ^x.y.z # 请替换为实际的版本号
然后运行flutter pub get
来安装依赖。
接下来,在你的Flutter项目中,你可以按照以下步骤使用autologin
插件:
- 导入插件:
import 'package:autologin/autologin.dart';
- 存储登录凭证:
当用户成功登录后,你可以将凭证存储到autologin
插件中。这里假设凭证是一个简单的用户名和密码对。
void storeCredentials(String username, String password) async {
Map<String, String> credentials = {
'username': username,
'password': password,
};
await AutoLogin().storeData(key: 'userCredentials', value: credentials);
}
- 检索登录凭证:
在应用程序启动时,你可以尝试从autologin
插件中检索存储的凭证,并自动登录用户。
Future<Map<String, String>?> retrieveCredentials() async {
return await AutoLogin().retrieveData(key: 'userCredentials');
}
- 实现自动登录逻辑:
你可以在应用启动时调用retrieveCredentials
函数,并根据返回的结果决定是否自动登录用户。
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 尝试检索存储的登录凭证
Map<String, String>? credentials = await retrieveCredentials();
runApp(MyApp(credentials: credentials));
}
class MyApp extends StatelessWidget {
final Map<String, String>? credentials;
MyApp({required this.credentials});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Auto Login Demo')),
body: credentials != null
? AutoLoggedInScreen(credentials: credentials!)
: LoginScreen(onLogin: storeCredentials),
),
);
}
}
class LoginScreen extends StatefulWidget {
final void Function(String, String) onLogin;
LoginScreen({required this.onLogin});
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
String _username = '';
String _password = '';
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Username'),
onChanged: (value) {
setState(() {
_username = value;
});
},
),
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
onChanged: (value) {
setState(() {
_password = value;
});
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.onLogin(_username, _password);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => MyApp(credentials: null)),
);
}
},
child: Text('Login'),
),
],
),
),
);
}
}
class AutoLoggedInScreen extends StatelessWidget {
final Map<String, String> credentials;
AutoLoggedInScreen({required this.credentials});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Welcome, ${credentials['username']}!'),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// 实现登出逻辑,比如清除存储的凭证
AutoLogin().clearData(key: 'userCredentials');
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => MyApp(credentials: null)),
);
},
child: Text('Logout'),
),
],
),
);
}
}
在这个示例中,AutoLogin
类是一个假设的插件接口,用于存储和检索数据。实际的autologin
插件可能会有不同的API,所以你需要参考插件的官方文档来调整代码。
这个示例展示了如何在Flutter应用中实现自动登录功能,包括存储登录凭证、检索凭证以及根据凭证的存在与否显示不同的屏幕。希望这对你有所帮助!