Flutter開發中的一些Tips(三)

距離flutter_deer開源快3個月了,目前爲止收穫了1600+的Star,感謝你們的對此項目的承認支持。不過雖然表面看上去光鮮亮麗,但我知道仍是有不少不規範不合理的用法及寫法,爲了避免對初學者形成誤導做用,因此這期間我幾乎天天都在完善優化它(如今應該還不錯吧)。

今天繼續分享一些在Flutter開發中須要注意的點,但願對你有所幫助。本篇的全部例子,都在我開源的flutter_deer中。但願Star、Fork支持,有問題建議能夠Issue。附上連接:https://github.com/simplezhli...html

本系列前兩篇:java

Flutter開發中的一些Tipsandroid

Flutter開發中的一些Tips(二)git

在這裏插入圖片描述

1.多語言配置(國際化)

默認狀況下,Flutter是沒有進行多語言配置。因此不管咱們的手機系統環境是不是中文,一些Widget的文字都是英文顯示。好比常見的輸入框(TextField)的操做菜單、日期選擇(showDatePicker)上的年月日。github

在這裏插入圖片描述

既然沒有配置,那我咱們添加上便可。api

  1. 在 pubspec.yaml 中添加依賴:
flutter_localizations:
    sdk: flutter
  1. MaterialApp 中配置 localizationsDelegatessupportedLocales兩個屬性。
import 'package:flutter_localizations/flutter_localizations.dart';

class MyApp extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Deer',
      home: SplashPage(),
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('zh', 'CH'),
        const Locale('en', 'US')
      ]
    );
  }
}

這裏我只配置了中英文兩個語言的支持。有其餘語言的須要能夠自行添加。緩存

  1. 若是是iOS平臺,還須要在Info.plist文件中作以下配置:

在這裏插入圖片描述

其中:Localized resources can be mixed 爲 YES 表示容許應用程序獲取框架庫內語言。性能優化

若是你完成了上述的配置,那麼在系統爲中文環境時,就會自動將英文替換爲中文。app

在這裏插入圖片描述

其實翻看flutter_localizations的源碼,你會發現它內置了多國語言的翻譯。若是你以爲文字不恰當,也能夠繼承對應的Localizations去修改。在這裏插入圖片描述
本問題詳細的代碼見:點擊查看框架

2.文字字號

默認文字配置是ThemeData經過Typography(platform: platform).black方法獲取的。

在這裏插入圖片描述

好比咱們經常使用的Text文字配置就是TextTheme中的body1Typography中按照Material的規範將語言分爲三大類別

  1. 英語類字體(englishLike)

西歐、中歐、東歐和非洲大部分地區的語言一般用拉丁字母書寫。越南語是一個明顯的例外,雖然它使用了拉丁文書寫系統的本地化形式,但它的重音符號可能比西歐語言中的要高得多。希臘語和西里爾語的書寫系統與拉丁文很是類似。

  1. 高字體(tall)

語言腳本,須要額外的行高來容納較大的象形文字,包括南亞、東南亞和中東語言,如阿拉伯語、印地語、泰語和越南語。

  1. 密集字體(dense)

須要額外的行高才能容納更大的象形文字的語言腳本,包括中文、日語和韓文。

那麼針對這三種類型的語言,默認的文字大小也會有調整。好比下圖中englishLikedense的對比(點擊查看源碼):

在這裏插入圖片描述
能夠看到默認狀況下,中文、日文、韓文會比英文類的文字大一個字號。好比經常使用的Text文字配置body1在切換爲中文環境後,文字的默認大小變爲了15.0,然而在英文環境下是14.0

這個問題也是我是在作完多語言配置後發現的一個問題,由於文字變大致使個別頁面形成了文字溢出組件。因此儘可能指定文字大小以免沒必要要的這類問題。

3.預先緩存圖片

在Flutter中,加載本地圖片會存在一個加載過程。好比點擊圖標作圖標的切換時,那麼首次會發生閃動的狀況。尤爲是作相似引導頁這類需求是,經過左右滑動切換圖片時會發生比較明顯的白屏一閃而過。

在這裏插入圖片描述

解決方法很簡單,就是使用 precacheImage,它將圖像預存到圖像緩存中。若是圖像稍後被ImageBoxDecationFadeInImage使用,它會被加載得更快。

precacheImage(AssetImage("assets/logo"), context);

本問題詳細的代碼見:點擊查看

4. 屏幕方向

新建的Flutter項目默認並無限制屏幕的橫豎屏,因此若是你的項目並無適配橫豎屏,須要限制某一方向。我以限制豎屏爲例:

Flutter方法:

void main(){
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown
  ]).then((_){
    runApp(MyApp());
  });
}

原生方法:

Android在android -> app -> src-> main -> AndroidManifest.xml中的activity標籤添加 screenOrientation屬性。

<activity
        ...
        android:screenOrientation="portrait">
    </activity>

iOS在Runner ->Info.plist中刪除UISupportedInterfaceOrientations中的 UIInterfaceOrientationLandscapeLeft與·UIInterfaceOrientationLandscapeRight。最終以下:

<key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
    </array>

