Flutter LinkedIn登录插件linkedin_openid_login的使用

Flutter LinkedIn登录插件linkedin_openid_login的使用

简介

这个插件是基于 d3xt3r2909/linkedin_login 开发的,用于在Flutter应用中集成LinkedIn的OAuth 2.0 API。

安装

你可以通过以下命令安装此插件:

flutter pub add linkedin_openid_login

重要说明

在使用之前,你需要替换以下值:

final String redirectUrl = 'YOUR-REDIRECT-URL';
final String clientId = 'YOUR-CLIENT-ID';
final String clientSecret = 'YOUR-CLIENT-SECRET';

请注意,clientSecret 字段仅在 LinkedInUserWidget 中需要。

Hybrid Composition vs Virtual displays (Android Only)

要获取这些值,你需要在 LinkedIn开发者页面 创建一个应用。

请检查你的 minSdkVersion

  • 如果你使用的是 Hybrid Composition(从版本2.1.0开始默认),那么你的 minSdkVersion 应该至少为19。
  • 如果你想使用 Virtual displays(在版本2.1.0之前默认),那么你的 minSdkVersion 应该至少为20。

更多关于这两种模式的信息可以参阅 webview_flutter 的文档。

示例

你可以在 项目示例 查看完整的例子。

获取用户信息
LinkedInUserWidget(
  redirectUrl: redirectUrl,
  clientId: clientId,
  clientSecret: clientSecret,
  onGetUserProfile: (UserSucceededAction linkedInUser) {
    print('Access token ${linkedInUser.user.token.accessToken}');
    print('First name: ${linkedInUser.user.firstName.localized.label}');
    print('Last name: ${linkedInUser.user.lastName.localized.label}');
  },
  onError: (UserFailedAction e) {
    print('Error: ${e.toString()}');
  },
)
获取授权码
LinkedInAuthCodeWidget(
  redirectUrl: redirectUrl,
  clientId: clientId,
  onGetAuthCode: (AuthorizationSucceededAction response) {
    print('Auth code ${response.codeResponse.code}');
    print('State: ${response.codeResponse.state}');
  },
  onError: (AuthorizationFailedAction e) {
    print('Error: ${e.toString()}');
  },
)

如果你想要注销用户(清除会话),只需在 LinkedInUserWidgetLinkedInAuthCodeWidget 中将 destroySession 设置为 true。同时,请确保在本地存储中销毁该用户的会话数据。目前,LinkedIn的OAuth 2.0 API尚不支持销毁访问令牌。

LinkedInUserWidget可用属性

String firstName;
String lastName;
String accessToken;
int expiresIn;
String profilePicture;
String email;
String userId; (从版本0.1.0开始)

投影 - 用户账户属性可通过LinkedIn API访问

从版本1.2.x开始,你可以通过提供字符串数组来控制投影,这些字符串数组可以通过 projection 属性传递给 LinkedInUserWidget。默认情况下,这些属性包括:

static const String id = "id";
static const String localizedLastName = "localizedLastName";
static const String firstName = "firstName";
static const String lastName = "lastName";
static const String localizedFirstName = "localizedFirstName";

你还可以包含 profilePicture 来获取用户头像的URL。如果更改此属性为自定义值,则需要手动添加所有这些属性到数组中。更多信息请参阅示例项目。

范围 - 定义所需的范围

从版本2.3.1开始,你可以控制从LinkedIn获取的范围。默认情况下,你将拥有 r_emailaddressr_liteprofile,但如果你使用 scope 属性,可以在 LinkedInUserWidgetLinkedInAuthCodeWidget 中随时更改它。

final scopes = const [
  EmailAddressScope(),
  LiteProfileScope(),
];

你也可以通过扩展 Scope 类来创建自定义范围:

class CustomScope extends Scope {
  const CustomScope() : super('r_emailaddress');
}

然而,请注意这个库在某些方面存在已知限制。

LinkedInAuthCodeWidget可用属性

String code; // 授权码
String state;

小部件

标准的LinkedIn登录按钮。这个小部件是可以修改的。

LinkedInButtonStandardWidget(onTap: () {});

贡献

要重新生成模拟文件和生成的文件,请运行以下命令:

