美文网首页
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