
正文
flutter单例,flutter单例弹窗
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
Flutter实践--屏幕适配
做移动端开发的小伙伴都知道,针对不同型号和尺寸的手机要进行页面适配,且Android和iOS适配方案各不相同,那flutter端如何进行适配呢?以下为近期flutter开发过程中关于适配的一些学习和记录~~~~
说到flutter屏幕适配,就不得不提到插件 flutter_screenutil ,提到flutter_screenutil就不得不说以下几点?
默认宽1080px
默认高1920px
allowFontScaling为false,即不跟随系统字体大小设置变化
初始化单位为px
需要把context传进去,因为内部是通过 MediaQuery 来获取屏幕尺寸等相关信息的
无需再传context,因为内部是通过单例 window 来获取屏幕尺寸等相关信息的
作为iOS开发,之前都是以pt为参照进行比例适配的,且架构组已经定义了一套适配相关常量,传px进去不太方便,所以需要对flutter_screenutil进行扩展
公司设计图是以iPhone X的尺寸提供的即物理设备尺寸为375x812,像素比例为750x1624,像素密度比为2
初始化仍用px来初始化
dart sdk 2.7正式支持 extension-method ,即为已有类扩展方法,从 flutter_screenutil 这种 540.w 写法点进去,我们可以看到
flutter_screenutil为num类扩展了一系列简写方法,那我们当然可以按照它这种方式进行扩展
网上提供的解决方案:
第一步:修改 pubspec.yaml
第二步:执行 flutter pub get
第三步:重启 AndroidStudio
解决方案:去掉const即可
UI设计中px、pt、ppi、dpi、dp、sp之间的关系
Dart/Flutter - 扩展方法(ExtensionMethod)
相关问答
Q1: Dart类(构造函数、单例)
默认是首先调用父类无参构造函数,然后子类的无参构造函数
调用顺序
在初始化列表中显示调用父类构造函数
了解调用顺序前,首先明确一下什么是初始化列表, 初始化列表就是构造函数名的冒号: 后面,大括号{ 前面的部分
初始化列表的执行顺序,在整个构造函数的最前面, 它的作用就是
dart创建单例和java思想差不多,static,私有,dart没有private,但是可以用_表示私有,除此之外dart可以借助factory来使正常的Test test = Test()这种写法同样获取单例
控制台输出test1、test2、test3的hashcode,相同
DartPad(好用的dart在线编辑器)
Realank-Dart构造函数介绍
马嘉伦-在Flutter中使用dart的单例模式
Q2: Flutter 网络请求类封装及搜索框实现
在 Flutter 中定时器相对 iOS 来说比较好的一点就是定时器事件的执行不会受视图拖拽的影响,不涉及到模式。但是需要注意一点的是在页面离开的时候要对定时器进行销毁。
开始的时候我们是在页面中直接使用三方框架 http 进行网络的请求,这里不好的一点就是如果将来我们更换了网络请求框架的话,项目中涉及到网络的请求的地方都需要改动,对项目的影响会比较大。所以这里我们自己封装了一个网络请求类,定义自己的网络请求方法,即使将来更换三方框架的话,我们只需要在我们自己网络请求类里面更换就好,项目的其他地方不用改动。对网络请求进行封装,相信不管是 iOS 项目还是安卓项目肯定也都是这样做的。
这里我们是基于 Dio 这个三方框架进行封装的,在 HttpManager 类中我们定义了 Dio 的单例对象 _dioInstance ,通过单例方法 _getDioInstance 来获取单例对象。我们定义了 post 跟 get 两个静态方法,在这两个方法中我们都调用了私有方法 _sendRequest , _sendRequest 方法中通过该传入的枚举参数 HttpMethod 来区分 Dio 单例对象是调用 get 还是 post 请求。这里需要注意的是方法中一定要使用 async ,返回值前要加 await 。
在聊天页面中我们可以看到顶部的搜索框,这个搜索框是跟列表一起滚动的,所以比较好的实现方式就是把搜索框定义为一个 cell 。其实这个搜索框只有点击事件,点击之后跳转一个新的页面,所以我们只需要使用小部件来实现搜索框的展示就好。搜索框由白色底视图跟图片和文本组成,所以这里我们通过 Stack 部件来实现, children 的第一个元素为白色底视图,图片跟搜索文字用 Row 部件来实现,图片跟文字布局左右排列。
Q3: Flutter Dio源码分析(四)--封装
Flutter Dio源码分析(一)--Dio介绍
Flutter Dio源码分析(二)--HttpClient、Http、Dio对比
Flutter Dio源码分析(三)--深度剖析
Flutter Dio源码分析(四)--封装
Flutter Dio源码分析(一)--Dio介绍视频教程
Flutter Dio源码分析(二)--HttpClient、Http、Dio对比视频教程
Flutter Dio源码分析(三)--深度剖析视频教程
Flutter Dio源码分析(四)--封装视频教程
github仓库地址
本文会手把手教你该怎么去封装一个类库,平时在我们的工作中都是拿着别人的造好的轮子在使用,这篇文章将带你怎么去自己造轮子,以后再碰到别的类库需要对其进行封装的时候提供一个的思路和方法。
在前面的文章中,我们对 Dio 的基本使用、请求库对比、源码分析,我们知道 Dio 的使用非常的简单,那为什么还需要进行封装呢?有两点如下:
当组件库方法发生重要改变需要迁移的时候如果有多处地方用到,那么需要对使用到的每个文件都进行修改,非常的繁琐而且很容易出问题。
当不需要 Dio 库的时候,我们可以随时方便切换到别的网络请求库,当然 Dio 目前内置支持使用第三方库的适配器。
因为一个应用程序基本都是统一的配置方式,所以我们可以针对 拦截器 、 转换器 、 缓存 、 统一处理错误 、 代理配置 、 证书校验 等多个配置进行统一管理。
因为我们的应用程序在每个页面中都会用到网络请求,那么如果我们每次请求的时候都去实例化一个 Dio ,无非是增加了系统不必要的开销,而使用单例模式对象一旦创建每次访问都是同一个对象,不需要再次实例化该类的对象。
这是通过静态变量的私有构造器来创建的单例模式
我们对 超时时间 、 响应时间 、 BaseUrl 进行统一设置
因为不管是 get() 还是 post() 请求, Dio 内部最终都会调用 request 方法,只是传入的 method 不一样,所以我们这里定义一个枚举类型在一个方法中进行处理
我们已经把 Restful API 风格简化成了一个方法,通过 DioMethod 来标明不同的请求方式。在我们平时开发的过程中,需要在请求前、响应前、错误时对某一些接口做特殊的处理,那我们就需要用到拦截器。 Dio 为我们提供了自定义拦截器功能,很容易轻松的实现对请求、响应、错误时进行拦截
我们发现虽然 Dio 框架已经封装了一个 DioError 类库,但如果需要对返回的错误进行统一弹窗处理或者路由跳转等就只能自定义了
在我们发送请求的时候会碰到几种情况,比如需要对非open开头的接口自动加上一些特定的参数,获取需要在请求头增加统一的 token
在我们请求接口前可以对响应数据进行一些基础的处理,比如对响应的结果进行自定义封装,还可以针对单独的 url 做特殊处理等。
我们看了转换器的介绍,发现和拦截器的功能差不多,那为什么还要存在转换器,有两点:
执行流程: 请求拦截器 请求转换器 发起请求 响应转换器 响应拦截器 最终结果 。
只会被用于 'PUT'、 'POST'、 'PATCH'方法,因为只有这些方法才可以携带请求体(request body)
会被用于所有请求方法的返回数据。
在开发过程中,客户端和服务器打交道的时候,往往会用一个 token 来做校验,因为每个公司处理刷新token的逻辑都不一样,我这里举一个简单的例子
为什么我们需要有取消请求的功能,如果当我们的页面在发送请求时,用户主动退出当前界面或者app应用程序退出的时候数据还没有响应,那我们就需要取消该网络请求,防止不必要的错误。
由 服务器生成 的 一小段文本信息 ,发送给浏览器,浏览器把 cookie 以kv形式保存到本地 某个目录下的文本文件内,下一次请求同一网站时会把该 cookie 发送给服务器。
cookie 的使用需要用到两个第三方组件 dio_cookie_manager 和 cookie_jar
因为在我们平时的开发过程中,会碰到一种情况,在进行网络请求时,我们希望能正常访问到上次的数据,对于用户的体验比较好,而不是展示一个空白的页面,该缓存主要是 《Flutter实战》网络接口缓存 提供参考。
我们在程序退出后内存缓存将会消失,所以我们用 shared_preferences 进行磁盘缓存数据。
在我们用flutter进行抓包的时候需要配置 Dio 代理。由 DefaultHttpClientAdapter 提供了一个 onHttpClientCreate 回调来设置底层 HttpClient 的代理。
用于验证正在访问的网站是否真实。提供安全性,因为证书和域名绑定,并且由根证书机构签名确认。
日志打印主要是帮助我们开发时进行辅助排错
Q4: flutter 单例的写法, dio的封装
直接进入正题
新建一个NetworkTool工具类,下面是适配了Dart空安全的写法
接下来我们来测试一下,是不是同个实例,用 identical(Object a, Object b) ,如果a和b是同一个实例,则会返回true,否则返回false;
结果如下:
可以看出来,我们单例的写法没有什么问题,不管是调用getter方法 NetworkTool.instance ,还是调用工厂构造方法 NetworkTool() ,创建出来的实例都是同一个。
我们来封装一下Dio这个http请求库,只作简单的演示
外面的使用如下:
结果当然也是返回true;
使用如下:
希望看完此文章后,小伙伴们自己可以写出单例出来, 至少见到其他人的单例写法,心里不慌,完全看得懂,hold得住!
小伙伴们,今天的分享至此结束! 如果小伴们,觉得有点用的话,或者已经看到这里面来的请加关注及点个赞吧~~ 后续会分享更多有关flutter的文章。如果有疑问的话,请在下方留言~
Q5: Flutter图片加载与缓存
其中,参数 image 类型为抽象类 ImageProvider ,定义了图片数据获取和加载的相关接口。
根据不同的数据来源,派生出不同的 ImageProvider :
抽象类 ImageProvider 提供了一个用于加载数据源的抽象方法 @protected ImageStreamCompleter load(T key, DecoderCallback decode); 接口,不同的数据源定义各自的实现。
子类 NetworkImage 实现如下:
load 方法返回类型为抽象类 ImageStreamCompleter ,其中定义了一些管理图片加载过程的接口,比如 addListener 、 removeListener 、 addOnLastListenerRemovedCallback 等, MultiFrameImageStreamCompleter 为其子类。
MultiFrameImageStreamCompleter 第一个参数 codec 类型为 Futureui.Codec ,用来对突破进行解码,当 codec 准备好的时候,就会立即对图片第一帧进行解码操作。
codec 为 _loadAsync 方法返回值,
_loadAsync 方法实现:
decode 方法的类型:
其中解码传入的回调方法 image_provider.DecoderCallback decode ,
传入 Uint8List ,返回 Futureui.Codec 。
而对 decode 回调方法的具体定义,在 ImageProvider 的 resolveStreamForKey 方法中做了定义, resolveStreamForKey 方法在 ImageProvider 的 resolve 方法中有调用, resolve 方法则为 ImageProvider 类层级结构的公共入口点。
resolveStreamForKey 和 resolve 实现如下:
decode 方法,即 PaintingBinding.instance!.instantiateImageCodec ,即为具体图片解码的方法实现。
ui.instantiateImageCodec 实现:
descriptor.instantiateCodec 方法实现:
_instantiateCodec 方法的实现,最终到了 native 的实现:
其中返回值类型 Codec 里定义了一些属性:
obtainKey 方法:
ImageProvider 定义了一个抽象方法 FutureT obtainKey(ImageConfiguration configuration); ,供子类来实现,其中 NetworkImage 的实现为:
obtainKey 作用:
配合实现图片缓存, ImageProvider 从数据源加载完数据后,会在 ImageCache 中缓存图片数据,图片数据缓存时一个 Map ,其中 Map 中的 key 便是 obtainKey 。
resolve 作为 ImageProvider 提供给 Image 的主入口方法,参数为 ImageConfiguration ,
resolve 其中调用了 _createErrorHandlerAndKey 方法,设置了成功回调和失败回调:
其中 _createErrorHandlerAndKey 方法的实现,便调用了 obtainKey 来设置 key 。
在成功回调里,调用了方法 resolveStreamForKey ,里面有具体的缓存实现 PaintingBinding.instance!.imageCache!.putIfAbsent :
PaintingBinding.instance!.imageCache 是ImageCache的一个实例,是 PaintingBinding 的一个属性,是一个单例,图片缓存是全局的。
如上述判断:
ImageCache 定义:
ImageCache 缓存池:
在 NetworkImage 中,对 ImageProvider 的抽象方法 obtainKey 进行了实现,将自己创建了一个同步 Future 进行返回:
同时,自身又重写了 ImageProvider 定义的 == 比较操作符,通过图片 url 和图片的缩放比例 scale 进行比较:
通过ImageCache提供的方法来设置:






