Flutter 即學即用系列博客——07 RenderFlex overflowed 引起的思考

背景

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

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

界面的體現就是黃色區域。github

這裏的代碼是在上一篇的基礎上返回下面的 Widget:bash

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

模擬器效果以下:網絡

思考

其實通常遇到這種狀況,都應該考慮一下是否這樣佈局合理。less

上面這個咱們只是舉個例子,由於通常若是隻有一張圖片,是不須要給他套一層 Row 的。ide

由於狀況比較多,這裏假設有時候真的就須要這麼處理,怎麼辦?佈局

解決方法

若是你某個 Widget 出現了上面的問題,並且真的不是佈局問題,而是真的就是有可能出現這種狀況,可是你不但願 debug 模式顯示這個錯誤,那麼能夠給他套一層 Expanded。測試

官網有以下說明:flex

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'),,
        )
      ],
    );
複製代碼

效果以下:

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,
          ),
        ),
      ],
    );
複製代碼

效果圖以下:

能夠看出 Expanded 的 flex 屬性會按比例佈局。

Sample

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

以下圖,能夠看到是一個網絡錯誤時,點擊重試的頁面。

假設你以前習慣了 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
                ),
              )
          )
        ],
      ),
    );
  }

}
複製代碼

效果以下:

你會發現這種實現方式的適配性會不好,並且可能出現上面的問題。

所以咱們看下使用 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(),
          ),
        ],
      ),
    );
  }

}
複製代碼

效果以下:

其實,看到上面用到的 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),
              )),
        ],
      ),
    );
  }
}
複製代碼

效果以下:

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

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

相關代碼及 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 集錦

相關文章
相關標籤/搜索