Flutter ICP认证插件flutter_icp_auth的使用

发布于 1周前 作者 nodeper 来自 Flutter

Flutter ICP认证插件flutter_icp_auth的使用

✨ 功能

  • LOG IN:允许用户通过互联网身份登录。
  • AUTO LOGIN:允许用户自动登录。
  • RELOAD:基于会话数据/委托对象,在应用重新加载或打开时自动登录用户。
  • LOGOUT:提供用户登出功能。

⚠️ 重要信息

1. isLocal 参数

  • isLocal 用于表示您是使用主网链接还是本地运行应用程序。
    • 如果 isLocaltrue,则将 backendCanisterIdmiddlePageCanisterId 替换为本地部署的 canister ID。
    • 如果 isLocalfalse,则将 backendCanisterIdmiddlePageCanisterId 替换为主网部署的 canister ID。

2. 后端函数

对于本地或主网 backendCanisterId,请确保您的后端有以下函数:

Rust 示例

use ic_cdk::api::caller;
#[ic_cdk::query]
fn whoami() -> String {
    let principal_id = caller().to_string();
    format!("principal id - : {:?}", principal_id)
}

Motoko 示例

import Principal "mo:base/Principal";
actor {
    public shared (msg) func whoami() : async Text {
        Principal.toText(msg.caller);
    };
};

对于主网,您可以使用我们的 canister ID:cni7b-uaaaa-aaaag-qc6ra-cai

3. 中间页 Canister ID

对于本地或主网 middlePageCanisterId,请确保您有以下内容:

  • 本地:从 GitHub 克隆并本地部署。
  • 主网
    • 您可以将克隆的 中间页 部署到主网,并使用主网 canister ID。
    • 或者,您可以使用我们的主网 ID:nplfj-4yaaa-aaaag-qjucq-cai

4. IDL 和服务

您可以使用 candid_dart 生成 did 文件和 IDL 服务,然后在应用程序中使用它们,如示例应用程序所示。或者,您可以手动添加/编写文件或代码。

🚀 开始使用

1. 设置

  • 添加依赖项到 pubspec.yaml
    dependencies:
      flutter_icp_auth: ^1.0.0
      agent_dart: ^1.0.0-dev.22
    
  • 导入包到 main.dart
    import 'package:agent_dart/agent_dart.dart';
    import 'package:flutter_icp_auth/flutter_icp_auth.dart';
    
  • 配置 AndroidManifest.xml 的深度链接 打开 android/app/src/main/AndroidManifest.xml,添加以下片段,并替换 android:schemeandroid:host 为您应用程序的值:
    <meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="your_app_scheme" android:host="your_app_callback" />
        <data android:scheme="https" />
    </intent-filter>
    

2. 初始化

  • 声明变量
    bool isLocal = false; // 确认是否在本地运行项目
    Service idlService = FieldsMethod.idl; // Idl 服务 (位置: lib/integration.dart)
    String backendCanisterId = 'cni7b-uaaaa-aaaag-qc6ra-cai'; // 替换为您的后端 canisterId
    String middlePageCanisterId = 'nplfj-4yaaa-aaaag-qjucq-cai'; // 替换为您的中间页 canisterId
    
  • 定义 initState 和 dispose 方法
    [@override](/user/override)
    void initState() {
      super.initState();
      AuthLogIn.checkLoginStatus(isLocal, backendCanisterId).then((loggedIn) {
        setState(() {
          isLoggedIn = loggedIn;
          if (loggedIn) {
            _principalId = AuthLogIn.getPrincipal;
          }
        });
        if (!loggedIn) {
          UrlListener.handleInitialUri(_fetchAgent, () {});
          UrlListener.initListener(_fetchAgent);
        }
      });
    }
    
    [@override](/user/override)
    void dispose() {
      UrlListener.cancelListener();
      super.dispose();
    } 
    
  • 手动登录
    Future<void> _manualLogin(Uri uri) async {
      List<dynamic> result = await AuthLogIn.fetchAgent(
          uri.queryParameters, isLocal, backendCanisterId, idlService);
      if (result.isNotEmpty) {
        setState(() {
          isLoggedIn = uri.queryParameters['status'] == "true" ? true : false;
          _principalId = result[0];
        });
      } else {
        setState(() {
          isLoggedIn = false;
          _principalId = "Log in to see your principal";
        });
      }
    }
    

