从零开始的Flutter之旅-MethodChannel

40次阅读

共计 5715 个字符,预计需要花费 15 分钟才能阅读完成。

往期回顾

从零开始的 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 IDClient SecretAuthorization 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>

后面的 actioncategory配置是固定的,如果须要反对不同的 scheme,次要批改的是data 中的配置。

schemehost别离对应到 REDIRECT_URI 中的数值。

IOS

找到 info.plist 文件,增加 URL types 便签,在它的 item 下配置对应的 URL identifierURL 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 值,只需通过 resultsuccess办法,将获取到的 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 版本。

如果你喜爱我的文章模式,或者对我接下来的文章感兴趣,你能够关注我的微信公众号:【Android 补给站】

或者扫描下方二维码,与我建设无效的沟通,同时可能更不便的收到相干的技术推送。

正文完
 0