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:网络

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

思考

其实通常遇到这种状况,都应该考虑一下是否这样布局合理。ide

上面这个咱们只是举个例子,由于通常若是只有一张图片,是不须要给他套一层 Row 的。布局

由于状况比较多,这里假设有时候真的就须要这么处理,怎么办?测试

解决方法

若是你某个 Widget 出现了上面的问题,并且真的不是布局问题,而是真的就是有可能出现这种状况,可是你不但愿 debug 模式显示这个错误,那么能够给他套一层 Expanded。flex

官网有以下说明:ui

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 仓库👇:
https://github.com/nesger/FlutterSample

其中分支 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 集锦

相关文章
相关标签/搜索