1、屏幕适配
当前手机屏幕尺寸各不相同,导致我们平时写布局的时候会在个不同的移动设备上显示的效果不同。
为了达到一套代码所有手机(安卓、苹果)体验一致效果,需要做尺寸上的适配。
适配方案
计算公式 = 实际尺寸 = UI尺寸 * 设备宽度/设计图宽度
实现代码
import 'package:flutter/material.dart';
/// @desc:屏幕适配
/// @time 2019/3/11 5:07 PM
/// @author Cheney
class LcfarmSize {
static LcfarmSize instance = LcfarmSize();
//设计稿的设备尺寸修改
double width;
double height;
bool allowFontScaling;
static MediaQueryData _mediaQueryData;
static double _screenWidth;
static double _screenHeight;
static double _pixelRatio;
static double _statusBarHeight;
static double _bottomBarHeight;
static double _textScaleFactor;
LcfarmSize({
this.width = 375,
this.height = 677,
this.allowFontScaling = false,
});
static LcfarmSize getInstance() {
return instance;
}
void init(BuildContext context) {
///release 模式下取不到
// _mediaQueryData = MediaQueryData.fromWindow(window);
MediaQueryData mediaQuery = MediaQuery.of(context);
_mediaQueryData = mediaQuery;
_pixelRatio = mediaQuery.devicePixelRatio;
_screenWidth = mediaQuery.size.width;
_screenHeight = mediaQuery.size.height;
_statusBarHeight = mediaQuery.padding.top;
_bottomBarHeight = _mediaQueryData.padding.bottom;
_textScaleFactor = mediaQuery.textScaleFactor;
// print("lcfarm init _screenWidth=$_screenWidth");
// print("lcfarm init _screenHeight=$_screenHeight");
// print("lcfarm init _textScaleFactor=$_textScaleFactor");
// print("lcfarm init scaleWidth=${_screenWidth / instance.width}");
}
static MediaQueryData get mediaQueryData => _mediaQueryData;
///每个逻辑像素的字体像素数,字体的缩放比例
static double get textScaleFactory => _textScaleFactor;
///设备的像素密度
static double get pixelRatio => _pixelRatio;
///当前设备宽度 dp
static double get screenWidth => _screenWidth;
///当前设备高度 dp
static double get screenHeight => _screenHeight;
///当前设备宽度 px
static double get screenWidthPx => _screenWidth * _pixelRatio;
///当前设备高度 px
static double get screenHeightPx => _screenHeight * _pixelRatio;
///状态栏高度 刘海屏会更高 dp
static double get statusBarHeight => _statusBarHeight;
///底部安全区距离 dp
static double get bottomBarHeight => _bottomBarHeight;
///状态栏高度 刘海屏会更高 px
static double get statusBarHeightPx => _statusBarHeight * _pixelRatio;
///底部安全区距离 px
static double get bottomBarHeightPx => _bottomBarHeight * _pixelRatio;
///根据屏幕宽度适配,实际的dp与设计稿px的比例
static double get ratio => instance.scaleWidth;
///根据屏幕宽度适配,实际的dp与设计稿px的比例
get scaleWidth => _screenWidth / instance.width;
/// 根据设计稿的设备高度适配
/// 当发现设计稿中的一屏显示的与当前样式效果不符合时,
/// 或者形状有差异时,高度适配建议使用此方法
/// 高度适配主要针对想根据设计稿的一屏展示一样的效果
get scaleHeight => _screenHeight / instance.height;
///默认根据宽度适配
static double dp(double width) => instance.setWidth(width);
///根据设计稿的设备宽度适配
///高度也根据这个来做适配可以保证不变形
double setWidth(double width) => width * scaleWidth; //
/// 高度也根据setWidth来做适配可以保证不变形(当你想要一个正方形的时候)
///setHeight方法主要是在高度上进行适配, 在你想控制UI上一屏的高度与实际中显示一样时使用.
double setHeight(double height) => height * scaleHeight;
///字体大小适配方法
static double sp(double fontSize) => instance.setSp(fontSize);
///字体大小适配方法,在 ios 中_textScaleFactor 不起作用
///@param fontSize 传入设计稿上字体的px ,
///@param allowFontScaling 控制字体是否要根据系统的“字体大小”辅助选项来进行缩放。默认值为true。
///@param allowFontScaling Specifies whether fonts should scale to respect Text Size accessibility settings. The default is true.
double setSp(double fontSize) {
return allowFontScaling
? setWidth(fontSize)
: setWidth(fontSize) / _textScaleFactor;
}
}
思路
1、默认375设计图
2、引入 'dart:ui' 获得屏幕尺寸相关信息
3、计算真实像素值
使用
//在build方法中进行初始化,后续方法中直接使用设置控件顶部间距10为设计尺寸,内部间距25为设计尺寸
@override
Widget build(BuildContext context) {
super.build(context); //保存状态必须加上
LcfarmSize.getInstance().init(context);
// LogUtil.v(
// "ModalRoute.of(context).settings.name=${ModalRoute.of(context).settings.name}");
}
Container(
margin: EdgeInsets.only(top: LcfarmSize.dp(10)),
padding: EdgeInsets.all(LcfarmSize.dp(25)),
child: Column(
children: <Widget>[
_buildPasswordField(),
Container(
margin: EdgeInsets.only(top: LcfarmSize.dp(60)),
child: _buildNextBtn(),
),
],
),
);
// 设置文本大小 17 为设计图尺寸
Text(
'原登录密码',
style: TextStyle(
fontSize: LcfarmSize.sp(17),
)
)
2、颜色管理
在flutter中,color使用的ARGB,
Widget _buildChild() {
return Container(
margin: const EdgeInsets.all(10.0),
color: const Color(0xFF0099ff),///0x 后面开始 两位FF表示透明度16进制,
///之后的0099ff 代表RGB色值
height: 100.0,
child: Text('iam Container'),
);
}
Flutter 内置了一套 Material Design 的颜色系统,在应用里我们可以直接使用这个颜色系统里提供的各种不同的颜色。这些颜色是在 Colors 这个类里定义的一些静态属性,所以使用它们的时候一般就是用 Colors 这个类的名字后面加上要用的颜色的名字,比如 Colors.red,Colors.yellow,Colors.blue。
Widget _buildChild() {
return new Container(
margin: const EdgeInsets.all(10.0),
color: const Color(0xFF0099ff),///0x 后面开始 两位FF表示透明度16进制,
///之后的0099ff 代表RGB色值
height: 100.0,
child: new Text('iam Container',
new TextStyle(
color:Colors.red///这里是flutter内部封装的 同样的方式也可以使用Colors.black,
///这里另外需要注意的是,我们还可以这样color:Colors.red[100],给它一个值,
///值的范围是 100的整数倍,比如说这样color:Colors.red[300],
///color:Colors.red[600]呈线形变化
)
),
);
}
自定义颜色使用
项目中颜色主要是以主色调加辅助色来搭配使得,颜色相对比较统一。
参考官方写法,对于项目中使用到的颜色统一使用LcfarmColor 来使用。
import 'dart:ui';
/// @desp:统一颜色管理
/// @time 2019/3/11 4:31 PM
/// @author chenyun
/// 格式argb [0-255]
///
class LcfarmColor {
static const Color colorFFFFFF = const Color(0xffffffff);
static const Color color00000000 = const Color(0x00000000);
static const Color color80000000 = const Color(0x80000000);
static const Color color333333 = const Color(0xff333333);
static const Color color999999 = const Color(0xff999999);
static const Color color666666 = const Color(0xff666666);
static const Color color10BFC7 = const Color(0xff10BFC7);
static const Color color8010BFC7 = const Color(0x8010BFC7);
}
约定 :
命名采用color+6或8位颜色数值组成,8位时前2位是16进制的透明值
使用如下:
RaisedButton(
onPressed: _isBtnEnabled ? _btnClick() : null,
child: Text(
'下一步',
style: LcfarmStyle.styleFFFFFF_18,
),
textColor: LcfarmColor.colorFFFFFF,
disabledTextColor: LcfarmColor.colorFFFFFF,
color: LcfarmColor.color10BFC7,
disabledColor: LcfarmColor.color8010BFC7,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(LcfarmSize.dp(23)),
),
)
3、样式管理
在项目中经常使用 Text 控件,必然要设置其颜色、字体大小等。则必须使用 TextStyle 设置样式。项目中大多 Text 样式是相同的,设计也希望按一定规范去设计。可以参考布谷农场设计规范(如上图)。
从 规范中可以看出颜色与字体大小的组合是有限的,很多地方可能会反复写一些重复代码。
常用写法
直接在控件代码中设置 TextStyle。
Text(
'下一步',
style: TextStyle(
color: LcfarmColor.colorFFFFFF,
fontSize: LcfarmSize.sp(18),
),
)
缺点
不灵活,不方便统一修改,代码重复
优化写法
对于样式相同的,在使用时多处设置其 TextStyle,不如直接封装出一个,使用时直接调用即可。
具体参与 LcfarmStyle
/// @desp: 格式 颜色_字号
/// @time 2019/3/13 4:44 PM
/// @author chenyun
///
class LcfarmStyle {
///白色相关字号
static final TextStyle styleFFFFFF_12 = TextStyle(
color: LcfarmColor.colorFFFFFF,
fontSize: LcfarmSize.sp(12),
);
static final TextStyle styleFFFFFF_18 = TextStyle(
color: LcfarmColor.colorFFFFFF,
fontSize: LcfarmSize.sp(18),
);
///灰色相关字号
static final TextStyle style999999_12 = TextStyle(
color: LcfarmColor.color999999,
fontSize: LcfarmSize.sp(12),
);
<font color=red>约定 :
只定义文本颜色、文本字体大小
命名采用 style+颜色(大写)+ 下划线+字体大小
</font>
上面代码可以优化成:
Text(
'下一步',
style: LcfarmStyle.styleFFFFFF_18,
)
优点
统一的管理方式,方便新增和修改</br>使用方便,自动提示
4、Icon引入管理
前端开发中,Icon的使用是必不可少,大多都可以通过icon图片图标及 iconfont字体图标来展示。
在 Flutter 中同样如此。
使用图片
这个主要就是配置一下 pubspec.yaml里的assets,在pubspec.yaml文件中的配置如下。
flutter:
# To add Flutter specific assets to your application, add an assets section,
# like this:
assets:
- icons/
使用的话跟图片一样,缺点就是大小和颜色不太好控制,而且会增加项目的体积。
IconButton(
iconSize: LcfarmSize.dp(18),
icon: ImageIcon(
AssetImage('icons/icon_clear.png'),
color: Colors.blue,
),
onPressed: () {
_controller.clear();
},
)
使用 Iconfont
在Flutter中,可以像web开发一样使用iconfont,iconfont即“字体图标”,它是将图标做成字体文件,然后通过指定不同的字符而显示不同的图片。
在字体文件中,每一个字符都对应一个位码,而每一个位码对应一个显示字形,不同的字体就是指字形不同,即字符对应的字形是不同的。而在iconfont中,只是将位码对应的字形做成了图标,所以不同的字符最终就会渲染成不同的图标。
在Flutter开发中,iconfont和图片相比有如下优势:
- 体积小:可以减小安装包大小。
- 矢量的:iconfont都是矢量图标,放大不会影响其清晰度。
- 可以应用文本样式:可以像文本一样改变字体图标的颜色、大小对齐等。
- 可以通过TextSpan和文本混用。
使用Material Design字体图标
Flutter默认包含了一套Material Design的字体图标,在pubspec.yaml文件中的配置如下
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
Material Design所有图标可以在其官网查看:https://material.io/tools/icons/
使用如下:
IconButton(
iconSize: LcfarmSize.dp(18),
icon: Icon(Icons.clear),
onPressed: () {
_controller.clear();
}
)
通过这里可以看到,使用图标就像使用文本一样,Flutter封装了一个IconData和Icon来专门显示字体图标。</br>
Icons类中包含了所有Material Design图标的IconData静态变量定义。
内置图标还是挺多的,基本上还是够用的,但对于实际项目,往往图标都是设计师根据当前应用主调定制的,并不能完全满足应用需求。
使用自定义字体图标
对于实际项目还可以使用自定义字体图标。iconfont.cn上有很多字体图标素材,我们可以选择自己需要的图标打包下载后,会生成一些不同格式的字体文件,在Flutter中,我们使用ttf格式即可。
- 将设计师提供的 svg 文件上传至 iconfont.cn 上对应的项目中
<font color="red">注意:这里的文件名统一采用小写+下划线,以便后续统一生成 icon统一规范。</font>
-
下载字体文件到本地
-
将下载的字体文件放到某个目录下,比如说 fonts 文件夹下:
找到pubspec.yaml文件,添加引用路径
这里需要注意右边第一个fonts左边的空格,如果不对会引用不到,family是自定义的名字
- 执行generate_iconfont.dart文件,生成自定义图标的 IconData 代码
- 使用如下
IconButton(
padding: widget.isPassword
? EdgeInsets.only(right: LcfarmSize.dp(40))
: EdgeInsets.only(right: LcfarmSize.dp(0)),
iconSize:
!StringUtil.isEmpty(_controller.text) ? LcfarmSize.dp(18) : 0,
icon: Icon(
LcfarmIcon.icon_clear,
color: LcfarmColor.color999999,
),
onPressed: () {
_controller.clear();
},
)
最后
如果在使用过程遇到问题,欢迎下方留言交流。