欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Flutter桌面應(yīng)用如何進行多分辨率適配

 更新時間:2023年02月12日 09:39:01   作者:Karl_wei  
這篇文章主要為大家介紹了Flutter桌面應(yīng)用如何進行多分辨率適配的方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

通過此篇文章,你將了解到:

Flutter windows和Android桌面應(yīng)用屏幕適配的解決方案;

屏幕適配的相關(guān)知識和原理;

flutter_screenutil的實現(xiàn)原理和缺陷。

Flutter桌面應(yīng)用的開發(fā)過程中,勢必需要適配不同尺寸的屏幕。我們的預(yù)期是在不同尺寸的設(shè)備上,用戶的使用感觀基本一致。 如:在個人pc上,應(yīng)用與屏幕的高度比是2/3;那么到60寸的大設(shè)備上,應(yīng)用的尺寸依然需要2/3比例。

屏幕適配的一些基礎(chǔ)概念

  • 屏幕尺寸:屏幕的實際大小,主要看屏幕的對角線的長度,如:6.6英寸。
  • 分辨率:屏幕上像素點的總和,如:2400×1176。設(shè)備的屏幕其實是由N個像素格子組合成的,屏幕上顯示的所有元素(圖片、文字)從微觀上都是為特定的像素格子繪制上內(nèi)容。
  • 屏幕密度(dpi):每英寸的像素格子數(shù)。每英寸展示160個像素時稱為一倍素;120個稱為低倍素...

假設(shè)我們需要展示一張800×800的圖片,那么在160dpi的手機屏幕上,我們只要設(shè)置800×800px的寬高;

但在320dpi的屏幕上,由于每英寸的像素點翻倍了,為了用戶的視覺感受一致,就需要將圖片設(shè)置的寬高設(shè)為1600×1600px。這就是屏幕適配最基本的原理,我們開發(fā)中所用到的適配庫,最基礎(chǔ)的能力就是提供這層轉(zhuǎn)換。

Flutter 移動端開發(fā)的通用做法

Flutter移動端的生態(tài)已經(jīng)很完備,我們一般在開發(fā)過程中會使用flutter_screenutil這個插件。這是一個純Dart的pub,閱讀源碼發(fā)現(xiàn)其做法也很簡單粗暴。

  • 根據(jù)傳入的設(shè)計稿尺寸,與設(shè)備的邏輯像素尺寸的比值作為縮放倍數(shù)
  • 開發(fā)者設(shè)置的尺寸都會去乘以對應(yīng)的縮放倍數(shù),從而實現(xiàn)widget大小的轉(zhuǎn)換。
extension SizeExtension on num {
  ///[ScreenUtil.setWidth]
  double get w => ScreenUtil().setWidth(this);
  ///[ScreenUtil.setHeight]
  double get h => ScreenUtil().setHeight(this);
  ......
)
double get screenHeight =>
    _context != null ? MediaQuery.of(_context!).size.height : _screenHeight;
double setHeight(num height) => height * scaleHeight;
// 高度的縮放比:設(shè)備的邏輯像素的高度/設(shè)計稿的高度
double get scaleHeight =>
    (_splitScreenMode ? max(screenHeight, 700) : screenHeight) /
    _uiSize.height;

邏輯像素screenHeight從哪來?

獲取MediaQueryData的size,即應(yīng)用窗口的分辨率。

extension on MediaQueryData? {
  MediaQueryData? nonEmptySizeOrNull() {
    if (this?.size.isEmpty ?? true)
      return null;
    else
      return this;
  }
}
/// The size of the media in logical pixels (e.g, the size of the screen).
///
/// Logical pixels are roughly the same visual size across devices. Physical
/// pixels are the size of the actual hardware pixels on the device. The
/// number of physical pixels per logical pixel is described by the
/// [devicePixelRatio].
final Size size;

存在的問題

flutter_screenutil 這個庫在移動端使用是完全沒有問題的。手機尺寸雖說層出不窮,但是也遵循瘦長的長方形規(guī)則,最重要的是應(yīng)用默認都是全屏的,那么上面第3點獲取到的應(yīng)用窗口高度screenHeight和設(shè)備的大小剛好是完全吻合的。從而計算出的縮放比(設(shè)計稿尺寸/設(shè)備的尺寸 = 縮放比值)是偏差不大的。
我們在Android的桌面應(yīng)用中,這個庫也可以支持各種設(shè)備。

