美文网首页
Flutter入门08 -- Widget之多个子元素的布局类

Flutter入门08 -- Widget之多个子元素的布局类

作者: YanZi_33 | 来源:发表于2022-02-22 09:13 被阅读0次
  • 涉及多个子元素布局类的Widget有:Flex,Row、Column、Stack、IndexedStack、Wrap、Flow、Table、ListBody、CustomMultiChildLayout、LayoutBuilder、Flexible,Expanded

Flex(弹性布局)

  • Flex(弹性布局):内部按照一定的方式排列子组件,是Row与Column的基类,其构造函数如下:
Flex({
    Key key,
    @required this.direction,
    this.mainAxisAlignment = MainAxisAlignment.start,
    this.mainAxisSize = MainAxisSize.max,
    this.crossAxisAlignment = CrossAxisAlignment.center,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
    this.textBaseline,
    this.clipBehavior = Clip.hardEdge,
    List<Widget> children = const <Widget>[],
})
  • direction:内部子组件的排列方向,属于Axis类型,其值有如下:
    • Axis.horizontal:水平方向排布,与Row等价;
    • Axis.vertical:垂直方向排布,与Column等价;
  • children:child子组件数组;
  • mainAxisAlignment:表示在主轴方向上的child对齐方式,属于MainAxisAlignment类型,具体值有如下:
    • start:主轴的开始位置依次摆放元素;
    • end:主轴的结束位置依次摆放元素;
    • center:主轴中心点对齐;
    • spaceBetween:左右间距为0,其他元素之间间距平分;
    • spaceAround:左右间距是其他元素之间间距的一半;
    • spaceEvenly:所有间距平分;
  • crossAxisAlignment:表示在交叉轴方向上的child对齐方式,属于CrossAxisAlignment类型,具体值有如下:
    • start:交叉轴的开始位置依次摆放元素;
    • end:交叉轴的结束位置依次摆放元素;
    • center:交叉轴中心点对齐;
    • textBaseline:基线对齐(必须有文本内容才有效果);
    • stretch:将所有子元素,拉伸到最大;
  • mainAxisSize:控制Flex在主轴方向的尺寸大小,属于MainAxisSize类型,有两种数值;
    • MainAxisSize.min:主轴方向上自适应子child的大小;
    • MainAxisSize.min:主轴方向上占据整个屏幕大小;
  • textBaseline:文本内容按照基线进行对齐;
  • 案例代码如下:
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter布局"),
      ),
      body: SFHomeBody(),
    );
  }
}

class SFHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      color: Colors.orange,
      child: Flex(
        children: [
          Container(
            width: 50,
            height: 50,
            color: Colors.green,
          ),
          Container(
            width: 50,
            height: 60,
            color: Colors.green,
          ),
          Container(
            width: 50,
            height: 70,
            color: Colors.green,
          ),
          Container(
            width: 50,
            height: 80,
            color: Colors.green,
          )
        ],
        direction: Axis.horizontal, //指明主轴方向为水平方向 与Row等价
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.start,
      )
    );
  }
}
  • 效果图如下:
image.png

Row(水平布局)

  • Row(水平布局):在水平方向上,排列子组件,其主轴方向为水平方向,构造函数如下:
Row({
    Key key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline textBaseline,
    List<Widget> children = const <Widget>[],
})
  • 案例代码如下:
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter布局"),
      ),
      body: SFHomeBody(),
    );
  }
}

class SFHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      color: Colors.orange,
      child: Row(
        children: [
          Container(
            width: 50,
            height: 60,
            color: Colors.green,
          ),
          Container(
            width: 50,
            height: 70,
            color: Colors.green,
          ),
          Container(
            width: 50,
            height: 80,
            color: Colors.green,
          ),
          Container(
            width: 50,
            height: 90,
            color: Colors.green,
          )
        ],
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.max,
        textBaseline: TextBaseline.alphabetic,
      ),
    );
  }
}
  • 效果图如下:
image.png

Column

  • Column:在垂直方向上,排列子组件,其·主轴方向为垂直方向`,构造方法与Row(水平布局)完全一样,主要区别在于主轴方向的不同;
Column({
    Key key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline textBaseline,
    List<Widget> children = const <Widget>[],
})
  • 案例代码如下:
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter布局"),
      ),
      body: SFHomeBody(),
    );
  }
}

class SFHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      color: Colors.orange,
      child: Column(
        children: [
          Container(
            width: 50,
            height: 50,
            color: Colors.green,
          ),
          Container(
            width: 60,
            height: 50,
            color: Colors.green,
          ),
          Container(
            width: 70,
            height: 50,
            color: Colors.green,
          ),
          Container(
            width: 80,
            height: 50,
            color: Colors.green,
          )
        ],
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.max,
        textBaseline: TextBaseline.alphabetic,
      ),
    );
  }
}
  • 效果图如下:
image.png
  • 如何给Row和Column设置背景颜色,可给Row和Column外层套一个Container组件,Container在不设置宽高的情况下,其内容大小就是内部子child的尺寸大小;

Flexible与Expanded

  • Flexible是Expanded的父类,这两者可以让Row、Column、Fiex等子组件在其主轴上方向展开并填充可用的剩余空间
  • Flexible的构造方法如下:
const Flexible({
    Key key,
    this.flex = 1,
    this.fit = FlexFit.loose,
    @required Widget child,
})
  • Expanded的构造方法如下:
class Expanded extends Flexible {
  /// Creates a widget that expands a child of a [Row], [Column], or [Flex]
  /// so that the child fills the available space along the flex widget's
  /// main axis.
  const Expanded({
    Key key,
    int flex = 1,
    @required Widget child,
  }) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
}
  • 可以看出Flexible与Expanded的区别在于属性fit的设值不同,FlexFit.tightFlexFit.loose
  • Flexible,FlexFit.loose,尽可能大的填满剩余空间,但是可以不填满;
  • Expanded,FlexFit.tight,必须(强制)填满剩余空间;
  • Flexible与Expanded必须使用在Row、Column、Fiex的内部;
  • flex:是针对剩余空间的分配比例;
  • 案例代码如下:
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter布局"),
      ),
      body: SFHomeBody(),
    );
  }
}

class SFHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      color: Colors.orange,
      child: Row(
        children: [
          Container(
            width: 50,
            height: 50,
            color: Colors.green,
          ),
          Container(
            width: 50,
            height: 60,
            color: Colors.green,
          ),
          Flexible(
            flex: 1,
            child: Container(height: 120,color: Colors.red),
          ),
          Expanded(
            flex: 1,
            child: Container(height: 80,color: Colors.green),
          ),
          Expanded(
            flex: 1,
            child: Container(
              height: 100,
              color: Colors.purple,
            ),
          )
        ],
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
      )
    );
  }
}
  • 效果图如下:
image.png

Stack组件

  • 在开发中,我们多个组件可能需要重叠显示,在Android中使用Frame布局实现,而在Flutter中我们需要使用层叠布局Stack来实现;
  • 内部子组件默认从左上角开始排布;
  • Stack默认的大小是包裹内容的尺寸大小;
  • 构造函数如下:
Stack({
    Key key,
    this.alignment = AlignmentDirectional.topStart,
    this.textDirection,
    this.fit = StackFit.loose,
    this.overflow = Overflow.clip,
    this.clipBehavior = Clip.hardEdge,
    List<Widget> children = const <Widget>[],
}) : assert(clipBehavior != null), super(key: key, children: children);
  • children:内部子组件数组;
  • alignment:表示从什么位置开始排布所有的子组件,只作用于没有使用Positioned或部分定位的子组件,属于Alignment类型;
  • fit:子组件如何去适应Stack的大小,只作用于没有使用Positioned或部分定位的子组件,属于StackFit类型;
    • StackFit.expand:扩伸到Stack的大小,只作用于没有使用Positioned的子组件;
    • StackFit.loose:使用子组件的大小,只作用于没有使用Positioned的子组件;
    • StackFit.passthrough:传递使用Stack的父级约束;
  • overflow:对于超出父组件区域的子组件的显示处理,属于Overflow类型;
    • Overflow.flip:超出部分被裁减;
    • Overflow.visible:超出部分依然显示;
  • 案例代码如下:
import 'package:flutter/material.dart';

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

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("基础widget")), body: SFHomeContent());
  }
}

class SFHomeContent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print("createState");
    return _SFHomeContentState();
  }
}

class _SFHomeContentState extends State<SFHomeContent> {
  @override
  Widget build(BuildContext context) {
    return StackDemo1();
  }
}

class StackDemo2 extends StatelessWidget {
  const StackDemo2({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Container(
            height: 200,
            child: Image.asset("asset/images/180.png", fit: BoxFit.fill),
            width: double.infinity),
        Positioned(
          child: Container(
            child: Row(
              children: [
                Text("这是一行文本", style: TextStyle(fontSize: 17, color: Colors.white)),
                IconButton(icon: Icon(Icons.favorite),color: Colors.white,onPressed: (){
                })
              ],
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
            ),
            color: Color.fromARGB(150, 0, 0, 0),
            width: double.infinity,
            padding: EdgeInsets.symmetric(horizontal: 8),
          ),
          left: 0,
          right: 0,
          bottom: 0,
        )
      ],
    );
  }
}

class StackDemo1 extends StatelessWidget {
  const StackDemo1({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Image.asset("asset/images/180.png"),
        Positioned(
          child: Container(
            width: 100,
            height: 100,
            color: Colors.green,
          ),
          right: 0,
        ),
        Positioned(
          child: Text("推客图标", style: TextStyle(fontSize: 20)),
          left: 0,
        )
      ],
      alignment: AlignmentDirectional.bottomStart,
      overflow: Overflow.visible,
    );
  }
}
  • StackDemo1与StackDemo2的效果图如下:
image.png image.png

IndexedStack

  • IndexedStack:层叠布局,继承自Stack,其构造函数如下:
IndexedStack({
    Key key,
    AlignmentGeometry alignment = AlignmentDirectional.topStart,
    TextDirection textDirection,
    StackFit sizing = StackFit.loose,
    this.index = 0,
    List<Widget> children = const <Widget>[],
})
  • alignment:属于AlignmentDirectional类型;
  • textDirection:文本方向;
  • sizing:子组件如何去适应IndexedStack的大小,就是Stack的fit属性,但是在这里sizing属性不会对子组件大小有影响;
  • index = 0:显示第几个子组件;
  • children:内部子组件数组;
  • IndexedStack只能够显示 children列表 中单个子组件,默认第0个,可切换显示;

Wrap

  • Wrap:可实现流式布局,比如商品列表,瀑布流、标签页等等;
  • 子组件排列时,超出屏幕的宽高后,会自动换行,换列;
  • 其构造函数如下:
Wrap({
    Key key,
    this.direction = Axis.horizontal,
    this.alignment = WrapAlignment.start,
    this.spacing = 0.0,
    this.runAlignment = WrapAlignment.start,
    this.runSpacing = 0.0,
    this.crossAxisAlignment = WrapCrossAlignment.start,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
    this.clipBehavior = Clip.hardEdge,
    List<Widget> children = const <Widget>[],
})
  • children:子组件数组;
  • direction:主轴方向,默认水平方向;
  • spacing:主轴方向每个 item之间的间距;
  • runSpacing:交叉轴方向item之间的间距;
  • 案例代码如下:
import 'package:YYShop/core/extension/color_extesion.dart';
import 'package:YYShop/ui/common/appsize_fit.dart';
import 'package:flutter/material.dart';

class SFMinePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Wrap(
      direction: Axis.horizontal,
      children: createItems(),
      spacing: 5,
      runSpacing: 10,
    );
  }

  List<Widget> createItems() {
    List<Widget> widgets = new List();
    for (int i = 0; i < 10; i++) {
      widgets.add(SFItem());
    }
    return widgets;
  }
}

class SFItem extends StatelessWidget {
  const SFItem({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    double width = (SFAppSizeFit.screenWidth - 5) / 2;
    return Container(
      width: width,
      height: 100,
      color: SFColor.randomColor(),
    );
  }
}
  • 效果图如下:
image.png

相关文章

网友评论

      本文标题:Flutter入门08 -- Widget之多个子元素的布局类

      本文链接:https://www.haomeiwen.com/subject/jweslrtx.html