flutter布局详解及代码示例(下)

布局

基本布局

  • GridView(二维滚动列表):比ListView多了一个方向的数据填充。
  • ListBody(滚动列表):相比ListView,没有回收复用,简单易用。
  • Table(表格布局):子元素类似表格一样在X轴和Y轴排列分布。
  • Flow(流式布局):相比Wrap布局的子元素自动换行,Flow需要自行在Delegate里写算法实现换行。
  • Wrap(流式布局):子元素在X轴放得下就放,放不下就去下一行的布局。
  • ScrollView(滚动视图):一般搭配Column使用的类似滚动列表的布局。

GridView

  • 就是二维的ListView
  • 有五种构造方式
    • GridView()
      • 默认构造;
      • 硬编码子widget,数量多有性能风险
    • GridVIew.builder()
      • 懒加载
    • GridView.costom()
    • GirdView.count()
      • 在交叉轴方向上固定数目
    • GridView.extent()
      • 在交叉轴方向上固定数目且有长度限制

代码

复制代码
/// GridView()
GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,//交叉轴方向的数目
  ),
  children: <Widget>[
    Text('1'),Text('2'),Text('3'),
    Text('4'),Text('5'),Text('6'),
    Text('7'),Text('8'),Text('9'),
    ],
),
/// GridView.builder()
final List<String> name = <String>['1','2','3',];
GridView.builder(
  itemCount: name.length,
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,//交叉轴方向item的数量
    mainAxisSpacing: 2.0,//主轴方向的间隔
    crossAxisSpacing: 2.0,//交叉轴之间的间隔
  ),
  itemBuilder: (context, index) {
    return Container(
      child: Text(name[index]),
    );
  },
),
/// GridView.costom()
 GridView.custom(
    gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
      maxCrossAxisExtent: 80.0,//在交叉轴方向上单个item的最大长度
      crossAxisCount: 3,//交叉轴方向item的数量
	  mainAxisSpacing: 2.0,//主轴方向的间隔
    ),
    childrenDelegate: SliverChildBuilderDelegate(
      (context, index) {
        return Container(
          child: Text(name[index]),
        );
      },
      childCount: name.length,
    ),
),
/// GirdView.count()
GridView.count(
  crossAxisCount: 3,
  children: <Widget>[
    Text('1'),Text('2'),Text('3'),
    Text('4'),Text('5'),Text('6'),
    Text('7'),Text('8'),Text('9'),
    ],
),
/// GridView.extent()
GridView.extent(
  maxCrossAxisExtent: 100.0,//在交叉轴方向上item的最大长度
  crossAxisCount: 3,//交叉轴方向item的数量
  mainAxisSpacing: 2.0,//主轴方向的间隔
  children: <Widget>[
    Container(
          child: Text("1"),
        ),
    Container(
          child: Text("2"),
        ),
    Container(
          child: Text("3"),
        ),
  ],
),

完整示例代码

复制代码
import 'package:flutter/material.dart';

class HomeTabPage1 extends StatelessWidget {
  List listData = [
    {
      "title": "标题1",
      "author": "内容1",
      "image": "https://www.itying.com/images/flutter/1.png"
    },
    {
      "title": "标题2",
      "author": "内容2",
      "image": "https://www.itying.com/images/flutter/2.png"
    },
    {
      "title": "标题3",
      "author": "内容3",
      "image": "https://www.itying.com/images/flutter/3.png"
    },
    {
      "title": "标题4",
      "author": "内容4",
      "image": "https://www.itying.com/images/flutter/4.png"
    },
    {
      "title": "标题5",
      "author": "内容5",
      "image": "https://www.itying.com/images/flutter/5.png"
    },
    {
      "title": "标题6",
      "author": "内容6",
      "image": "https://www.itying.com/images/flutter/6.png"
    },
    {
      "title": "标题7",
      "author": "内容7",
      "image": "https://www.itying.com/images/flutter/7.png"
    },
    {
      "title": "标题8",
      "author": "内容8",
      "image": "https://www.itying.com/images/flutter/1.png"
    },
    {
      "title": "标题9",
      "author": "内容9",
      "image": "https://www.itying.com/images/flutter/2.png"
    },
    {
      "title": "标题1",
      "author": "内容1",
      "image": "https://www.itying.com/images/flutter/1.png"
    },
    {
      "title": "标题2",
      "author": "内容2",
      "image": "https://www.itying.com/images/flutter/2.png"
    },
    {
      "title": "标题3",
      "author": "内容3",
      "image": "https://www.itying.com/images/flutter/3.png"
    },
    {
      "title": "标题4",
      "author": "内容4",
      "image": "https://www.itying.com/images/flutter/4.png"
    },
    {
      "title": "标题5",
      "author": "内容5",
      "image": "https://www.itying.com/images/flutter/5.png"
    },
    {
      "title": "标题6",
      "author": "内容6",
      "image": "https://www.itying.com/images/flutter/6.png"
    }
  ];

