本篇博客不講原理,只講實現方法,從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 頁面時會出現白屏現象,需要等待一段時間,體驗不是很好。如有問題可以留言。