Flutter 研习系列三

2021/4/18 posted in  Flutter

理论

布局类型 布局 Widget 备注
线性布局 Row, Column
弹性布局 Flex, Expanded
流式布局 Wrap, Flow
层叠布局 Stack, Positioned

布局原则

  1. 上层(父级) widget 向下层(子级) widget 传递约束条件
  2. 下层(子级) widget 向上层(父级) widget 传递大小信息
  3. 上层(父级) widget 决定下层(子级) widget 的位置

布局实践

目标页面(RN 面板)

IMG_A0A50E240E3D-1

原始布局结构


瞎 xx 乱搞成果

Simulator Screen Shot - iPhone 12 - 2021-04-20 at 16.09.45

Column

上图中的最外层使用 Column 进行布局

Column(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          AVFunctionInlineBar(),
          RenderArea(),
          AVFunctionBar(),
          _FunctionArea(),
        ],
      ),

Row

顶部工具栏使用 Row 配合 Expanded 完成布局。

ConstrainedBox(
      constraints: BoxConstraints(minWidth: double.infinity, maxHeight: 50.0),
      child: Container(
        padding: EdgeInsets.all(5),
        child: Row(
          verticalDirection: VerticalDirection.down,
          children: [
            SizedBox(
              width: 50,
              height: 30,
              child: TextButton(
                onPressed: () {},
                child: Text("高清"),
              ),
            ),
            SizedBox(
              width: 30,
              height: 40,
              child: Image.asset('images/music.png'),
            ),
            Expanded(
              child: Text(
                '温度 / 湿度',
                textAlign: TextAlign.center,
              ),
            ),
            SizedBox(
              width: 40,
              height: 40,
              child: TextButton(
                onPressed: () {},
                child: Image.asset('images/music.png'),
              ),
            ),
            SizedBox(
              width: 40,
              height: 40,
              child: TextButton(
                onPressed: () {},
                child: Image.asset('images/music.png'),
              ),
            ),
          ],
        ),
        color: Colors.green,
      ),
    );

Wrap


功能列表部分使用 Wrap 和 Column 实现布局

Container(
      padding: EdgeInsets.all(20.0),
      child: SizedBox(
        width: double.infinity,
        child: Wrap(
          alignment: WrapAlignment.center,
          direction: Axis.horizontal,
          spacing: 10,
          runSpacing: 20,
          children: _generateFunctionList(),
        ),
      ),
      color: Colors.yellow,
    );

Flex


中间工具栏原本使用 wrap 流式布局展示如上,后改成 Flex 弹性布局。布局代码分别如下:

Wrap


List<Widget> _generateFunctionList() {
  List<Widget> funcs = [];
  for (int i = 0; i < 5; i++) {
    funcs.add(Container(
      width: 50,
      height: 50,
      color: Colors.green,
    ));
  }
  return funcs;
}

ConstrainedBox(
      constraints: BoxConstraints(minWidth: double.infinity, minHeight: 70.0),
      child: Container(
        padding: EdgeInsets.all(10),
        color: Colors.red,
        child: Wrap(
          alignment: WrapAlignment.center,
          spacing: 20,
          children: _generateFunctionList(),
        ),
      ),
    );

Flex

List<Widget> _generateFunctionList() {
  List<Widget> funcs = [];
  for (int i = 0; i < 5; i++) {
    funcs.add(Expanded(
      flex: 1,
      child: Container(
        width: 50,
        height: 70,
        color: Colors.green,
      ),
    ));
  }
  return funcs;
}

class _AVFunctionBarState extends State<AVFunctionBar> {
  @override
  Widget build(BuildContext context) {
    return ConstrainedBox(
      constraints: BoxConstraints(minWidth: double.infinity, minHeight: 70.0),
      child: Container(
        color: Colors.red,
        child: Flex(
          direction: Axis.horizontal,
          children: _generateFunctionList(),
        ),
      ),
    );
  }
}

Stack & Positioned


渲染画面部分的缩放比例按钮使用绝对定位实现布局

Container(
        child: AspectRatio(
          aspectRatio: 4 / 3,
          child: Stack(
            children: [
              Positioned(
                left: 16,
                bottom: 16,
                width: 50,
                height: 50,
                child: TextButton(
                  autofocus: true,
                  style: ButtonStyle(
                    backgroundColor: MaterialStateProperty.resolveWith(
                        (states) => Colors.grey),
                    textStyle:
                        MaterialStateProperty.all(TextStyle(fontSize: 14)),
                    minimumSize: MaterialStateProperty.all(Size(50, 50)),
                    shape: MaterialStateProperty.all(RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(25.0))),
                  ),
                  child: Text(_scaleRange[_scaleIndex].toString() + 'x'),
                  onPressed: scaleTapAction,
                ),
              )
            ],
          ),
        ),
        color: Colors.black,
      ),

布局中遇到问题的处理办法

  1. 使用 Dart DevTools 查看页面结构,查看需要关注的 Widget 的约束
  2. 在程序 main 函数中打开debugPaintSizeEnabled 可以看到布局结构

    void main() {
      debugPaintSizeEnabled = true;
    runApp(Camera());
    }

    Simulator Screen Shot - iPhone 12 - 2021-04-20 at 10.42.15

遇到问题

  1. 颜色因为颜色不匹配不能进行赋值

正确使用方式:

backgroundColor: MaterialStateProperty.resolveWith(
                        (states) => Colors.grey),
                        
                
textStyle:MaterialStateProperty.all(TextStyle(fontSize: 14)),