美文网首页
Flutter 混合开发 之 Android studio 加载

Flutter 混合开发 之 Android studio 加载

作者: 付小影子 | 来源:发表于2020-01-10 10:03 被阅读0次

    在Android原生项目中添加flutter

    官方文档
    1.创建flutter module,有两种方法:
    用Android studio创建flutter module;
    用命令创建flutter module

    cd some/path/(原生项目的根目录)
     flutter create -t module --org com.example my_flutter
    

    2.设置 settings.gradle 添加下列代码

    setBinding(new Binding([gradle: this]))                                 
    evaluate(new File(                                                     
      settingsDir.parentFile,                                              
      'my_flutter/.android/include_flutter.groovy'   // 必须是flutter module的真实路径
    )) 
    
    1. 设置app build.gradle
    dependencies {
      implementation project(':flutter')
    }
    
    1. 注意事项
    
     //Java 版本号
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    minSdkVersion 16(最低)
    
    1. Android 代码加载 flutter 界面
      Flutter提供两种引入方法:一个是View,会默认生成一个View,一个是FlutterFragment方式。
    package com.example.administrator.shadowapplication.flutter
    
    import android.os.Bundle
    import android.widget.FrameLayout
    import androidx.fragment.app.FragmentActivity
    import com.example.administrator.shadowapplication.flutter.channel.EventChannelPlugin
    import io.flutter.facade.Flutter
    import io.flutter.facade.FlutterFragment
    
    /**
     * native项目加载flutter页面
       将flutter页面构建成View,通过addView来显示flutter页面
       将flutter页面构建成Fragment,通过对fragment的操作来显示flutter页面
     */
    class FlutterActivity : FragmentActivity() {
        companion object {
            private const val TAG_FLUTTER_FRAGMENT = "flutter_fragment"
            private var flutterFragment: FlutterFragment? = null
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //方法1
            /* setContentView(R.layout.activity_flutter)
             val fragmentManager: FragmentManager = supportFragmentManager
             flutterFragment = fragmentManager.findFragmentByTag(TAG_FLUTTER_FRAGMENT) as FlutterFragment?
             if (flutterFragment == null) {
                 val newFlutterFragment = Flutter.createFragment("这里是flutter页面")
                 flutterFragment = newFlutterFragment
                 fragmentManager.beginTransaction()
                         .add(R.id.flutterContent, newFlutterFragment, TAG_FLUTTER_FRAGMENT)
                         .commit()
             }*/
    
            //方法2
            val flutterView = Flutter.createView(this, lifecycle, "route1")
            val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
            layoutParams.topMargin = 20
            addContentView(flutterView, layoutParams)
            
    
        }
    }
    
    1. flutter 代码
    import 'dart:async';
    import 'dart:ui';
    
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() => runApp(MyApp(
          initParams: window.defaultRouteName,
        ));
    
    class MyApp extends StatelessWidget {
      final String initParams;
    
      MyApp({Key key, @required this.initParams}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter 混合开发',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: _getHomePage(),
        );
      }
    
      Widget _getHomePage() {
        switch (initParams) {
          case "route1":
            return MyHomePage(
              title: 'flutter 混合开发',
              initParams: "hello shadow!!",
            );
            break;
        }
      }
    }
    
    class MyHomePage extends StatefulWidget {
      final String initParams;
    
      MyHomePage({Key key, this.title, this.initParams}) : super(key: key);
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState(initParams);
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      final String initParams;
      int _counter = 0;
      _MyHomePageState(this.initParams);
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'initParams:$initParams',
                  style: TextStyle(color: Colors.red),
                ),
                Text(
                  '$_counter',
                  style: Theme.of(context).textTheme.display1,
                ),
              ],
            ),
          ),
        );
      }
    }
    
    

    flutter 和Android 平台通信

    官方文档

    平台通道简介

    所谓“平台特定”或“特定平台”,平台指的就是指Flutter运⾏的平台,如Android或IOS,可以认为就是应⽤的原⽣部分。 所以,平台通道正是Flutter和原⽣之间通信的桥梁,它也是Flutter插件的底层基础设施。
    Flutter使⽤了⼀个灵活的系统,允许您调⽤特定平台的API,⽆论在Android上的Java或Kotlin代码中,还是iOS上的 ObjectiveC或Swift代码中均可⽤。

    Flutter与原⽣之间的通信依赖灵活的消息传递⽅式:

    应⽤的Flutter部分通过平台通道(platform channel)将消息发送到其应⽤程序的所在的宿主(iOS或Android)应 ⽤(原⽣应⽤)。 宿主监听平台通道,并接收该消息。然后它会调⽤该平台的API,并将响应发送回客户端,即应⽤程序的Flutter部 分。
    平台通道
    使⽤平台通道在Flutter(client)和原⽣(host)之间传递消息


    1975877-6079b324ae9d7bf7.png

    图中可以看到,Flutter 是 Client 端,Native 是 Host,Client 和 host 通信是通过 PlatformChannel,Client 通过 PlatformChannel 向 Host 发送消息,Host 监听 PlatformChannel 并接收消息,然后将响应结果发送给 Client。
    消息和响应以异步方式传递,以确保 UI 不阻塞。另外,PlatformChannel 是双工的,这意味着 Flutter 和 Native 可以交替做 Client 和 Host。
    Flutter调用原生主要使用插件方式,通过MethodChannel的方式。
    Flutter 定义了三种不同类型的 PlatformChannel,它们分别是:

    1. MethodChannel:用于传递方法调用,是比较常用的 PlatformChannel。
    2. EventChannel: 用于传递事件。
    3. BasicMessageChannel:用于传递数据。

    BinaryMessenger
    BinaryMessenger 是 PlatformChannel 与 Flutter 端的通信的工具,其通信使用的消息格式为二进制格式数据,BinaryMessenger 在 Android 中是一个接口,它的实现类为 FlutterNativeView。

    Codec
    Codec 是消息编解码器,主要用于将二进制格式的数据转化为 Handler 能够识别的数据,Flutter 定义了两种 Codec:MessageCodec 和 MethodCodec。MessageCodec 用于二进制格式数据与基础数据之间的编解码,BasicMessageChannel 所使用的编解码器是 MessageCodec。MethodChannel 和 EventChannel 所使用的编解码均为 MethodCodec。

    Handler
    Flutter 定义了三种类型的 Handler,它们与 PlatformChannel 类型一一对应,分别是 MessageHandler、MethodHandler、StreamHandler。
    在使用 PlatformChannel 时,会为它注册一个 Handler,PlatformChannel 会将该二进制数据通过 Codec 解码为转化为 Handler 能够识别的数据,并交给 Handler 处理。
    当 Handler 处理完消息之后,会通过回调函数返回 result,将 result 通过编解码器编码为二进制格式数据,通过 BinaryMessenger 发送回 Flutter 端。

    MethodChannel 可以实现 Flutter 调用 Android,也可以实现 Android 调用 Flutter,这里分别来进行举例。

    MethodChannel 实现通信

    Android端 代码 实现

    package com.example.administrator.shadowapplication.flutter
    
    import android.content.Context
    import android.content.ContextWrapper
    import android.content.Intent
    import android.content.IntentFilter
    import android.os.BatteryManager
    import android.os.Build
    import android.os.Bundle
    import android.widget.FrameLayout
    import androidx.fragment.app.FragmentActivity
    import com.example.administrator.shadowapplication.flutter.channel.MethodChannelPlugin
    import io.flutter.facade.Flutter
    import io.flutter.facade.FlutterFragment
    
    /**
     * native项目加载flutter页面
    将flutter页面构建成View,通过addView来显示flutter页面
    将flutter页面构建成Fragment,通过对fragment的操作来显示flutter页面
     */
    class FlutterActivity : FragmentActivity() {
      
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val flutterView = Flutter.createView(this, lifecycle, "route1")
            val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
            layoutParams.topMargin = 20
            addContentView(flutterView, layoutParams)
    
            //生成plateForm,注册 handle
           MethodChannelPlugin(flutterView)
    
        }
    
        /**
         * 获取当前面 电量
         */
         fun getBatteryLevel(): Int {
            val batteryLevel: Int
            batteryLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
                batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
            } else {
                val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
                intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
            }
            return batteryLevel
        }
    }
    
    //======================== 封装了 MethodChannelPlugin====
    package com.example.administrator.shadowapplication.flutter.channel
    
    import android.app.Activity
    import com.example.administrator.shadowapplication.flutter.FlutterActivity
    import io.flutter.plugin.common.MethodCall
    import io.flutter.plugin.common.MethodChannel
    import io.flutter.view.FlutterView
    
    /**
     * @author 付影影
     * @desc 用于数据流 event streams的通信,持续通信,通常用于native向dart的通信
     * 如手机电量变化,网络连接变化,陀螺仪,传感器等
     * @date 2020/1/9
     */
    class MethodChannelPlugin constructor(flutterView: FlutterView) : MethodChannel.MethodCallHandler {
    
        var activity: Activity = flutterView.context as Activity
        private val CHANNEL_NAME = "MethodChannelPlugin"
    
        init {
            MethodChannel(flutterView, CHANNEL_NAME).setMethodCallHandler(this)
        }
    
    
        override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
            when (call.method) {
                "getBatteryLevel" -> {
                    val batteryLevel = (activity as FlutterActivity).getBatteryLevel()
                    if (batteryLevel != -1) {
                        result.success(batteryLevel)
                    } else {
                        result.error("UNAVAILABLE", "Battery    level   not available.", null)
                    }
                }
                else -> {
                    result.notImplemented()
                }
            }
        }
    }
    
    

    Dart 代码 实现

    import 'dart:async';
    import 'dart:ui';
    
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() => runApp(MyApp(
          initParams: window.defaultRouteName,
        ));
    
    class MyApp extends StatelessWidget {
      final String initParams;
    
      MyApp({Key key, @required this.initParams}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter 混合开发',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: _getHomePage(),
        );
      }
    
      ///根据 native 传递的参数,选择要展示的界面
      Widget _getHomePage() {
        switch (initParams) {
          case "route1":
            return MyHomePage(
              title: 'flutter 混合开发',
              initParams: "hello shadow!!",
            );
            break;
        }
      }
    }
    
    class MyHomePage extends StatefulWidget {
      final String initParams;
    
      MyHomePage({Key key, this.title, this.initParams}) : super(key: key);
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState(initParams);
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      final String initParams;
    
      _MyHomePageState(this.initParams);
    
      ///调⽤通道上的⽅法,指定通过字符串标识符调⽤⽅法    getBatteryLevel 。
      ///该调⽤可能失败(平台不⽀持平台 API,例如在模拟器中运⾏时),所以我们将invokeMethod调⽤包装在try-catch语句中。
      ///我们使⽤返回的结果,在    setState    中来更新⽤户界⾯状态  batteryLevel
      String _batteryLevel = "unknow battery level";
      MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
    ///获取 当前 电量
      Future<Null> _getBatteryLevel() async {
        String batteryLevel;
        try {
          final int result = await _methodChannel.invokeMethod("getBatteryLevel");
          batteryLevel = 'Battery   level   at  $result %   .';
        } on PlatformException catch (e) {
          batteryLevel = "Failed    to  get battery level:  '${e.message}'.";
        }
        setState(() {
          _batteryLevel = batteryLevel;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  onPressed: () {
                    _getBatteryLevel();
                  },
                  child: Text(
                    'FlutterActivity 传递数据:$initParams',
                    style: TextStyle(color: Colors.red),
                  ),
                ),
                Text(
                  '$_batteryLevel',
                  style: Theme.of(context).textTheme.display1,
                ),
              ],
            ),
          ),
        );
      }
    }
    

    实现 结果

    微信图片_20200110105330.png

    MethodChannel 实现 在Android 中调用 flutter 方法

    有的时候 Flutter 调用 Android 后,Android 还会将结果返回给 Flutter,虽然有时可以用 result 来实现,但 Android 端的处理可能是异步的,result 对象也不能长期的持有,这时就需要 Android 来调用 Flutter。
    因为页面 UI 是 Flutter 端绘制的,我们很难在页面中控制 Android 端,要实现 Android 调用 Flutter,可以利用 Android 的 Activty 的生命周期,如果将应用切到后台再切回前台,这样 Activty 的 onResume 方法就会被调用,我们在 onResume 方法中实现调用 Flutter 的功能就可以了。

    Android 端代码实现

    package com.example.administrator.shadowapplication.flutter
    
    import android.content.Context
    import android.content.ContextWrapper
    import android.content.Intent
    import android.content.IntentFilter
    import android.os.BatteryManager
    import android.os.Build
    import android.os.Bundle
    import android.util.Log
    import android.widget.FrameLayout
    import androidx.fragment.app.FragmentActivity
    import com.example.administrator.shadowapplication.flutter.channel.MethodChannelPlugin
    import io.flutter.facade.Flutter
    import io.flutter.facade.FlutterFragment
    import io.flutter.plugin.common.MethodChannel
    import io.flutter.view.FlutterView
    import java.util.*
    
    
    /**
     * native项目加载flutter页面
    将flutter页面构建成View,通过addView来显示flutter页面
    将flutter页面构建成Fragment,通过对fragment的操作来显示flutter页面
     */
     private lateinit var flutterView: FlutterView;
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
      
            flutterView = Flutter.createView(this, lifecycle, "route1")
            val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
            layoutParams.topMargin = 20
            addContentView(flutterView, layoutParams)
        }
    
        override fun onResume() {
            super.onResume()
            val map = HashMap<String, String>()
            map["content"] = "Hello 付小影子,我是Android"
            val methodChannel = MethodChannel(flutterView, "MethodChannelPlugin")
            methodChannel.invokeMethod("showText", map, object : MethodChannel.Result {
                //2
                override fun success(o: Any?) {
                    Log.d("hh", o as String?)
                }
    
                override fun error(errorCode: String, errorMsg: String?, errorDetail: Any?) {
                    Log.d("hh", "errorCode:" + errorCode + " errorMsg:" + errorMsg + " errorDetail:" + errorDetail as String?)
                }
    
                override fun notImplemented() {
                    Log.d("hh", "notImplemented")
                }
            })
        }
    }
    

    Dart 端 代码实现

    class _MyHomePageState extends State<MyHomePage> {
      final String initParams;
    
      _MyHomePageState(this.initParams);
      //通信方式2
      MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
      String textContent = " Flutter 初始数据";
    
      @override
      void initState() {
        super.initState();
        _methodChannel.setMethodCallHandler((MethodCall call) async {
          switch (call.method) {
            case "showText":
              String content = await call.arguments["content"];
              if (content != null && content.isNotEmpty) {
                setState(() {
                  textContent = content;
                });
                return "success";
              } else {
                throw PlatformException(
                    code: "error", message: "失败", details: "content is null");
              }
              break;
            default:
              throw MissingPluginException();
          }
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                  child: Text(
                    'FlutterActivity 传递数据:$initParams',
                    style: TextStyle(color: Colors.red),
                  ),
                ),
                Text(
                  '$_batteryLevel',
                  style: Theme.of(context).textTheme.display1,
                ),
    
                Text(" Android端数据 -- $textContent")
    
              ],
            ),
          ),
        );
      }
    }
    
    

    BasicMessageChannel 实现 通信

    Android 代码实现

    package com.example.administrator.shadowapplication.flutter.channel
    
    import android.app.Activity
    import com.example.administrator.shadowapplication.crash_log.ToastUtil
    import io.flutter.plugin.common.BasicMessageChannel
    import io.flutter.plugin.common.StringCodec
    import io.flutter.view.FlutterView
    
    /**
     * @author 付影影
     * @desc BasicMessageChannel (主要是传递字符串和一些半结构体的数据)
     * BasicMessageChannel(@NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MessageCodec<T> codec)
     * @date 2020/1/9
     */
    class BasicMessageChannelPlugin constructor(flutterView: FlutterView) : BasicMessageChannel.MessageHandler<String> {
        lateinit var activity: Activity;
        lateinit var messageChannel: BasicMessageChannel<String>;
        val CHANNEL_NAME = "BasicMessageChannelPlugin"
    
        /**
         * BasicMessageChannel的初始化,入参有三个,
         * 第一个是BinaryMessage,
         * 第二个参数是定义的channel name,
         * 第三个参数是MessageCodec解码器
         * 官方提供了四种消息编码器,最终自动转为ByteBuffer
         */
        init {
            activity = flutterView.context as Activity
            messageChannel = BasicMessageChannel<String>(flutterView, CHANNEL_NAME, StringCodec.INSTANCE);
            //设置消息处理器,处理来自dart的消息
            messageChannel.setMessageHandler(this)
        }
    
    
        /**
         * 第二种 使用:dart 主动发起,Android端为接收方(收到dart的消息,并且发送回复)
         */
        override fun onMessage(message: String?, reply: BasicMessageChannel.Reply<String>) {
            reply.reply("BasicMessageChannel收到:$message") //可以通过reply进行回复
            ToastUtil.showMsg(message) //接收到的消息
        }
    
        /**
         * 第一种 使用:发送消息到Dart ,并且 接收到 dart的回复
         */
        fun send(message: String, callBack: BasicMessageChannel.Reply<String>) {
            messageChannel.send(message, callBack)
        }
    }
    

    Dart 端代码实现

    class BasicMessageChannelPage extends StatefulWidget {
      @override
      _BasicMessageChannelPageState createState() =>
          _BasicMessageChannelPageState();
    }
    
    class _BasicMessageChannelPageState extends State<BasicMessageChannelPage> {
    
      String _receiveData ="flutter 默认数据";
      var _messageChannel = BasicMessageChannel<String>("BasicMessageChannelPlugin", StringCodec());
    
      //发送消息 到 Android,并且收到Android的回复
      Future<String> sendMessage() async {
        String reply = await _messageChannel.send("hello shadow,我是 dart");
        print(reply);
        return reply;
      }
    
      //从Android 端接收消息,并且发送回复
      void receiveMessage() {
        _messageChannel.setMessageHandler((message) async {
          print("接收 Android 端发送的消息,$message");
          return "哈哈哈哈,这是我给你的回复";
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Text("EventChanne 接收的Android数据:$_receiveData"),
        );
      }
    }
    

    EventChannel 实现通信

    Android 代码实现

    package com.example.administrator.shadowapplication.flutter.channel
    
    import io.flutter.plugin.common.EventChannel
    import io.flutter.view.FlutterView
    
    /**
     * @author 付影影
     * @desc 用于数据流 event streams的通信,持续通信,通常用于native向dart的通信
     * 如手机电量变化,网络连接变化,陀螺仪,传感器等
     * @date 2020/1/9
     */
    class EventChannelPlugin constructor(flutterView: FlutterView) : EventChannel.StreamHandler {
        private val CHANNEL_NAME = "EventChannelPlugin"
    
        init {
            EventChannel(flutterView, CHANNEL_NAME).setStreamHandler(this)
        }
    
        /**
         * onCancel代表对面不再接收,这里我们应该做一些clean up的事情。
         * onListen则代表通道已经建好,Native可以发送数据了。
         * 注意onListen里带的EventSink这个参数,后续Native发送数据都是经过EventSink的
         */
        var events: EventChannel.EventSink? = null
        override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
            this.events = events
        }
    
        override fun onCancel(arguments: Any?) {
            events = null
        }
    
        /**
         * 给 dart端发送消息
         */
        fun send(params: Any) {
            if (events != null) {
                events!!.success(params)
            }
        }
    }
    

    Dart 代码实现

    class EventChannelPage extends StatefulWidget {
      @override
      _EventChannelPageState createState() => _EventChannelPageState();
    }
    
    class _EventChannelPageState extends State<EventChannelPage> {
      String _receiveData = "Flutter 默认数据";
      var CHANNEL_NAME = "EventChannelPlugin";
    
      var __eventChannel = EventChannel("CHANNEL_NAME");
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        __eventChannel.receiveBroadcastStream().listen(eventListener);
      }
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Text("EventChanne 接收的Android数据:$_receiveData"),
        );
      }
    
      void eventListener(event) {
        print("收到的数据:$event");
        setState(() {
          _receiveData = event;
        });
      }
    }
    

    相关文章

      网友评论

          本文标题:Flutter 混合开发 之 Android studio 加载

          本文链接:https://www.haomeiwen.com/subject/njkpactx.html