  List<Widget> _getData() {
    List<Widget> list = [];
    for (var i = 0; i < listData.length; i++) {
      list.add(Container(
        child: Column(
          children: [
            Image.network(
              listData[i]["image"],
              fit: BoxFit.cover,
            ),
            Text(
              listData[i]["title"],
              textAlign:TextAlign.center,
            )
          ],
        ),
      ));
    }
    return list;
  }


  @override
  Widget build(BuildContext context) {
    return GridView.count(
      //设置滚动方向
      scrollDirection: Axis.vertical,
      //设置列数
      crossAxisCount: 5,
      //设置内边距(整个GridView的)
      padding: EdgeInsets.all(30),
      //设置横向间距(3个间距一起用就能控制item各种距离了)
      crossAxisSpacing: 30,
      //设置主轴间距
      mainAxisSpacing: 30,
      children: _getData(),
    );
  }
}

常用参数

gridDelegate
  • 有两个实现类
    • SliverGridDelegateWithFixedCrossAxisCount
      • 根据设置的个数显示二维滚动列表
    • SliverGridDelegateWithMaxCrossAxisExtent
      • 根据设置的长度显示二维滚动列表
scrollDirection
复制代码
* 滚动方向
    * Axis.vertical
        * 竖向滚动
    * Axis.horizontal
        * 横向滚动
reverse
复制代码
* 组件反向排序
controller
复制代码
* 滚动监听
primary
复制代码
* 值为false,内容不足不可滑动
* 值为true,内容不足可以尝试滑动
shrinkWrap
复制代码
* 内容适配,默认为false
padding
复制代码
* 内边距
crossAxisCount
复制代码
* 列数
mainAxisSpacing
复制代码
* 主轴之间的间距
crossAxisSpacing
复制代码
* 横轴之间的间距
childAspectRatio
复制代码
* 设置宽高的比例
* GridView的子组件直接设置宽高没有反应,可以通过childAspectRatio修改宽高
cacheExtent
复制代码
* 设置预加载区域
children
复制代码
* 组件元素 const Widget列表
* 数组内添加widget类型的数据
* flutter的所有组件都是widget,也就是说所有的GridView可以添加所有的组件
semanticChildCount
复制代码
* 提供语义信息的子组件数量

ListBody

  • 很少单独使用,搭配如Row、Column、ListView、Flex一起使用。

代码

复制代码
Column(
  //主轴垂直排列的列表,未限制宽,默认将充满屏幕
  children: <Widget>[
    ListBody(
      //指定主轴方向与父框架相同
      mainAxis: Axis.vertical,
      reverse: false,//不反向
      children: <Widget>[
        Container(color: Colors.red, width: 50.0, height: 50.0),
        Container(color: Colors.yellow, width: 50.0, height: 50.0),
        Container(color: Colors.green, width: 50.0, height: 50.0),
        Container(color: Colors.blue, width: 50.0, height: 50.0),
        Container(color: Colors.black, width: 50.0, height: 50.0),
      ],
    )
  ],
)

Table

  • 像表格一样布局

代码