flutter packages pub run build_runner build --delete-conflicting-outputs

已知限制

  • 登录范围:主要与 w_member_social 相关。
  • Firebase:由于Firebase不支持LinkedIn,因此此功能未在库中实现。
  • Web:Web端尚未支持,因为LinkedIn不允许其API注入iFrame。

示例代码

main.dart

import 'package:flutter/material.dart';
import 'package:linkedin_openid_login/linkedin_openid_login.dart';

// ignore_for_file: avoid_print
void main() => runApp(const MyApp());

// [@TODO](/user/TODO) IMPORTANT - you need to change variable values below
// You need to add your own data from LinkedIn application
// From: https://www.linkedin.com/developers/
// Please read step 1 from this link https://developer.linkedin.com/docs/oauth2

// const String redirectUrl = 'https://www.youtube.com/callback';
const String redirectUrl =
    'https://www.linkedin.com/developers/tools/oauth/redirect';
const String clientId = '';
const String clientSecret = '';

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

  // This widget is the root of your application.
  [@override](/user/override)
  Widget build(final BuildContext context) {
    return MaterialApp(
      title: 'Flutter LinkedIn demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: DefaultTabController(
        length: 2,
        child: Scaffold(
          appBar: AppBar(
            title: const Text('LinkedIn Authorization Demo'),
          ),
          body: const LinkedInProfileExamplePage(),
        ),
      ),
    );
  }
}

class LinkedInProfileExamplePage extends StatefulWidget {
  const LinkedInProfileExamplePage({super.key});

  [@override](/user/override)
  State createState() => _LinkedInProfileExamplePageState();
}

class _LinkedInProfileExamplePageState
    extends State<LinkedInProfileExamplePage> {
  UserObject? user;
  bool logoutUser = false;

  [@override](/user/override)
  Widget build(final BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ...(user == null
              ? [
                  LinkedInButtonStandardWidget(
                    onTap: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute<void>(
                          builder: (final BuildContext context) =>
                              LinkedInUserWidget(
                            appBar: AppBar(
                              title: const Text('OAuth User'),
                            ),
                            destroySession: logoutUser,
                            redirectUrl: redirectUrl,
                            clientId: clientId,
                            clientSecret: clientSecret,
                            projection: const [
                              ProjectionParameters.id,
                              ProjectionParameters.localizedFirstName,
                              ProjectionParameters.localizedLastName,
                              ProjectionParameters.firstName,
                              ProjectionParameters.lastName,
                              ProjectionParameters.profilePicture,
                            ],
                            scope: const [
                              EmailAddressScope(),
                              LiteProfileScope(),
                            ],
                            onError: (final UserFailedAction e) {
                              print('Error: ${e.toString()}');
                              print('Error: ${e.stackTrace.toString()}');
                            },
                            onGetUserProfile:
                                (final UserSucceededAction linkedInUser) {
                              user = UserObject(
                                firstName: linkedInUser.user.firstName,
                                lastName: linkedInUser.user.lastName,
                                email: linkedInUser.user.email,
                                profileImageUrl: linkedInUser.user.picture,
                                fullName: linkedInUser.user.name,
                              );

                              setState(() {
                                logoutUser = false;
                              });

                              Navigator.pop(context);
                            },
                          ),
                          fullscreenDialog: true,
                        ),
                      );
                    },
                  ),
                ]
              : [
                  Image.network(
                    user?.profileImageUrl ??
                        'https://beforeigosolutions.com/wp-content/uploads/2021/12/dummy-profile-pic-300x300-1.png',
                    height: 100,
                    width: 100,
                    fit: BoxFit.contain,
                  ),
                  const SizedBox(height: 8),
                  Text('Name: ${user?.fullName} '),
                  Text('Given Name: ${user?.firstName} '),
                  Text('Family Name: ${user?.lastName} '),
                  Text('Email: ${user?.email}'),
                  const SizedBox(height: 15),
                  LinkedInButtonStandardWidget(
                    onTap: () {
                      setState(() {
                        user = null;
                        logoutUser = true;
                      });
                    },
                    buttonText: 'Logout',
                  ),
                ])
        ],
      ),
    );
  }
}

