尺寸与适配
Flutter 的尺寸系统基于逻辑像素(Logical Pixels),框架会自动处理不同屏幕密度的适配。但在多端开发中,你仍需要关注安全区域、屏幕适配等细节。
逻辑像素与物理像素
Flutter 使用逻辑像素(也叫设备独立像素,dp)作为单位:
| 逻辑像素 | 物理像素 | |
|---|---|---|
| 说明 | Flutter 代码中使用的单位 | 屏幕实际像素 |
| 关系 | 物理像素 = 逻辑像素 × devicePixelRatio | |
| 示例 | 100 × 100 逻辑像素 | 在 3x 设备上 = 300 × 300 物理像素 |
你只需要关心逻辑像素,框架会自动处理物理像素转换。
MediaQuery — 屏幕信息获取
MediaQuery 提供了当前屏幕的各种信息。
常用属性
dart
import 'package:flutter/material.dart';
class MediaQueryExample extends StatelessWidget {
const MediaQueryExample({super.key});
@override
Widget build(BuildContext context) {
// ─── ★ 获取屏幕信息 ──────────────
final mediaQuery = MediaQuery.of(context);
// ─── ☆ 获取屏幕信息 ──────────────
return Scaffold(
appBar: AppBar(title: const Text('MediaQuery 示例')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('屏幕尺寸: ${mediaQuery.size}'),
Text('宽度: ${mediaQuery.size.width}'),
Text('高度: ${mediaQuery.size.height}'),
Text('像素密度: ${mediaQuery.devicePixelRatio}'),
Text('安全区域: ${mediaQuery.padding}'),
Text('键盘高度: ${mediaQuery.viewInsets.bottom}'),
Text('屏幕方向: ${mediaQuery.orientation}'),
Text('文字缩放: ${mediaQuery.textScaler}'),
],
),
),
);
}
}文字缩放
Flutter 3.16+ 使用 TextScaler 替代了 textScaleFactor:
dart
// 获取文字缩放倍数
final textScale = MediaQuery.textScalerOf(context);
// 固定文字不随系统缩放
MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(1.0),
),
child: child,
)判断屏幕方向
dart
final isPortrait = MediaQuery.of(context).orientation == Orientation.portrait;
final isLandscape = MediaQuery.of(context).orientation == Orientation.landscape;判断屏幕大小类别
dart
final width = MediaQuery.sizeOf(context).width;
if (width < 600) {
// 手机
} else if (width < 1200) {
// 平板
} else {
// 桌面
}SafeArea — 安全区域适配
SafeArea 自动避开系统 UI(状态栏、导航栏、刘海/挖孔屏等),是最常用的安全区域适配方式。
基本用法
dart
import 'package:flutter/material.dart';
class SafeAreaExample extends StatelessWidget {
const SafeAreaExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// ─── ★ SafeArea 自动避开安全区域 ──────────────
body: SafeArea(
child: Center(child: Text('安全区域内容')),
),
// ─── ☆ SafeArea 自动避开安全区域 ──────────────
);
}
}常用属性
dart
import 'package:flutter/material.dart';
class SafeAreaPropertiesExample extends StatelessWidget {
const SafeAreaPropertiesExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
left: true,
top: true,
right: true,
bottom: true,
minimum: const EdgeInsets.only(bottom: 16),
child: const Text('带最小间距的安全区域'),
),
);
}
}只避开特定方向
dart
import 'package:flutter/material.dart';
class SafeAreaTopOnlyExample extends StatelessWidget {
const SafeAreaTopOnlyExample({super.key});
@override
Widget build(BuildContext context) {
// 只避开顶部(适用于底部需要全屏展示的场景)
return Scaffold(
body: SafeArea(
bottom: false,
child: const Text('只避开顶部'),
),
);
}
}Edge-to-Edge 适配
Flutter 3.27+ 默认启用 Edge-to-Edge 模式,内容延伸到状态栏和导航栏区域。
适配方式
- 使用
SafeArea包裹内容(最常用) - 使用
Scaffold,它会自动处理大部分安全区域 - 通过
MediaQuery.of(context).padding获取安全区域尺寸
回退 Edge-to-Edge
如需回退旧模式,在 AndroidManifest.xml 中设置:
xml
<application
android:enableOnBackInvokedCallback="false"
...>响应式布局策略
使用 LayoutBuilder
dart
import 'package:flutter/material.dart';
class ResponsiveExample extends StatelessWidget {
const ResponsiveExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('响应式布局')),
// ─── ★ LayoutBuilder 响应式布局 ──────────────
body: LayoutBuilder(
builder: (context, constraints) {
// ─── ★ 根据宽度切换布局 ──────────────
if (constraints.maxWidth > 600) {
return const Center(child: Text('宽屏布局(平板/桌面)', style: TextStyle(fontSize: 24)));
} else {
return const Center(child: Text('窄屏布局(手机)', style: TextStyle(fontSize: 24)));
}
// ─── ☆ 根据宽度切换布局 ──────────────
},
),
// ─── ☆ LayoutBuilder 响应式布局 ──────────────
);
}
}使用 MediaQuery 断点
dart
final width = MediaQuery.sizeOf(context).width;
// 常用断点
// < 360: 小屏手机
// 360~600: 手机
// 600~840: 大屏手机/小平板
// 840~1200: 平板
// > 1200: 桌面Dimensions — 尺寸资源管理
将常用尺寸定义为常量,统一管理:
dart
class Dimens {
// 间距
static const double spacing4 = 4;
static const double spacing8 = 8;
static const double spacing12 = 12;
static const double spacing16 = 16;
static const double spacing24 = 24;
static const double spacing32 = 32;
// 圆角
static const double radiusSmall = 4;
static const double radiusMedium = 8;
static const double radiusLarge = 16;
static const double radiusXLarge = 24;
// 图标
static const double iconSmall = 16;
static const double iconMedium = 24;
static const double iconLarge = 32;
}使用:
dart
Padding(
padding: EdgeInsets.all(Dimens.spacing16),
child: Icon(Icons.home, size: Dimens.iconMedium),
)适配速查
| 需求 | 方案 |
|---|---|
| 获取屏幕尺寸 | MediaQuery.of(context).size |
| 避开状态栏/导航栏 | SafeArea |
| 获取安全区域尺寸 | MediaQuery.of(context).padding |
| 获取键盘高度 | MediaQuery.of(context).viewInsets.bottom |
| 固定文字大小 | TextScaler.linear(1.0) |
| 响应式布局 | LayoutBuilder + 断点判断 |
| 统一尺寸管理 | 定义 Dimens 常量类 |
