Skip to content

尺寸与适配

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 模式,内容延伸到状态栏和导航栏区域。

适配方式

  1. 使用 SafeArea 包裹内容(最常用)
  2. 使用 Scaffold,它会自动处理大部分安全区域
  3. 通过 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 常量类

下一步

基于 Flutter 官方文档整理