美文网首页
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