然而在windows等桌面端卻沒那么簡單:

  • 首先桌面設(shè)備的尺寸層出不窮,從個人筆記本到演示廳的屏幕,物理大小就已經(jīng)差了幾十倍,而像素密度卻差別不大,這在適配上本身就存在更大難度。
  • 且通過驗證,F(xiàn)lutterMediaQueryData獲取的是應(yīng)用窗口的大小,但是桌面設(shè)備屏幕大小跟應(yīng)用窗口大小可不是一樣大的,這就是最大的問題所在!

通過實踐我們也驗證了flutter_screenutil在桌面端的適配基本不起作用,且還會造成不少問題,比如:第一次運行字體都會偏大;當有多個擴展屏的時候,主副屏切換有bug。

桌面端解決方案

一、需求分析

我們希望flutter開發(fā)出來的應(yīng)用,在不同的設(shè)備中:

  • 應(yīng)用的大小占比屏幕物理尺寸的比例是一致的;
  • 系統(tǒng)顯示設(shè)置中的的縮放倍數(shù)不會影響應(yīng)用的大?。?/li>
  • 資源大小可以進行適配,讓圖片等資源在不同尺寸的設(shè)備上都能顯示清晰。

分析以上預(yù)期效果,可以提煉出一個原則:應(yīng)用的尺寸必須跟屏幕的物理大小占比一致,與分辨率、像素密度、縮放比都沒關(guān)系。

二、實現(xiàn)原理

由于Android端用了flutter_screenutil這個庫,F(xiàn)lutter又是跨平臺的。為了降低開發(fā)成本,我試著fork源碼下來更改,并且做了以下的操作:

  • 在構(gòu)造函數(shù)上,我加了一個參數(shù)app2screenWithWidth,讓用戶告知應(yīng)用窗口寬度與屏幕寬度的比值,如:70%傳入0.7;
// 文件路徑:flutter_screenutil/lib/src/screenutil_init.dart
class ScreenUtilInit extends StatefulWidget {
  /// A helper widget that initializes [ScreenUtil]
  const ScreenUtilInit({
    Key? key,
    required this.builder,
    this.child,
    this.rebuildFactor = RebuildFactors.size,
    this.designSize = ScreenUtil.defaultSize,
    this.app2screenWithWidth = 1,
    this.splitScreenMode = false,
    this.minTextAdapt = false,
    this.useInheritedMediaQuery = false,
  }) : super(key: key);
  final ScreenUtilInitBuilder builder;
  final Widget? child;
  final bool splitScreenMode;
  final bool minTextAdapt;
  final bool useInheritedMediaQuery;
  final RebuildFactor rebuildFactor;
  /// The [Size] of the device in the design draft, in dp
  final Size designSize;
  /// 適用于桌面應(yīng)用,應(yīng)用窗口寬度與設(shè)備屏幕寬度的比例
  final double app2screenWithWidth;
  @override
  State<ScreenUtilInit> createState() => _ScreenUtilInitState();
}
  • yaml中引入 screenRetriever,獲取到真實的設(shè)備屏幕像素,這個是真實的屏幕像素,跟應(yīng)用窗口沒關(guān)系;然后可以計算出應(yīng)用窗口的大小,得出轉(zhuǎn)換系數(shù);
dependencies:
  flutter:
    sdk: flutter
  # 獲取屏幕物理參數(shù)
  screen_retriever: ^0.1.2
