做移动应用开发,屏幕适配是个绕不开的话题。同样的代码在不同设备上显示效果可能天差地别。今天我们来聊聊如何用flutter_screenutil实现完美的屏幕适配。

为什么需要屏幕适配

你可能遇到过这种情况:在自己手机上看着挺好的界面,到了别人手机上要么挤成一团,要么空荡荡的。这就是没做好屏幕适配的结果。

不同设备的屏幕尺寸、分辨率、像素密度都不一样。如果直接写死尺寸,肯定会出问题。我们需要一个方案,让界面在各种设备上都能保持合适的比例。

flutter_screenutil简介

flutter_screenutil是一个专门用于屏幕适配的库。它的核心思路很简单:基于设计稿尺寸,按比例缩放所有元素。

比如设计稿是375x812的,实际设备是414x896的,那所有尺寸都按比例放大。这样就能保证视觉效果一致。

依赖配置

先在pubspec.yaml中添加依赖:

dependencies:
  flutter_screenutil: ^5.9.0

这个版本已经适配了OpenHarmony平台,可以放心使用。

初始化配置

在应用入口处初始化ScreenUtil:

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return ScreenUtilInit(
      designSize: const Size(375, 812),
      builder: (context, child) => GetMaterialApp(
        title: 'Web开发助手',
        theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
        home: const MainApp(),
        debugShowCheckedModeBanner: false,
      ),
    );
  }
}

ScreenUtilInit的作用:这是整个适配方案的核心。它会在应用启动时获取设备的实际尺寸,然后计算缩放比例。

designSize参数:这是设计稿的尺寸。我们用的是375x812,这是iPhone X的尺寸,也是目前比较通用的设计稿尺寸。

builder回调:在这里返回实际的应用Widget。ScreenUtil会确保在构建Widget之前完成初始化。

基础用法

初始化完成后,就可以使用各种适配方法了:

// 宽度适配
Container(
  width: 100.w,  // 设计稿上是100像素宽
  height: 50.h,  // 设计稿上是50像素高
  padding: EdgeInsets.all(16.w),
  child: Text(
    '示例文本',
    style: TextStyle(fontSize: 14.sp),  // 字体大小适配
  ),
)

.w扩展:用于宽度适配。100.w表示设计稿上的100像素宽度,实际显示时会根据屏幕宽度按比例缩放。

.h扩展:用于高度适配。原理和.w一样,只是基于屏幕高度计算。

.sp扩展:用于字体大小适配。它会同时考虑屏幕宽度和系统字体缩放设置。

.r扩展:用于半径适配,常用于圆角、圆形等场景。

实际应用示例

