往期回顾
从零开始的Flutter之旅: StatelessWidget
从零开始的Flutter之旅: StatefulWidget
从零开始的Flutter之旅: InheritedWidget
从零开始的Flutter之旅: Provider
从零开始的Flutter之旅: Navigator
在flutter_github有这么一个场景:通过authorization认证形式进行登录。而authorization的具体登录模式是,通过跳转一个网页链接进行github受权登录,胜利之后会携带对应的code到指定客户端中,而后客户端能够通过这个code来进行oauth受权登录,胜利之后客户端能够拿到该账户的token,所以之后的github操作都能够通过该token来进行申请。因为token是有时效性,同时也能够手动解除受权,所以绝对于在客户端进行账户明码登录来说更加平安。
那么要实现下面这个场景,Flutter就须要与原生客户端进行通信,拿到返回的code,而后再到Flutter中进行oauth受权登录申请。
通信形式能够应用MethodChannel,这个就是明天的主题。
OAuth App
authorization认证的原理曾经晓得了,上面间接来看实现计划。
首先咱们须要一个OAuth App用来提供用户通过github受权的利用。
这个在github上能够间接注册的
在注册的OAuth App时会有一个Authorization callback URL
必填项。这个callback url的作用就是当你通过该链接认证通过后会以App Link的形式应用该url跳转到对应的App利用,同时返回认证胜利的code。这里将其定义为REDIRECT_URI
注册胜利之后,咱们拿到它的Client ID
、Client Secret
与Authorization callback URL
,拼接成上面的连贯
const String URL_AUTHORIZATION = 'https://github.com/login/oauth/authorize?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&scope=user%20repo%20notifications%20';
有了跳转到内部的认证链接之后,上面就是在利用中实现这个跳转认证流程。
url_launcher
首先须要跳转内部浏览器拜访下面的authorization链接。这一步的实现须要借助url_launcher
,它可能帮忙咱们查看链接是否无效,同时启动内部浏览器进行跳转。
在应用之前须要在pubspec.yaml
中增加依赖
dependencies: flutter: sdk: flutter http: 0.12.0+4 dio: 3.0.7 shared_preferences: 0.5.6+1 url_launcher: 5.4.1 ...
依赖胜利之后,应用canLaunch()
来查看链接的有效性;launch()
来启动跳转
authorization() { return () async { FocusScope.of(context).requestFocus(FocusNode()); if (await canLaunch(URL_AUTHORIZATION)) { // 为设置forceSafariVC,IOS 默认会关上APP外部WebView // 而APP外部WebView不反对重定向跳转到APP await launch(URL_AUTHORIZATION, forceSafariVC: false); } else { throw 'Can not launch $URL_AUTHORIZATION)'; } }; }
Scheme
通过authorization()
办法能够胜利跳转到内部浏览器进行登录授认证。受权胜利之后会返回到之前的app,具体页面门路与链接中配置的REDIRECT_URI
无关。
const String REDIRECT_URI = 'github://login';
这里定义了一个Scheme
,为了可能胜利返回到客户端指定的页面,咱们须要为Android与IOS配置对应的Scheme。
Android
找到AndroidManifest
文件,在activity
便签下增加intent-filter
属性
<intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="login" android:scheme="github" /> </intent-filter>
后面的action
与category
配置是固定的,如果须要反对不同的scheme
,次要批改的是data
中的配置。
将scheme
与host
别离对应到REDIRECT_URI
中的数值。
IOS
找到info.plist
文件,增加URL types
便签,在它的item下配置对应的URL identifier
与URL Schemes
配置完scheme之后,就可能失常返回到对应的客户端页面。
接下来须要思考的是,如何拿到返回的code值
MethodChannel
这个时候明天的配角就该上场了。
MethodChannel
简略的说就是Flutter
提供与客户端通信的渠道,应用时相互约定一个渠道name
与对应的调用客户端指定办法的method
。
所以咱们先来约定好这两个值
const String METHOD_CHANNEL_NAME = 'app.channel.shared.data'; const String CALL_LOGIN_CODE = 'getLoginCode';
而后通过MethodChannel
来获取对应的渠道
callLoginCode(AppLifecycleState state) async { if (state == AppLifecycleState.resumed) { final platform = const MethodChannel(METHOD_CHANNEL_NAME); final code = await platform.invokeMethod(CALL_LOGIN_CODE); if (code != null) { _getAccessTokenFromCode(code); } } }
应用invokeMethod
来调用客户端对应的办法,这里是用来获取受权胜利后返回客户端的code。
这是Flutter调用客户端办法的步骤,上面再看客户端的实现
Android
首先咱们将约定好的渠道名称与回调办法名定义为常量
object Constants { const val AUTHORIZATION_CODE = "code" const val METHOD_CHANNEL_NAME = "app.channel.shared.data" const val CALL_LOGIN_CODE = "getLoginCode" }
在之前咱们曾经在AndroidManifest.xml
中定义的scheme,所以认证胜利后回返回客户端的MainActivity
页面,同时回调onNewIntent
办法。
所以获取返回code的形式能够在onNewIntent
中进行,同时还须要建设对应的MethodChannel
与提供回调的办法。具体实现如下:
class MainActivity : FlutterActivity() { private var mAuthorizationCode: String? = null override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine) setupMethodChannel() } override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) getExtra(intent) } private fun getExtra(intent: Intent?) { // from author login mAuthorizationCode = intent?.data?.getQueryParameter(Constants.AUTHORIZATION_CODE) } private fun setupMethodChannel() { MethodChannel(flutterEngine?.dartExecutor, Constants.METHOD_CHANNEL_NAME).setMethodCallHandler { call, result -> if (call.method == Constants.CALL_LOGIN_CODE && !TextUtils.isEmpty(mAuthorizationCode)) { result.success(mAuthorizationCode) mAuthorizationCode = null } } } }
MethodChannel
建设渠道,setMethodCallHandler
来响应Flutter中须要调用的办法。通过判断回调的办法名称,即之前在Flutter中约定的CALL_LOGIN_CODE
。来执行对应的逻辑
因为咱们须要返回的code值,只需通过result
的success
办法,将获取到的code传递过来即可。之后Flutter就可能获取到该值。
IOS
在AppDelegate.swift
中定义一个methodChannel,应用约定好的name。
methodChannel的创立IOS是通过FlutterMethodChannel.init
来生成。之后的回调与Android的根本相似
@UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { var paramsMap: Dictionary<String, String> = [:] override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) let controller: FlutterViewController = window?.rootViewController as! FlutterViewController let methodChannel = FlutterMethodChannel.init(name: "app.channel.shared.data", binaryMessenger: controller.binaryMessenger) methodChannel.setMethodCallHandler { (call, result) in if "getLoginCode" == call.method && !self.paramsMap.isEmpty { result(self.paramsMap["code"]) self.paramsMap.removeAll() } } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let absoluteString = url.absoluteURL.absoluteString let urlComponents = NSURLComponents(string: absoluteString) let queryItems = urlComponents?.queryItems for item in queryItems! { paramsMap[item.name] = item.value } return true } }
在setMethodCallHandler
中判断回调的办法是否与约定的办法名统一,如果统一再通过result
办法将code传递给Flutter。
至此Android与IOS都与Flutter建设了通信,它们之间的桥梁就是通过MethodChannel来搭建的。
最初code传回到Flutter之后,咱们再将code进行申请获取到对应的token。
到这里整个受权认证就实现了,之后咱们就能够通过token来申请用户相干的接口,获取对应的数据。
token的获取与相干接口的调用能够通过查看flutter_github源码获取
flutter_github
flutter_github,这是一个基于Github Open Api开发的Flutter版本的Github客户端。该我的项目次要是用来练习Flutter,感兴趣的能够退出一起来学习,如果有帮忙的话也请不要悭吝你的关注。
当然如果你想理解Android原生,AwesomeGithub是一个不错的抉择。它是flutter_github的纯Android版本。