经过学习本片文章中的知识点,你能够避免掉不少坑,从而轻松的实现 Flutter 在 Android 项目中的集成。android
1. Kotlingit
Kotlin,由 JetBrains 于 2011.07 推出,一款面向 JVM 在 Java 虚拟机上运行的静态类型编程语言。github
相比 Java,它能够静态检测不少陷阱,好比常见多发的空指针,因此开发效率更高。web
并且经过支持variable type inference,higher-order functions (closures),extension functions,编程
mixins and first-class delegation等实现,使得它比 Java 更加简洁。虽然它与 Java 语法并不兼容,json
但 Kotlin 能够和 Java 代码相互运做。更为重要的是,bash
在 2017 年的 Goofle I/O 上,也宣布 kotlin 为 Android 的官方开发语言。app
github 地址:Kotlin框架
2. MVPless
在这里,MVP 就再也不赘述,在个人上一篇文章,已经详细介绍过了。
demo 里的是 Kotlin 版,但实现原理都是同样的。
有兴趣的点下方连接:
从 0 到 1,带你解剖 MVP 的神秘之处,并本身动手实现 MVP !
3. Flutter
Flutter,由 Google 在 2018. 02 推出的移动UI框架,
能够快速在 Android 和 iOS 上构建高质量的原生用户界面。
Flutter 的优点,在这里我也再也不多说了。在 Flutter 中文网 都是有的。
优点有不少,固然劣势也不少!虽然说跨平台,可是对于适配问题,还须要去优化并解决。
性能相关,常常会出现一些卡顿现象,而且对于动画的实现效果,也不是那么的理想。
固然,还有不少其余的问题。毕竟如今发布的也只是 beta 版,上述的这些问题,也会获得很好的解决的。
ok,下面切入正题,咱们如何在项目中,去使用 Flutter。
在 Android 原有项目的基础,去集成并使用 Flutter,确定会有下面几个疑问?
如何在原生上,展现 Flutter 界面?
原生如何给 Flutter 传送数据?Flutter 如何接收?
Flutter 如何调用原生的 method ?经过什么来调用?
咱们知道在 Flutter 中,主入口只有一个 void main()
,
若是在原生界面 A,要显示一个 ListView
。在原生界面 B,要显示一个 webView
。
那咱们在 Flutter 中,经过什么来判断我要加载的是 ListView
仍是 webView
呢?
ps:若是电脑前的同窗没有安装 Flutter,建议先安装。
打开你的项目,找到 Terminal
,输入终端命令:flutter channel
默认分支应该是 beta,如今咱们须要切换到 master 分支。
继续输入终端命令:flutter channel master
,
等待执行完毕以后,咱们就成功的切换到了 master 分支。为何要切换到 master 分支?
由于咱们在安装 Flutter 的时候,默认安装的是 beta 版本。
该版本,目前是不支持在现有项目中集成 Flutter Module 模块功能的。
若是在 beta 版本中,执行了建立 Module 命令:flutter create -t module 你要建立的库的名字
,
它会提示你 "module" is not an allowed value for option "template"
。
执行终端命令,建立你的 Flutter Library:flutter create -t module flutter_library
。
等待执行,建立成功后,会以下所示:
flutter_library
, 是我对 Flutter Library 的命名。你能够替换为你的命名。 将 flutter_library 添加到 Android 工程
找到 Project 层 setting.gradle 文件并打开,添加以下代码:
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'/你的工程目录名/flutter_library/.android/include_flutter.groovy'
))
复制代码
编译经过后,在 app 目录下的 build.gradle,添加依赖:
dependencies {
implementation project(':flutter')
}
复制代码
至此,我么已经成功将 Flutter Module 添加到 Android 工程中了。是否是很简单?skr skr skr ......
打开咱们 app 目录下的 MainActivity
,添加以下代码:
addContentView(Flutter.createView(this, lifecycle, "route1"),
FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
复制代码
以上代码,就是建立了一个宽高均充满屏幕的 FlutterView,能够将 FlutterView
看做为展现 Flutter Widget 的容器。
”route1“ 是什么鬼?这个待会儿再解释,如今你不须要关心。如今运行代码,会看到以下所示:
如今呢,咱们已经成功在原生上,将 Flutter 界面成功的展现出来。
在这里,咱们须要用到 EventChannel
。
这个类的做用,能够简单理解为从原生向 Flutter
,push data:主动的推送数据。
修改后的 Activity
代码以下:
class MainActivity : AppCompatActivity() {
companion object {
val GET_NAME_CHANNEL = "sample.flutter.io/get_name"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flutterView = Flutter.createView(this, lifecycle, "route1")
addContentView(flutterView, FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
EventChannel(flutterView, GET_NAME_CHANNEL).setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(p0: Any?, events: EventChannel.EventSink?) {
events?.success(getName())
}
override fun onCancel(p0: Any?) {
}
})
}
fun getName(): String? = "flutter_library"
}
复制代码
看 Flutter 端接收的代码:
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const EventChannel eventChannel =
EventChannel('sample.flutter.io/get_name');
String _name = 'unknown';
void _receiveData() {}
@override
void initState() {
super.initState();
eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
}
void _onEvent(Object event) {
setState(() {
_name = event.toString();
});
}
void _onError(Object error) {
setState(() {
_name = 'Battery status: unknown.';
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text('Flutter', key: const Key('Battery level label')),
new Padding(
padding: const EdgeInsets.all(16.0),
child: new RaisedButton(
child: const Text('Refresh'),
onPressed: _receiveData,
),
),
],
),
new Text('从原生 push 过来的数据:' + _name),
],
),
),
);
}
}
复制代码
注意:在建立 EventChannel 对象的时候,传入的 name,
必定要和你在原生中传入的 name 对应起来,不然将接收不到。这个很好理解。
MethodChannel
:
当 Flutter
向原生调用方法或获取数据时,须要用到这个类来实现。
接下来看 Android
端实现代码,修改后以下:
class MainActivity : AppCompatActivity() {
companion object {
val PUSH_CHANNEL = "sample.flutter.io/push"
val PULL_CHANNEL = "sample.flutter.io/pull"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flutterView = Flutter.createView(this, lifecycle, "route1")
addContentView(flutterView, FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
EventChannel(flutterView, PUSH_CHANNEL).setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(p0: Any?, events: EventChannel.EventSink?) {
events?.success(getName())
}
override fun onCancel(p0: Any?) {
}
})
MethodChannel(flutterView, PULL_CHANNEL).setMethodCallHandler { methodCall, result ->
run {
if (methodCall.method.equals("refresh")) {
refresh()
result.success("")
} else {
result.notImplemented()
}
}
}
}
fun getName(): String? = "flutter_library"
fun refresh() {
showShort("refresh")
}
}
复制代码
当 Flutter 调用 refresh 方法时,android 端调用 refresh() 方法,这里实现了一个简单的吐司,并返回了空字符串。
固然你也能够作其余操做,好比跳转页面、实现动画、获取数据等等。
咱们在 MainActivity
加载 FlutterView
时,有传入一个参数 "route1"
。
点击进入 createView
的源码时,有这样一句注释:
The default initialRoute is "/".
复制代码
经过查看源码得知,initialRoute
的默认值为 "/"
。由于入口只有一个:void main()
,
因此判断 route
,加载不一样界面的逻辑应该也就在这里了。具体请看代码实现:
void main() => runApp(new MyApp(window.defaultRouteName));
class MyApp extends StatelessWidget {
final String route;
MyApp(this.route);
@override
Widget build(BuildContext context) {
switch (route) {
case "route1":
return new MaterialApp(
title: "Android-Flutter-Demo",
home: new MyHomePage(title: 'Android-Flutter-Demo'),
);
break;
default:
return Center(
child:
Text('Unknown route: $route', textDirection: TextDirection.ltr),
);
}
}
}
复制代码
怎么样,很简单的吧?到这里呢,文章开头说的那四个问题,咱们也都一一解决掉了。
下面说一下个人 demo 实现,在 Android 端获取接口数据,而后转化成 json 格式,
经过 Flutter 端的调用,以列表形式进行展现。最后效果图以下:
demo 中的代码实现,没有考虑实际需求。
只是为了验证,android 和 flutter 混合开发,这条路是行得通的。
最后,奉上 github demo 地址:
喜欢的同窗能够点点 star ~~~