Flutter完整開發(fā)實(shí)戰(zhàn)詳解(八、 實(shí)用技巧與填坑)

作為系列文章的第八篇,本篇是主要講述 Flutter 開發(fā)過程中的實(shí)用技巧,讓你少走彎路少掉坑,全篇屬于很干的干貨總結(jié),以實(shí)用為主,算是在深入原理過程中穿插的實(shí)用篇章。

文章匯總地址:

Flutter 完整實(shí)戰(zhàn)實(shí)戰(zhàn)系列文章專欄

Flutter 番外的世界系列文章專欄

1、Text 的 TextOverflow.ellipsis 不生效

有時(shí)候我們?yōu)?Text 設(shè)置 ellipsis ,卻發(fā)現(xiàn)并沒有生效,而是出現(xiàn)如下圖左邊提示 overflowed 的警告。

其實(shí)大部分時(shí)候,這是 Text 內(nèi)部的 RenderParagraph 在判斷 final bool didOverflowWidth = size.width < textSize.width; 時(shí), size.widthtextSize.width 是相等導(dǎo)致的。

所以你需要給 Text 設(shè)置一個(gè) Container 之類的去約束它的大小,或者是 Row 中通過 Expanded + Container 去約束你的 Text,如果不知道于應(yīng)該多大,可以通過 LayoutBuilder 設(shè)置。

請無視圖片

2、獲取控件的大小和位置

看過第六篇的同學(xué)應(yīng)該知道, 我們可以用 GlobalKey ,通過 key 去獲取到控件對象的 BuildContext,而前面我們也說過 BuildContext 的實(shí)現(xiàn)其實(shí)是 Element ,而 Element 持有 RenderObject 。So,我們知道的 RenderObject ,實(shí)際上獲取到的就是 RenderBox ,那么通過 RenderBox 我們就只大小和位置了:

  showSizes() {
    RenderBox renderBoxRed = fileListKey.currentContext.findRenderObject();
    print(renderBoxRed.size);
  }

  showPositions() {
    RenderBox renderBoxRed = fileListKey.currentContext.findRenderObject();
    print(renderBoxRed.localToGlobal(Offset.zero));
  }

3、獲取狀態(tài)欄高度和安全布局

如果你看過 MaterialApp 的源碼,你應(yīng)該會看到它的內(nèi)部是一個(gè) WidgetsApp ,而 WidgetsApp 內(nèi)有一個(gè) MediaQuery,熟悉它的朋友知道我們可以通過 MediaQuery.of(context).size 去獲取屏幕大小。

其實(shí) MediaQuery 是一個(gè) InheritedWidget ,它有一個(gè)叫 MediaQueryData 的參數(shù),這個(gè)參數(shù)是通過如下圖設(shè)置的,再通過源碼我們知道,一般情況下 MediaQueryDatapaddingtop 就是狀態(tài)欄的高度。

所以我們可以通過 MediaQueryData.fromWindow(WidgetsBinding.instance.window).padding.top 獲取到狀態(tài)欄高度,當(dāng)然有時(shí)候可能需要考慮 viewInsets 參數(shù)。

image

至于 AppBar 的高度,默認(rèn)是 Size.fromHeight(kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),kToolbarHeight 是一個(gè)固定數(shù)據(jù),當(dāng)然你可以通過實(shí)現(xiàn) PreferredSizeWidget 去自定義 AppBar

同時(shí)你可能會發(fā)現(xiàn),有時(shí)候在布局時(shí)發(fā)現(xiàn)布局位置不正常,居然是從狀態(tài)欄開始計(jì)算,這時(shí)候你需要用 SafeArea 嵌套下,至于為什么,看源碼你就會發(fā)現(xiàn) MediaQueryData 的存在。

4、設(shè)置狀態(tài)欄顏色和圖標(biāo)顏色

簡單的可以通過 AppBarbrightness 或者 ThemeData 去設(shè)置狀態(tài)欄顏色。

但是如果你不想用 AppBar ,那么你可以嵌套 AnnotatedRegion<SystemUiOverlayStyle> 去設(shè)置狀態(tài)欄樣式,通過 SystemUiOverlayStyle 就可以快速設(shè)置狀態(tài)欄和底部導(dǎo)航欄的樣式。

同時(shí)你還可以通過 SystemChrome.setSystemUIOverlayStyle 去設(shè)置,前提是你沒有使用 AppBar 。需要注意的是,所有狀態(tài)欄設(shè)置是全局的, 如果你在 A 頁面設(shè)置后,B 頁面沒有手動設(shè)置或者使用 AppBar ,那么這個(gè)設(shè)置將直接呈現(xiàn)在 B 頁面。

