189 8069 5689

flutter事件拦截的简单介绍

flutter跳转原生页面后的穿透问题

现象:

我们提供的服务有:成都网站制作、网站建设、外贸网站建设、微信公众号开发、网站优化、网站认证、滕州ssl等。为上1000+企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的滕州网站制作公司

flutter页面通过present跳转原生页面后,原生页面上的点击会首先响应下面的flutter页面中的内容(比如按钮什么的)。

这是flutter框架一直存在的一个bug。在github上有相关的issue。

原因推测:

推测是flutter对控制器(或者view)加了分类,重写了控制器的点击事件,用来计算是否在对应的点击位置有flutter响应事件。没有的话再扔出去点击事件。

解决方案1:

在原生控制器中,加入点击事件的几个方法的空实现,用以覆盖flutter框架中的实现:

-(void)touchesBegan:(NSSetUITouch * *)touches withEvent:(UIEvent *)event{

}

-(void)touchesMoved:(NSSetUITouch * *)touches withEvent:(UIEvent *)event{

}

-(void)touchesCancelled:(NSSetUITouch * *)touches withEvent:(UIEvent *)event{

}

-(void)touchesEnded:(NSSetUITouch * *)touches withEvent:(UIEvent *)event{

}

让事件不被flutter截获即可。

解决方案2:

直接切换window的根控制器到原生控制器即可。别忘暂时保存flutter控制器。

在返回时再切换回flutter中。

解决方案3:

在flutter跳转到原生页面之前,在flutter中加上一个蒙层,用来隔绝手势往flutter下面的view传递。原生页面返回flutter时再移除这个蒙层。

Flutter开发-屏蔽Widget的多点触控行为

在Flutter中我们在 Widget 实现一些手势交互通常会使用 GestureDetector 装饰器来实现,但是默认情况下, widget 是支持多点触控,但是在一些特定需求下,我们不需要多点触控的操作,或者多点触控反而给一些功能带来麻烦,而且比较不方便的是,多点触控无法通过操作 widget 或者 GestureDetector 来屏蔽掉。查阅了官方文档发现的这个玩意: RawGestureDetector

大概意思是:一个小部件,用于检测由给定手势工厂描述的手势。对于常用手势,请使用GestureRecognizer。 RawGestureDetector主要在开发自己的手势识别器时很有用。

例如:

我们可以通过RawGestureDetector来自定义手势。

有时,你可能需要禁用多点触摸或在Flutter应用程序中点击小部件。 例如,有一个列表,并且一次只能单击其中一项。 您不希望用户同时用三个手指点击或触摸并立即选择三个项。基本上,您要防止用户多次点击或多点触摸。

我们先创建一个简单的页面,页面加载一个 ListView.builder() ,

这个列表上的cell都支持多点触控,效果图:

【图】

Flutter允许在 GestureRecognizer 基类的帮助下创建自定义手势识别器小部件。 该类已经有两个抽象的实现,可以实现多次轻击和单次轻击手势。

首先创建一个自定义窗口小部件,以使其子窗口小部件只能具有单一触摸手势。

在 build() 方法中,我们将返回仅支持单点触摸手势的手势检测器小部件。 因此,我们为此创建另一个名为 _SingleTouchRecognizer 的类

现在我们回到我们的 SingleTouchRecognizerWidget 中,通过 RawGestureDetector 装饰器来定义我们刚才创建的单击手势检测器:

现在, build() 方法返回了一个 RawGestureDetector ,该 RawGestureDetector 处理 _SingleTouchRecognizer 类中的手势。接下来,我们需要在识别器类中实现这些方法。我们首先覆盖 GestureRecognizer 的 addAllowedPointer 方法。

在这里, startTrackingPointer 方法注册了将由识别器处理的相关事件。 然后, resolve() 负责确保是否允许继续进行触摸事件。

如果我们传入 GestureDisposition.rejected ,则当前的触摸事件无法处理。 因此,此触摸事件将被传递并允许其继续。 但是,如果传递了 GestureDisposition.accepted ,则将解析触摸事件,并且不会再调用其他事件。

通过handleEvent函数重置控制变量_p的值。

这样就完成了_SingleTouchRecognizer类的实现。

现在,只需要将该 Widget 包裹在想要支持单点触控的 widget 外层即可。

参考文献:

disable-multi-touch-on-a-widget-in-flutter

api.flutter.dev

Flutter注册iOS推送

####总结:

