使用flutter_boost混合开发时,android端返回键返回时数据回传问题

  • 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();
map.put("a", "a");
Intent intent = getIntent().putExtra(IFlutterViewContainer.RESULT_KEY, (Serializable) map);
setResult(0, intent);
  • FlutterA->FlutterB
FlutterBoost.singleton
  .open('FlutterB')
  .then((Map value) {
    // 回传数据处理
  });

// FlutterB close并回传:
final BoostContainerSettings settings = BoostContainer.of(context).settings;
FlutterBoost.singleton
  .close(settings.uniqueId, result: {'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 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 _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(() {});
  }

跟进performBackPressed可见,其中backPressedHandler在state初始化时设置为maybePop,maybePop中会有页面关闭的相关处理

  void performBackPressed() {
    Logger.log('performBackPressed');

    backPressedHandler?.call();
  }

  @override
  void initState() {
    super.initState();
    backPressedHandler = () => maybePop();
  }

  @override
  Future maybePop([T result]) async {
    if(routerHistory.isEmpty){
      pop(result);
      return true;
    }

    final Route 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 willPop() async {
    final _ModalScopeState scope = _scopeKey.currentState;
    assert(scope != null);
    for (final WillPopCallback callback in List.from(_willPopCallbacks)) {
      if (await callback() != true)
        return RoutePopDisposition.doNotPop;
    }
    return await super.willPop();
  }

了解了以上流程,我们可以在FlutterB中添加WillPopScope拦截,onWillPop中返回false即可,这样flutter_boost不会关闭当前页面,我们在onWillPop中调用Navigator的pop关闭页面和回传数据即可。

你可能感兴趣的:(使用flutter_boost混合开发时,android端返回键返回时数据回传问题)