// 文件路徑:flutter_screenutil/lib/src/screen_util.dart
/// Initializing the library.
static Future<void> init(
  BuildContext context, {
  Size designSize = defaultSize,
  double app2screenWithWidth = 1,
  bool splitScreenMode = false,
  bool minTextAdapt = false,
}) async {
  final navigatorContext = Navigator.maybeOf(context)?.context as Element?;
  final mediaQueryContext =
      navigatorContext?.getElementForInheritedWidgetOfExactType<MediaQuery>();
  final initCompleter = Completer<void>();
  WidgetsFlutterBinding.ensureInitialized().addPostFrameCallback((_) {
    mediaQueryContext?.visitChildElements((el) => _instance._context = el);
    if (_instance._context != null) initCompleter.complete();
  });
  // ** 我修改的代碼 **
  Orientation orientation = Orientation.landscape;
  Size deviceSize = Size.zero;
  if (isDesktop) {
    Display primaryDisplay = await screenRetriever.getPrimaryDisplay();
    deviceSize = primaryDisplay.size;
    orientation = deviceSize.width > deviceSize.height
        ? Orientation.landscape
        : Orientation.portrait;
  } else {
    final deviceData = MediaQuery.maybeOf(context).nonEmptySizeOrNull();
    deviceSize = deviceData?.size ?? designSize;
    orientation = deviceData?.orientation ??
        (deviceSize.width > deviceSize.height
            ? Orientation.landscape
            : Orientation.portrait);
  }
  _instance
    .._context = context
    .._uiSize = designSize
    .._splitScreenMode = splitScreenMode
    .._minTextAdapt = minTextAdapt
    .._orientation = orientation
    .._screenWidth = deviceSize.width
    .._screenHeight = deviceSize.height;
  // 桌面區(qū)分設(shè)置下窗口大小
  if (isDesktop) {
    double appWidth = deviceSize.width * app2screenWithWidth;
    double appHeight = appWidth / (designSize.width / designSize.height);
    _instance._uiSize = Size(appWidth, appHeight);
  }
  _instance._elementsToRebuild?.forEach((el) => el.markNeedsBuild());
  return initCompleter.future;
}
  • 之后setWidth等方法都不需要懂了,因為都是拿上面的轉(zhuǎn)換系數(shù)去計算的,系數(shù)對了轉(zhuǎn)換自然就準確了。同時開發(fā)過程中也不需要做任何區(qū)分,該用.w的就用.w。我們看下.w等是如何通過擴展巧妙的把setWidth這些接口 做的輕量的。
extension SizeExtension on num {
  ///[ScreenUtil.setWidth]
  double get w => ScreenUtil().setWidth(this);
  ///[ScreenUtil.setHeight]
  double get h => ScreenUtil().setHeight(this);
  ///[ScreenUtil.radius]
  double get r => ScreenUtil().radius(this);
  ///[ScreenUtil.setSp]
  double get sp => ScreenUtil().setSp(this);
  ///smart size :  it check your value - if it is bigger than your value it will set your value
  ///for example, you have set 16.sm() , if for your screen 16.sp() is bigger than 16 , then it will set 16 not 16.sp()
  ///I think that it is good for save size balance on big sizes of screen
  double get sm => min(toDouble(), sp);
  ///屏幕寬度的倍數(shù)
  ///Multiple of screen width
  double get sw => ScreenUtil().screenWidth * this;
  ///屏幕高度的倍數(shù)
  ///Multiple of screen height
  double get sh => ScreenUtil().screenHeight * this;
  ///[ScreenUtil.setHeight]
  Widget get verticalSpace => ScreenUtil().setVerticalSpacing(this);
  ///[ScreenUtil.setVerticalSpacingFromWidth]
  Widget get verticalSpaceFromWidth =>
      ScreenUtil().setVerticalSpacingFromWidth(this);
  ///[ScreenUtil.setWidth]
  Widget get horizontalSpace => ScreenUtil().setHorizontalSpacing(this);
  ///[ScreenUtil.radius]
  Widget get horizontalSpaceRadius =>
      ScreenUtil().setHorizontalSpacingRadius(this);
  ///[ScreenUtil.radius]
  Widget get verticalSpacingRadius =>
      ScreenUtil().setVerticalSpacingRadius(this);
}
  • 資源適配,定義三個設(shè)備類型:大、中、小級別;然后在asset目錄下區(qū)分三套資源,命名規(guī)范區(qū)分下larger、medium、small即可。
  • 這個做法非常硬核,我目前也沒這個需求(O(∩_∩)O~。后續(xù)考慮渠道編譯,減少包體積,同時開發(fā)時也不用區(qū)分名稱。

寫在最后

以上方案,我在demo項目中驗證過是沒有問題的。接下來我希望能跟作者溝通下這個方案,看能否提pr合并進去。不然以后就沒辦法很輕松的享受到flutter_screenutil的更新迭代了。

期待Flutter桌面社區(qū)越來越豐富!更多關(guān)于Flutter桌面應(yīng)用多分辨率適配的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論