3. 使用 IIDLogin 按钮

当传递参数给 IIDLogin 按钮时,请记得传递您的 app:host 和 app:callback:

  • 登录按钮调用
    AuthLogIn.authenticate(isLocal, middlePageCanisterId, "exampleCallback", "example");
    
  • 登出按钮调用
    List<Object> logoutValidation = await AuthLogout.logout(isLocal, backendCanisterId);
    

❗ 故障排除

  1. ZipFileEncoder.close 和 SingingBlockZipFileEncoder.close 错误

    • 前往 agent_dart 包的位置:agent_dart-1.0.0-dev.22/lib/archiver/encoder.dart:162:8
    • 将以下代码:
      void close() {
        _encoder.writeBlock(_output);
        _encoder.endEncode();
        _output.close();
      }
      
      替换为:
      Future<void> close() async{
        _encoder.writeBlock(_output);
        _encoder.endEncode();
        await _output.close();
      }
      
  2. minSdkVersion 错误

    • 前往代码位置:example/android/app/build.gradle
    • minSdkVersion 更改为 23
      defaultConfig {
          minSdkVersion 23
          targetSdkVersion flutter.targetSdkVersion
          versionCode flutterVersionCode.toInteger()
          versionName flutterVersionName
      }
      

ℹ️ 附加信息

  • 示例文件位置:example/lib/main.dart
  • 该包依赖以下依赖项:
    • agent_dart: ^1.0.0-dev.22
    • flutter_custom_tabs: ^2.0.0+1
    • flutter_secure_storage: ^9.2.1
    • shared_preferences: ^2.2.3
    • uni_links: ^0.5.1

完整示例 Demo

import 'dart:async';
import 'integration.dart';

import 'package:flutter/material.dart';
import 'package:agent_dart/agent_dart.dart';
import 'package:flutter_icp_auth/flutter_icp_auth.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter IID Login',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurpleAccent),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter IID Login'),
      debugShowCheckedModeBanner: false,
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _principalId = "Log in to see your principal";
  bool isLoggedIn = false;

  // 必须在应用程序中声明这些变量
  bool isLocal = false; // 确认是否在本地运行项目
  Service idlService = FieldsMethod.idl; // Idl 服务 (位置: lib/integration.dart)
  String backendCanisterId = 'cni7b-uaaaa-aaaag-qc6ra-cai'; // 替换为您的后端 canisterId
  String middlePageCanisterId = 'nplfj-4yaaa-aaaag-qjucq-cai'; // 替换为您的中间页 canisterId

  [@override](/user/override)
  void initState() {
    super.initState();
    AuthLogIn.checkLoginStatus(isLocal, backendCanisterId).then((loggedIn) {
      setState(() {
        isLoggedIn = loggedIn;
        if (loggedIn) {
          _principalId = AuthLogIn.getPrincipal;
        }
      });
      if (!loggedIn) {
        UrlListener.handleInitialUri(_manualLogin, () {});
        UrlListener.initListener(_manualLogin);
      }
    });
  }

  [@override](/user/override)
  void dispose() {
    UrlListener.cancelListener();
    super.dispose();
  }

  Future<void> _manualLogin(Uri uri) async {
    List<dynamic> result = await AuthLogIn.fetchAgent(
        uri.queryParameters, isLocal, backendCanisterId, idlService);
    if (result.isNotEmpty) {
      setState(() {
        isLoggedIn = uri.queryParameters['status'] == "true" ? true : false;
        _principalId = result[0];
      });
    } else {
      setState(() {
        isLoggedIn = false;
        _principalId = "Log in to see your principal";
      });
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Center(child: Text(widget.title)),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              SizedBox(
                width: 200,
                height: 100,
                child: Image.asset('assets/images/logo.png'),
              ),
              const Text(
                'Principal ID:',
                style: TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.w600,
                ),
              ),
              Text(
                _principalId,
                style: const TextStyle(
                  fontSize: 12,
                ),
              ),
              const SizedBox(
                height: 40,
              ),
              isLoggedIn == false
                  ? ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        foregroundColor: Colors.white,
                        backgroundColor: Colors.pink,
                        elevation: 8,
                      ),
                      onPressed: () async {
                        await AuthLogIn.authenticate(isLocal,
                            middlePageCanisterId, "exampleCallback", "example");
                      },
                      child: const Text(
                        'Log In',
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.w600,
                        ),
                      ),
                    )
                  : ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        foregroundColor: Colors.white,
                        backgroundColor: Colors.pink,
                        elevation: 8,
                      ),
                      onPressed: () async {
                        List<Object> logoutValidation =
                            await AuthLogout.logout(isLocal, backendCanisterId);
                        setState(() {
                          isLoggedIn = logoutValidation.whereType<bool>().first;
                          _principalId = isLoggedIn
                              ? logoutValidation[1].toString()
                              : "Log in to see your principal";
                        });
                      },
                      child: const Text(
                        'Log Out',
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.w600,
                        ),
                      ),
                    )
            ],
          ),
        ),
      ),
    );
  }
}

