Android项目集成flutter模块
创建Flutter module
在做混合开发之前我们首先需要创建一个Flutter module
假如Native项目是:xxx/flutter_hybrid/native项目
$ cd xxx/flutter_hybrid
//create flutter module
$ flutter create -t module flutter_module
//指定报名
$ flutter create -t module --org cn.xxstudy flutter_module
也可以使用Androidstudio创建
添加Flutter module依赖
settings.gradle
添加如下代码
include ':app'
...
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_module/.android/include_flutter.groovy'
))
//可选,主要作用是可以在当前projec中显示flutter_module方便查看和编写代码
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')
setBinding
与evaluate
允许Flutter模块包括它自己在内的任何flutter插件,在settings.gradle
中以类似::flutter
、package_info
、:vidoe_palyer
的方式存在
添加依赖:app/build.gradle
...
dependencies{
...
implementation project(':flutter')
...
}
必须添加Java8的编译选项
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
Native跳转Flutter页面 -Activity
- 最简单的方式(不推荐)
在AndroidManifest.xml
中注册
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/MyTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
startActivity
:startActivity(new Intent(context, FlutterActivity.class
这个时候target flutter page 就是Flutter module中的默认页面,如果flutter 中有多个页面,需要跳转时指定路由名称,flutter 中也需要进行路由配置
flutter路由配置
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: '/',
routes: {
'login': (context) => const ConnectLogin(),
'/': (context) => const RefreshListViewDemo()
},
);
}
}
Android中跳转指定路由
Intent intent = FlutterActivity.withNewEngine()
.initialRoute("login").build(this);
startActivity(intent);
- 推荐方式
由于上述的方式每次都需要创建一个FlutterEngine
,创建FlutterEngine
代价是比较大的,一些低端手机在跳转时会出现短暂的黑屏问题,因此可以先提前缓存好FlutterEngine
单例FlutterEngine缓存管理
public class FlutterCacheManager {
public static final String LIST = "list";
public static final String LOGIN = "login";
private FlutterCacheManager() {
}
public static FlutterCacheManager getInstance() {
return FlutterCacheManagerHolder.INSTANCE;
}
private static class FlutterCacheManagerHolder {
private static final FlutterCacheManager INSTANCE = new FlutterCacheManager();
}
public void preLoad(Context context) {
Looper.myQueue().addIdleHandler(() -> {
initFlutterEngine(context, LIST);
initFlutterEngine(context, LOGIN);
return false;
});
}
@NonNull
public FlutterEngine getCacheFlutterEngine(Context context, String moduleName) {
FlutterEngine flutterEngine = FlutterEngineCache.getInstance().get(moduleName);
if (flutterEngine == null) {
flutterEngine = initFlutterEngine(context, moduleName);
}
return flutterEngine;
}
private FlutterEngine initFlutterEngine(Context context, String moduleName) {
FlutterEngine flutterEngine = new FlutterEngine(context);
//set route name
flutterEngine.getNavigationChannel().setInitialRoute(moduleName);
//init bridge
FlutterBridge.getInstance().init(flutterEngine);
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
);
FlutterEngineCache.getInstance().put(moduleName, flutterEngine);
}
}
在Application中初始化
@Override
public void onCreate() {
context = this;
super.onCreate();
FlutterCacheManager.getInstance().preLoad(this);
...
}
使用
FlutterCacheManager.getInstance().getCacheFlutterEngine(this, FlutterCacheManager.LOGIN);
Intent intent=FlutterActivity.withCachedEngine(FlutterCacheManager.LOGIN).build(this);
Flutter跳转到Native
Native创建抽象Bridge
public interface IBridge<P, Callback> {
void sendMessage(String method, Object arguments);
void sendMessage(String method, Object arguments, MethodChannel.Result callback);
void onBack(P p);
void goToNative(P p);
}
public class FlutterBridge implements IBridge<Object, MethodChannel.Result>, MethodChannel.MethodCallHandler {
private List<MethodChannel> methodChannels = new ArrayList<>();
private FlutterBridge() {
}
public static FlutterBridge getInstance() {
return FlutterBridgeHolder.INSTANCE;
}
private static class FlutterBridgeHolder {
private static final FlutterBridge INSTANCE = new FlutterBridge();
}
//需要在FlutterCacheManager 创建FlutterEngine时初始化
public void init(FlutterEngine engine) {
//name need to be the same as flutter
MethodChannel methodChannel = new MethodChannel(engine.getDartExecutor(), "FlutterBridge");
//set handler
methodChannel.setMethodCallHandler(this);
methodChannels.add(methodChannel);
}
@Override
public void sendMessage(String method, Object arguments) {
for (MethodChannel methodChannel : methodChannels) {
methodChannel.invokeMethod(method, arguments);
}
}
@Override
public void sendMessage(String method, Object arguments, MethodChannel.Result callback) {
for (MethodChannel methodChannel : methodChannels) {
methodChannel.invokeMethod(method, arguments, callback);
}
}
@Override
public void onBack(Object o) {
Activity currentActivity = ActivityManager.getInstance().getCurrentActivity();
if (currentActivity != null && !currentActivity.isDestroyed()) {
currentActivity.onBackPressed();
}
}
@Override
public void goToNative(Object o) {
//start HomeActivity
Activity currentActivity = ActivityManager.getInstance().getCurrentActivity();
currentActivity.startActivity(new Intent(currentActivity, HomeActivity.class));
currentActivity.finish();
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
switch (call.method) {
case "onBack":
onBack(call.arguments);
break;
case "goToNative":
goToNative(call.arguments);
break;
default:
break;
}
}
}
Flutter创建Bridge
class FlutterBridge {
static final FlutterBridge _instance = FlutterBridge._();
final MethodChannel _bridge = const MethodChannel('FlutterBridge');
final _listener = {};
FlutterBridge._() {
_bridge.setMethodCallHandler((call) {
String method = call.method;
if (_listener[method] != null) {
return _listener[method](call);
}
return Future.value();
});
}
static FlutterBridge getInstance() {
return _instance;
}
register(String method, Function(MethodCall) callback) {
_listener[method] = callback;
}
unRegister(String method) {
_listener.remove(method);
}
MethodChannel get bridge => _bridge;
goToNative([Map? params]) {
_bridge.invokeMethod("goToNative", params);
}
onBack() {
_bridge.invokeMethod('onBack');
}
}
使用
child: ElevatedButton(
onPressed: () {
FlutterBridge.getInstance().goToNative();
},
style: ButtonStyle(
elevation: MaterialStateProperty.all(0),
backgroundColor:
MaterialStateProperty.all<Color>(Colors.transparent)
child: Text("LOGIN",
style: TextStyle(
fontFamily: 'QuicksandMedium', fontSize: 19.sp)))),
Native端发送消息到Flutter端
sendMessage.setOnClickListener((view) -> {
FlutterBridge.getInstance().sendMessage("listener", "by Native message", new MethodChannel.Result() {
@Override
public void success(@Nullable Object result) {
LogUtils.Sensi("LoginHomeActivity.java",
"success(LoginHomeActivity.java:156)" + result);
}
@Override
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
LogUtils.Sensi("LoginHomeActivity.java",
"error(LoginHomeActivity.java:163)errorCode=" + errorCode + ",errorMessage=" + errorMessage);
}
@Override
public void notImplemented() {
}
});
});
@override
void initState() {
FlutterBridge.getInstance().register('listener', (MethodCall call) {
print(
'receive Native message arguments=${call.arguments} method=${call.method}');
return Future.value(
'by flutter message ${DateTime.now().toIso8601String()}');
});
super.initState();
}
@override
void dispose() {
FlutterBridge.getInstance().unRegister('listener');
super.dispose();
}
构建flutter aar
$ cd .android/
$ ./gradlew flutter:assembleRelease
将在.android/Flutter/build/outputs/aar/
中生成一个flutter-release.aar
归档文件