You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 10:24:54