談談我對 Flutter 未來發展 和 “巢狀地獄” 的淺顯看法

老孟Flutter發表於2020-06-23

談談我對 Flutter 未來發展 和 “巢狀地獄” 的淺顯看法

Flutter 未來發展

提到 Flutter 就不得不提到 Fuchsia 系統,這是一個尚未正式釋出的操作的系統,引用 Android 和 Chrome 的高階副總裁 Hiroshi Lockheimer 在一檔播客節目中對 Fuchsia 的介紹是:

不僅僅是手機和個人電腦,在物聯網的世界裡,越來越多的裝置需要作業系統、新的軟體執行環境等支援。我認為,在具有不同優勢和專業化的諸多作業系統中還存在很大的發展空間。Fuchsia 就是其中之一,所以,請繼續保持關注。

是的,Fuchsia 系統是為物聯網研發的作業系統,物聯網簡稱 IoT,現在全世界都在押注 IoT,包括華為、小米等國內公司。

那 Flutter 和 Fuchsia 又有什麼關係呢?

Flutter 是 Fuchsia 官方指定的唯一UI開發框架。

現在有很多物聯網作業系統 ,Fuchsia 就一定可以脫穎而出嗎?

不一定,未來的事情誰說的準呢,但在我看來 Fuchsia 是最有可能發展起來的物聯網作業系統,因為一個作業系統的發展除了本身優秀以外,最大的阻礙其實是生態,而 Fuchsia 在生態方面具有天然的優勢, 國外的一篇報導曾說:

Google 希望將 Android App 無縫移植到 Fuchsia 上,而且一直在做相關工作。

試想一下,一旦 Google 將 Android App 無縫移植到 Fuchsia 上,其他物聯網作業系統如何與之抗衡。

這裡引用 Google 公眾號底部的一句話送給大家:

預測未來不如創造未來

在跨平臺技術上 Flutter 還有很多競爭對手,比如 HTML5、React Native、Weex、快應用、小程式等,我曾在跨平臺技術發展簡介 中詳細說明了各個跨平臺技術的發展歷史及優缺點。

Flutter 的出現會終結其他跨平臺技術?我想不會的, React Native 發展了這麼多年也沒有完全乾掉 HTML5,應為 HTML5 有其獨特的應用場景,比如 營銷活動場景、新聞或者部落格詳情頁面等,這些場景非常適合 HTML5。因此 Flutter 也不可能終結其他跨平臺技術,總結一句話就是:

未來很長一段時間,將會是跨平臺技術共存的時代,但 Flutter 適用場景更為廣闊。

Flutter 巢狀地獄

現在網路上對 Flutter 吐槽最多大概就是 Flutter “巢狀地獄”寫法了,為什麼會出現這種現象?個人認為最大的原因就是目前大部分開源的 Flutter 專案都是這種巢狀寫法(包括我自己以前也是如此),導致後來的初學者認為這麼寫沒有問題,當專案越來越複雜時,這種巢狀寫法給專案的維護帶來了巨大的挑戰。下面說說如何避免這種巢狀寫法?

比如實現如下效果:

談談我對 Flutter 未來發展 和 “巢狀地獄” 的淺顯看法

巢狀地獄 的寫法:

@override
Widget build(BuildContext context) {
  return Column(
    children: <Widget>[
      Container(
        height: 45,
        child: Row(
          children: <Widget>[
            SizedBox(
              width: 30,
            ),
            Icon(
              Icons.notifications,
              color: Colors.blue,
            ),
            SizedBox(
              width: 30,
            ),
            Expanded(
              child: Text('訊息中心'),
            ),
            Container(
              padding: EdgeInsets.symmetric(horizontal: 10),
              decoration: BoxDecoration(
                  shape: BoxShape.rectangle,
                  borderRadius: BorderRadius.all(Radius.circular(50)),
                  color: Colors.red),
              child: Text(
                '2',
                style: TextStyle(color: Colors.white),
              ),
            ),
            SizedBox(
              width: 15,
            ),
          ],
        ),
      ),
      Divider(),
      //類似上面的佈局寫6個
    ],
  );
}
複製程式碼

上面還僅僅是第一項的佈局,下面還有7個,一個30多行程式碼,7個就是200多行的佈局程式碼,這還僅僅是佈局程式碼,如果加上邏輯,都不敢想象啊。