Flutter在iOS中AppDelegate继承自FlutterAppDelegate,所以很多方法必须重写父类中的方法。iOS的推送注册流程还是一样的。不一样的是需要给推送设置别名或者将设备的deviceToken上传到推送服务器,这一步可以原生实现也可以flutter实现,但是还是需要和flutter进行交互,这是就需要注册一个通道实现这个。通道也可以增加别的一些例如:信息处理等。

正文:

在进行iOS上开发,发现Flutter创建的项目不走didRegisterForRemoteNotificationsWithDeviceToken,起初以为是没有设置UNUserNotificationCenterDelegate,后来发现AppDelegate是继承于FlutterAppDelegate的,

也就是将原生的UIApplicationDelegate的方法都会被FlutterAppDelegate拦截,即使我们不实现didRegisterForRemoteNotificationsWithDeviceToken,我觉得应该有两种方法可以实现:第一种是需要重写父类的推送方法。第二种就是在dart文件中监听系统代理,通过通道回调appdelegate来实现,

下面是百度云推送,重写父类代理的实现:

在didFinishLaunchingWithOptions launchOptions: 中注册原生交互channel

override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? ) - Bool {

    //Flutter Plugin插件注册

    GeneratedPluginRegistrant.register(with: self);

    //调用appdelegate 的 设置、注册远程推送

    self.requestAuthorization(application: application);

    //Flutter原生交互通道

    self.BPushChannel();

    //注册BPush通道

    BPush.registerChannel(launchOptions, apiKey: BPushKey, pushMode: BPushMode.development, withFirstAction: "打开", withSecondAction: "关闭", withCategory: nil, useBehaviorTextInput: true, isDebug: true);

    //禁用地理位置信息推送

    BPush.disableLbs();

    return super.application(application, didFinishLaunchingWithOptions: launchOptions);

}

//MARK:注册远程推送通知

func requestAuthorization(application: UIApplication) {

    if #available(iOS 10.0, *) {

        UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate;

        UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert]) { (granted, error) in

            if granted == true {

                DispatchQueue.main.async {

                    application.registerForRemoteNotifications()

                }

            }

        }

    } else if #available(iOS 8.0, *) {

        let types:UIUserNotificationType = [.badge , .alert , .sound]

        let settings:UIUserNotificationSettings = UIUserNotificationSettings(types: types, categories: nil)

        application.registerUserNotificationSettings(settings)

    } else {

        let types:UIRemoteNotificationType = [UIRemoteNotificationType.alert, UIRemoteNotificationType.badge, .sound]

        application.registerForRemoteNotifications(matching: types)

    }

}

//百度推送通道

func BPushChannel() - Void {

    //获取系统的跟控制器

    let controller = self.window.rootViewController

    //建立rootViewController和Flutter的通信通道

    let pushChannel = FlutterMethodChannel.init(name: channelNameForPush, binaryMessenger:controller as! FlutterBinaryMessenger)

    //设置Method回调 FlutterMethodCall包含了method的Name,ID等信息, FlutterResult是给Native和Flutter的通信回调

pushChannel.setMethodCallHandler { (FlutterMethodCall, FlutterResult) in

        print("pushChannel");

    }

    //绑定channelId到服务器

    let pushBind = FlutterMethodChannel.init(name:channelNameForPushBind, binaryMessenger: controller as! FlutterBinaryMessenger)

    pushBind.setMethodCallHandler { (FlutterMethodCall, FlutterResult) in

        //FlutterResult();结果回调,回调的结果只能为string类型

        if(self.channelId.isEmpty){

            FlutterResult(FlutterMethodNotImplemented);

        } else{

            print("channelId",self.channelId);

            let dic : DictionaryString,String = ["channelId":self.channelId];

            let data = try? JSONSerialization.data(withJSONObject: dic, options: [])

            let encodingStr = String(data: data!, encoding: String.Encoding.utf8)!

//将信息传到Flutter,

            FlutterResult(encodingStr);

        }

    }

}

// 重写远程推送通知 注册成功

override func application(_ application: UIApplication , didRegisterForRemoteNotificationsWithDeviceToken deviceToken:Data) {

    //  向云推送注册 device token

    print("deviceToken = %@", deviceToken);

    BPush.registerDeviceToken(deviceToken as Data)

    // 绑定channel.将会在回调中看获得channnelid appid userid 等

    BPush.bindChannel(completeHandler: { (result, error) - Void in

        if ((result) != nil){

            self.channelId = BPush.getChannelId();

            BPush.setTag("MyTag", withCompleteHandler: { (result, error) - Void in

                if ((result) != nil){

                }

            })

        }

    })

    super.application(application, didRegisterForRemoteNotificationsWithDeviceToken:deviceToken)

}

// 重写注册失败

