flutter_screenutil 是 Flutter 生态中最常用的屏幕适配库,核心原理是基于设计稿尺寸做等比例缩放 ,支持宽度 / 高度适配、字体适配、边框适配等,能快速解决不同设备的 UI 适配问题。下面从安装配置、核心用法、高级技巧、避坑指南四个维度全面解析。
一、基础准备:安装与初始化
1. 安装依赖
在 pubspec.yaml 中添加最新版本依赖:
yaml
dependencies:
flutter:
sdk: flutter
flutter_screenutil: ^5.9.0 # 推荐使用最新稳定版
执行安装命令:
arduino
flutter pub get
2. 全局初始化(关键步骤)
必须在 APP 入口处初始化,指定设计稿的基准尺寸(通常以 iPhone 14/15 的 390×844 或 iPhone 13 的 375×812 为例):
Dart
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) {
// 初始化ScreenUtil
return ScreenUtilInit(
// 设计稿尺寸(单位:px),根据你的UI设计稿修改
designSize: const Size(390, 844),
// 适配规则:默认宽度适配,也可设为BoxConstraints.tightFor(height: ...)
minTextAdapt: true, // 开启字体适配
splitScreenMode: true, // 支持分屏适配
builder: (context, child) {
return MaterialApp(
title: 'ScreenUtil示例',
theme: ThemeData(primarySwatch: Colors.blue),
home: child, // 传递子页面
builder: (context, widget) {
// 解决字体缩放导致的适配问题
ScreenUtil.init(context);
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: widget!,
);
},
);
},
child: const HomePage(), // 你的首页
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('屏幕适配示例')),
body: const Center(child: AdaptingWidget()),
);
}
}
二、核心用法:适配各种 UI 属性
flutter_screenutil 提供了两种常用适配方式:扩展方法 (推荐)和静态方法,下面是最常用的适配场景:
1. 尺寸适配(宽 / 高 / 边距 / 圆角)
Dart
class AdaptingWidget extends StatelessWidget {
const AdaptingWidget({super.key});
@override
Widget build(BuildContext context) {
return Padding(
// 边距适配:设计稿上的20px → 自动缩放
padding: EdgeInsets.all(20.w),
child: Container(
// 宽度适配:设计稿上的300px
width: 300.w,
// 高度适配:设计稿上的150px
height: 150.h,
// 圆角适配:设计稿上的16px
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(16.r),
// 边框宽度适配:设计稿上的2px
border: Border.all(width: 2.w, color: Colors.white),
),
// 内边距适配
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 8.h),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 字体适配:设计稿上的18px
Text(
'适配后的文字',
style: TextStyle(fontSize: 18.sp),
),
SizedBox(height: 10.h), // 间距适配
// 图标大小适配
Icon(Icons.phone, size: 24.sp, color: Colors.white),
],
),
),
),
);
}
}
2. 核心扩展方法说明
| 扩展方法 | 作用 | 示例 |
|---|---|---|
num.w |
宽度适配(等比例缩放) | 300.w → 设计稿 300px 宽度适配到当前设备 |
num.h |
高度适配(等比例缩放) | 150.h → 设计稿 150px 高度适配到当前设备 |
num.r |
圆角 / 半径适配(优先按宽度适配) | 16.r → 设计稿 16px 圆角适配 |
num.sp |
字体大小适配(支持最小字体限制) | 18.sp → 设计稿 18px 字体适配 |
num.sw |
屏幕宽度占比(0-1) | 0.8.sw → 占屏幕宽度 80% |
num.sh |
屏幕高度占比(0-1) | 0.5.sh → 占屏幕高度 50% |
3. 静态方法(无上下文时使用)
如果在工具类、初始化代码等无context的场景下,可使用静态方法:
Dart
// 获取适配后的尺寸
double width = ScreenUtil().setWidth(300);
double height = ScreenUtil().setHeight(150);
double fontSize = ScreenUtil().setSp(18);
// 获取屏幕信息
double screenWidth = ScreenUtil().screenWidth; // 设备实际宽度
double screenHeight = ScreenUtil().screenHeight; // 设备实际高度
double pixelRatio = ScreenUtil().pixelRatio; // 设备像素比
三、高级技巧:适配优化与特殊场景
1. 字体适配优化
默认的sp适配可能在小屏设备上字体过小,可设置最小字体限制:
Dart
// 方式1:全局初始化时设置
ScreenUtilInit(
designSize: const Size(390, 844),
minTextAdapt: true,
// 自定义字体适配规则
builder: (context, child) {
// 设置最小字体为12sp
ScreenUtil.init(context, minTextSize: 12);
return child!;
},
child: const HomePage(),
);
// 方式2:单独设置字体(强制最小字体)
Text(
'最小12sp的文字',
style: TextStyle(
fontSize: 10.spMin, // 即使适配后小于12sp,也显示12sp
),
);
2. 横竖屏适配
处理横竖屏切换时的适配更新:
Dart
class OrientationPage extends StatefulWidget {
const OrientationPage({super.key});
@override
State<OrientationPage> createState() => _OrientationPageState();
}
class _OrientationPageState extends State<OrientationPage> {
@override
Widget build(BuildContext context) {
// 横竖屏切换时重新初始化
ScreenUtil.init(context);
return Scaffold(
body: Center(
child: Text(
'屏幕宽度:${ScreenUtil().screenWidth.w}\n屏幕高度:${ScreenUtil().screenHeight.h}',
style: TextStyle(fontSize: 16.sp),
),
),
);
}
// 监听横竖屏切换
@override
void didChangeDependencies() {
super.didChangeDependencies();
final orientation = MediaQuery.of(context).orientation;
print('当前方向:$orientation');
}
}
3. ListView/GridView 适配
避免滚动组件因适配导致的布局异常:
Dart
// ListView 适配示例
ListView.builder(
// 高度适配
itemExtent: 80.h,
itemCount: 10,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.person, size: 32.sp),
title: Text('列表项 $index', style: TextStyle(fontSize: 16.sp)),
subtitle: Text('副标题', style: TextStyle(fontSize: 12.sp)),
contentPadding: EdgeInsets.symmetric(horizontal: 16.w),
);
},
);
// GridView 适配示例
GridView.count(
crossAxisCount: 2, // 列数
crossAxisSpacing: 10.w, // 列间距
mainAxisSpacing: 10.h, // 行间距
childAspectRatio: 16/9, // 宽高比(无需适配)
padding: EdgeInsets.all(10.w),
children: List.generate(4, (index) {
return Container(
width: (ScreenUtil().screenWidth - 30.w) / 2, // 手动计算宽度
height: 100.h,
color: Colors.grey,
);
}),
);
4. 自定义适配规则
针对特殊 UI(如固定宽高比的图片),可混合使用sw/sh:
Dart
// 保持16:9比例的图片
Container(
width: 0.9.sw, // 占屏幕宽度90%
height: (0.9.sw) * 9 / 16, // 按16:9计算高度
child: Image.network(
'https://example.com/image.jpg',
fit: BoxFit.cover,
),
);
四、避坑指南:常见问题与解决方案
1. 适配后布局变形
| 问题 | 解决方案 |
|---|---|
| 部分 UI 元素过大 / 过小 | 检查设计稿尺寸是否设置正确(如把 dp 当成 px);优先用w/h而非sw/sh |
| 字体缩放导致布局错乱 | 在MaterialApp的builder中设置textScaleFactor: 1.0,禁用系统字体缩放 |
| 圆角适配不一致 | 统一使用r而非w/h,r会自动适配宽高比 |
2. 无上下文时初始化失败
-
错误场景:在
main函数中直接调用ScreenUtil().setWidth(300); -
解决方案:先初始化
WidgetsFlutterBinding,再手动初始化ScreenUtil:Dartvoid main() async { WidgetsFlutterBinding.ensureInitialized(); // 手动初始化(指定设计稿尺寸) await ScreenUtil.ensureScreenSize(); ScreenUtil.init( BoxConstraints( maxWidth: WidgetsBinding.instance.window.physicalSize.width / WidgetsBinding.instance.window.devicePixelRatio, maxHeight: WidgetsBinding.instance.window.physicalSize.height / WidgetsBinding.instance.window.devicePixelRatio, ), designSize: const Size(390, 844), ); runApp(const MyApp()); }
3. 分屏 / 折叠屏适配
-
开启
splitScreenMode: true(初始化时); -
避免使用固定的
sh(屏幕高度占比),优先用MediaQuery获取可用高度:arduinodouble availableHeight = MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top - kToolbarHeight;
总结
- 核心流程 :先在
ScreenUtilInit中指定设计稿尺寸,再通过w/h/r/sp扩展方法实现 UI 适配,覆盖 90% 的适配场景; - 最佳实践 :字体用
sp(开启最小字体限制)、尺寸用w/h、圆角用r、占比用sw/sh; - 避坑要点 :禁用系统字体缩放、横竖屏切换时重新初始化、无上下文时先初始化
WidgetsFlutterBinding。
如果需要针对某一特殊场景(如适配折叠屏、自定义设计稿尺寸、国际化多语言适配)的代码示例,我可以进一步补充。