Flutter中获取精确DPI/PPI以设置flutter_quill为A4页面宽度
精准匹配A4页面宽度的Flutter实现方案
要让flutter_quill编辑器精准匹配A4页面宽度,核心是获取设备真实的PPI(每英寸像素数),而非依赖MediaQuery.of(context).devicePixelRatio * 96的估算值(该值基于网页96DPI的假设,和实际屏幕密度偏差较大)。以下是Android、Windows、Linux平台的精准实现方案:
一、核心计算逻辑
A4纸物理宽度为210mm,先转换为英寸:210 / 25.4 ≈ 8.2677英寸。
编辑器的逻辑像素宽度计算公式:
double a4LogicalWidth = (210 / 25.4) * realPPI / MediaQuery.of(context).devicePixelRatio;
其中realPPI是设备真实的每英寸像素数,需分平台获取。
二、Android平台获取真实PPI
Android系统提供了屏幕真实密度参数,通过MethodChannel调用原生API获取:
Flutter端代码
import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; class ScreenPpi { static const MethodChannel _channel = MethodChannel('screen_ppi'); static Future<double> getRealPPI(BuildContext context) async { try { final double ppi = await _channel.invokeMethod('getRealPPI'); return ppi; } on PlatformException catch (_) { // 调用失败时降级为估算值 return MediaQuery.of(context).devicePixelRatio * 96; } } }
Android原生代码(MainActivity.kt)
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL = "screen_ppi" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "getRealPPI") { val displayMetrics = resources.displayMetrics // 取水平方向PPI,更适合计算宽度 val realPPI = displayMetrics.xdpi result.success(realPPI) } else { result.notImplemented() } } } }
三、Windows平台获取真实PPI
通过win32 API获取当前显示器的物理尺寸和像素分辨率,计算真实PPI:
依赖配置
在pubspec.yaml添加win32包:
dependencies: win32: ^5.0.0
Flutter端代码
import 'package:win32/win32.dart'; double getWindowsRealPPI() { final hdc = GetDC(NULL); try { // 获取物理宽度(单位:毫米) final physicalWidthMm = GetDeviceCaps(hdc, HORZSIZE); // 获取像素宽度 final pixelWidth = GetDeviceCaps(hdc, HORZRES); // 转换为英寸并计算PPI(1英寸=25.4毫米) return pixelWidth / (physicalWidthMm / 25.4); } finally { ReleaseDC(NULL, hdc); } }
多显示器场景下,可替换GetDC(NULL)为GetWindowDC(windowHandle),获取当前窗口所在显示器的参数。
四、Linux平台获取真实PPI
通过GTK API获取显示器信息,计算真实PPI:
依赖配置
在pubspec.yaml添加gtk包:
dependencies: gtk: ^2.0.0
Flutter端代码
import 'package:gtk/gtk.dart'; double getLinuxRealPPI() { final display = GdkDisplay.getDefault(); final monitor = display.getPrimaryMonitor(); final geometry = monitor.getGeometry(); final scaleFactor = monitor.getScaleFactor(); // 获取物理宽度(单位:毫米) final physicalWidthMm = monitor.getWidthMm(); // 获取带缩放的像素宽度 final pixelWidth = geometry.width * scaleFactor; return pixelWidth / (physicalWidthMm / 25.4); }
五、flutter_quill应用示例
将计算出的A4宽度应用到编辑器:
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; // ... 其他代码 final realPPI = kIsWeb ? MediaQuery.of(context).devicePixelRatio * 96 : Platform.isAndroid ? await ScreenPpi.getRealPPI(context) : Platform.isWindows ? getWindowsRealPPI() : Platform.isLinux ? getLinuxRealPPI() : MediaQuery.of(context).devicePixelRatio * 96; final a4Width = (210 / 25.4) * realPPI / MediaQuery.of(context).devicePixelRatio; QuillEditor( controller: _controller, readOnly: false, focusNode: _focusNode, scrollController: _scrollController, padding: EdgeInsets.zero, builder: (context, scrollController) { return ConstrainedBox( constraints: BoxConstraints(maxWidth: a4Width), child: SingleChildScrollView( controller: scrollController, child: QuillEditorWidget( controller: _controller, readOnly: false, focusNode: _focusNode, ), ), ); }, )
注意事项
- Android的
xdpi在部分设备上可能存在微小误差,但远优于估算值; - 桌面端多显示器场景下,需确保获取当前窗口所在显示器的参数;
- 所有平台都需保留降级方案,避免API调用失败导致功能异常。
内容的提问来源于stack exchange,提问作者Tyn
相关产品推荐
相关产品推荐