override func application(_ application: UIApplication , didFailToRegisterForRemoteNotificationsWithError error: Error ) {

    print("deviceToken = %@", error)

    super.application(application, didFailToRegisterForRemoteNotificationsWithError: error)

}

**如果解决了你的问题,点个赞呗!**

一文解决Flutter中使用TextField遇到的各种疑难杂症

可以使用 SingleChildScrollView 包裹布局

这里还需要了解一个 Scaffold 中的一个属性 resizeToAvoidBottomInset

官方文档给出的解释就是处理键盘遮挡问题,默认是 true,如果不希望顶起需要设置为 false。

在 sdk 低版本的时候是使用 resizeToAvoidBottomPadding 需要将其设置为 false,现在已经弃用。但网上很多文章还没有改正,仍然用的 resizeToAvoidBottomPadding。

分两种情况

一种是使用系统的返回键,比如 android 底部导航自带的返回,

另一种是使用导航栏自定义的返回键

第一种情况需要在页面根布局使用 WillPopScope 在 onWillPop 中拦截返回处理。

原理都是通过判断输入框是否获取了焦点

当底部有固定的组件,比如提交按钮,我们在键盘弹起的时候希望按钮贴着键盘顶部固定,但是中间滚动视图可以自由滚动

可以在 SingleChildScrollView 外部再使用 Stack 包裹,悬浮按钮使用 Positioned 定位,

还要⚠️注意要给滚动组件底部留出距离防遮挡,同时还有动态加上 bottomBar 的高度,因为在 iphoneX 以上的手机,会有个虚拟按键,如果不加上该按键高度,同样会被遮挡

高度获取方法: MediaQuery.of(context).padding.bottom

在 showDialog 布局中使用 Scaffold 包裹,不要忘了将 backgroundColor 设为透明。

如果弹窗过高,还是需要将高度固定,然后使用 SingleChildScrollView ,弹窗中同样也可以在执行关闭的时候拦截,判断键盘是否弹起,如果弹起则要先关闭键盘。

给所有输入框绑定 FoucusNode

在 maxLines=1 的情况下,输入框不支持换行,换行按钮会变成 done

监听 onEditingComplete 方法

根布局使用 GestureDetector 或者 InkWell 包裹,点击的时候收起键盘。

最后要记得销毁

Flutter 网络请求 Dio 拦截器详解

昔日的小王凭借这他的小心谨慎和借助漂亮能干的女友 Dio 的辅助,终于干下了一番事业,成为中华大地响当当的人物,小王也变成老王。如今,老王已经年近花甲,看似迈上了人生巅峰,却也遇到了人生的烦恼——那就是他的儿子,新的小王。

小王和他爹当年的小心谨慎不同,小王自海外留学回来,也不愿意接手老王的事业。反而迷恋起了互联网,玩游戏、微博喷人、撩网红等等。前两项倒还好,但是后一项,让老王心烦得很。这网红哪能随便撩的,万一弄出许多小小王来,多大家业都不够分的啊!

关键时刻,还是老王的媳妇,曾经被 金屋藏娇 的Dio 想出了新的招术,再次让老王佩服不已。老王媳妇Dio给小王搞了个拦截器,只要小王要在互联网做什么,都会被她给先拦截下来,然后她再根据小王要做的事情决定是不是要替他发出去;或者是收到什么消息的时候,也会先看一遍,没问题再给小王看。而且,最为关键的是,小王对这一切压根都不知道!

老王媳妇一开始是这么干的,小王在互联网有什么新的动向直接向老王汇报。

这下小王在互联网就完全被监视了——而且他压根不知道!只是,每次他说要钱的时候,老王不再随便给了!

但这个时候,小王还能在网上撩,毕竟上网在这个时代是不怎么要钱的。

老王媳妇 Dio 一看这种方式不行,就又心生一计,每次小王聊网红的时候,直接狠心拒绝!

小王这下子懵圈了,难道是他的那些“土味情话”已经失效了?每次发出去消息都遭受到了无情的打击,让他心灰意冷。渐渐地他就淡出了互联网,至于现在在干什么,谁也不知道。感觉又像是当初老王金屋藏娇一样,现在的小王也逐渐被隐藏了起来。从此,互联网只剩下小王和各个网红的传说。

借着老王和小王的故事,我们讲述了 Dio 的封装和 Dio 的拦截器。其中拦截器可以应用于很多实际场景:

注意,Dio 的实例可以同时添加多个拦截器,以便处理不同的情况。


新闻标题:flutter事件拦截的简单介绍
网站网址:http://jkwzsj.com/article/hoggeg.html

其他资讯