距離flutter_deer開源快3個月了,目前爲止收穫了1600+的Star,感謝你們的對此項目的承認支持。不過雖然表面看上去光鮮亮麗,但我知道仍是有不少不規範不合理的用法及寫法,爲了避免對初學者形成誤導做用,因此這期間我幾乎天天都在完善優化它(如今應該還不錯吧)。今天繼續分享一些在Flutter開發中須要注意的點,但願對你有所幫助。本篇的全部例子,都在我開源的flutter_deer中。但願Star、Fork支持,有問題建議能夠Issue。附上連接:https://github.com/simplezhli...html
本系列前兩篇:java
Flutter開發中的一些Tipsandroid
默認狀況下,Flutter是沒有進行多語言配置。因此不管咱們的手機系統環境是不是中文,一些Widget的文字都是英文顯示。好比常見的輸入框(TextField
)的操做菜單、日期選擇(showDatePicker
)上的年月日。github
既然沒有配置,那我咱們添加上便可。api
flutter_localizations: sdk: flutter
MaterialApp
中配置 localizationsDelegates
和 supportedLocales
兩個屬性。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') ] ); } }
這裏我只配置了中英文兩個語言的支持。有其餘語言的須要能夠自行添加。緩存
其中:Localized resources can be mixed
爲 YES 表示容許應用程序獲取框架庫內語言。性能優化
若是你完成了上述的配置,那麼在系統爲中文環境時,就會自動將英文替換爲中文。app
其實翻看flutter_localizations的源碼,你會發現它內置了多國語言的翻譯。若是你以爲文字不恰當,也能夠繼承對應的Localizations
去修改。
本問題詳細的代碼見:點擊查看框架
默認文字配置是ThemeData
經過Typography(platform: platform).black
方法獲取的。
好比咱們經常使用的Text
文字配置就是TextTheme
中的body1
。Typography
中按照Material的規範將語言分爲三大類別:
西歐、中歐、東歐和非洲大部分地區的語言一般用拉丁字母書寫。越南語是一個明顯的例外,雖然它使用了拉丁文書寫系統的本地化形式,但它的重音符號可能比西歐語言中的要高得多。希臘語和西里爾語的書寫系統與拉丁文很是類似。
語言腳本,須要額外的行高來容納較大的象形文字,包括南亞、東南亞和中東語言,如阿拉伯語、印地語、泰語和越南語。
須要額外的行高才能容納更大的象形文字的語言腳本,包括中文、日語和韓文。
那麼針對這三種類型的語言,默認的文字大小也會有調整。好比下圖中englishLike
與dense
的對比(點擊查看源碼):
能夠看到默認狀況下,中文、日文、韓文會比英文類的文字大一個字號。好比經常使用的Text
文字配置body1
在切換爲中文環境後,文字的默認大小變爲了15.0,然而在英文環境下是14.0。
這個問題也是我是在作完多語言配置後發現的一個問題,由於文字變大致使個別頁面形成了文字溢出組件。因此儘可能指定文字大小以免沒必要要的這類問題。
在Flutter中,加載本地圖片會存在一個加載過程。好比點擊圖標作圖標的切換時,那麼首次會發生閃動的狀況。尤爲是作相似引導頁這類需求是,經過左右滑動切換圖片時會發生比較明顯的白屏一閃而過。
解決方法很簡單,就是使用 precacheImage,它將圖像預存到圖像緩存中。若是圖像稍後被Image
、BoxDecation
或FadeInImage
使用,它會被加載得更快。
precacheImage(AssetImage("assets/logo"), context);
本問題詳細的代碼見:點擊查看
新建的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這個插件。
在書寫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
構造函數,更精細粒度的重建)debugFillProperties
)若是你以前已經寫了大量的方法建立返回widget的代碼,可使用Rémi Rousselet的functional_widget來改善這個問題。
StatelessWidget
。其實爲了不因刷新局部widget調用setState
而致使整個頁面刷新形成的性能損耗,咱們能夠將局部刷新的地方抽離爲StatefulWidget
。我曾經寫過一個頁面通過這樣的優化,耗時由25ms降到了6ms,所以控制刷新範圍是很必要的。Offstage
來作隱藏功能,雖然說它能夠隱藏指定的widget。可是它仍是會建立出對應的widget,只是放在了看不見的「後臺」。我以前就將一個CupertinoActivityIndicator()
這樣隱藏了起來,結果在PerformanceOverlay
中就看到頁面不斷在繪製。。。因此若是你須要隱藏widget,可使用 isGone ? const SizedBox() : CupertinoActivityIndicator()
這類三元運算符的方式處理。isolate
中處理,避免某些性能很差的設備在解析數據時形成的卡頓。詳細例子能夠查看文檔。這篇斷斷續續寫了半個月,終於完成了!我能夠安心的去參加GDD了。碼字不易,但願點贊支持!最後再次奉上Deer的Github地址,順手也能夠支持一波!