- flutter_boost版本(1.17.1, 1.22.4)
在使用flutter_boost进行混合开发时,有时候需要在关闭页面时向前一页面回传数据,原生端处理时一般是通过startActivityForResult启动页面,然后在页面的onActivityResult中接收回传内容。flutter_boost提供以下回传方式:
- Flutter->Native
flutter中打开native页面,并从native回传数据。常见场景例如flutter中需要选择文件或图片上传时,需要打开原生页面选择,然后把文件路径回传给flutter。通过如下代码以startActivityForResult方式启动native页面,native页面关闭时setResult即可
FlutterBoost.singleton.open('url').then((result){...})
// native侧setResult
Map map = new HashMap<String, String>();
map.put("a", "a");
Intent intent = getIntent().putExtra(IFlutterViewContainer.RESULT_KEY, (Serializable) map);
setResult(0, intent);
- FlutterA->FlutterB
FlutterBoost.singleton
.open('FlutterB')
.then((Map<dynamic, dynamic> value) {
// 回传数据处理
});
// FlutterB close并回传:
final BoostContainerSettings settings = BoostContainer.of(context).settings;
FlutterBoost.singleton
.close(settings.uniqueId, result: <String, dynamic>{'result': 'data from FlutterB'});
目前在FlutterA->FlutterB情况下,回传数据时,发现android端在物理/系统虚拟返回键返回时,没办法调用FlutterBoost.singleton.close回传数据。查看boost源码,back键返回时处理流程大概如下:
BoostFlutterActivity中onBackPressed
->FlutterActivityAndFragmentDelegate中onBackPressed
->mSyncer.onBackPressed
// BoostFlutterActivity.java
private FlutterActivityAndFragmentDelegate delegate;
public void onBackPressed() {
this.delegate.onBackPressed();
}
// FlutterActivityAndFragmentDelegate.java
protected IOperateSyncer mSyncer;
public void onBackPressed() {
this.mSyncer.onBackPressed();
this.ensureAlive();
}
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.v("FlutterActivityAndFragmentDelegate", "Creating FlutterView.");
this.mSyncer = FlutterBoost.instance().containerManager().generateSyncer(this);
...
}
// FlutterBoost.java
static FlutterBoost sInstance = null;
private FlutterViewContainerManager mManager;
public static FlutterBoost instance() {
if (sInstance == null) {
sInstance = new FlutterBoost();
}
return sInstance;
}
public IContainerManager containerManager() {
return sInstance.mManager;
}
// FlutterViewContainerManager.java
public IOperateSyncer generateSyncer(IFlutterViewContainer container) {
Utils.assertCallOnMainThread();
ContainerRecord record = new ContainerRecord(this, container);
if (this.mRecordMap.put(container, record) != null) {
Debuger.exception("container:" + container.getContainerUrl() + " already exists!");
}
this.mRefs.add(new FlutterViewContainerManager.ContainerRef(record.uniqueId(), container));
return record;
}
// ContainerRecord.java
public void onBackPressed() {
Utils.assertCallOnMainThread();
if (this.mState == 0 || this.mState == 4) {
Debuger.exception("state error");
}
HashMap<String, String> map = new HashMap();
map.put("type", "backPressedCallback");
map.put("name", this.mContainer.getContainerUrl());
map.put("uniqueId", this.mUniqueId);
FlutterBoost.instance().channel().sendEvent("lifecycle", map);
}
查看部分源码可见按下back键后,flutter_boost native侧会通过channel("flutter_boost")向flutter侧发送名为lifecycle的事件,类型为backPressedCallback,flutter_boost的flutter侧在FlutterBoost单例初始化时,会初始化ContainerCoordinator,其中会注册"lifecycle"的监听。部分代码如下:
// flutter_boost.dart
FlutterBoost() {
ContainerCoordinator(_boostChannel);
}
// container_coordinator.dart
ContainerCoordinator(BoostChannel channel) {
assert(_instance == null);
_instance = this;
channel.addEventListener("lifecycle",
(String name, Map arguments) => _onChannelEvent(arguments));
channel.addMethodHandler((MethodCall call) => _onMethodCall(call));
}
flutter_boost channel在收到事件时,会执行相关回调方法。back键按下后,lifecycle的回调会触发,可见backPressedCallback类型的处理如下部分代码所示:
Future<dynamic> _onChannelEvent(dynamic event) {
if (event is Map) {
Map map = event;
final String type = map['type'];
Logger.log('onEvent $type');
switch (type) {
//Handler back key pressed event.
case 'backPressedCallback':
{
final String id = map['uniqueId'];
FlutterBoost.containerManager
?.containerStateOf(id)
?.performBackPressed();
}
break;
...
}
}
return Future<dynamic>(() {});
}
跟进performBackPressed可见,其中backPressedHandler在state初始化时设置为maybePop,maybePop中会有页面关闭的相关处理
void performBackPressed() {
Logger.log('performBackPressed');
backPressedHandler?.call();
}
@override
void initState() {
super.initState();
backPressedHandler = () => maybePop();
}
@override
Future<bool> maybePop<T extends Object>([T result]) async {
if(routerHistory.isEmpty){
pop(result);
return true;
}
final Route<T> route = routerHistory.last;
final RoutePopDisposition disposition = await route.willPop();
if (mounted) {
switch (disposition) {
case RoutePopDisposition.pop:
pop(result);
return true;
break;
case RoutePopDisposition.doNotPop:
return false;
break;
case RoutePopDisposition.bubble:
pop(result);
return true;
break;
}
}
return false;
}
了解以上流程后,我们大概可以明白为何back键返回时,无法回传内容了,因为maybePop未传入相关result内容。其实想想可可以理解,因为框架也不知道我们具体要回传什么。查看上面maybePop的源码,我们会发现一个关键的地方
final RoutePopDisposition disposition = await route.willPop();
按下back时页面是否pop,完全取决于willPop的返回值。route默认为MaterialPageRoute,其继承自PageRoute,PageRoute继承自ModalRoute,在ModalRoute中我们可以看到willPop的具体实现。可见WillPopCallbacks非空且其中callback返回值为false时,返回值为RoutePopDisposition.doNotPop,maybePop中页面才不会关闭。
@override
Future<RoutePopDisposition> willPop() async {
final _ModalScopeState<T> scope = _scopeKey.currentState;
assert(scope != null);
for (final WillPopCallback callback in List<WillPopCallback>.from(_willPopCallbacks)) {
if (await callback() != true)
return RoutePopDisposition.doNotPop;
}
return await super.willPop();
}
了解了以上流程,我们可以在FlutterB中添加WillPopScope拦截,onWillPop中返回false即可,这样flutter_boost不会关闭当前页面,我们在onWillPop中调用Navigator的pop关闭页面和回传数据即可。