上面的方法都是針對整個應用的。若是你但願部分頁面能夠橫屏或者豎屏,只能使用Flutter的方法在對應的頁面去指定方向。

不過Flutter這個方法在iOS端有點問題,它並不能強制屏幕旋轉(也就是屏幕當前爲豎屏,你指定頁面橫屏顯示,它並不會生效)。因此有這方面需求的同窗可使用flutter_orientation這個插件。

5.拆分widget

在書寫Flutter的頁面時,不免會嵌套的層級很深或者存在許多重複使用widget。因此通常咱們都會將一些widget抽離出來。抽離的方法有兩種,一種是直接抽成方法(函數)返回。一種是抽出一個自定義的widget來使用。我下面舉例說明一下:

class _TestPageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Test"),
      ),
      body: Column(
        children: <Widget>[
          const Text("Android", style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),),
          const Text("iOS", style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),),
          const Text("Flutter", style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),),
        ],
      ),
    );
  }
}

上面代碼中存在着三個樣式一致,只是文字不一樣的Text。咱們將它抽離出來(固然你也能夠直接抽離Column)。

使用第一種方式:

Widget _buildText(String text){
    return Text(text, style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),);
  }

使用方式:

Column(
    children: <Widget>[
      _buildText("Android"),
      _buildText("iOS"),
      _buildText("Flutter"),
    ],
  )

第二種方式:

class _MyText extends StatelessWidget {
  
  const _MyText(this.text, {Key key}) : super(key: key);
  
  final String text;
  
  @override
  Widget build(BuildContext context) {
    return Text(text, style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),);
  }
}

使用方式:

Column(
    children: <Widget>[
      const _MyText("Android"),
      const _MyText("iOS"),
      const _MyText("Flutter"),
    ],
  )

看起來是第一種很方便,但不知道你有沒有發現第一種方式沒法添加 const關鍵字。其實問題就出在了這裏。在我以前的博客中就有提到儘可能使用const關鍵字 來定義常量。那麼這是爲何呢?

咱們來再看一個小例子:
在這裏插入圖片描述

我直接調用上面抽出的widget,每次點擊按鈕setState刷新頁面,輸出widget的hashCode。
在這裏插入圖片描述
能夠看到使用了const 關鍵字修飾的widget,並不會重複建立。看來養成隨手添加const的好習慣會在無形中提高應用的性能,好比經常使用的Color、TextStyle、分隔塊咱們整合起來,直接調用就是很不錯的作法。
在這裏插入圖片描述

這個問題是由Provider的做者Rémi Rousselet提出的,What is the difference between functions and classes to create widgets?

做者得出的結論是:永遠不要使用方法返回的形式建立可重用的widget,始終將它們封裝到StatelessWidget中。 注意這個結論中的可重用

在上面的例子中,咱們的文字是固定的(可重用),這致使咱們能夠直接在widget上添加 const。實際中,咱們的展現的數據都是請求的並非固定的,咱們即便抽離出StatelessWidget,也沒法直接添加 const來使用。因此若是你的widget並沒有法重用,使用上述兩種方法的哪種效果都是同樣的。

上述的例子中,即便Text沒法使用const標記,可是Text中的TextStyle確能夠重用。這一切取決於你的widget拆分的顆粒度是否足夠合理,來儘量的避免這種性能上的浪費。

固然我更推薦StatelessWidget的方式。正如做者說的,它具有如下優勢:

  • 容許性能優化(const 構造函數,更精細粒度的重建)
  • 有熱重載
  • 集成到widget檢查器中(debugFillProperties
  • 能夠定義Key(關於Key的做用能夠看這裏的解答
  • 能夠方便的使用context
  • 規範全部widget以相同的方式使用(始終使用構造函數)
  • 能夠確保在兩個不一樣佈局之間切換時,正確的配置信息(函數可能重用一些之前的狀態)

若是你以前已經寫了大量的方法建立返回widget的代碼,可使用Rémi Rousselet的functional_widget來改善這個問題。

6.其餘

  • 上面有說道抽離出StatelessWidget。其實爲了不因刷新局部widget調用setState而致使整個頁面刷新形成的性能損耗,咱們能夠將局部刷新的地方抽離爲StatefulWidget。我曾經寫過一個頁面通過這樣的優化,耗時由25ms降到了6ms,所以控制刷新範圍是很必要的。
  • 通常狀況下不建議使用 Offstage來作隱藏功能,雖然說它能夠隱藏指定的widget。可是它仍是會建立出對應的widget,只是放在了看不見的「後臺」。我以前就將一個CupertinoActivityIndicator()這樣隱藏了起來,結果在PerformanceOverlay 中就看到頁面不斷在繪製。。。因此若是你須要隱藏widget,可使用 isGone ? const SizedBox() : CupertinoActivityIndicator()這類三元運算符的方式處理。
  • 能夠將數據解析放在 isolate 中處理,避免某些性能很差的設備在解析數據時形成的卡頓。詳細例子能夠查看文檔

這篇斷斷續續寫了半個月,終於完成了!我能夠安心的去參加GDD了。碼字不易,但願點贊支持!最後再次奉上Deer的Github地址,順手也能夠支持一波!

相關文章
相關標籤/搜索