使用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<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关闭页面和回传数据即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容