学习内容:
屏幕适配:
MediaQuery获取屏幕宽高,按比例设置组件大小平台判断:
Platform.isAndroid/Platform.isIOS(区分安卓 /iOS 样式)实践任务:
做一个 “设置页面”:安卓显示 “返回键”,iOS 显示 “完成键”(用平台判断)
按钮宽高按屏幕比例设置(如宽度 = 屏幕宽 * 0.8)
注意:Flutter Web不支持使用dart.io(包含文件、网络、系统信息等原生API)。
一、理论基础:屏幕适配与平台判断※
1. 屏幕适配:MediaQuery※
为什么需要屏幕适配?※
Flutter应用可能运行在各种尺寸和分辨率的设备上(手机、平板、手表等),如果硬编码组件的尺寸(如:width: 300.0),在大屏幕上可能显得很小,在小屏幕上可能超出边界。屏幕适配就是让UI在不同设备上都能保持合理的布局和美观的比例。
使用MediaQuery获取屏幕信息※
MediaQuery是一个可以获取当前设备屏幕信息的工具类。它可以用来获取屏幕的宽、高、像素密度、方向等。
核心代码
// 在 build 方法中获取 MediaQueryData final mediaQueryData = MediaQuery.of(context); // 获取屏幕宽高(逻辑像素) final screenWidth = mediaQueryData.size.width; final screenHeight = mediaQueryData.size.height; // 获取设备像素比 final pixelRatio = mediaQueryData.devicePixelRatio; // 获取状态栏高度 final statusBarHeight = mediaQueryData.padding.top; // 获取屏幕方向 final orientation = mediaQueryData.orientation; // Orientation.portrait 或 Orientation.landscape按比例设置组件大小
这是最常用也是最简单的适配方法。如:希望一个按钮的宽度是屏幕宽度的80%,高度是屏幕高度的6%,代码如下:
Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final screenHeight = MediaQuery.of(context).size.height; return Scaffold( body: Center( child: Container( // 宽度 = 屏幕宽度 * 0.8 width: screenWidth * 0.8, // 高度 = 屏幕高度 * 0.06 height: screenHeight * 0.06, color: Colors.blue, child: const Center(child: Text("自适应按钮")), ), ), ); }- 优点
- 实现简单,代码直观。
- 能快速适配大部分场景,保持UI元素的相对比例。
- 缺点
- 对于复杂的UI,纯比例适配可能不够惊喜,某些组件可能需要更灵活的调整。这时可能需要结合LayoutBuilder或响应式框架(如:flutter_screenutil)。
2. 平台判断:Platform※
为什么需要平台判断?※
Android和iOS平台在设计规范和用户习惯上存在差异,为了提供更贴近原生的用户体验,有时需要为不同平台设计略有不同的UI:
- 导航栏:Android的返回键通常在左上角,而iOS的“完成”或“取消”按钮在右上角。
- 表单控件:开关(Switch)、滑块(Slider)等组件在两个平台上的默认样式不同。
- 页面跳转:Android通常是从右向左滑入,iOS是从下向上滑入(模态窗口)。
使用dart: io中的Platform类※
Platform类提供了判断当前操作系统的方法。
核心代码
import 'dart:io'; if (Platform.isAndroid) { // 安卓平台的逻辑 print('Running on Android'); } else if (Platform.isIOS) { // iOS 平台的逻辑 print('Running on iOS'); } else if (Platform.isWindows) { // Windows 平台的逻辑 } else if (Platform.isMacOS) { // macOS 平台的逻辑 } else if (Platform.isLinux) { // Linux 平台的逻辑 } else if (Platform.isFuchsia) { // Fuchsia 平台的逻辑 }注意:Platform判断的是应用运行的操作系统,不是编译时的目标平台。
二、综合实战※
- 任务需求:制作自适应设置页面
- 页面结构
- 一个标准的Scaffold。
- AppBar的标题为“设置”。
- AppBar的leading(左侧)或actions(右侧)按钮根据平台显示:
- Android:在leading位置显示一个“返回”图标按钮(Icon(Icon.arrow_back))。
- iOS:在actions位置显示一个“完成”文本按钮(Text(“完成”))。
- body为一个ListView,包含若干设置项
- UI适配
- AppBar上的按钮(返回/完成)的padding或minWidth按屏幕比例设置,使其在不同尺寸屏幕上大小合适。
- ListView中的设置项(如:一个模拟的“清除缓存”按钮),其宽度为屏幕宽度的80%,居中显示,高度也按比例设置。
- 页面结构
代码实现
import 'package:flutter/material.dart'; import 'dart:io' as io; import 'dart:math'; import 'dart:async'; import 'package:flutter/foundation.dart' show kIsWeb; void main() { AppPlatform.initMockPlatform(); runApp(const App()); } class AppPlatform { static String? _mockPlatform; static void initMockPlatform() { if (kIsWeb) { _mockPlatform = 'ios'; } } static bool get isAndroid { if (kIsWeb) { return _mockPlatform == 'android'; } return io.Platform.isAndroid; } static bool get isIOS{ if (kIsWeb) { return _mockPlatform == 'ios'; } return io.Platform.isIOS; } } class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue ), home: const SettingsPage(), ); } } class SettingsPage extends StatefulWidget { const SettingsPage({super.key}); @override State<SettingsPage> createState() => _SettingsPageState(); } class _SettingsPageState extends State<SettingsPage>{ bool _notificationsEnabled = true; double _cacheSize = 2.5; Timer? _cacheGrowthTimer; final Random _random = Random(); @override void initState() { super.initState(); _startNextRandomGrowthTimer(); } @override void dispose() { _cacheGrowthTimer?.cancel(); super.dispose(); } void _startNextRandomGrowthTimer() { int randomSeconds = _random.nextInt(26) + 5; Duration randomDuration = Duration(seconds: randomSeconds); _cacheGrowthTimer = Timer(randomDuration, (){ _growCache(); _startNextRandomGrowthTimer(); }); } void _growCache() { setState(() { double growth = _random.nextDouble() * 0.4 + 0.1; _cacheSize += growth; _cacheSize = double.parse(_cacheSize.toStringAsFixed(1)); }); } void _onBackOrDonePressed() { Navigator.of(context).pop(); } void _onClearCachePressed() { // 模拟清除缓存 setState(() { _cacheSize = 0.0; }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('缓存已清除')), ); } @override Widget build(BuildContext context) { // 获取屏幕尺寸 final screenWidth = MediaQuery.of(context).size.width; final screenHeight = MediaQuery.of(context).size.height; // 定义自适应尺寸 final buttonWidth = screenWidth * 0.8; final buttonHeight = screenHeight * 0.06; return Scaffold( appBar: AppBar( title: const Text('设置'), centerTitle: true, leading: AppPlatform.isAndroid ? IconButton(onPressed: _onBackOrDonePressed, icon: const Icon(Icons.arrow_back)) : null, actions: AppPlatform.isIOS ? [ TextButton( onPressed: _onBackOrDonePressed, child: const Text( '完成', style: TextStyle(color: Colors.white, fontSize: 16), ), ) ] : null, ), body: ListView( padding: EdgeInsets.symmetric(vertical: screenHeight * 0.02), children: [ SwitchListTile( title: const Text('启用通知'), subtitle: const Text('接收应用通知'), value: _notificationsEnabled, onChanged: (bool value){ setState(() { _notificationsEnabled = value; }); } ), const Divider(height: 1,), ListTile( title: const Text('主题'), subtitle: const Text('深色模式'), trailing: const Icon(Icons.chevron_right), onTap: () { // 跳转主题设置页面 }, ), const Divider(height: 1,), Padding( padding: EdgeInsets.symmetric( horizontal: screenWidth * 0.1, vertical: screenHeight * 0.01 ), child: SizedBox( width: buttonWidth, height: buttonHeight, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[200], foregroundColor: Colors.black87, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8) ) ), onPressed: _onClearCachePressed, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('清除缓存'), Text('${_cacheSize.toStringAsFixed(1)}MB'), ], ) ), ), ) ], ), ); } }运行效果

- 说明:
- 代码中增加了随机增加缓存模拟,在5~30s内随机小幅度增加0.1~0.5M缓存。
如果在web平台运行,Platform将会报错,这是因为dart.io不支持在Flutter Web中使用,因此示例代码中做了模拟封装。
class AppPlatform { static String? _mockPlatform; static void initMockPlatform() { if (kIsWeb) { _mockPlatform = 'ios'; // 模拟ios系统 } } static bool get isAndroid { if (kIsWeb) { return _mockPlatform == 'android'; } return io.Platform.isAndroid; } static bool get isIOS{ if (kIsWeb) { return _mockPlatform == 'ios'; } return io.Platform.isIOS; } }