Flutter上的back返回处理

作者: hqdoremi22 | 来源:发表于2019-08-16 18:45 被阅读0次

    Android上的back键

    跟踪Android端back调用链
    FlutterActivity->eventDelegate.onBackPressed()->flutterView.popRoute()->navigationChannel.popRoute()

    public void onBackPressed() {
            if (!this.eventDelegate.onBackPressed()) {
                super.onBackPressed();
            }
        }
    

    看popRoute中做了什么操作:

        public void popRoute() {
            Log.v("NavigationChannel", "Sending message to pop route.");
            this.channel.invokeMethod("popRoute", (Object)null);
        }
    

    这里的channel对应为

    public NavigationChannel(@NonNull DartExecutor dartExecutor) {
            this.channel = new MethodChannel(dartExecutor, "flutter/navigation", JSONMethodCodec.INSTANCE);
        }
    

    可以看出,Android端back调用最后会使用flutter/navigation的channel向dart端发送popRoute消息。

    dart端处理

    在flutter/lib/src/services/system_channels.dart中找到flutter/navigation的channel定义

      static const MethodChannel navigation = MethodChannel(
          'flutter/navigation',
          JSONMethodCodec(),
      );
    

    继续跟踪一下flutter/lib/src/widgets/binding.dart中

    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    
    Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
        switch (methodCall.method) {
          case 'popRoute':
            return handlePopRoute();
          case 'pushRoute':
            return handlePushRoute(methodCall.arguments);
        }
        return Future<dynamic>.value();
      }
    
      Future<void> handlePopRoute() async {
        for (WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
          if (await observer.didPopRoute())
            return;
        }
        SystemNavigator.pop();
      }
    

    可以看到handlePopRoute的处理主要分为两部分

    1. WidgetsBindingObserver的处理
    2. SystemNavigator.pop()
      先看第一步,跟踪下WidgetsBindingObserver中observer是什么时候注册的,flutter/packages/flutter/lib/src/widgets/app.dart中
    class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserver {
    ......
      @override
      void initState() {
        super.initState();
       ......
        WidgetsBinding.instance.addObserver(this);
      }
    
      @override
      Future<bool> didPopRoute() async {
        assert(mounted);
        final NavigatorState navigator = _navigator?.currentState;
        if (navigator == null)
          return false;
        return await navigator.maybePop();
      }
    
      Future<bool> maybePop<T extends Object>([ T result ]) async {
        final Route<T> route = _history.last;
        assert(route._navigator == this);
        final RoutePopDisposition disposition = await route.willPop();
        if (disposition != RoutePopDisposition.bubble && mounted) {
          if (disposition == RoutePopDisposition.pop)
            pop(result);
          return true;
        }
        return false;
      }
    

    如果route.willPop有能pop的东西,则执行pop操作。关注下这里的route.willPop()

      Future<RoutePopDisposition> willPop() async {
        return isFirst ? RoutePopDisposition.bubble : RoutePopDisposition.pop;
      }
    

    这个方法1)在ModelRoute中会被复写成:

      Future<RoutePopDisposition> willPop() async {
        final _ModalScopeState<T> scope = _scopeKey.currentState;
        assert(scope != null);
        for (WillPopCallback callback in List<WillPopCallback>.from(_willPopCallbacks)) {
          if (!await callback())
            return RoutePopDisposition.doNotPop;
        }
        return await super.willPop();
      }
    

    可以看出这里主要是处理_willPopCallbacks,那这个callback什么时候会被赋值呢,这里就引申出来了WillPopScope这个Widget

      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        if (widget.onWillPop != null)
          _route?.removeScopedWillPopCallback(widget.onWillPop);
        _route = ModalRoute.of(context);
        if (widget.onWillPop != null)
          _route?.addScopedWillPopCallback(widget.onWillPop);
      }
      @override
      void didUpdateWidget(WillPopScope oldWidget) {
        super.didUpdateWidget(oldWidget);
        assert(_route == ModalRoute.of(context));
        if (widget.onWillPop != oldWidget.onWillPop && _route != null) {
          if (oldWidget.onWillPop != null)
            _route.removeScopedWillPopCallback(oldWidget.onWillPop);
          if (widget.onWillPop != null)
            _route.addScopedWillPopCallback(widget.onWillPop);
        }
      }
    

    所以我们平时用WillPopScope可以进行back的拦截处理,判断要不要做页面返回。
    WillPopScope({ Key key, @required this.child, @required this.onWillPop, })
    2)在LocalHistoryRoute中会被复写成:

      @override
      Future<RoutePopDisposition> willPop() async {
        if (willHandlePopInternally)
          return RoutePopDisposition.pop;
        return await super.willPop();
      }
      @override
      bool get willHandlePopInternally {
        return _localHistory != null && _localHistory.isNotEmpty;
      }
      @override
      bool didPop(T result) {
        if (_localHistory != null && _localHistory.isNotEmpty) {
          final LocalHistoryEntry entry = _localHistory.removeLast();
          assert(entry._owner == this);
          entry._owner = null;
          entry._notifyRemoved();
          if (_localHistory.isEmpty)
            changedInternalState();
          return false;
        }
        return super.didPop(result);
      }
    

    这里的_localHistory通过addLocalHistoryEntry进行添加

      void addLocalHistoryEntry(LocalHistoryEntry entry) {
        assert(entry._owner == null);
        entry._owner = this;
        _localHistory ??= <LocalHistoryEntry>[];
        final bool wasEmpty = _localHistory.isEmpty;
        _localHistory.add(entry);
        if (wasEmpty)
          changedInternalState();
      }
    

    可以看出如果_localHistory中有entry,会先处理entry中设置的onRemove事件,常见的有drawer侧边栏中:

     _historyEntry = LocalHistoryEntry(onRemove: _handleHistoryEntryRemoved);
            route.addLocalHistoryEntry(_historyEntry);
    
     void _handleHistoryEntryRemoved() {
        _historyEntry = null;
        close();
      }
    

    即,侧边栏展开操作会向route中添加localHistoryEntry,表示展开状态下,back键会触发drawer的close。
    回到之前flutter/lib/src/widgets/binding.dart中handlePopRoute方法,分析完WidgetsBindingObserver的处理后,再看看SystemNavigator.pop()
    这个方法很简单,通过channel发送SystemNavigator.pop方法给Nativie端。

    static Future<void> pop() async {
        await SystemChannels.platform.invokeMethod<void>('SystemNavigator.pop');
      }
    

    其中SystemChannels.platform为

    static const MethodChannel platform = OptionalMethodChannel(
          'flutter/platform',
          JSONMethodCodec(),
      );
    

    这时候又回到Native端,找到channel对应的Native端类PlatformChannel,

    case "SystemNavigator.pop":
        platformMessageHandler.popSystemNavigator();
        result.success(null);
        break;
    
    @Override
    public void popSystemNavigator() {
        PlatformPlugin.this.popSystemNavigator();
    }
    

    PlatformPlugin中popSystemNavigator方法为

    private void popSystemNavigator() {
           this.activity.finish();
    }
    

    channel双端定义

    插一句,上文中提到的一些通用channel Native侧是在flutterview中初始化的

    public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {
        private static final String TAG = "FlutterView";
        private final DartExecutor dartExecutor;
        private final FlutterRenderer flutterRenderer;
        private final NavigationChannel navigationChannel;
        private final KeyEventChannel keyEventChannel;
        private final LifecycleChannel lifecycleChannel;
        private final LocalizationChannel localizationChannel;
        private final PlatformChannel platformChannel;
        private final SettingsChannel settingsChannel;
        private final SystemChannel systemChannel;
    

    dart侧是在WidgetsBinding中进行设置

    mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
      @override
      void initInstances() {
        super.initInstances();
        _instance = this;
        ......
        SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
        SystemChannels.system.setMessageHandler(_handleSystemMessage);
      }
    

    总结

    总结一下基本流程:

    1. back事件由native端onBackPressed监听到,通navigation channel 传递到dart端,
    2. dart端先判断WidgetsBindingObserver列表中有无处理didPopRoute的observer,这里didPopRoute方法会调用route.willPop方法判断是否能pop,注意这里的willPop可以通过WillPopScope,LocalHistoryEntry进行自己处理。
    3. 如果没做特殊处理,则会判断dart端有无可弹出的route,有则弹出,没有则会通过SystemChannels.platform通知到Native端。

    相关文章

      网友评论

        本文标题:Flutter上的back返回处理

        本文链接:https://www.haomeiwen.com/subject/zfoqsctx.html