美文网首页Flutter
解决flutter与原生的滑动冲突

解决flutter与原生的滑动冲突

作者: 狂奔的胖蜗牛 | 来源:发表于2022-03-08 10:06 被阅读0次

1.问题描述

原型设计图如下:


image.png

不要关心写代码过程,最终的结果就是中间的视频tab一栏全部是用flutter写的,其余的是原生。如图:


image.png
由于下方的tab是可以滚动切换的,也就是说视频页的时候,往左滑动,会到讲义页。这个时候问题来了,可以看到flutter页面里面也有一个左右滑动的widget,而flutter整个页面是放在原生的scrollview里面的。这个时候,如果scrollview是可以滚动的,那flutter内部不会响应事件,也就是flutter页面的内容不能滚动。如果把scrollview的滚动给禁止了,那flutter页面内的滚动一切正常,但是无法实现往左滑动切换tab的需求了。这要怎么办呢?

2.解决问题的思路

分析一下,事件的优先级,肯定是flutter的滚动优先,如果我们滚动的地方是在flutter的滚动widget里,那么就是flutter的widget自己滚动,如果滚动的是widget外部,那么就让scrollview滚动。这么一分析,那就知道,肯定是要让外部的scrollview不能滚动,这样才能让flutter响应事件,然后我们给flutter页面最外层套一个手势,监听滚动事件,然后把偏移量传递给原生,原生再用代码来设置scrollview的offset,是不是就可以实现了。同时还要设置在视频tab的时候scrollview不能滚动,但是到了讲义tab,又能够滚动,响应原生的事件。

3.实现代码

1. flutter代码

flutter这边很简单,就是在页面的最外层套一个GestureDetector,然后监听水平滑动手势。
PS:安卓没有该问题!

Widget build(BuildContext context) {
    return Scaffold(
        body: Platform.isIOS
            /// iOS平台上,由于flutter横着滑动与系统scroll view手势发生冲突,需要特殊处理
            ? GestureDetector(
                child: buildPage(context),
                onHorizontalDragUpdate: (detail) {
                  /// 将水平方向偏移量传递给iOS原生
                  methodIOSVideoHorizontalScroll({
                    'event': 'in',
                    'value': detail.delta.dx
                  });
                },
                onHorizontalDragStart: (_) {
                  methodIOSVideoHorizontalScroll({'event': 'start'});
                },
                onHorizontalDragEnd: (detail) {
                  methodIOSVideoHorizontalScroll({
                    'event': 'end',
                    'value': detail.primaryVelocity
                  });
                },
              )
            : buildPage(context));
  }

在代码中,我把滑动进行中的偏移量传递给了原生,滑动结束时的速度也传递给了原生。传递给原生的代码。

methodIOSVideoHorizontalScroll(Map<String, dynamic> content) {
  Map params = {
    ACTION: NativeAction.resolveIOSVideoListGestureConflicts,
    CONTENT: content
  };
  _methodChannelWithParams(params);
}

具体的代码根据各自不同的传递方式有所不同,但最终的思路都是不变的,就是把滑动进行中的偏移量和结束时的速度给原生。偏移量我们用来设置scrollview的offset,结束时的速度,如果有速度,我们就可以理解为原生的轻扫手势。

2.原生代码

首先是接收flutter传递过来的数据

[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        // flutter传参给iOS
        if([call.method isEqualToString:METHOD_CHANNEL_CHATTING]){
            NSDictionary *dic = call.arguments;
            if (dic == nil) {
                return;;
            }else {
                if () {}......
                } else if (action == resolveIOSVideoListGestureConflicts) {
                    // 将消息告诉当前vc,当前vc需要做出处理来解决手势冲突
                    id content = [dic objectForKey:@"content"];
                    if (self.flutterNoSlidingInVideoListSlideWidget && [content isKindOfClass:[NSDictionary class]]) {
                        NSString *msg = [(NSDictionary *)content objectForKey:@"event"];
                        double value = [(NSNumber *)[(NSDictionary *)content objectForKey:@"value"] doubleValue];
                        self.flutterNoSlidingInVideoListSlideWidget(msg, value);
                    }
                }
            }
        }
    }];

然后是页面处理逻辑

// 解决手势冲突
    [DAVideoFlutterManager shareManager].flutterNoSlidingInVideoListSlideWidget = ^(NSString * _Nonnull msg, double value) {
        // 如果当前只有视频一个tab,则不需要处理flutter的手势
        if (weakself.myCategoryView.titles.count == 1) {
            return;
        }
        if ([msg isEqualToString:@"start"]) {
            weakself.lastOffsetX = 0;
        } else if ([msg isEqualToString:@"end"]) {
            weakself.lastOffsetX = 0;
            NSInteger current = 0;
            // 缓慢滑动,过了列表半个屏幕,跳到第二个页面
            if (weakself.listContainerView.scrollView.contentOffset.x > DAVI_ScreenWidth/2) {
                current = 1;
            }
            // 如果结束时,是有速度的,可以理解为轻扫屏幕,小于0表示往左轻扫
            if (value < 0) {
                current = 1;
            }
            [weakself.myCategoryView selectItemAtIndex:current];
            [weakself.myCategoryView reloadDataWithoutListContainer];
        } else if ([msg isEqualToString:@"in"]) {
            // 计算偏移量
            CGFloat offsetX = weakself.listContainerView.scrollView.contentOffset.x-value-weakself.lastOffsetX;
            // 如果是往右偏移,则不移动
            if (offsetX < 0) {
                offsetX = 0;
                weakself.lastOffsetX = 0;
            } else {
                weakself.lastOffsetX += value;
            }
            weakself.listContainerView.scrollView.contentOffset = CGPointMake(offsetX, 0);
        }
    };

具体的处理方法看代码以及注释。其中有一件事情我们需要特别注意。那就是计算scrollview的偏移量的时候,flutter传递过来的手势是相对于flutter页面本身的,而不是相对于外面的原生的view,所以外部scrollview计算偏移量的时候,得把自身上一次的偏移量给记录下来,然后加上flutter传递过来的偏移量。

3.其余代码

其余代码就不贴上来了,主要是flutter和原生手势冲突这块实现了,其余的都是小意思了。

相关文章

网友评论

    本文标题:解决flutter与原生的滑动冲突

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