Flutter 入门-本地访问-MethodChannel

10,603 阅读4分钟

Dart4Flutter - 01 – 变量、类型和函数

Dart4Flutter – 02 –控制流 和异常

Dart4Flutter – 03 – 类和泛型

Dart4Flutter – 04 – 异步和库

Dart4Flutter - 拾遗01 - flutter-dart环境搭建

Dart4Flutter - 不可变性

Flutter入门 - 状态管理

Flutter 入门实例1

Flutter 入门 - Container 属性详解

Flutter 入门-本地访问-MethodChannel

Flutter 实例 - 加载更多的ListView

Flutter 实例 - 从本地到Flutter通信 - Event Channels

Flutter 作为一种跨平台的解决方案,有访问本地资源的能力。主要是通过Channel完成,你可以称之为隧道。主要是MethodChannel和MessageChannel两种,第一种是调用方法,第二种是传递信息。首先通信的双方是Flutter和本地操作系统或者应用,而且方法的调用和消息的方法可以从任何一方发起,类似RPC(远程过程调用)。

重点内容:

  • 主要是Flutter是一个比较独立的环境,要想访问本地的资源,Flutter提供了Channel机制,类似Cilent-Server模式或者RPC
  • 通过Channel的名称打通Channel,隧道。
  • Flutter和本地是对称的,请求可以从任何一发发起,本教程主要是从Flutter给本地发送请求。

Flutter部分

如果你对Flutter的界面编写部分不熟悉,可以参考官方的文档;

下面是UI部分的代码

import 'package:flutter/material.dart';


void main() {
  runApp(new MaterialApp(
    home: new Scaffold(
      body: new PlatformTestBody(),
    ),
  ));
}

class PlatformTestBody extends StatefulWidget {
  @override
  PlatformTestBodyState createState() {
    return new PlatformTestBodyState();
  }
}

class PlatformTestBodyState extends State<PlatformTestBody> {

  String nativeMessage =''; 
  @override
  Widget build(BuildContext context) {
    return new Container(
      color: Colors.pinkAccent,
      child: new Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          new Padding(
            padding: const EdgeInsets.only(left: 18.0, top: 200.0),
            child: new Text(
              'Tap the button to change your life!',
              style: new TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.w500,
                  fontSize: 23.0),
            ),
          ),
          new Padding(
            padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 102.0),
            child: new RaisedButton(
              child: new Text('Click Me'),
              onPressed: () => print(''),
            ),
          ),
          new Padding(
            padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 102.0),
            child: new Text(
              nativeMessage,
              style: new TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.w500,
                  fontSize: 23.0),
            ),
          )
        ],
      ),
    );
  }
}

现在我们介绍关于本地资源访问的问题,首先我们要引入service包和async包。

// Add these lines to the top of the file
import 'package:flutter/services.dart';
import 'dart:async';

有了service包,我们就可以配置Flutter,发送请求到本地。声明一个static const变量platformMethodChannel ,赋值MethodChannel类型的对象。

class PlatformTestBodyState extends State<PlatformTestBody> {
  //Add this line 
  static const platformMethodChannel = const MethodChannel('com.test/test');

  String nativeMessage ='';  
  @override
  Widget build(BuildContext context) {
      ...

注意,我们给MethodChannel一个名字com.test/test,这个名字和本地那边的要一样。

我们创建了MethodChannel,我们准备一个用来发送请求的函数。我们创建一个叫doNativeSuff的异步函数

Future<Null> doNativeSuff() async {
 
  }

代替RaisedButton中的onPressed函数 print('')doNativeSuff()

child: new RaisedButton(
    child: new Text('Click Me'),
    onPressed: () => print(''),
),
child: new RaisedButton(
    child: new Text('Click Me'),
    onPressed: () => doNativeSuff(),
),

下面解释整个doNativeSuff 方法

Future<Null> doNativeSuff() async {
    String _message; // 1
    try {
      final String result =
          await platformMethodChannel.invokeMethod('changeLife');// 2
      _message = result;
      print(result);
    } on PlatformException catch (e) {
      _message = "Sadly I can not change your life: ${e.message}.";
    }
    setState(() {
        nativeMessage = _message; // 3
    });
  }
  1. 声明一个String变量_message,用于存需要显示在界面上的信息,可能来自本地,也可能来自Flutter
  2. 发送请求,await platformMethodChannel.invokeMethod('changeLife') 是调用本地的changeLife 方法,并且这个方法是异步的,await表示阻塞执行。
  3. nativeMessage = _message; 表示通知Flutter状态改变,刷新界面。

本地部分

本教程只关注Android方面的代码。在Android studio 中打开MainActivity.java,在文件的最顶上添加一下代码,是Flutter插件的相关依赖

import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

声明CHANNEL 变量,和Flutter中MethodChannel的名字一样com.test/test,建议直接从Flutter那边复制粘贴。

public class MainActivity extends FlutterActivity {
  private static final String CHANNEL = "com.test/test"; 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    ...

使用上面的CHANNELFlutterView 创建一个MethodChannel

public class MainActivity extends FlutterActivity {
  private static final String CHANNEL = "com.test/test";
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
    new MethodChannel(getFlutterView(), CHANNEL)// 重点

在MethodChannel的中调用.setMethodCallHandler() 方法,需要一个MethodCallHandler 对象,是一个匿名内部类,有一个方法onMethodCall,在Flutter发送请求事,onMethodCall方法会执行。

public class MainActivity extends FlutterActivity {
  private static final String CHANNEL = "com.test/test";
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
    new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
      new MethodCallHandler() {
        @Override
        public void onMethodCall(MethodCall methodCall, Result result) {
        }
      }
    );
  }

}

onMethodCall方法中有两个参数 MethodCall 和 result,MethodCall 中有关当前请求的信息,例如调用方法的名字changeLife;Result用于发送信息给Flutter。

在onMethodCall中判断当前请求是否为changeLife,如果是通过resultsuccess 返回信息:Life Changed .

public void onMethodCall(MethodCall methodCall, Result result) {
  if (methodCall.method.equals("changeLife")){
    String message ="Life Changed";
    result.success(message);
  }
  ...

这样Flutter就可以访问,本地的方法了。

通常应用于访问本地资源,例如访问相机,本地存储,图片的选择

本教程主要了解的是MethodChannel,还有MessageChannel没有解析,下次再说。

参考