Flutter空安全

[toc]

Flutter空安全

参考文章:

一、进行空安全适配

当开启空安全之后,然后运行下项目你会看到很多的报错,然后定位到报错的文件,大体有三类文件需要修改:

  1. 自定义Widget
  2. 数据模型(Model)
  3. 单例

详细看上面的参考文章。

二、项目空安全升级

升级 Flutter 空安全通常需要以下几个步骤:

  1. 更新 Flutter SDK 版本:首先需要更新 Flutter SDK 版本到支持空安全的版本。可以通过运行以下命令来更新 Flutter SDK:

    1
    flutter upgrade
  2. 添加空安全特性:在 Flutter SDK 更新后,需要在项目中添加空安全特性。可以通过运行以下命令来添加空安全特性:

    1
    2
    3
    dart migrate

    // 运行上述命令后,会自动添加空安全相关的代码和特性,例如非空断言、空值检查等。但是,需要根据项目的实际情况进行手动调整和修复。
  3. 验证代码:在添加空安全特性后,需要对代码进行验证,以确保代码中不存在空指针异常和其他错误。可以通过运行以下命令来验证代码:

    Copy

    1
    2
    3
    dart analyze

    // 运行上述命令后,会检查代码中存在的空指针异常和其他错误,并给出相应的提示和建议。需要根据提示和建议进行修复。
  4. 更新依赖库:在升级 Flutter 空安全时,可能需要更新一些依赖库,以确保依赖库也支持空安全。可以通过运行以下命令来更新依赖库:

    1
    2
    3
    flutter pub upgrade --null-safety

    // 运行上述命令后,会自动更新依赖库,以确保依赖库也支持空安全。
  5. 发布应用程序:在完成以上步骤后,需要重新构建和发布应用程序。可以通过运行以下命令来构建和发布应用程序:

    1
    2
    flutter build apk
    flutter install

以上是升级 Flutter 空安全的主要步骤,需要根据项目的实际情况进行调整和修复。在升级过程中,需要注意备份代码和依赖库,并在升级前进行充分的测试,以确保升级后应用程序的正常运行。

1、保证自己非空安全工程/package没有错误(此时版本号低于空安全的2.12.0)。

2、检查是否所在flutter工程依赖库是否都升级到了空安全版本

1
2
3
cd 到pubspec.yaml所在路径

dart pub upgrade --null-safety

3、执行 dart migrate,一键升级空安全。(容易犯的错:要一键升级的package,提前升级了空安全的版本号到2.12.0以上。)

企业微信截图_6607f7fd-ac49-4b68-b676-f3f0f4eac934

4、移动文件归属,解决依赖关系,使其完全不报红。

组件的使用-Button

[toc]

组件的使用-Button

为了统一,各视图分别使用如下组件

一、按钮Button

不用再写一堆嵌套地狱代码。(支持高亮效果,目前app按钮点击效果不佳,后续可能需要加,提前避免)

按钮的分类:

编号 类名 样式举例 备注
1 纯背景的按钮:
ThemeBGButton
纯背景的按钮
2 有边框的按钮:
ThemeBorderButton
有边框的按钮
3 切状态的按钮:
ThemeStateButton
状态按钮之未选中状态未选中状态(selected=false)
状态按钮之已选中状态已选中状态(selected=true)

1、纯背景的按钮:ThemeBGButton

纯背景的按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
import 'package:flutter_baseui_kit/flutter_baseui_kit.dart';

ThemeBGButton(
//width: 300, // 不设置会根据内容自适应
//height: 80, // 不设置会根据内容自适应
bgColorType: ThemeBGType.pink,
needHighlight: true, // 默认false,不需要高亮
title: '红底白字的按钮',
titleStyle: ButtonBoldTextStyle(fontSize: 18.0),
cornerRadius: 20,
//enable: true, // 不设置,默认true
onPressed: () {},
),

如果你还需要在文字前添加图片,

纯背景并带有图片的按钮

