亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

前言

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

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

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

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

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

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

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

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

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

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

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

  • 根據(jù)傳入的設(shè)計(jì)稿尺寸,與設(shè)備的邏輯像素尺寸的比值作為縮放倍數(shù);
  • 開發(fā)者設(shè)置的尺寸都會(huì)去乘以對應(yīng)的縮放倍數(shù),從而實(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è)計(jì)稿的高度
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 這個(gè)庫在移動(dòng)端使用是完全沒有問題的。手機(jī)尺寸雖說層出不窮,但是也遵循瘦長的長方形規(guī)則,最重要的是應(yīng)用默認(rèn)都是全屏的,那么上面第3點(diǎn)獲取到的應(yīng)用窗口高度screenHeight和設(shè)備的大小剛好是完全吻合的。從而計(jì)算出的縮放比(設(shè)計(jì)稿尺寸/設(shè)備的尺寸 = 縮放比值)是偏差不大的。
我們在Android的桌面應(yīng)用中,這個(gè)庫也可以支持各種設(shè)備。

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

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

通過實(shí)踐我們也驗(yàn)證了flutter_screenutil在桌面端的適配基本不起作用,且還會(huì)造成不少問題,比如:第一次運(yùn)行字體都會(huì)偏大;當(dāng)有多個(gè)擴(kuò)展屏的時(shí)候,主副屏切換有bug。

桌面端解決方案

一、需求分析

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

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

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

二、實(shí)現(xiàn)原理

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

  • 在構(gòu)造函數(shù)上,我加了一個(gè)參數(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í)的設(shè)備屏幕像素,這個(gè)是真實(shí)的屏幕像素,跟應(yīng)用窗口沒關(guān)系;然后可以計(jì)算出應(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等方法都不需要懂了,因?yàn)槎际悄蒙厦娴霓D(zhuǎn)換系數(shù)去計(jì)算的,系數(shù)對了轉(zhuǎn)換自然就準(zhǔn)確了。同時(shí)開發(fā)過程中也不需要做任何區(qū)分,該用.w的就用.w。我們看下.w等是如何通過擴(kuò)展巧妙的把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);
}
  • 資源適配,定義三個(gè)設(shè)備類型:大、中、小級(jí)別;然后在asset目錄下區(qū)分三套資源,命名規(guī)范區(qū)分下larger、medium、small即可。
  • 這個(gè)做法非常硬核,我目前也沒這個(gè)需求(O(∩_∩)O~。后續(xù)考慮渠道編譯,減少包體積,同時(shí)開發(fā)時(shí)也不用區(qū)分名稱。

寫在最后

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

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

相關(guān)文章

最新評論