做移动端开发的同学都知道,针对不同型号和尺寸的手机要进行页面的适配,且 iOS 和 Android 适配方案各不相同,那我们用 Flutter 开发要怎么处理屏幕适配呢?
Flutter中的单位
Flutter使用的是类似于iOS中的点pt,也就是point。我们经常说 iPhone8 的尺寸是375x667,但是它的分辨率其实是 750x1334 。因为iPhone8的像素比(devicePixelRatio)是2.0,iPhone8plus的像素比是3.0。
iPhone各型号设备的尺寸
![](https://i-blog.csdnimg.cn/blog_migrate/0cced416a1d3efd1971109ff08ed34b2.png)
Flutter 中获取设备信息
获取设备相关的信息,可以使用官方提供的一个库
device_info: ^2.0.3
//获取设备的信息
Future getDeviceInfo() async{
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
var dataInfo;
//判断是iOS平台还是Android平台
//IosDeviceInfo里面包含我们常用到的设备信息
if(Platform.isIOS) {
print('IOS设备:');
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;dataInfo = iosInfo;
} else if(Platform.isAndroid) {
print('Android设备');
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;dataInfo = androidInfo;
}
return dataInfo;
}
获取屏幕的尺寸
需要引入
import'dart:ui';
获取屏幕的宽高、刘海高度、分辨率等信息
// 1.媒体查询信息final mediaQueryData = MediaQuery.of(context);
// 2.获取宽度和高度final screenWidth = mediaQueryData.size.width;
final screenHeight = mediaQueryData.size.height;
final physicalWidth = window.physicalSize.width;
final physicalHeight = window.physicalSize.height;
final dpr = window.devicePixelRatio;
// 3.状态栏的高度// 有刘海的屏幕:44 没有刘海的屏幕为20final statusBarHeight = mediaQueryData.padding.top;
// 有刘海的屏幕:34 没有刘海的屏幕0final bottomHeight = mediaQueryData.padding.bottom;
print('屏幕width:$screenWidth height:$screenHeight');
print('分辨率: $physicalWidth * $physicalHeight');
print('像素比: $dpr');
print('状态栏height: $statusBarHeight 底部高度:$bottomHeight');
打印出的设备尺寸
Performing hot restart...
Syncing files to device iPhone 12 Pro...
Restarted application in 575ms.
flutter: 屏幕width:390.0 height:844.0
flutter: 分辨率: 1170.0 * 2532.0
flutter: 像素比: 3.0
flutter: 状态栏height: 47.0 底部高度:34.0
适配方案
1.通过等比换算的方式
计算公式:实际尺寸 = UI尺寸 * 设备宽度/设计图宽度
这里封装了一个类Adapt,通过类可以获取屏幕的信息
class Adapt {
static double screenWidth = 0;
static double screenHeight = 0;
static double physicalWidth = 0;
static double physicalHeight = 0;
static double dpr = 0;
static double ratio = 1.0;
static double statusBarHeight = 0;
static double bottomHeight = 0;
static void initialize(BuildContext context, {double UIWidth = 375}) {
// 1.媒体查询信息
final mediaQueryData = MediaQuery.of(context);
// 2.获取宽度和高度
screenWidth = mediaQueryData.size.width;screenHeight = mediaQueryData.size.height;physicalWidth = window.physicalSize.width;physicalHeight = window.physicalSize.height;
//像素比
dpr = window.devicePixelRatio;
// 3.状态栏的高度
// 顶部有刘海:47pt 没有刘海的屏幕为20pt
statusBarHeight = mediaQueryData.padding.top;
// 底部有刘海:34pt 没有刘海的屏幕0pt
bottomHeight = mediaQueryData.padding.bottom;
//比例
ratio = screenWidth/UIWidth;
}
static pt(size){
return size * Adapt.ratio;
}
}
示例代码
UI给出的设计稿的尺寸是375*667,定义一个大小为(300*300)pt的盒子,盒子中显示字体大小为30pt的文字
@override
Widget build(BuildContext context) {
Adapt.initialize(context);
returnScaffold(
appBar: AppBar(
title: const Text('屏幕适配'),
),
body: Center(
child: Container(
width: Adapt.pt(300),
height: Adapt.pt(300),
color: Colors.orange,
alignment: Alignment.center,
child: Text(
'Hello Word',
style: TextStyle(fontSize: Adapt.pt(30)),
textAlign: TextAlign.center,
),
),
),
);
}
运行效果图,明显右边做适配的更符合效果
![](https://i-blog.csdnimg.cn/blog_migrate/e871f85346585a81cf5ea90da2db5d2d.png)
2. rpx方案适配
rpx是小程序中的适配方案,它将750px作为设计稿,1rpx=屏幕宽度/750,其它所有的单位都使用rpx单位。
不管是什么屏幕,统一分成750份
在iPhone8上: 1rpx = 375/750 = 0.5px
在iPhone12proMax 上:1rpx = 428/750 = 0.571px
所以我们就可以通过上面的计算方式,算出一个rpx,再将自己的size和rpx单位相乘即可:比如300px的宽度:3002rpx
在iPhone8上计算出的结果是300px
在在iPhone12proMax上计算出的结果是342.6px通过计算可以看出,还是有明显数据上的差别的。
这里也是封装一个类 Adapt,直接可以获取屏幕的信息
class Adapt {
static MediaQueryData _mediaQueryData = MediaQueryData();
static double screenWidth = 0;
static double screenHeight = 0;
static double rpx = 0;
static double px = 0;
static void initialize(BuildContext context, {double standardWidth = 750}) {
_mediaQueryData = MediaQuery.of(context);screenWidth = _mediaQueryData.size.width;screenHeight = _mediaQueryData.size.height;rpx = screenWidth / standardWidth;px = screenWidth / standardWidth * 2;
}
// 按照像素来设置
static double setPx(double size) {
return Adapt.rpx * size * 2;
}
// 按照rxp来设置
static double setRpx(double size) {
return Adapt.rpx * size;
}
}
示例代码
和上面的需求是一样的,最后适配的效果是一样的
@override
Widget build(BuildContext context) {
Adapt.initialize(context);
returnScaffold(
appBar: AppBar(
title: const Text('屏幕适配'),
),
body: Center(
child: Container(
width: Adapt.setPx(300),
height: Adapt.setPx(300),
color: Colors.orange,
alignment: Alignment.center,
child: Text(
'Hello Word',
style: TextStyle(fontSize: Adapt.setPx(30)),
textAlign: TextAlign.center,
),
),
),
);
}
![](https://i-blog.csdnimg.cn/blog_migrate/4b718d612417320f08b29494f46c4edc.png)
注意:一定要在已经有 MaterialApp 的 Widget 中使用 context ,否则是无效的。我这里定义默认效果图的尺寸是 375*667,这里是可以自定义传入效果图的宽度的。
3. flutter_screenutil 插件
pubspec.yaml中引入
flutter_screenutil: ^5.0.1
在已经有 MaterialApp 的 Widget 中使用 context 进行初始化
ScreenUtil.init(context,width:750,height:1334,allowFontScaling:false);
示例代码
Container(
width: ScreenUtil().setWidth(300),
height: ScreenUtil().setHeight(300)
}
Text(
'Hello Word',
style: TextStyle(fontSize: ScreenUtil.getInstance().setSp(30)),
textAlign: TextAlign.center,
)
其他更多用法可以参考官网文档:pub.flutter-io.cn/packages/fl…