可以通过 imageWidget 添加

1
2
3
4
5
6
7
8
9
10
import 'package:flutter_baseui_kit/flutter_baseui_kit.dart';

ThemeBGButton(
...
title: '以主题色(红色)为背景的按钮',
titleStyle: ButtonBoldTextStyle(fontSize: 18.0),
imageWidget: ImageAsset('images/xx.jpg', width: 22, height: 22,),
//imageTitleGap: 5, // 默认5
...
)

2、有边框的按钮:ThemeBorderButton

有边框的按钮

1
2
3
4
5
6
7
8
9
10
11
12
import 'package:flutter_baseui_kit/flutter_baseui_kit.dart';

ThemeBorderButton(
//width: 300, // 不设置会根据内容自适应
//height: 80, // 不设置会根据内容自适应
borderColorType: ThemeBGType.pink,
title: '白底红字红框的按钮',
titleStyle: ButtonBoldTextStyle(fontSize: 18.0),
cornerRadius: 20,
//enable: true, // 不设置,默认ture
onPressed: () {},
)

3、状态切换按钮:ThemeStateButton

未选中状态(selected=false):状态按钮之未选中状态
已选中状态(selected=true):状态按钮之已选中状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 'package:flutter_baseui_kit/flutter_baseui_kit.dart';

ThemeStateButton(
//width: 300, // 不设置会根据内容自适应
//height: 80, // 不设置会根据内容自适应
normalBGColorType: ThemeBGType.pink,
normalTitle: '修改',
selectedTitle: '提交',
titleStyle: ButtonBoldTextStyle(fontSize: 18.0),
cornerRadius: 20,
selected: false,
//enable: true, // 不设置,默认ture
onPressed: () {},
)

其他按钮样式:

image-20211217121410240

image-20211218024015942

image-20211218024157575

image-20211218024244344

组件的使用-Overlay

[toc]

组件的使用-Overlay

为了统一,各视图分别使用如下组件

一、ToastUtil

弹出toast:

1
2
3
import 'package:flutter_overlay_kit/flutter_overlay_kit.dart';

ToastUtil.showMessage('登录成功');

开发卡在产品h5的时候,请使用如下弹出提示:

1
2
3
4
5
6
7
8
9
10
11
import 'package:flutter_overlay_kit/flutter_overlay_kit.dart';

// 需要产品补充完善需求
ToastUtil.showNeedProduct(
message: '我的首页中的愿望即将实现',
);

// 需要H5补充url
ToastUtil.showNeedH5(
message: '登录页面中的用户协议',
);

二、AlertUtil

弹窗

1
2
3
4
5
6
7
8
9
10
11
import 'package:flutter_overlay_kit/flutter_overlay_kit.dart';

AlertUtil.showCancelOKAlert(
context: context,
title: "退出登录",
message: "您确定要退出登录吗?",
cancelHandle: null,
okHandle: () {
print('点击确认按钮');
},
);

组件的使用

[toc]

组件的使用-Picker

为了统一,各视图分别使用如下组件

二、选择器Picker

1、事项选择器:ItemPickerUtil

事项选择器

1
2
3
4
5
6
7
8
9
10
import 'package:flutter_datepicker/flutter_datepicker.dart';

ItemPickerUtil.chooseItem(
context,
title: '更换头像',
itemTitles: ['拍照上传', '从相册选择'],
onConfirm: (int selectedIndex) {
dealAvatar(selectedIndex);
},
);

2、日期选择器DatePickerUtil

日期选择器

1
2
3
4
5
6
7
8
9
10
11
import 'package:flutter_datepicker/flutter_datepicker.dart';

String currentBirthday = '2000-01-01';

DatePickerUtil.chooseBirthday(context,
title: '选择你的生日', selectedyyyyMMddDateString: currentBirthday,
onConfirm: (String yyyyMMddDateStirng) {
setState(() {
currentBirthday = yyyyMMddDateStirng;
});
});

组件设计规范

