本文主要嘗試解決如下幾個(gè)問(wèn)題:
- 如何在在已經(jīng)項(xiàng)目加入Flutter
- 混合跳轉(zhuǎn)
- 混合棧問(wèn)題
- 混合棧數(shù)據(jù)問(wèn)題
跳轉(zhuǎn)黑屏是因?yàn)閐ebug的緣故借卧,打release包則沒(méi)有畦木。
1. 如何在在已經(jīng)項(xiàng)目加入Flutter
直接參考這篇文章Add Flutter to existing apps
2. 混合跳轉(zhuǎn)
首先:Android跳轉(zhuǎn)到Flutter
創(chuàng)建一個(gè)FlutterActivity
直接繼承FlutterActivity或者自定義一個(gè)CustomFlutterActivity蛙粘,我用的自定義亲茅,是做了一些封裝和flutter插件注冊(cè)
public abstract class CustomFlutterActivity extends AppCompatActivity implements FlutterView.Provider, PluginRegistry, FlutterActivityDelegate.ViewFactory {
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
private final FlutterActivityEvents eventDelegate;
private final FlutterView.Provider viewProvider;
private final PluginRegistry pluginRegistry;
public CustomFlutterActivity() {
this.eventDelegate = this.delegate;
this.viewProvider = this.delegate;
this.pluginRegistry = this.delegate;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
if (injectRouter())
ARouter.getInstance().inject(this);
super.onCreate(savedInstanceState);
this.eventDelegate.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
registerCustomPlugin(this);
}
protected boolean injectRouter() {
return false;
}
//省略部分代碼
.........
.........
private static void registerCustomPlugin(PluginRegistry registrar) {
FlutterPluginJumpToAct.registerWith(registrar.registrarFor(FlutterPluginJumpToAct.CHANNEL));
}
}
做一個(gè)Flutter用的容器闪檬,通過(guò)容器統(tǒng)一管理需要跳轉(zhuǎn)的Flutter界面
@Route(path = RouterPath.MainPath.MAIN_FLUTTER_CONTAINER, name = "FlutterContainerActivity")
public class FlutterContainerActivity extends CustomFlutterActivity {
private static String CHANNEL = "com.jzhu.msg/plugin";
private static int TIME_ONE_SECOND = 1000;
@Autowired(name = "path")
String path;
@Override
protected boolean injectRouter() {
return true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initBasicMessageChannel();
}
@Override
public FlutterView createFlutterView(Context context) {
WindowManager.LayoutParams matchParent = new WindowManager.LayoutParams(-1, -1);
FlutterNativeView nativeView = this.createFlutterNativeView();
FlutterView flutterView = new FlutterView(FlutterContainerActivity.this, (AttributeSet) null, nativeView);
flutterView.setInitialRoute(path);
flutterView.setLayoutParams(matchParent);
this.setContentView(flutterView);
return flutterView;
}
private void initBasicMessageChannel() {
switch (path) {
case RouterPath.ModuleFlutterPath.FLUTTER_HOME:
initHomeBasicMessage();
break;
case RouterPath.ModuleFlutterPath.FLUTTER_TEST:
initTestBasicMessage();
break;
}
}
private void initTestBasicMessage() {
BasicMessageChannel channel = new BasicMessageChannel<String>(
getFlutterView(), CHANNEL, StringCodec.INSTANCE);
channel.setMessageHandler((o, reply) -> {
ToastUtils.show((String)o,3000);
reply.reply("FlutterContainerActivity:回條消息給你");
});
new Handler().postDelayed(() -> channel.send("FlutterContainerActivity:發(fā)條消息給你"), TIME_ONE_SECOND);
}
private void initHomeBasicMessage() {
//todo
}
}
Flutter提供的頁(yè)面
void main() {
runApp(new MaterialApp(
routes: <String, WidgetBuilder>{
'/homepage': (BuildContext context) => new MyHomePage(),
'/testpage': (BuildContext context) => new TestPage(),
}, home: new MyHomePage()));
}
路由
public interface RouterPath {
interface MainPath{
String MAIN_FLUTTER_CONTAINER = "/main/FlutterContainerActivity";
String MAIN_TEST= "/main/TestActivity";
}
interface ModuleFlutterPath{
String FLUTTER_HOME= "/homepage";
String FLUTTER_TEST= "/testpage";
}
}
點(diǎn)擊跳轉(zhuǎn)
ARouter.getInstance()
.build(RouterPath.MainPath.MAIN_FLUTTER_CONTAINER)
.withString("path", RouterPath.ModuleFlutterPath.FLUTTER_HOME)
.navigation();
最后:Flutter跳轉(zhuǎn)到Android
通過(guò)插件實(shí)現(xiàn)派昧,用法參考我之前寫一篇Flutter知識(shí)點(diǎn): Flutter與原生(Android)的交互
3. 混合棧問(wèn)題
- 如果是Android的頁(yè)面跳轉(zhuǎn)到Android頁(yè)面,所以就是普通的Activity棧剃浇。
- 如果是Android的頁(yè)面跳轉(zhuǎn)到Flutter頁(yè)面巾兆,那么都使用了我們的容器FlutterContainerActivity猎物,所以就是普通的Activity棧,這里面遇到個(gè)坑角塑,下面會(huì)提出并嘗試解決蔫磨。
- 如果是Flutter的頁(yè)面跳轉(zhuǎn)到Flutter頁(yè)面,那么由Flutter自己內(nèi)部的棧管理圃伶。
- 如果是Flutter的頁(yè)面跳轉(zhuǎn)到Android頁(yè)面堤如,那么自己管自己就好啦。
如果你把Flutter的頁(yè)面全裝到到容器FlutterContainerActivity展示窒朋,那就都是普通的Activity棧搀罢,省事!
混合跳轉(zhuǎn)注意:
- Android某些頁(yè)面的啟動(dòng)模式Standard侥猩,SingleTop榔至,SingleTask,SingleInstance
- Flutter頁(yè)面也存在一些特殊的跳轉(zhuǎn)欺劳,popAndPushNamed唧取,pushNamedAndRemoveUntil,popUntil划提,pushReplacementNamed
以上需要根據(jù)實(shí)際情況處理兵怯,存在特殊跳轉(zhuǎn),銷毀等邏輯
開發(fā)中需要特別需要注意的一個(gè)問(wèn)題:
跳轉(zhuǎn)順序:Android頁(yè)面 -> Flutter頁(yè)面(使用了FlutterContainerActivity)-> Flutter頁(yè)面(原始)
期望結(jié)果:Flutter頁(yè)面(原始)-> Flutter頁(yè)面(使用了FlutterContainerActivity)-> Android頁(yè)面
真實(shí)結(jié)果:Flutter頁(yè)面(原始)-> Flutter頁(yè)面(使用了FlutterContainerActivity)-> Flutter頁(yè)面(homepage) -> Android頁(yè)面
得到疑問(wèn):我們并沒(méi)有啟動(dòng)homepage腔剂,為什么多了一個(gè)homepage媒区?
代碼猜想:Flutter棧里初始化了一個(gè)homepage, 其他Flutter的頁(yè)面都在這個(gè)棧之上掸犬。
void main() {
runApp(new MaterialApp(
routes: <String, WidgetBuilder>{
............
}, home: new MyHomePage()));
}
如何解決:使用SystemNavigator.pop()袜漩,
Tells the operating system to close the application, or the closest equivalent.
WillPopScope參考這篇Flutter學(xué)習(xí)中的問(wèn)題記錄: 如何監(jiān)聽(tīng)實(shí)體/虛擬返回鍵和AppBar返回鍵
class _MyHomePageState extends State<MyHomePage> {
static const jumpPlugin = const MethodChannel('com.jzhu.jump/plugin');
Future<Null> _jumpToNative() async {
Map<String, String> map = {"path": "/main/TestActivity"};
String result = await jumpPlugin.invokeMethod('jump2act', map);
print(result);
}
Future<bool> _requestPop() {
SystemNavigator.pop();
return new Future.value(false);
}
@override
Widget build(BuildContext context) {
return new WillPopScope(
child: new Scaffold(
appBar: new AppBar(
title: new Text("Home Page"),
),
body: new Center(
child: new RaisedButton(
child: new Text("跳到TestActivity"), onPressed: _jumpToNative),
),
// This trailing comma makes auto-formatting nicer for build methods.
),
onWillPop: _requestPop);
}
}
4. 混合棧數(shù)據(jù)問(wèn)題,以插件的方式解決 湾碎。
MethodChannel宙攻,EventChanneld的數(shù)據(jù)傳輸,用法參考我之前寫一篇Flutter知識(shí)點(diǎn): Flutter與原生(Android)的交互
主要舉例BasicMessageChannel和BinaryMessages
FlutterContainerActivity中初始化介褥,監(jiān)聽(tīng)座掘,發(fā)送
為什么要延遲發(fā)送?
因?yàn)镕lutterView可能還沒(méi)初始化柔滔,這時(shí)候無(wú)法接收消息
private static String CHANNEL = "com.jzhu.msg/plugin";
private static String CHANNEL_BINARY = "com.jzhu.msg.binary/plugin";
private static int TIME_ONE_SECOND = 1000;
private void initTestBasicMessage() {
BasicMessageChannel channel = new BasicMessageChannel<String>(
getFlutterView(), CHANNEL, StringCodec.INSTANCE);
channel.setMessageHandler((o, reply) -> {
ToastUtils.show((String)o,3000);
reply.reply("FlutterContainerActivity:回條消息給你");
});
new Handler().postDelayed(() -> channel.send("FlutterContainerActivity:發(fā)條消息給你"), TIME_ONE_SECOND);
ByteBuffer message = ByteBuffer.allocateDirect(256);
message.putDouble(3.14);
message.putInt(123456789);
new Handler().postDelayed(() -> getFlutterView().send(CHANNEL_BINARY,message), TIME_ONE_SECOND);
getFlutterView().setMessageHandler(CHANNEL_BINARY, (byteBuffer, binaryReply) -> {
byteBuffer.order(ByteOrder.nativeOrder());
double x = byteBuffer.getDouble();
int n = byteBuffer.getInt();
Log.i("zj", "Received: "+x+ " and "+ n);
binaryReply.reply(message);
});
}
Flutter中監(jiān)聽(tīng)溢陪,發(fā)送
static const channel = const BasicMessageChannel<String>('com.jzhu.msg/plugin', StringCodec());
static const String channelBinary = 'com.jzhu.msg.binary/plugin';
void _sendMsg2Android() async {
_replyMsg = await channel.send('TestPage:發(fā)條消息給你');
setState(() {});
final WriteBuffer buffer = WriteBuffer()
..putFloat64(3.14)
..putInt32(123456789);
final ByteData message = buffer.done();
_replyBinaryMsg = await BinaryMessages.send(channelBinary, message);
_decodeData(message);
}
void _initMessageHandler() {
channel.setMessageHandler((String message) async {
_receivedMsg = message;
setState(() {});
});
BinaryMessages.setMessageHandler(channelBinary, (ByteData message) async {
_decodeData(message);
});
}
void _decodeData(ByteData message){
final ReadBuffer readBuffer = ReadBuffer(message);
final double x = readBuffer.getFloat64();
final int n = readBuffer.getInt32();
print('Received $x and $n');
}