是什么
flutter_screenutil的核心作用就是:按照设计稿的尺寸,自动帮你换算成不同设备上的逻辑像素,从而实现屏幕自适应。
- 不用自己每次去除以
devicePixelRatio或MediaQuery, - 你只要在写布局时加上
.w、.h、.sp等,就能保证在不同分辨率设备上大小一致。
基本使用
安装依赖
dependencies:
flutter_screenutil: ^5.9.0 # 版本号可能会更新
初始化
你需要在 MaterialApp 外层初始化 ScreenUtil:
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
void main() {
runApp(
ScreenUtilInit(
designSize: Size(375, 812), // 设计稿的尺寸(比如 iPhone X 的 375x812)
minTextAdapt: true, // 是否根据宽度/高度缩放文字
splitScreenMode: true, // 是否支持分屏模式
builder: (context, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Demo',
home: child,
);
},
child: MyHomePage(),
),
);
}
designSize 就是 UI 设计稿的逻辑分辨率(常见 375×812、414×896、360×690 等)。
designSize的作用是得到一个缩放比例
scaleWidth = screenWidth / designSize.width;
scaleHeight = screenHeight / designSize.height;
这个缩放比例可以用过计算后面的长宽和字体大小等。
独立开发时的处理方式
很多独立开发者其实没有设计师,也不会真的有一份「375×812 的 Sketch/PS/Figma 设计稿」,这个时候自己就选一个基准设备作为“伪设计稿”。
比如最常见的是选一台 常见手机的逻辑分辨率:
- iPhone X (375×812)
- Android 主流机型 (360×690)
比如你习惯在 iPhone 模拟器上写界面,就直接用:
designSize: Size(375, 812)
使用方法
尺寸适配
Container(
width: 200.w, // 根据设计稿宽度200,自适应换算
height: 100.h, // 根据设计稿高度100,自适应换算
color: Colors.blue,
);
字体适配
Text(
"Hello Flutter",
style: TextStyle(fontSize: 16.sp), // 根据屏幕缩放字体
);
边距/圆角适配
Padding(
padding: EdgeInsets.all(10.w), // 水平垂直统一缩放
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.r), // 圆角适配
),
),
);
常用属性
.w → 宽度适配
.h → 高度适配
.sp → 字体大小适配
.r → 圆角、边框等适配
如何计算
designSize = Size(375, 812)可以得到两个缩放比例
scaleWidth = screenWidth / designSize.width;
scaleHeight = screenHeight / designSize.height;
然后可以更加这个计算出.w,.h,.sp和.r
200.w = 200 * scaleWidth = screenWidth * (200/designSize.width);
100.h = 100 * scaleHeight = screenHeight * (100/designSize.height);
// 如果 minTextAdapt = false(默认):字体只随宽度缩放
fontSize = designFontSize * scaleWidth;
// 如果 minTextAdapt = true:字体会取 宽高中最小的缩放比例,避免因为屏幕特别长导致字体太大。
fontSize = designFontSize * min(scaleWidth, scaleHeight);
8.r = 8 * min(scaleWidth, scaleHeight);
案例
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
// 基准设计稿尺寸(假设用 iPhone X 375×812)
designSize: const Size(375, 812),
minTextAdapt: true, // 字体也能跟随缩放
builder: (context, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const HomePage(),
);
},
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: Center(
child: Container(
width: 300.w, // ✅ 尺寸适配:宽度随屏幕比例变化
height: 150.h, // ✅ 尺寸适配:高度随屏幕比例变化
padding: EdgeInsets.all(16.w), // ✅ 边距适配
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(20.r), // ✅ 圆角适配
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"标题文本",
style: TextStyle(
fontSize: 24.sp, // ✅ 字体适配
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(height: 10.h), // ✅ 间距适配
Text(
"这是副标题,文字会随屏幕缩放",
style: TextStyle(
fontSize: 14.sp, // ✅ 字体适配
color: Colors.white70,
),
textAlign: TextAlign.center,
),
],
),
),
),
);
}
}
这是MacOS的模拟器。首先,里面的元素确实都会根据窗口的大小进行调整(通过scaleWidth和scaleHeight)。
但是,如果我们把macOS 的大小变成下面的矩形(width>height),就会报错报错。注意是现在容器高度变小了,字体超出了容器。
两个属性
minTextAdapt
minTextAdapt:字体在缩放时,不仅仅跟随宽度,还会考虑高度,取一个最小的缩放因子来保证适配。
默认行为(minTextAdapt: false)
ScreenUtil计算字体缩放时,通常是根据宽度(screenWidth / designWidth)来算的。- 如果某个设备特别「长」或特别「宽」,就可能导致字体大小不合适。
开启后(minTextAdapt: true)
screenWidth / designWidth和screenHeight / designHeight两个比例都会算出来。- 取最小的那个比例作为字体缩放因子。
- 这样字体在“特别长”或“特别宽”的屏幕上也不会失真。
**举例:**假设设计稿是 375 × 812。
| 设备 | 实际宽 | 实际高 | 宽度比例 | 高度比例 | 字体缩放因子 |
|---|---|---|---|---|---|
| 普通 iPhone | 375 | 812 | 1.0 | 1.0 | 1.0 |
| 平板横屏 | 800 | 600 | 2.13 | 0.74 | 0.74(min) |
| 长屏安卓 | 360 | 900 | 0.96 | 1.10 | 0.96(min) |
如果没有 minTextAdapt: true,字体可能被放大得很夸张;加上之后,就保持了更自然的效果。
splitScreenMode
splitScreenMode:支持分屏/多窗口模式下的适配。
默认行为(splitScreenMode: false)
ScreenUtil默认取整个物理屏幕的宽高来计算。- 但如果你的应用被放在「分屏窗口」里(比如安卓分屏,或者平板的多任务模式),计算出来的比例就不对了。
开启后(splitScreenMode: true)
ScreenUtil会使用 当前应用窗口的宽高 进行计算,而不是整个屏幕的宽高。- 保证在分屏模式下,布局和字体依然正确。
举例:假设一台平板,物理屏幕是 1600 × 1200。你开了分屏,App 窗口只占一半(800 × 1200)。
| 模式 | 获取的宽度 | ScreenUtil 基准 |
结果 |
|---|---|---|---|
| 默认 | 1600 | 按全屏算 | 字体太大,布局溢出 |
| 分屏模式 | 800 | 按窗口算 | 正常显示,和全屏一致比例 |
案例
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
// 设计稿的尺寸
designSize: const Size(375, 812),
// 打开下面两个开关后,对比效果会明显
minTextAdapt: true, // 是否根据宽高最小值适配字体
splitScreenMode: true, // 是否支持分屏
builder: (context, child) {
return MaterialApp(title: 'ScreenUtil Demo', home: const HomePage());
},
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("ScreenUtil Demo")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 尺寸适配
Container(
width: 200.w,
height: 100.h,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(16.r), // 圆角适配
),
child: Center(
child: Text(
"适配的盒子",
style: TextStyle(fontSize: 18.sp), // 字体适配
),
),
),
const SizedBox(height: 20),
// 对比字体效果
Text("字体大小: 18.sp", style: TextStyle(fontSize: 18.sp)),
Text("字体大小: 固定18", style: const TextStyle(fontSize: 18)),
],
),
),
);
}
}
即使设置了
splitScreenMode: false,在 分屏模式 下页面比例依旧正常。【GPT回答】
按
flutter_screenutil文档说,false时是“只按主屏幕全尺寸计算缩放比例”。但在实际运行时,很多设备(特别是 Android 和 macOS 窗口应用)里,Flutter 报给
ScreenUtil的尺寸本身就是 当前窗口的大小。
所以就算splitScreenMode: false,拿到的基准值已经是分屏后的结果了。
自然你就感觉比例“正常”。
它更多是为 某些特殊设备(比如平板、可折叠屏、桌面系统的外接显示器)准备的。
在这些设备上,如果不设true,可能ScreenUtil会坚持用“最大屏幕”来算比例 → 分屏后比例就不对。
但在大多数手机/桌面 Flutter 应用里,窗口变化已经被 Flutter 自动处理,所以false和true的区别不大。