[toc]

组件设计规范

为了统一,各视图分别使用如下组件

一、样式设计规范

在第六章进阶Flutter控件的封装一文中,我们已经知道使用继承父类式封装这种方式,不管在封装时候,还是在使用时候,写的代码都是最简洁的。而且后期如果要直接使用系统样式,也只需要改回类名,其他结构和属性都不用动即可

所以,即使是你所定义的类只有一个入参,也一定要遵守使用继承父类式封装的设计规范。

以下以按钮中 textStyle 的传值为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
import 'package:flutter_baseui_kit/flutter_baseui_kit.dart';

ThemeBGButton(
//width: 300, // 不设置会根据内容自适应
//height: 80, // 不设置会根据内容自适应
bgColorType: ThemeBGType.pink,
title: '红底白字的按钮',
//titleStyle: ButtonThemeUtil.PingFang_FontSize_Bold(18.0), // bad
titleStyle: ButtonBoldTextStyle(fontSize: 18.0), // good
cornerRadius: 20,
//enable: true, // 不设置,默认true
onPressed: () {},
),

good:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import 'package:flutter/material.dart';

// medium 的文本样式
class ButtonMediumTextStyle extends TextStyle {
final double fontSize;
// final Color color;

ButtonMediumTextStyle({
@required this.fontSize,
// this.color,
}) : assert(fontSize != null),
// assert(color != null),
super(
fontFamily: 'PingFang SC',
fontSize: fontSize,
fontWeight: FontWeight.w500,
// color: color,
);
}

组件的使用-Cache

[toc]

组件的使用-Cache

初始化:init

1
2
3
import 'package:flutter_cache_kit/flutter_cache_kit.dart';

LocalStorage.init();

Flutter 类型数据保存和获取方法:save/get

1
2
3
4
5
6
7
import 'package:flutter_cache_kit/flutter_cache_kit.dart';

// 保存方法:支持bool\int\double\String\List<String>
LocalStorage.save('test_string', string);

// 获取方法:支持bool\int\double\String\List<String>
String string = LocalStorage.get('test_string');

自定义的类的保存和获取方法:saveCustomBean\getCustomBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import 'package:flutter_cache_kit/flutter_cache_kit.dart';

// 保存
TSCustomBean customBean = TSCustomBean(
proxyId: '1001',
proxyIp: '192.168.1.1',
name: '代理',
);
LocalStorage.saveCustomBean(
'test_custom',
customBean,
itemToJson: (TSCustomBean bItem) {
return bItem.toJson();
},
);

// 获取
TSCustomBean customBean = LocalStorage.getCustomBean(
'test_custom',
fromJson: (bMap) {
return TSCustomBean.fromJson(bMap);
},
);

自定义的数组类的保存和获取方法:saveCustomBeans\getCustomBeans

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'package:flutter_cache_kit/flutter_cache_kit.dart';

// 保存
LocalStorage.saveCustomBeans(
'test_customBeans',
customBeans,
itemToJson: (TSCustomBean bItem) {
return bItem.toJson();
},
);

// 获取
List<TSCustomBean> customBeans = LocalStorage.getCustomBeans(
'test_customBeans',
fromJson: (bMap) {
return TSCustomBean.fromJson(bMap);
},
);

页面多入口优化

[toc]

页面多入口优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class WishPublishPage extends BJHBasePage {
bool isUpdateBusiness;
Wish_detail_model wishDetailModel;

WishPublishPage({
Key key,
this.isUpdateBusiness = false,
this.wishDetailModel,
}) : super(key: key);

WishPublishPage.fromTemplatePage({
Key key,
this.isUpdateBusiness = false,
WishTemplateModel templateModel,
}) : super(key: key) {
wishDetailModel =
Wish_detail_model.fromTemplateJson(templateModel.toJson());
}

WishPublishPage.fromOtherUser({
Key key,
List<Items> selectGoodsList,
}) : super(key: key) {
isUpdateBusiness = false;
wishDetailModel.items = selectGoodsList;
}

@override
_WishPublishPageState createState() => _WishPublishPageState();
}

