Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

AndroidTraveler發表於2019-03-10

Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

背景

在進行 Flutter UI 開發的時候,控制檯報出了下面錯誤:

flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY >╞═════════════════════════════════════════════════════════ flutter: The following message was thrown during layout: flutter: A RenderFlex overflowed by 826 pixels on the right.

介面的體現就是黃色區域。

這裡的程式碼是在上一篇的基礎上返回下面的 Widget:

return Row(
      children: <Widget>[
        Image.network(
            'https://upload-images.jianshu.io/upload_images/5361063-cfad13c672a06084.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240')
      ],
    );
複製程式碼

模擬器效果如下:

Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

思考

其實一般遇到這種情況,都應該考慮一下是否這樣佈局合理。

上面這個我們只是舉個例子,因為一般如果只有一張圖片,是不需要給他套一層 Row 的。

因為情況比較多,這裡假設有時候真的就需要這麼處理,怎麼辦?

解決方法

如果你某個 Widget 出現了上面的問題,而且真的不是佈局問題,而是真的就是有可能出現這種情況,但是你不希望 debug 模式顯示這個錯誤,那麼可以給他套一層 Expanded。

官網有如下說明:

A widget that expands a child of a RowColumn, or Flex.

Using an Expanded widget makes a child of a RowColumn, or Flex expand to fill the available space in the main axis (e.g., horizontally for a Row or vertically for a Column). If multiple children are expanded, the available space is divided among them according to the flex factor.

所以對於 Row、Column 以及 Flex 都可以用 Expanded 來解決子元件報上面錯誤問題。

所以這裡可以修改為

return Row(
      children: <Widget>[
        Expanded(
          child: Image.network(
              'https://upload-images.jianshu.io/upload_images/5361063-cfad13c672a06084.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240'),,
        )
      ],
    );
複製程式碼

效果如下:

Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

Expanded 妙用

Expanded 除了可以解決上面的問題之外,還有一個妙用就是比例佈局。

什麼意思呢?

我們寫下程式碼,然後給下效果圖你就懂了。

return Column(
      children: <Widget>[
        Expanded(
          flex: 1,
          child: Container(
            color: Colors.red,
          ),
        ),
        Expanded(
          flex: 1,
          child: Container(
            color: Colors.blue,
          ),
        ),
        Expanded(
          flex: 1,
          child: Container(
            color: Colors.grey,
          ),
        ),
      ],
    );
複製程式碼

效果圖如下:

Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

可以看出 Expanded 的 flex 屬性會按比例佈局。

Sample

我們來實現一個簡單的 UI。

如下圖,可以看到是一個網路錯誤時,點選重試的頁面。

Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

假設你之前習慣了 sketch 邊距開發,你看到這個頁面,就直接根據邊距進行開發,寫出了下面的程式碼。

實現方式一:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        backgroundColor: Color(0xFFF0F1F0),
        body: Center(
          child: _buildWidget(),
        ),
      ),
    );
  }

  Widget _buildWidget() {
    return Container(
      child: Column(
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.only(left: 97.0, right: 97.0, top: 125),
            child: Image.asset('assets/images/refresh.png', width: 49, height: 44,),
          ),
          SizedBox(
            height: 42.0,
          ),
          FlatButton(
              padding: const EdgeInsets.symmetric(horizontal: 50.0),
              //注意這裡 alpha 最大值是 255, sketch 上面最大值是 100
              color: Color.fromARGB(255, 13, 46, 172),
              //這裡 onPressed 不能為 null,如果寫 null 會怎樣,大家可以試下~
              onPressed: (){},
              child: Text(
                //演示而已,實際開發需要多語言
                '重新整理',
                style: TextStyle(
                    color: Colors.white,
                    fontSize: 12,
                    fontWeight: FontWeight.w600
                ),
              )
          )
        ],
      ),
    );
  }

}
複製程式碼

效果如下:

Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

你會發現這種實現方式的適配性會很差,而且可能出現上面的問題。

因此我們看下使用 Expanded 如何實現。

觀察一下,我們發現介面大概可以分成 3 塊。

每一塊佔的比例差不多,因此可以如下實現。

實現方式二:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        backgroundColor: Color(0xFFF0F1F0),
        body: Center(
          child: _buildWidget(),
        ),
      ),
    );
  }

  Widget _buildWidget() {
    return Container(
      child: Column(
        children: <Widget>[
          Expanded(
            flex: 1,
            child: Container(),
          ),
          Image.asset('assets/images/refresh.png', width: 49, height: 44,),
          SizedBox(
            height: 42.0,
          ),
          FlatButton(
              padding: const EdgeInsets.symmetric(horizontal: 50.0),
              //注意這裡 alpha 最大值是 255, sketch 上面最大值是 100
              color: Color.fromARGB(255, 13, 46, 172),
              //這裡 onPressed 不能為 null,如果寫 null 會怎樣,大家可以試下~
              onPressed: (){},
              child: Text(
                //演示而已,實際開發需要多語言
                '重新整理',
                style: TextStyle(
                    color: Colors.white,
                    fontSize: 12,
                    fontWeight: FontWeight.w600
                ),
              )
          ),
          Expanded(
            flex: 1,
            child: Container(),
          ),
        ],
      ),
    );
  }

}
複製程式碼

效果如下:

Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

其實,看到上面用到的 Column,我們可以直接利用上次說到的一個屬性,就可以很巧妙的實現適配。

實現方式三:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Color(0xFFF0F1F0),
        body: Center(
          child: _buildWidget(),
        ),
      ),
    );
  }

  Widget _buildWidget() {
    return Container(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Image.asset(
            'assets/images/refresh.png',
            width: 49,
            height: 44,
          ),
          SizedBox(
            height: 42.0,
          ),
          FlatButton(
              padding: const EdgeInsets.symmetric(horizontal: 50.0),
              //注意這裡 alpha 最大值是 255, sketch 上面最大值是 100
              color: Color.fromARGB(255, 13, 46, 172),
              //這裡 onPressed 不能為 null,如果寫 null 會怎樣,大家可以試下~
              onPressed: () {},
              child: Text(
                //演示而已,實際開發需要多語言
                '重新整理',
                style: TextStyle(
                    color: Colors.white,
                    fontSize: 12,
                    fontWeight: FontWeight.w600),
              )),
        ],
      ),
    );
  }
}
複製程式碼

效果如下:

Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

其中實現方式一隻是說明,實際開發不推薦。

實現方式二和實現方式三都可以,推薦方式三。

相關程式碼及 sketch 圖都放到了 GitHub 倉庫?:
github.com/nesger/Flut…

其中分支 feature/ui-refresh-one 是實現方式一。
分支 feature/ui-refresh-two 是實現方式二。
分支 feature/ui-refresh-three 是實現方式三。

這裡按鈕寬度和高度沒有指定,大家可以根據情況確定是否指定哈~

總之就是:

實現方式千萬條,合適第一條。 適配不精確,測試兩行淚。

更多閱讀:
Flutter 即學即用系列部落格——01 環境搭建
Flutter 即學即用系列部落格——02 一個純 Flutter Demo 說明
Flutter 即學即用系列部落格——03 在舊有專案引入 Flutter
Flutter 即學即用系列部落格——04 Flutter UI 初窺
Flutter 即學即用系列部落格——05 StatelessWidget vs StatefulWidget
Flutter 即學即用系列部落格——06 超實用 Widget 集錦

Flutter 即學即用系列部落格——07 RenderFlex overflowed 引發的思考

相關文章