從零開始的Flutter之旅: StatelessWidgetandroid
從零開始的Flutter之旅: StatefulWidgetgit
從零開始的Flutter之旅: InheritedWidgetgithub
在flutter_github有這麼一個場景:經過authorization認證方式進行登陸。而authorization的具體登陸形式是,經過跳轉一個網頁連接進行github受權登陸,成功以後會攜帶對應的code到指定客戶端中,而後客戶端能夠經過這個code來進行oauth受權登陸,成功以後客戶端能夠拿到該帳戶的token,因此以後的github操做均可以經過該token來進行請求。因爲token是有時效性,同時也能夠手動解除受權,因此相對於在客戶端進行帳戶密碼登陸來講更加安全。安全
那麼要實現上面這個場景,Flutter就須要與原生客戶端進行通訊,拿到返回的code,而後再到Flutter中進行oauth受權登陸請求。bash
通訊方式可使用MethodChannel,這個就是今天的主題。微信
authorization認證的原理已經知道了,下面直接來看實現方案。app
首先咱們須要一個OAuth App用來提供用戶經過github受權的應用。less
這個在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';
複製代碼
有了跳轉到外部的認證連接以後,下面就是在應用中實現這個跳轉認證流程。
首先須要跳轉外部瀏覽器訪問上面的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)';
}
};
}
複製代碼
經過authorization()
方法能夠成功跳轉到外部瀏覽器進行登陸授認證。受權成功以後會返回到以前的app,具體頁面路徑與連接中配置的REDIRECT_URI
有關。
const String REDIRECT_URI = 'github://login';
複製代碼
這裏定義了一個Scheme
,爲了可以成功返回到客戶端指定的頁面,咱們須要爲Android與IOS配置對應的Scheme。
找到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
中的數值。
找到info.plist
文件,添加URL types
便籤,在它的item下配置對應的URL identifier
與URL Schemes
配置完scheme以後,就可以正常返回到對應的客戶端頁面。
接下來須要考慮的是,如何拿到返回的code值
這個時候今天的主角就該上場了。
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調用客戶端方法的步驟,下面再看客戶端的實現
首先咱們將約定好的渠道名稱與回調方法名定義爲常量
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就可以獲取到該值。
在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,這是一個基於Github Open Api開發的Flutter版本的Github客戶端。該項目主要是用來練習Flutter,感興趣的能夠加入一塊兒來學習,若是有幫助的話也請不要吝嗇你的關注。
固然若是你想了解Android原生,AwesomeGithub是一個不錯的選擇。它是flutter_github的純Android版本。
若是你喜歡個人文章模式,或者對我接下來的文章感興趣,你能夠關注個人微信公衆號:【Android補給站】
或者掃描下方二維碼,與我創建有效的溝通,同時可以更方便的收到相關的技術推送。