框架的重要性

[toc]

框架的重要性

一、ppt

详看:框架的重要性.pptx

总框架结构图.graffle

二、内容说明

框架是什么?

框架是解决问题的具体实现方法,能直接执行或复用。

为什么需要框架?

简单的讲是为了约束和统一。

举个例子:我们想要让所有的页面支持在无网络的时候,都有个缺省页。

如果没有一个框架,则我们每个页面都得进行很多判断和视图操作。

且后期如果需要修改,也会导致工作量巨大。

框架的好处是什么?

我们先从常见的几个工作中实际场景介绍:

网络请求:不进行框架/封装处理的话,可能遇到的问题:

①、每次进行网络请求,都得写一堆代码才能完成最基础的请求操作;

②、如果还要求对每个请求都添加一些公共参数、错误码处理,则又得每个请求添加一遍;

页面的缺省:不进行框架/封装处理的话,可能遇到的问题:

①、无网络等状态下,直接无缺省页,显示成白屏,体验极差;

②、有设置缺省页,但无进行框架化,导致每个页面都得堆一堆代码来实现缺省页功能;

③、有设置缺省页,但无进行框架化,后期需要修改时候,每个页面都修改,维护成本巨大;

视图控件(如按钮):不进行框架/封装处理的话,可能遇到的问题:

①、代码实现特长;

②、还没有点击效果,如果要添加又要一堆代码;

③、后期需要修改时候,每个页面都修改,维护成本巨大;

测试框架:

①、开发联调接口,无法设置代理抓包查看;

②、用户反馈问题,无法提供问题出现的版本等信息;

③、已发布包程序出现异常,开发无法定位;

场景问题解决要点/框架能解决的问题:

①、不用堆一堆类似代码,几行代码就实现

②、规范变化的时候,不要我关心和修改

③、增加通用功能的时候,不需要自己再去实现一遍

④、app异常时候的监测和反馈

框架化后,以上这些问题都能够得到解决。

所以,框架的好处有统一设计,

建立框架的意义:

好的框架能够保证和提升项目的可维护性,扩展性,健壮性。

能够提高工作效率

能够让风格更统一

各框架提供的功能

缺省页框架:

解决可能的初始”白屏”

无网络情况下的”空白页”

无数据情况下的”空白页”

为页面提供网络异常页并伴有刷新恢复重试

测试框架:

新增抓包设置(代理)

新增查看版本页面,方便对应反馈的问题出现的版本(防止是旧包)。

程序异常(上报+提示)

视图控件框架:

定义加载动画loading

愿望灯动画效果优化(底部下沉)

框架的使用

使用前(纯代码,未有任何封装):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
GestureDetector(
onTap: () => _shareWechatfriend(),
child: Container(
height: 38.h,
width: 260.w,
decoration: BoxDecoration(
color: color_theme,
borderRadius: BorderRadius.circular(19.h),
border: Border.all(color: Colors.white, width: 1),
),
child: Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/wish/yaoqing_icon.png",
width: 18.w,
height: 18.h,
),
SizedBox(width: 5.w),
Text(
"邀请好友许愿",
style: TextStyle(color: Colors.white, fontSize: 15.sp),
)
],
),
),
),
),

框架化后:

1
2
3
4
5
6
7
8
9
10
11
ThemeBGButton(
width: 260,
height: 38,
bgColorType: ThemeBGType.pink, // 此参数,让你可以直接切换到其他主题样式
needHighlight: true, // 添加此参数即会有高亮效果,而不需要再写一堆代码
title: '邀请好友许愿',
titleStyle: ButtonThemeUtil.PingFang_FontSize_Bold(15.0),
imageWidget: ImageAsset('images/wish/yaoqing_icon.png', width: 18, height: 18),
cornerRadius: 19,
onPressed: () => _shareWechatfriend(),
),