看看我们项目中的实际使用:

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Web开发助手')),
      body: GridView.builder(
        padding: EdgeInsets.all(16.w),
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          crossAxisSpacing: 12.w,
          mainAxisSpacing: 12.w,
        ),
        itemCount: 8,
        itemBuilder: (context, index) {
          return Card(
            elevation: 4,
            child: Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(12.r),
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.code, size: 40.sp, color: Colors.white),
                  SizedBox(height: 12.h),
                  Text(
                    '功能名称',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 14.sp,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

padding: EdgeInsets.all(16.w):四周的内边距都是16像素(设计稿尺寸)。

crossAxisSpacing: 12.w:网格之间的横向间距。

mainAxisSpacing: 12.w:网格之间的纵向间距。

borderRadius: BorderRadius.circular(12.r):圆角半径。使用.r确保圆角在各种设备上看起来都合适。

Icon(size: 40.sp):图标大小。

fontSize: 14.sp:文字大小。

SizedBox(height: 12.h):垂直间距。

适配原理

flutter_screenutil的适配原理其实很简单:

// 宽度适配
实际宽度 = 设计稿宽度 * (设备宽度 / 设计稿宽度)

// 高度适配
实际高度 = 设计稿高度 * (设备高度 / 设计稿高度)

// 字体适配
实际字体 = 设计稿字体 * min(宽度比例, 高度比例)

比如设计稿是375宽,设备是414宽,那100.w实际显示就是:

100 * (414 / 375) = 110.4像素

这样就能保证在不同设备上的视觉比例一致。

特殊场景处理

固定尺寸:有些元素不需要适配,比如分割线:

Container(
  width: double.infinity,
  height: 1,  // 直接写数字,不加.h
  color: Colors.grey,
)

最大最小限制:有时需要限制适配后的尺寸:

Container(
  width: 200.w.clamp(150, 250),  // 限制在150-250之间
  height: 100.h,
)

百分比布局:配合MediaQuery使用:

Container(
  width: MediaQuery.of(context).size.width * 0.8,  // 屏幕宽度的80%
  height: 200.h,
)

常见问题

问题1:文字在某些设备上显示不全

可能是只用了.w或.h,建议字体大小用.sp:

// 不推荐
Text('文字', style: TextStyle(fontSize: 14.w))

// 推荐
Text('文字', style: TextStyle(fontSize: 14.sp))

问题2:圆形变成了椭圆

圆形元素的宽高要保持一致:

// 错误
Container(
  width: 50.w,
  height: 50.h,  // .w和.h的比例可能不同
  decoration: BoxDecoration(shape: BoxShape.circle),
)

// 正确
Container(
  width: 50.w,
  height: 50.w,  // 都用.w或都用.h
  decoration: BoxDecoration(shape: BoxShape.circle),
)

问题3:适配后的尺寸太大或太小

检查designSize是否设置正确。如果设计稿是750宽的,要这样设置:

ScreenUtilInit(
  designSize: const Size(750, 1334),
  // ...
)

性能优化

避免重复计算:把常用的尺寸定义成常量:

class AppSizes {
  static final double padding = 16.w;
  static final double iconSize = 40.sp;
  static final double fontSize = 14.sp;
  static final double radius = 12.r;
}

// 使用
Container(
  padding: EdgeInsets.all(AppSizes.padding),
  child: Icon(Icons.home, size: AppSizes.iconSize),
)

合理使用适配:不是所有地方都需要适配。比如:

// 需要适配
padding: EdgeInsets.all(16.w),
fontSize: 14.sp,

// 不需要适配
elevation: 4,  // 阴影深度
opacity: 0.8,  // 透明度

实战建议

统一设计稿尺寸:团队开发时,大家要用同一个设计稿尺寸。常用的有375x812、414x896等。

保持一致性:同类元素用同样的适配方式。比如所有的padding都用.w,所有的字体都用.sp。

测试多种设备:在不同尺寸的设备上测试,确保适配效果。

注意极端情况:在特别小或特别大的屏幕上测试,看看是否需要特殊处理。

与其他方案对比

MediaQuery方案

// MediaQuery方式
width: MediaQuery.of(context).size.width * 0.5,

// ScreenUtil方式
width: 187.5.w,  // 375 * 0.5

ScreenUtil的优势是不需要context,代码更简洁。

LayoutBuilder方案

// LayoutBuilder方式
LayoutBuilder(
  builder: (context, constraints) {
    return Container(width: constraints.maxWidth * 0.5);
  },
)

// ScreenUtil方式
Container(width: 187.5.w)

ScreenUtil不需要额外的Builder,性能更好。

高级用法

动态修改设计稿尺寸

ScreenUtil.init(
  context,
  designSize: Size(375, 812),
  minTextAdapt: true,  // 文字最小适配
);

获取适配后的尺寸

double width = ScreenUtil().setWidth(100);  // 等同于100.w
double height = ScreenUtil().setHeight(100);  // 等同于100.h
double fontSize = ScreenUtil().setSp(14);  // 等同于14.sp

获取设备信息

double screenWidth = ScreenUtil().screenWidth;  // 屏幕宽度
double screenHeight = ScreenUtil().screenHeight;  // 屏幕高度
double statusBarHeight = ScreenUtil().statusBarHeight;  // 状态栏高度
double bottomBarHeight = ScreenUtil().bottomBarHeight;  // 底部安全区高度

实际效果

使用flutter_screenutil后,我们的应用在各种设备上都能保持一致的视觉效果:

小屏设备(如iPhone SE):所有元素按比例缩小,但布局比例不变。

大屏设备(如iPad):所有元素按比例放大,充分利用屏幕空间。

不同分辨率:无论是1080p还是2K屏,显示效果都一致。

调试技巧

查看实际尺寸

print('设计稿100.w实际是: ${100.w}');
print('设计稿14.sp实际是: ${14.sp}');

对比适配前后

// 适配前
Container(width: 100, height: 50)

// 适配后
Container(width: 100.w, height: 50.h)

// 打印对比
print('适配前: 100x50');
print('适配后: ${100.w}x${50.h}');

小结

flutter_screenutil让屏幕适配变得简单又可靠。只需要在初始化时设置好设计稿尺寸,然后在代码中使用.w、.h、.sp、.r这些扩展方法,就能实现完美的适配效果。

记住几个关键点:正确设置designSize、统一使用适配方法、在多种设备上测试。做好这些,你的应用就能在各种设备上都有出色的表现。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