复制代码
Container(
  width: 300.0,height: 200.0,
  padding: EdgeInsets.all(2.0),
  color: Color(0xFFC5CAE9),
  child: Table(
    //每行中单元格的宽度,TableRow内元素个数,即列数,从第一个到最后一个的宽度
    //如果排列根据排列方向显示不同
    columnWidths: const <int, TableColumnWidth>{
      0: FixedColumnWidth(30.0),
      1: FixedColumnWidth(70.0),
      2: FixedColumnWidth(50.0),
      3: FixedColumnWidth(100.0),
    },
    //默认未显示宽度  默认的每一列宽度值,默认情况下均分。
    defaultColumnWidth: const FlexColumnWidth(1.0),
    //每个表格的排列方向,此处设置从右到左
    textDirection: TextDirection.rtl,
    //表格边框,此处设置蓝色,2像素宽,实线
    border: TableBorder.all(color: Colors.blue, width: 2.0, style: BorderStyle.solid),
    //每一个单元格的垂直方向的对齐方式,默认为顶部对齐
    defaultVerticalAlignment: TableCellVerticalAlignment.top,
    //基线类型,与TableCellVerticalAlignment.baseline一起使用
    //textBaseline: null,
    children: <TableRow>[
      TableRow(
        decoration: BoxDecoration(color: Colors.purpleAccent),
        children: <Widget>[
          Text('A1'),Text('A2'),Text('A3'),Text('A4'),
        ],
      ),
      TableRow(
        decoration: BoxDecoration(color: Colors.purpleAccent),
        children: <Widget>[
          Container(color: Colors.red, child: Text('赤')),
          Container(color: Colors.orange, child: Text('橙')),
          Container(color: Colors.yellow, child: Text('黄')),
          Container(color: Colors.green, child: Text('绿')),
        ],
      ),
      TableRow(children: <Widget>[
        Text('B1'),Text('B2'),Text('B3'),Text('B4'),
      ]),
    ],
  ),
)

参数

  • columnWidth:每列单元格的宽度,int为从0到每行的个数-1
  • defaultColumnWidth:默认的每一列宽度值,默认情况下均分。
  • textDirection:每列的排列方向,默认从左到右
  • border:TableBorder 表格的边框
  • defaultVerticalAlignment:单元格默认垂直方向上的对齐方式,默认上对齐TableCellVerticalAlignment.top
  • textBaseline:TableCellVerticalAlignment.baseline与此属性配合使用。文本基线类型
  • children:存放每行的单元格内容的类别

Flow

  • 重点在于FlowDelegate的使用

FlowDelegate的方法

复制代码
abstract class FlowDelegate {
  const FlowDelegate({ Listenable repaint }) : _repaint = repaint;
  final Listenable _repaint;
  //重写设置尺寸
  Size getSize(BoxConstraints constraints) => constraints.biggest;
  //重写设置约束
  BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) => constraints;
  //绘制children的位置和大小
  void paintChildren(FlowPaintingContext context);
  //是否要从新布局,可自己定制规则
  bool shouldRelayout(covariant FlowDelegate oldDelegate) => false;
  //是否从新绘制,可自己定制规则
  bool shouldRepaint(covariant FlowDelegate oldDelegate);
  @override
  String toString() => '$runtimeType';
}

代码

复制代码
Flow(
  delegate: TestFlowDelegate(margin: EdgeInsets.all(5.0)),
  children: <Widget>[
    new Container(
      width: 60.0,  height: 60.0, color: Colors.red,
      child: Text('红'), alignment: Alignment.center, 
    ),
    new Container(
      width: 60.0, height: 60.0,color: Colors.orange,
      child: Text('橙'),alignment: Alignment.center,  
    ),
    new Container(
      width: 60.0,height: 60.0,color: Colors.yellow,
      child: Text('黄'),alignment: Alignment.center,      
    ),
    new Container(
      width: 60.0,height: 60.0,color: Colors.green,
      child: Text('绿'),alignment: Alignment.center,  
    ),
    new Container(
      width: 60.0,height: 60.0,color: Colors.cyan,
      child: Text('青'),alignment: Alignment.center,     
    ),
    new Container(
      width: 60.0, height: 60.0,color: Colors.blue,
      child: Text('蓝'),alignment: Alignment.center,  
    ),
    new Container(
      width: 60.0,height: 60.0,color: Colors.purple,
      child: Text('紫'),alignment: Alignment.center,   
    ),
  ],
)
    