class UserObject {
  UserObject({
    required this.firstName,
    required this.lastName,
    required this.email,
    required this.profileImageUrl,
    required this.fullName,
  });

  final String? firstName;
  final String? lastName;
  final String? email;
  final String? profileImageUrl;
  final String? fullName;
}

更多关于Flutter LinkedIn登录插件linkedin_openid_login的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用linkedin_openid_login插件来实现LinkedIn登录的示例代码。这个插件允许你通过OpenID Connect与LinkedIn进行身份验证。

首先,确保你已经在你的pubspec.yaml文件中添加了linkedin_openid_login依赖:

dependencies:
  flutter:
    sdk: flutter
  linkedin_openid_login: ^x.y.z  # 请替换为最新版本号

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

接下来,你需要在LinkedIn开发者门户中创建一个应用并获取客户端ID和客户端密钥。一旦你有了这些信息,你就可以在你的Flutter应用中配置LinkedIn登录了。

以下是一个完整的示例代码,展示了如何使用linkedin_openid_login插件:

import 'package:flutter/material.dart';
import 'package:linkedin_openid_login/linkedin_openid_login.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'LinkedIn Login Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: LinkedInLoginPage(),
    );
  }
}

class LinkedInLoginPage extends StatefulWidget {
  @override
  _LinkedInLoginPageState createState() => _LinkedInLoginPageState();
}

class _LinkedInLoginPageState extends State<LinkedInLoginPage> {
  final LinkedInOpenIdLogin _linkedinOpenIdLogin = LinkedInOpenIdLogin(
    clientId: 'YOUR_CLIENT_ID', // 替换为你的LinkedIn客户端ID
    redirectUri: 'YOUR_REDIRECT_URI', // 替换为你的重定向URI
    discoveryDocUrl: LinkedInOpenIdLogin.discoveryDocUrl, // 默认的发现文档URL
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('LinkedIn Login'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            try {
              String url = await _linkedinOpenIdLogin.getAuthorizationUrl();
              // 这里你可以使用url_launcher包来在WebView或浏览器中打开这个URL
              // 例如: await launch(url);
              // 注意:为了简化示例,这里直接打印URL。在实际应用中,你应该在一个WebView中打开它。
              print('Open this URL in a WebView or browser: $url');

              // 用户完成登录并授权后,LinkedIn会重定向到你的redirectUri,并带上授权码
              // 在实际应用中,你应该设置一个服务器来接收这个重定向请求,并交换访问令牌
              // 这里为了演示,我们假设你已经有了授权码,并直接用它来交换访问令牌
              String code = 'DUMMY_AUTHORIZATION_CODE'; // 替换为实际的授权码
              LinkedInCredential credential = await _linkedinOpenIdLogin.getCredential(code);
              print('LinkedIn Credential: ${credential.toJson()}');
            } catch (e) {
              print('Error: $e');
            }
          },
          child: Text('Login with LinkedIn'),
        ),
      ),
    );
  }
}

// 注意:LinkedInCredential 类是假设的,实际插件可能提供不同的数据结构
class LinkedInCredential {
  String accessToken;
  String idToken;
  // 其他字段...

  Map<String, dynamic> toJson() {
    return {
      'accessToken': accessToken,
      'idToken': idToken,
      // 其他字段转换为JSON...
    };
  }
}

重要注意事项

  1. 在实际应用中,你不应该在客户端代码中硬编码授权码。授权码应该通过你的服务器来获取和处理,以确保安全性。
  2. 上面的代码示例中,getAuthorizationUrl()方法返回的URL应该在WebView或系统浏览器中打开,而不是直接在应用内处理。用户完成登录并授权后,LinkedIn会重定向到你的redirectUri,并带上授权码。你的服务器应该接收这个请求,并使用授权码来交换访问令牌。
  3. 由于安全原因,redirectUri必须是一个HTTPS URL,并且需要在LinkedIn开发者门户中预先配置。
  4. LinkedInCredential类是一个假设的类,用于演示目的。实际插件可能会提供不同的数据结构来表示从LinkedIn获取到的凭证。

请根据你的具体需求和安全要求来调整上述代码。

回到顶部