Flutter屏幕適配

Flutter屏幕適配

目前移動端的設備已經很是多,而且不一樣的設備手機屏幕也不相同。前端

目前作移動端開發都要針對不一樣的設備進行必定的適配,不管是移動原生開發、小程序、H5頁面。vue

Flutter中如何針對不一樣的手機屏幕來進行適配呢?咱們一塊兒來聊聊這個話題。web

一. Flutter單位

1.1. Flutter中的單位

在進行Flutter開發時,咱們一般不須要傳入尺寸的單位,那麼Flutter使用的是什麼單位呢?算法

  • Flutter使用的是相似於iOS中的點pt,也就是point。
  • 因此咱們常常說iPhone6的尺寸是375x667,可是它的分辨率實際上是750x1334。
  • 由於iPhone6的dpr(devicePixelRatio)是2.0,iPhone6plus的dpr是3.0
iPhone設備參數
iPhone設備參數

在Flutter開發中,咱們使用的是對應的邏輯分辨率小程序

1.2. Flutter設備信息

獲取屏幕上的一些信息,能夠經過MediaQuery:api

// 1.媒體查詢信息
final mediaQueryData = MediaQuery.of(context);  // 2.獲取寬度和高度 final screenWidth = mediaQueryData.size.width; final screenHeight = mediaQueryData.size.height; final physicalWidth = window.physicalSize.width; final physicalHeight = window.physicalSize.height; final dpr = window.devicePixelRatio; print("屏幕width:$screenWidth height:$screenHeight"); print("分辨率: $physicalWidth - $physicalHeight"); print("dpr: $dpr");  // 3.狀態欄的高度 // 有劉海的屏幕:44 沒有劉海的屏幕爲20 final statusBarHeight = mediaQueryData.padding.top; // 有劉海的屏幕:34 沒有劉海的屏幕0 final bottomHeight = mediaQueryData.padding.bottom; print("狀態欄height: $statusBarHeight 底部高度:$bottomHeight"); 複製代碼

獲取一些設備相關的信息,可使用官方提供的一個庫:數據結構

dependencies:
 device_info: ^0.4.2+1 複製代碼

二. 適配方案

2.1. 適配概述

假如咱們有下面這樣一段代碼:app

  • 在屏幕中間顯示一個200*200的Container
  • Container中有一段文字是30
class HYHomePage extends StatelessWidget {
 @override  Widget build(BuildContext context) {  return Scaffold(  appBar: AppBar(  title: Text("首頁"),  ),  body: Center(  child: Container(  width: 200,  height: 200,  color: Colors.red,  alignment: Alignment.center,  child: Text("Hello World", style: TextStyle(fontSize: 30, color: Colors.white),),  ),  ),  );  } } 複製代碼

上面的代碼在不一樣屏幕上會有不一樣的表現:less

  • 很明顯,若是按照上面的規則,在iPhone5上面,尺寸過大,在iPhone6plus上面尺寸太小編輯器

  • 在開發中,咱們應該能夠根據不一樣的屏幕來完成尺寸的縮放

不一樣屏幕表現
不一樣屏幕表現

在前端開發中,針對不一樣的屏幕常見的適配方案有下面幾種:

  • rem:
    • rem是給根標籤(HTML標籤)設置一個字體大小;
    • 可是不一樣的屏幕要動畫設置不一樣的字體大小(能夠經過媒體查詢,也能夠經過js動態計算);
    • 其它全部的單位都使用rem單位(相對於根標籤);
  • vw、wh:
    • vw和vh是將屏幕(視口)分紅100等份,一個1vw至關因而1%的大小;
    • 其它全部的單位都使用vw或wh單位;
  • rpx:
    • rpx是小程序中的適配方案,它將750px做爲設計稿,1rpx=屏幕寬度/750;
    • 其它全部的單位都使用rpx單位;

這裏我採用小程序的rpx來完成Flutter的適配

2.2. rpx適配

小程序中rpx的原理是什麼呢?

  • 不論是什麼屏幕,統一分紅750份
  • 在iPhone5上:1rpx = 320/750 = 0.4266 ≈ 0.42px
  • 在iPhone6上: 1rpx = 375/750 = 0.5px
  • 在iPhone6plus上:1rpx = 414/750 = 0.552px
rpx的對應關係
rpx的對應關係

