Flutter自动登录插件autologin_android的使用
Flutter自动登录插件autologin_android的使用
在Android平台上,autologin
插件利用了 CredentialManager
API 来查询凭证。该API需要原生异步代码编写,并且使用了Kotlin语言和协程(Coroutines)。由于 CredentialManager
启动了一个系统Activity,因此需要维护Activity生命周期,这通过Android Jetpack的 Lifecycle
API 进行管理。
使用方法
此包被标记为推荐,这意味着您可以直接使用 autologin
包。添加此包后,它将自动包含在您的应用程序中,因此您无需将其添加到 pubspec.yaml
文件中。
然而,如果您导入此包以直接使用其API,则应像往常一样将其添加到 pubspec.yaml
文件中。
安装
更改您的应用以使用 FlutterFragmentActivity
为了使用 CredentialManager
API 查询凭证,您需要更改您的 MainActivity
类,使其继承自 FlutterFragmentActivity
。如果您的 MainActivity
是空的,可以直接在 AndroidManifest
中引用基类,然后可以删除您的 MainActivity
。在这种情况下,只需将 AndroidManifest
中的 android:name=".MainActivity"
替换为 android:name="io.flutter.embedding.android.FlutterFragmentActivity"
。
关于数字资产链接的重要说明
数字资产链接是一种将您的应用与网站关联起来的方法,也称为 App Links。为了提供完整的示例,示例应用需要正确签名。目前,签名密钥未被提交,但将来CI可能会获得签名示例应用的能力,以便您可以自行测试。
对于演示,您需要发布一个 .well-known/assetlinks.json
文件,该文件映射在 example/web/.well-known/assetlinks.json
中。但是,由于此文件发布在GitHub Pages上,文件夹将显示为 https://rekire.github.io/autologin_plugin/.well-known/assetlinks.json
而不是 https://rekire.github.io/.well-known/assetlinks.json
,因此我上传了该文件到另一个存储库 rekire/rekire.github.io
,其中实际托管的是最新版本。为了发布“点”目录,我还定义了一个 _config.yaml
文件。
如果您想测试自己的设置,请使用 声明列表生成器和测试工具。
简单的检查是否链接正常工作的方法是在您的Android手机上访问 此链接。如果此链接在Chrome中打开时没有出现Intent选择器并直接显示示例应用,则表示设置正确。
示例代码
以下是 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](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Autologin-Plugin 示例应用'),
),
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](/user/override)
State<DemoPage> createState() => _DemoPageState();
}
/// 大多数这些状态字段只是为了调试发生了什么
class _DemoPageState extends State<DemoPage> {
bool? isPlatformSupported;
String? usernameNote;
String? passwordNote;
bool obscurePassword = true;
final usernameController = TextEditingController();
final passwordController = TextEditingController();
String loginToken = '加载中...';
[@override](/user/override)
void initState() {
super.initState();
unawaited(initPlatformState());
usernameController.addListener(resetUsernameNote);
passwordController.addListener(resetPasswordNote);
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 ? '这是首次启动' : '平台不支持');
if (hasZeroTouchSupport) {
await AutologinPlugin.saveLoginToken('首次启动 ${DateTime.now()}');
}
}
}).onError((error, stackTrace) {
setState(() => loginToken = error.toString());
});
}
[@override](/user/override)
void dispose() {
super.dispose();
usernameController.removeListener(resetUsernameNote);
passwordController.removeListener(resetPasswordNote);
}
// 平台消息是异步的,所以我们初始化在一个异步方法中。
Future<void> initPlatformState() async {
final isSupported = await AutologinPlugin.isPlatformSupported;
setState(() => isPlatformSupported = isSupported);
}
void resetUsernameNote() {
setState(() => usernameNote = null);
}
void resetPasswordNote() {
setState(() => passwordNote = null);
}
Future<void> requestCredentials() async {
final credentials = await AutologinPlugin.requestCredentials();
if (mounted) {
setState(() {
if (credentials?.username != null) {
usernameController.text = credentials!.username!;
usernameNote = null;
} else {
usernameController.text = '';
usernameNote = 'API 没有提供用户名';
}
if (credentials?.password != null) {
passwordController.text = credentials!.password!;
passwordNote = null;
} else {
passwordController.text = '';
passwordNote = 'API 没有提供密码';
}
});
}
}
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('保存凭证失败!'),
),
);
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return Column(
children: [
if (isPlatformSupported != true)
const Padding(
padding: EdgeInsets.only(bottom: 16),
child: Text('⚠️ 此平台不支持 ⚠️'),
),
TextFormField(
controller: usernameController,
textInputAction: TextInputAction.next,
autofillHints: const [AutofillHints.username],
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: '用户名',
helperText: usernameNote,
),
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: '密码',
helperText: passwordNote,
suffixIcon: IconButton(
icon: Icon(obscurePassword ? Icons.visibility : Icons.visibility_off),
onPressed: () {
setState(() => obscurePassword = !obscurePassword);
},
tooltip: obscurePassword ? '显示密码' : '隐藏密码',
),
),
onFieldSubmitted: (_) => saveCredentials(),
),
const SizedBox(height: 16),
FilledButton(
onPressed: isPlatformSupported == true ? saveCredentials : null,
child: const Text('保存凭证'),
),
const SizedBox(height: 8),
OutlinedButton(
onPressed: () {
usernameController.text = 'Some-Username';
passwordController.text = r'Example-P@§$w0rd!';
},
child: const Text('输入示例数据'),
),
const SizedBox(height: 8),
OutlinedButton(
onPressed: isPlatformSupported == true ? requestCredentials : null,
child: const Text('请求登录数据'),
),
const SizedBox(height: 8),
Text('登录令牌: $loginToken'),
],
);
}
}
更多关于Flutter自动登录插件autologin_android的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter自动登录插件autologin_android的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用autologin_android
插件来实现自动登录功能的示例代码。autologin_android
插件允许你通过Android的AccountManager实现自动登录功能,这在处理单点登录(SSO)或记住用户登录状态时非常有用。
首先,确保你已经在pubspec.yaml
文件中添加了autologin_android
依赖:
dependencies:
flutter:
sdk: flutter
autologin_android: ^最新版本号 # 请替换为最新的版本号
然后,运行flutter pub get
来安装依赖。
接下来,你需要配置Android项目以使用AccountManager。这通常涉及在AndroidManifest.xml
中添加必要的权限和配置。不过,autologin_android
插件通常已经处理了大部分配置,你可能只需要确保你的应用有Internet权限(如果需要进行网络请求):
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourapp">
<uses-permission android:name="android.permission.INTERNET"/>
<application
... >
...
</application>
</manifest>
现在,让我们编写一些Flutter代码来使用autologin_android
插件。以下是一个基本的示例,展示如何保存和检索登录凭证:
import 'package:flutter/material.dart';
import 'package:autologin_android/autologin_android.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String? username;
@override
void initState() {
super.initState();
// 尝试自动登录
_attemptAutoLogin();
}
Future<void> _attemptAutoLogin() async {
String? savedUsername = await AutologinAndroid.retrieveUsername();
if (savedUsername != null) {
// 如果检索到用户名,可以假设用户已经登录
// 这里你可以进一步检索密码或其他令牌,或者直接导航到主屏幕
setState(() {
username = savedUsername;
});
print("Auto-logged in as: $username");
} else {
print("No saved login credentials found.");
}
}
Future<void> _saveLoginCredentials(String username) async {
// 这里假设密码或其他敏感信息已经以安全方式处理
// 仅保存用户名作为示例
await AutologinAndroid.saveUsername(username);
print("Login credentials saved for: $username");
// 实际应用中,你可能还想保存一个令牌或密码的哈希值
// await AutologinAndroid.savePasswordHash(passwordHash);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Autologin Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Current User: ${username ?? 'Not Logged In'}',
style: TextStyle(fontSize: 24),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// 模拟用户登录
String newUsername = 'testuser123';
_saveLoginCredentials(newUsername);
// 更新状态以反映登录
setState(() {
username = newUsername;
});
},
child: Text('Login as testuser123'),
),
],
),
),
),
);
}
}
在这个示例中,我们有一个简单的Flutter应用,它在启动时尝试自动登录。如果没有找到保存的登录凭证,它会显示“Not Logged In”。用户可以点击按钮模拟登录,这将保存用户名(在实际应用中,你可能还会保存令牌或密码的哈希值)。
请注意,这个示例仅用于教学目的,并假设所有安全考虑(如密码存储和传输)都已经被妥善处理。在实际应用中,确保你遵循最佳安全实践来处理敏感信息。