这个完整的示例展示了如何在 Flutter 应用程序中集成和使用 flutter_icp_auth 插件进行 ICP 认证。


更多关于Flutter ICP认证插件flutter_icp_auth的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter ICP认证插件flutter_icp_auth的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中集成和使用flutter_icp_auth插件的详细步骤及代码示例。flutter_icp_auth插件通常用于在中国大陆的应用中实现ICP(Internet Content Provider)认证,以确保应用符合当地的法律法规要求。

步骤 1: 添加依赖

首先,在你的pubspec.yaml文件中添加flutter_icp_auth插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  flutter_icp_auth: ^最新版本号 # 请替换为最新的版本号

然后运行flutter pub get来安装依赖。

步骤 2: 导入插件

在你的Dart文件中导入flutter_icp_auth插件。

import 'package:flutter_icp_auth/flutter_icp_auth.dart';

步骤 3: 配置ICP信息

你需要在你的应用中配置ICP信息,这通常包括ICP许可证号和网站域名。

void main() {
  // 在应用启动时配置ICP信息
  FlutterIcpAuth.configure(
    icpLicenseNumber: '你的ICP许可证号', // 替换为你的ICP许可证号
    icpWebsiteDomain: '你的网站域名',    // 替换为你的网站域名
  );

  runApp(MyApp());
}

步骤 4: 检查ICP认证状态

你可以在应用的关键位置检查ICP认证状态,比如启动页面或用户登录后。

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    // 检查ICP认证状态
    _checkIcpAuthStatus();
  }

  Future<void> _checkIcpAuthStatus() async {
    try {
      bool isAuthenticated = await FlutterIcpAuth.checkAuthStatus();
      if (isAuthenticated) {
        print('ICP认证通过');
        // 执行通过后的逻辑,比如跳转到主页
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(builder: (context) => HomePage()),
        );
      } else {
        print('ICP认证未通过');
        // 执行未通过的逻辑,比如显示错误信息或退出应用
        showDialog(
          context: context,
          builder: (context) => AlertDialog(
            title: Text('ICP认证未通过'),
            content: Text('你的应用未通过ICP认证,无法继续使用。'),
            actions: <Widget>[
              FlatButton(
                child: Text('退出'),
                onPressed: () {
                  SystemNavigator.pop(); // 退出应用
                },
              ),
            ],
          ),
        );
      }
    } catch (e) {
      print('检查ICP认证状态时发生错误: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('ICP认证检查'),
        ),
        body: Center(
          child: CircularProgressIndicator(), // 显示加载指示器
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('主页'),
      ),
      body: Center(
        child: Text('欢迎来到应用主页!'),
      ),
    );
  }
}

注意事项

  1. ICP许可证号和网站域名:确保你提供的ICP许可证号和网站域名是准确的,否则认证可能会失败。
  2. 错误处理:在实际应用中,你应该添加更多的错误处理逻辑,以处理各种可能的异常情况。
  3. 用户隐私:确保在处理ICP认证时遵守相关的用户隐私政策和法律法规。

以上是如何在Flutter项目中集成和使用flutter_icp_auth插件的基本步骤和代码示例。根据你的具体需求,你可能需要调整这些代码。

回到顶部