class TestFlowDelegate extends FlowDelegate {
  EdgeInsets margin = EdgeInsets.zero;
  TestFlowDelegate({this.margin});

  @override
  void paintChildren(FlowPaintingContext context) {
    var x = margin.left;
    var y = margin.top;
    for (int i = 0; i < context.childCount; i++) {
      var w = context.getChildSize(i).width + x + margin.right;
      if (w < context.size.width) {
        context.paintChild(i,
            transform: new Matrix4.translationValues(x, y, 0.0));
        x = w + margin.left;
      } else {
        x = margin.left;
        y += context.getChildSize(i).height + margin.top + margin.bottom;
        context.paintChild(i,
            transform: new Matrix4.translationValues(x, y, 0.0));
        x += context.getChildSize(i).width + margin.left + margin.right;
      }
    }
  }

  @override
  bool shouldRepaint(FlowDelegate oldDelegate) {
    return oldDelegate != this;
  }
}

Wrap

  • 在mainAxis上空间不足时,则向crossAxis上去扩展显示。
  • Wrap使用方便一些,但是Flow能实现Wrap,Flow更强一些。

参数

  • direction :主轴方向,默认水平
  • alignment :主轴方向方式,值为WrapAlignment的枚举值,详情请看Row的对齐方式
  • spacing :主轴方向上child之间的间距,默认为0
  • runAlignment : 新一行或一列的对齐方式
  • runSpacing :新的一行或一列的间距,默认为0
  • crossAxisAlignment:交叉轴的对齐方式,默认是从主轴开始位置开始
  • textDirection:每一行或一列的排列方式
    • 如果一行有三个元素,则第一行取出前三个元素2,1,0这样排列
  • verticalDirection:垂直方向上排列方式,值为VerticalDirection的枚举值,默认从上到下

代码

复制代码
Container(
    alignment: Alignment.topCenter,
    child:  Wrap(
    //主轴方向,默认水平
    direction: Axis.horizontal,
    //主轴方向方式,包裹在一个控件内效果明显,默认主轴方向开始位置开始
    alignment: WrapAlignment.spaceBetween,
    //主轴方向上child之间的间距,默认为0
    spacing: 6.0,
    // 新一行或一列的对齐方式
    runAlignment: WrapAlignment.spaceBetween,
    //新的一行或一列的间距,默认为0
    runSpacing: 0.0,
    //交叉轴的对齐方式,默认是从主轴开始位置开始
    crossAxisAlignment: WrapCrossAlignment.start,
    //每一行或一列的排列方式,如果一行有三个元素,则第一行取出前三个元素2,1,0这样排列
    //默认从左到右
    textDirection: TextDirection.ltr,
    //垂直方向上排列方式,默认从上到下
    verticalDirection: VerticalDirection.down,
    children: <Widget>[
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('1')),
        label: Text('Hamilton'),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('2')),
        label: Text('Lafayette'),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('3')),
        label: Text('Mulligan'),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('4')),
        label: Text('Laurens'),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('5')),
        label: Text('Hamilton'),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('6')),
        label: Text('Lafayette'),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('7')),
        label: Text('Mulligan'),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('8')),
        label: Text('Laurens'),
      ),
      Chip(
        avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('9')),
        label: Text('Hamilton'),
      ),
      Chip(
        avatar: CircleAvatar(
            backgroundColor: Colors.blue.shade900, child: Text('10')),
        label: Text('Lafayette'),
      ),
      Chip(
        avatar: CircleAvatar(
            backgroundColor: Colors.blue.shade900, child: Text('11')),
        label: Text('Mulligan'),
      ),
      Chip(
        avatar: CircleAvatar(
            backgroundColor: Colors.blue.shade900, child: Text('12')),
        label: Text('Laurens'),
      ),
    ],
  ),
)