那麼咱們就能夠經過上面的計算方式,算出一個rpx,再將本身的size和rpx單位相乘便可:

  • 好比100px的寬度:100 * 2 * rpx
  • 在iPhone5上計算出的結果是84px
  • 在iPhone6上計算出的結果是100px
  • 在iPhone6plus上計算出的結果是110.4px

咱們本身來封裝一個工具類:

  • 工具類須要進行初始化,傳入context
    • 能夠經過傳入context,利用媒體查詢獲取屏幕的寬度和高度
    • 也能夠傳入一個可選的參數,以什麼尺寸做爲設計稿
class HYSizeFit {
 static MediaQueryData _mediaQueryData;  static double screenWidth;  static double screenHeight;  static double rpx;  static double px;   static void initialize(BuildContext context, {double standardWidth = 750}) {  _mediaQueryData = MediaQuery.of(context);  screenWidth = _mediaQueryData.size.width;  screenHeight = _mediaQueryData.size.height;  rpx = screenWidth / standardWidth;  px = screenWidth / standardWidth * 2;  }   // 按照像素來設置  static double setPx(double size) {  return HYSizeFit.rpx * size * 2;  }   // 按照rxp來設置  static double setRpx(double size) {  return HYSizeFit.rpx * size;  } } 複製代碼

初始化HYSizeFit類的屬性:

  • 注意:必須在已經有MaterialApp的Widget中使用context,不然是無效的
class HYHomePage extends StatelessWidget {
 @override  Widget build(BuildContext context) {  // 初始化HYSizeFit  HYSizeFit.initialize(context);  return null;  } } 複製代碼

使用rpx來完成屏幕適配:

class HYHomePage extends StatelessWidget {
 @override  Widget build(BuildContext context) {  HYSizeFit.initialize(context);  return Scaffold(  appBar: AppBar(  title: Text("首頁"),  ),  body: Center(  child: Container(  width: HYSizeFit.setPx(200),  height: HYSizeFit.setRpx(400),  color: Colors.red,  alignment: Alignment.center,  child: Text("Hello World", style: TextStyle(fontSize: HYSizeFit.setPx(30), color: Colors.white),),  ),  ),  );  } } 複製代碼

咱們來看一下實現效果:

rpx適配方案
rpx適配方案

2.3. 最佳實踐

若是每次咱們須要將如今的寬度或者高度,去使用HYSizeFit.setPx(200)或者HYSizeFit.setRpx(400)相似的方式去適配,顯然看起來很是麻煩。

有沒有更好的方案能夠實現了?好比 200.px或者400.rpx,很是的清晰簡潔

固然能夠,咱們須要依賴Dart語言的一個特性:extension

  • Dart從2.7.0開始,能夠經過extension來給現有的類進行擴展(事實上Swift裏面也有)
  • 對現有的類包括:自定義的類、第三方庫的類、系統的類

好比咱們如今對String類型擴展:

  • 擴展一個parseInt的方法,固然內部調用的是int.parse(this),只是調用者變成了String自己
// 步驟一:擴展代碼
extension NumberParsing on String {  int parseInt() {  return int.parse(this);  }  // ··· }  // 步驟二:調用代碼 // 導入擴展類對應的模塊 import 'string_apis.dart'; // 使用裏面的方法 print('42'.padLeft(5)); // 使用String原有的方法 print('42'.parseInt()); // 使用String擴展的方法 複製代碼

顯然,數字(好比200、200.0)有對應的包裝類int、double,咱們能夠對其進行擴展:

1.對int類型擴展

import '../shared/size_fit.dart';
 extension IntFit on int {  double get px {  return HYSizeFit.setPx(this.toDouble());  }   double get rpx {  return HYSizeFit.setRpx(this.toDouble());  } } 複製代碼

2.對double類型擴展

import '../shared/size_fit.dart';
 extension DoubleFit on double {  double get px {  return HYSizeFit.setPx(this);  }   double get rpx {  return HYSizeFit.setRpx(this);  } } 複製代碼

如何使用了?

import './extension/double_extension.dart';
import './extension/int_extension.dart';  print(200.px); // 在不一樣屏幕下200px是不一樣的值 print(400.rpx); // 在不一樣屏幕下400rpx是不一樣的值 複製代碼

備註:全部內容首發於公衆號,以後除了Flutter也會更新其餘技術文章,TypeScript、React、Node、uniapp、mpvue、數據結構與算法等等,也會更新一些本身的學習心得等,歡迎你們關注

公衆號
公衆號
相關文章
相關標籤/搜索