本篇博客不讲原理,只讲实现方法,从0到1实现 Flutter 和原生的混合开发。将会新建一个 Android 原生项目,取名叫 AndroidDemo,然后新建一个 FlutterModule 项目,取名为 flutter_module,两个项目都放在 flutter_boost 目录下,最后实现两个项目的混合。实现的功能包括:
- 安卓原生打开 Flutter 页面
- Flutter 打开安卓原生页面
实现步骤如下:
1 . 新建安卓原生项目 AndroidDemo
2 .打开原生项目,在 AndroidStudio 控制台中输入如下命令,用于新建 FlutterModule 项目
cd.. //由于新建的 FlutterModule 项目和 AndroidDemo 项目并列在同级目录下,退出到主目录下
flutter create -t module flutter_module //创建 flutter_module
控制台的截图如下:
最后 flutter_module 创建成功后,在目录中可以看到如下并列的文件结构:
3 .打开 flutter_module 项目,在控制台中执行如下指令
cd .android
./gradlew assembleDebug
4 .原生项目 AndroidDemo 的配置
在 app 目录下的 build.gradle 中添加如下代码:
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
并添加 Flutter 依赖:
implementation project(':flutter')
结构图如下:
最后在 setting.gradle 中添加如下代码,根据你的项目名称和路径自行调整:
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'/flutter_module/.android/include_flutter.groovy'
))
原生项目的配置环节到这里就完成了。
5 .在 AndroidDemo 项目中新建活动,取名为 FlutterActivity,并添加 MainActivity 代码和 FlutterActivity 代码
MainActivity 代码如下:
package com.example.shinelon.androiddemo;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this,FlutterActivity.class);
Bundle bundle = new Bundle();
bundle.putString("routeName", "first");
intent.putExtras(bundle);
startActivity(intent);
}
});
}
}
FlutterActivity 代码如下:
package com.example.shinelon.androiddemo;
import android.app.NativeActivity;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.FrameLayout;
import io.flutter.facade.Flutter;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.view.FlutterView;
public class FlutterActivity extends AppCompatActivity {
public static final String CHANNEL_NAME = "com.flutterbus/demo";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 获取由上一个页面传过来的routeName
String routeName = "";
Intent intent = getIntent();
if (intent != null && intent.getExtras() != null) {
routeName = intent.getExtras().getString("routeName");
}
// 根据指定routeName创建FlutterView用来展示对应dart中的Widget
FlutterView flutterView = Flutter.createView(this, this.getLifecycle(), routeName);
// 创建Platform Channel用来和Flutter层进行交互
new MethodChannel(flutterView, CHANNEL_NAME).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
methodCall(methodCall, result);
}
});
setContentView(flutterView);
}
/**
* 处理dart层传来的方法调用
*/
private void methodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("gotoNativePage")) {
startActivity(new Intent(this, SecondActivity.class));
result.success(true);
} else {
result.notImplemented();
}
}
}
FlutterActivity 对应的布局是一个 FrameLayout ,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FlutterActivity">
<FrameLayout
android:id="@+id/flutter_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</android.support.constraint.ConstraintLayout>
6 . flutter_module 项目中 main.dart 代码入下
import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:async';
import 'package:flutter/services.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route) {
switch (route) {
case 'first':
return MyApp();
case 'second':
return MyApp();
default:
return Center(
child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
);
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
static const platform = const MethodChannel("com.flutterbus/demo");
void _incrementCounter() {
setState(() {
_counter++;
});
}
//调用原生方法
Future<Null> gotoNativePage() async {
bool result;
try {
//参数为方法名称
result = await platform.invokeMethod("gotoNativePage");
} on PlatformException catch (e) {
print(e.message);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
//从flutter进入原生界面
RaisedButton(
onPressed: gotoNativePage,
child: Text("打开原生界面"),
),
//进入flutter界面
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
dart 代码实现了 flutter 打开原生页面的功能,对应于原生的 SecondActivity,自己新建一个活动就可以了。
到这里就已经实现了 Flutter 和原生的混合了,下面是实现页面跳转的方法。
安卓原生打开 Flutter 页面的方法:
Intent intent = new Intent();
intent.setClass(MainActivity.this,FlutterActivity.class);
Bundle bundle = new Bundle();
bundle.putString("routeName", "first");
intent.putExtras(bundle);
startActivity(intent);
Flutter 打开原生页面的方法,也就是调用原生的方法,然后在原生的方法中打开活动:
dart 代码调用原生方法
static const platform = const MethodChannel("com.flutterbus/demo");
//函数
Future<Null> gotoNativePage() async {
bool result;
try {
//参数为方法名称
result = await platform.invokeMethod("gotoNativePage");
} on PlatformException catch (e) {
print(e.message);
}
}
原生方法实现页面转跳:
/**
* 处理dart层传来的方法调用
*/
private void methodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("gotoNativePage")) {
//进入 SecondActivity
startActivity(new Intent(this, SecondActivity.class));
result.success(true);
} else {
result.notImplemented();
}
}
最后编译安装 AndroidDemo 项目,看一下实现结果:
打开 App 的第一个界面:
点击 进入FLUTTER界面 按钮后进入如下界面,也就是 Flutter 界面:
点击 打开原生界面 后进入如下界面,也就是我们的 SecondActivity:
这种方法实现混合开发效果不是很好,在原生打开 Flutter 页面时会出现白屏现象,需要等待一段时间,体验不是很好。如有问题可以留言。