或許有一點封裝思想開發者會將每一個 Item封裝為一個方法,寫法如下:

_buildItem(IconData iconData, Color iconColor, String title, Widget widget) {
  return Container(
    height: 45,
    child: Row(
      children: <Widget>[
        SizedBox(
          width: 30,
        ),
        Icon(
          iconData,
          color: iconColor,
        ),
        SizedBox(
          width: 30,
        ),
        Expanded(
          child: Text('$title'),
        ),
        widget,
        SizedBox(
          width: 15,
        ),
      ],
    ),
  );
}

@override
Widget build(BuildContext context) {
  return Column(
    children: <Widget>[
      _buildItem(...),
      Divider(),
      _buildItem(...),
      Divider(),
      _buildItem(...),
      Divider(),
      _buildItem(...),
      Divider(),
      _buildItem(...),
      Divider(),
      _buildItem(...),
      Divider(),
    ],
  );
}
複製程式碼

這樣看起來好多了,基本解決了巢狀地獄問題,但這樣寫還存在一個非常大的問題-效能問題,一旦其中一個數字發生變化,整個頁面都要重建,Flutter 開發中非常重要的一個原則就是 儘可能少的重建元件,因此將上面封裝到方法中元件變為一個 Widget。

class SettingDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        _SettingItem(
          iconData: Icons.notifications,
          iconColor: Colors.blue,
          title: '訊息中心',
          suffix: _NotificationsText(
            text: '2',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.thumb_up,
          iconColor: Colors.green,
          title: '我贊過的',
          suffix: _Suffix(
            text: '121篇',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.grade,
          iconColor: Colors.yellow,
          title: '收藏集',
          suffix: _Suffix(
            text: '2個',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.shopping_basket,
          iconColor: Colors.yellow,
          title: '已購小冊',
          suffix: _Suffix(
            text: '100個',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.account_balance_wallet,
          iconColor: Colors.blue,
          title: '我的錢包',
          suffix: _Suffix(
            text: '10萬',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.location_on,
          iconColor: Colors.grey,
          title: '閱讀過的文章',
          suffix: _Suffix(
            text: '1034篇',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.local_offer,
          iconColor: Colors.grey,
          title: '標籤管理',
          suffix: _Suffix(
            text: '27個',
          ),
        ),
      ],
    );
  }
}

class _SettingItem extends StatelessWidget {
  const _SettingItem(
      {Key key, this.iconData, this.iconColor, this.title, this.suffix})
      : super(key: key);

  final IconData iconData;
  final Color iconColor;
  final String title;
  final Widget suffix;

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 45,
      child: Row(
        children: <Widget>[
          SizedBox(
            width: 30,
          ),
          Icon(iconData,color: iconColor,),
          SizedBox(
            width: 30,
          ),
          Expanded(
            child: Text('$title'),
          ),
          suffix,
          SizedBox(
            width: 15,
          ),
        ],
      ),
    );
  }
}

class _NotificationsText extends StatelessWidget {
  final String text;

  const _NotificationsText({Key key, this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 10),
      decoration: BoxDecoration(
          shape: BoxShape.rectangle,
          borderRadius: BorderRadius.all(Radius.circular(50)),
          color: Colors.red),
      child: Text(
        '$text',
        style: TextStyle(color: Colors.white),
      ),
    );
  }
}

class _Suffix extends StatelessWidget {
  final String text;

  const _Suffix({Key key, this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(
      '$text',
      style: TextStyle(color: Colors.grey.withOpacity(.5)),
    );
  }
}
複製程式碼

封裝為一個個單獨的小元件,將有變化的元件儘量單獨封裝,這樣就不會重建整個控制元件樹,增強了可讀性和可維護性,而且對效能有很大的提升。

最後總結一句:

雖然 Flutter 一切皆是元件,但並不代表一切都要寫在元件中。

當然這僅僅是我個人的看法,如果您有更好的方法歡迎一起討論,從我做起,規範寫法,為 Flutter 發展貢獻做出一點微不足道的貢獻。

交流

老孟Flutter部落格地址(330個控制元件用法):laomengit.com

歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】:

談談我對 Flutter 未來發展 和 “巢狀地獄” 的淺顯看法
談談我對 Flutter 未來發展 和 “巢狀地獄” 的淺顯看法

相關文章