ScrollView

  • 有很多子类实现:
    • SingleChildScrollView

SingleChildScrollView

  • 有子view数组的ScrollView
代码
复制代码
SingleChildScrollView(
    child: Column(
    children: <Widget>[
        Container(
        height: 200,
        color: Colors.red,
        ),
        Container(
        height: 200,
        color: Colors.green,
        ),
        Container(
        height: 200,
        color: Colors.blue,
        ),
        Container(
        height: 200,
        color: Colors.yellow,
        ),
        Container(
        height: 200,
        color: Colors.orange,
        ),
    ],
    ),
),

自定义ScrollPhysics控制滑动效果

  • 准确的说这个属于所有physics参数共用的一个案例

  • 继承ScrollPhysics可以实现自定义滑动的效果

    import 'package:flutter/material.dart';

    void main() {
    runApp(MyApp());
    }

    class MyCustomScrollPhysics extends ScrollPhysics {
    const MyCustomScrollPhysics({ScrollPhysics? parent}) : super(parent: parent);

    复制代码
    @override
    MyCustomScrollPhysics applyTo(ScrollPhysics? ancestor) {
      return MyCustomScrollPhysics(parent: buildParent(ancestor));
    }
    
    @override
    double applyBoundaryConditions(ScrollMetrics position, double value) {
      // 检查是否已经滑动到边界
      if (value < position.pixels && position.pixels <= position.minScrollExtent) {
        // 滑动到顶部边界时,允许继续向上滚动
        return 0.0;
      } else if (value > position.pixels && position.pixels >= position.maxScrollExtent) {
        // 滑动到底部边界时,允许继续向下滚动
        return 0.0;
      }
      // 其他情况,使用默认的边界条件
      return super.applyBoundaryConditions(position, value);
    }

    }

    class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    return MaterialApp(
    home: Scaffold(
    body: CustomScrollView(
    physics: MyCustomScrollPhysics(), // 使用自定义的ScrollPhysics
    slivers: <Widget>[
    SliverAppBar(
    expandedHeight: 200.0,
    pinned: true,
    flexibleSpace: FlexibleSpaceBar(
    title: Text('Custom Scroll Physics Example'),
    ),
    ),
    SliverList(
    delegate: SliverChildBuilderDelegate(
    (BuildContext context, int index) {
    return ListTile(
    title: Text('Item $index'),
    );
    },
    childCount: 100, // 你的列表项数量
    ),
    ),
    ],
    ),
    ),
    );
    }
    }

相关推荐
奋斗的小青年!!12 小时前
Flutter浮动按钮在OpenHarmony平台的实践经验
flutter·harmonyos·鸿蒙
程序员老刘15 小时前
一杯奶茶钱,PicGo + 阿里云 OSS 搭建永久稳定的个人图床
flutter·markdown
奋斗的小青年!!18 小时前
OpenHarmony Flutter 拖拽排序组件性能优化与跨平台适配指南
flutter·harmonyos·鸿蒙
小雨下雨的雨19 小时前
Flutter 框架跨平台鸿蒙开发 —— Stack 控件之三维层叠艺术
flutter·华为·harmonyos
行者9620 小时前
OpenHarmony平台Flutter手风琴菜单组件的跨平台适配实践
flutter·harmonyos·鸿蒙
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Flex 控件之响应式弹性布局
flutter·ui·华为·harmonyos·鸿蒙系统
cn_mengbei1 天前
Flutter for OpenHarmony 实战:CheckboxListTile 复选框列表项详解
flutter
cn_mengbei1 天前
Flutter for OpenHarmony 实战:Switch 开关按钮详解
flutter
奋斗的小青年!!1 天前
OpenHarmony Flutter实战:打造高性能订单确认流程步骤条
flutter·harmonyos·鸿蒙
Coder_Boy_1 天前
Flutter基础介绍-跨平台移动应用开发框架
spring boot·flutter