5、系統(tǒng)字體縮放

現(xiàn)在的手機(jī)一般都提供字體縮放,這給應(yīng)用開發(fā)的適配上帶來一定工作量,所以大多數(shù)時(shí)候我們會選擇禁止應(yīng)用跟隨系統(tǒng)字體縮放。

在 Flutter 中字體縮放也是和 MediaQueryDatatextScaleFactor 有關(guān)。所以我們可以在需要的頁面,通過最外層嵌套如下代碼設(shè)置,將字體設(shè)置為默認(rèn)不允許縮放。

    MediaQuery(
      data: MediaQueryData.fromWindow(WidgetsBinding.instance.window).copyWith(textScaleFactor: 1),
      child: new Container(),
    );

6、Margin 和 Padding

在使用 Container 的時(shí)候我們經(jīng)常會使用到 marginpadding 參數(shù),其實(shí)在上一篇我們已經(jīng)說過, Container 其實(shí)只是對各種布局的封裝,內(nèi)部的 marginpadding 其實(shí)是通過 Padding 實(shí)現(xiàn)的,而 Padding 不支持負(fù)數(shù),所以如果你需要用到負(fù)數(shù)的情況下,推薦使用 Transform 。

  Transform(
      transform: Matrix4.translationValues(10, -10, 0),
      child: new Container(),
    );

7、控件圓角裁剪

日常開發(fā)中我們大致上會使用兩種圓角方案:

  • 一種是通過 Decoration 的實(shí)現(xiàn)類 BoxDecoration 去實(shí)現(xiàn)。
  • 一種是通過 ClipRRect 去實(shí)現(xiàn)。

其中 BoxDecoration 一般應(yīng)用在 DecoratedBox 、 Container 等控件,這種實(shí)現(xiàn)一般都是直接 Canvas 繪制時(shí),針對當(dāng)前控件的進(jìn)行背景圓角化,并不會影響其 child 。這意味著如果你的 child 是圖片或者也有背景色,那么很可能圓角效果就消失了。

ClipRRect 的效果就是會影響 child 的,具體看看其如下的 RenderObject 源碼可知。

image

8、PageView

如果你在使用 TarBarView ,并且使用了 KeepAlive 的話,那么我推薦你直接使用 PageView 。因?yàn)槟壳暗?1.2 的版本,在 KeepAlive 的 狀態(tài)下,跨兩個(gè)頁面以上的 Tab 直接切換, TarBarView 會導(dǎo)致頁面的 dispose 再重新 initState。盡管 TarBarView 內(nèi)也是封裝了 PageView + TabBar 。

你可以直接使用 PageView + TabBar 去實(shí)現(xiàn),然后 tab 切換時(shí)使用 _pageController.jumpTo(MediaQuery.of(context).size.width * index); 可以避免一些問題。當(dāng)然,這時(shí)候損失的就是動畫效果了。事實(shí)上 TarBarView 也只是針對 PageView + TabBar 做了一層封裝。

除了這個(gè),其實(shí)還有第二種做法,使用如下方 PageStorageKey 保持頁面數(shù)狀態(tài),但是因?yàn)樗?save and restore values ,所以的頁面的 dispose 再重新 initState 方法,每次都會被調(diào)用。

    return new Scaffold(
      key: new PageStorageKey<your value type>(your value)
    )

9、懶加載

Flutter 中通過 FutureBuilder 或者 StreamBuilder 可以和簡單的實(shí)現(xiàn)懶加載,通過 future 或者 stream “異步” 獲取數(shù)據(jù),之后通過 AsyncSnapshot 的 data 再去加載數(shù)據(jù),至于流和異步的概念,以后再展開吧。

10、Android 返回鍵回到桌面

Flutter 官方已經(jīng)為你提供了 android_intent 插件了,這種情況下,實(shí)現(xiàn)回到桌面可以如下簡單實(shí)現(xiàn):

  Future<bool> _dialogExitApp(BuildContext context) async {
    if (Platform.isAndroid) {
      AndroidIntent intent = AndroidIntent(
        action: 'android.intent.action.MAIN',
        category: "android.intent.category.HOME",
      );
      await intent.launch();
    }

    return Future.value(false);
  }
·····
 return WillPopScope(
      onWillPop: () {
        return _dialogExitApp(context);
      },
      child:xxx);

自此,第八篇終于結(jié)束了!(///▽///)

資源推薦

完整開源項(xiàng)目推薦:
我們還會再見嗎?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容