花了一天時(shí)間初步進(jìn)行接入,有些坑還沒有踩全。包括第一次加載flutter界面的時(shí)候顯示很慢等。
以下是代碼集成方式,后續(xù)探索產(chǎn)物集成方式。

接入步驟
第一步、新建android原生項(xiàng)目
-
第二步、新建Flutter Module
-
通過(guò)命令行創(chuàng)建。切換到android項(xiàng)目的同級(jí)目錄下(這里建議直接使用Terminal)。執(zhí)行如下命令:
flutter create -t module my_flutter其中my_flutter為改module名字。
直接使用AS創(chuàng)建。File --> New --> New Flutter Project,然后選擇Flutter Module。然后填寫module的名稱、路徑。最后填寫module的包名,點(diǎn)擊Finish就創(chuàng)建好了一個(gè)Flutter Module。
-
-
第三步在android項(xiàng)目中引入 Flutter Module
-
在app下的build.gradle文件中添加以下配置
compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 }可以解決版本兼容性問題。如果不配置可能會(huì)報(bào)錯(cuò)Invoke-customs are only supported starting with Android O (--min-api 26)。
-
在項(xiàng)目的根目錄下的setting.gradle文件中配置
include ':app' // 加入下面配置 setBinding(new Binding([gradle: this])) evaluate(new File( settingsDir.parentFile, 'FlutterDemo/my_flutter/.android/include_flutter.groovy' ))需要修改為自己的module名字。
-
編譯成功后在app的build.gradle文件下添加依賴。
implementation project(':flutter')
-
android 與 Flutter交互
Flutter在1.12版本后舍棄了部分類,導(dǎo)致交互這里有較大變動(dòng)。
android原生跳轉(zhuǎn)到Flutter界面
一、Activity形式
-
Activity形式。新建一個(gè)Android Activity。進(jìn)行跳轉(zhuǎn)。代碼如下:
public class FlutterPageActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_flutter_page); initViews(); } private void initViews() { FlutterView flutterView = new FlutterView(this); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); FrameLayout flContainer = findViewById(R.id.fl_container); flContainer.addView(flutterView, lp); FlutterEngine flutterEngine = new FlutterEngine(this); flutterEngine.getNavigationChannel().setInitialRoute("route1"); flutterEngine.getDartExecutor().executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ); flutterView.attachToFlutterEngine(flutterEngine); } }因?yàn)閕o.flutter.facade,此處采用FlutterView(繼承自FrameLayout)替代了原來(lái)的Flutter.createView();
**attachToFlutterEngine(FlutterEngine flutterEngine) ** 方法的作用就是flutter的ui顯示到FlutterView中。
FlutterEngine 負(fù)責(zé)在android端執(zhí)行Dart代碼的引擎。
flutterEngine.getNavigationChannel().setInitialRoute("route1"); 設(shè)置界面路由。如果不設(shè)置默認(rèn)是“/”界面。
-
傳參可以采用類似get拼接的形式,例如:
"route1?{\"name\":\"LiLei\"}"將路由和參數(shù)采用?進(jìn)行隔開,后續(xù)可以添加上json字符串。在Flutter 端解析的時(shí)候采用window.defaultRouteName 獲取路由名稱和參數(shù)。
String url = window.defaultRouteName; // route名稱 String route = url.indexOf('?') == -1 ? url : url.substring(0, url.indexOf('?')); // 參數(shù)Json字符串 String paramsJson = url.indexOf('?') == -1 ? '{}' : url.substring(url.indexOf('?') + 1); // 解析參數(shù) Map<String, dynamic> params = json.decode(paramsJson); -
在flutter中路由的簡(jiǎn)單寫法。
void main() => runApp(_widgetForRoute(window.defaultRouteName)); Widget _widgetForRoute(String route) { switch (route) { case 'route1': return MyApp(); default: return Center( child: Text('Unknown route: $route', textDirection: TextDirection.ltr), ); } }
-
創(chuàng)建好Activity后原生界面A 就可以采用Intent的方式進(jìn)行跳轉(zhuǎn)。
Intent intent = new Intent(MainActivity.this, FlutterPageActivity.class); startActivity(intent);
二、Fragment形式
新建一個(gè)Fragment 繼承自Fragment。
-
在fragment的onCreate方法中創(chuàng)建FlutterView。具體方法和Activity中相同。
@Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { FlutterView flutterView = new FlutterView(getContext()); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); mFlutterEngine = new FlutterEngine(getContext()); mFlutterEngine.getNavigationChannel().setInitialRoute("route1"); mFlutterEngine.getDartExecutor().executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ); flutterView.attachToFlutterEngine(mFlutterEngine); return flutterView; }- 這里將FlutterEngine方法設(shè)置為成員變量為了后續(xù)界面間傳值。
-
在原生Activity中添加加載方法。
public class FlutterFragmentActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_flutter_fragment); initViews(); } private void initViews() { //寫法1 getSupportFragmentManager().beginTransaction().replace(R.id.fl_container, new FlutterPageFragment()).commit(); //寫法2 FlutterFragment fragment = FlutterFragment.withNewEngine().initialRoute("route1").build(); getSupportFragmentManager().beginTransaction().replace(R.id.fl_container, fragment).commit(); //寫法3 //通過(guò)FlutterFragment引入Flutter編寫的頁(yè)面 FlutterFragment flutterFragment = FlutterFragment.withNewEngine() .initialRoute("route2") .build(); getSupportFragmentManager() .beginTransaction() .replace(R.id.fl_container, flutterFragment) .commit(); } }- 寫法3中 在androidx環(huán)境中 flutterFragment需要繼承自androidx的fragment。
Flutter界面跳轉(zhuǎn)到Android原生界面
-
在布局中添加一個(gè)按鈕。
static const nativeChannel = const MethodChannel('com.example.flutter/native');首先要定義一個(gè)channel,全局唯一,需要和android端約定好。
RaisedButton( child: Text('跳轉(zhuǎn)到原生界面'), onPressed:() { // 返回給上一頁(yè)的數(shù)據(jù) Map<String, dynamic> result = {'name': '我從Flutter頁(yè)面過(guò)來(lái)了'}; nativeChannel.invokeMethod('jumpToNative', result); }),- dart采用map形式發(fā)送。定義好key value
- 通過(guò)nativeChannel.invokeMethod('jumpToNative', result); 方法傳遞出去。其中第一個(gè)參數(shù)是和android端預(yù)定好的方法名。
-
在fragment中進(jìn)行接收
private static final String CHANNEL_NATIVE = "com.example.flutter/native";@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); MethodChannel nativeChannel = new MethodChannel(mFlutterEngine.getDartExecutor(), CHANNEL_NATIVE); nativeChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { switch (methodCall.method) { case "jumpToNative": // 跳轉(zhuǎn)原生頁(yè)面 Intent jumpToNativeIntent = new Intent(getActivity(), NativeActivity.class); jumpToNativeIntent.putExtra("name", (String) methodCall.argument("name")); //因?yàn)閷懙膁emo所以直接采用了魔法數(shù)字 方便文章中看的直觀 startActivityForResult(jumpToNativeIntent, 1001); break; default: result.notImplemented(); break; } } }); }在實(shí)現(xiàn)Flutter頁(yè)面跳轉(zhuǎn)Android原生頁(yè)面之前首先介紹一下Platform Channel,它是Flutter和原生通信的工具,有三種類型:
- BasicMessageChannel:用于傳遞字符串和半結(jié)構(gòu)化的信息,F(xiàn)lutter和平臺(tái)端進(jìn)行消息數(shù)據(jù)交換時(shí)候可以使用。
- MethodChannel:用于傳遞方法調(diào)用(method invocation),F(xiàn)lutter和平臺(tái)端進(jìn)行直接方法調(diào)用時(shí)候可以使用。
- EventChannel:用于數(shù)據(jù)流(event streams)的通信,F(xiàn)lutter和平臺(tái)端進(jìn)行事件監(jiān)聽、取消等可以使用。
Flutter 跳轉(zhuǎn)到原生界面主要通過(guò)MethodChannel來(lái)實(shí)現(xiàn)。
- 在創(chuàng)建MethodChannel的時(shí)候需要傳入一個(gè)常量。需要保證唯一性。和dart端約定好采用一樣的常量。就是上文中提到的nativeChannel 。
- 第一個(gè)參數(shù)是BinaryMessenger類型。需要通過(guò)mFlutterEngine.getDartExecutor()方法獲取到。
- 這里有兩個(gè)前提:
- a. 需要界面加載完成后。所以我們寫在onViewCreated中;
- b. 需要從flutterEngine中獲取,所以我們將該變量改成成員變量。
- 這里有兩個(gè)前提:
- 通過(guò)setMethodCallHandler回調(diào)方式判斷返回的方法名。在進(jìn)行相應(yīng)處理。
- 獲取到想要的方法名之后,使用intent將獲取到的數(shù)據(jù)發(fā)送出去。
- 這里采用startActivityForResult 方法是因?yàn)樵谠缑嬷凶隽藬?shù)據(jù)回調(diào)。
從android原生界面返回到Flutter界面數(shù)據(jù)傳遞
-
接上面的跳轉(zhuǎn)原生界面成功后,在原生界面中添加按鈕進(jìn)行返回并傳值。
tv_demo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(); intent.putExtra("message", "我從原生頁(yè)面回來(lái)了"); setResult(RESULT_OK, intent); finish(); } }); -
在上訴的fragment的onActivityResult方法中進(jìn)行接收。
@Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode == Activity.RESULT_OK){ Log.e(TAG,"接收到回調(diào)結(jié)果"); // NativePageActivity返回的數(shù)據(jù) String message = data.getStringExtra("message"); Map<String, Object> result = new HashMap<>(); result.put("message", message); // 創(chuàng)建MethodChannel,這里的flutterView即Flutter.createView所返回的View MethodChannel flutterChannel = new MethodChannel(mFlutterEngine.getDartExecutor(), CHANNEL_FLUTTER); // 調(diào)用Flutter端定義的方法 flutterChannel.invokeMethod("onActivityResult", result); } }調(diào)用了flutter中的方法將值傳入到flutter中并顯示在界面上。
-
定義一個(gè)全局唯一常量,和flutter中約定好。
private static final String CHANNEL_FLUTTER = "com.example.flutter/flutter";static const flutterChannel = const MethodChannel('com.example.flutter/flutter'); -
在initState中進(jìn)行接收
String _backResult = "初步設(shè)置"; @override void initState() { super.initState(); Future<dynamic> handler(MethodCall call) async { switch (call.method) { case 'onActivityResult': // 獲取原生頁(yè)面?zhèn)鬟f的參數(shù) print(call.arguments['message']); setState(() { _backResult = call.arguments['message']; }); break; } } flutterChannel.setMethodCallHandler(handler); }用變量來(lái)記錄
-
新建一個(gè)text來(lái)顯示這label
Text( '$_backResult' ),
Flutter界面返回到android原生界面數(shù)據(jù)傳遞
-
新建一個(gè)返回按鈕
RaisedButton( child: Text('返回上一頁(yè)'), onPressed: () { // 返回給上一頁(yè)的數(shù)據(jù) Map<String, dynamic> result = {'message': '我從Flutter頁(yè)面回來(lái)了'}; nativeChannel.invokeMethod('goBackWithResult', result); }),并執(zhí)行返回的方法。方法名和key提前約定好。
-
原生fragmen中先接收相應(yīng)的參數(shù),然后使用intent進(jìn)行傳遞。
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); MethodChannel nativeChannel = new MethodChannel(mFlutterEngine.getDartExecutor(), CHANNEL_NATIVE); nativeChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { switch (methodCall.method) { case "goBackWithResult": // 返回上一頁(yè),攜帶數(shù)據(jù) Intent backIntent = new Intent(); backIntent.putExtra("message", (String) methodCall.argument("message")); getActivity().setResult(Activity.RESULT_OK, backIntent); getActivity().finish(); break; default: result.notImplemented(); break; } } }); }
-
在上一個(gè)原生界面的activity中進(jìn)行接收信息。
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { tv_result.setVisibility(View.VISIBLE); String result = data.getStringExtra("message"); tv_result.setText("result" + result); } }
demo
https://github.com/ShawEw/FlutterAndroidDemo/tree/master
參考
http://www.itdecent.cn/p/7b6522e3e8f1
http://www.itdecent.cn/p/4a27e091af0b