• Flutter组件渲染集合的几种方式之详解与实战举例(更新)


    一、Wrap组件

    Wrap
    以下是flutter默认给我们提供的接口

    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,
        List<Widget> children = const <Widget>[],
      }) : super(key: key, children: children);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    提供了很多属性,介绍一下属性都是干嘛的

    属性作用
    direction扩展方式 比如横向堆砌
    alignment对齐方式
    spacing主轴空隙间距
    runAlignment run的对齐方式
    runSpacing run空隙间距
    crossAxisAlignment纵轴对齐方式
    textDirection文本对齐方向
    verticalDirection确定垂直放置子元素的顺序,以及如何在垂直方向上解释开始和结束,默认down children 需要放置的组件列表

    当选中该金额项目时,触发点击事件和样式,如下用Wrap实现,for循环遍历,拿到当前索引值

    在这里插入图片描述

    int specIndex;
     Wrap(
              runSpacing: 10,
              spacing: 20,
              children: _buildMoneyItem(),
                ),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
      List<Widget> _buildMoneyItem() {
        List<Widget> spectListWidget = [];
        for (int i = 0; i < list.length; i++) {
          spectListWidget.add(GestureDetector(
            onTap: () {
              selectedAccountValueSave = listValue[i];
              setState(() {
                specIndex = i;
              });
            },
            child: Container(
              height: 40,
              width: 75,
              alignment: Alignment.center,
              child: Center(
                child: Text(
                  list[i],
                  style: TextStyle(fontSize: 16),
                ),
              ),
              // padding: EdgeInsets.only(left: 10, right: 10),
              decoration: BoxDecoration(
                  border: Border(
                    top: BorderSide(
                        width: 1.0, color: specIndex == i ? colorRed : colorGrey),
                    left: BorderSide(
                        width: 1.0, color: specIndex == i ? colorRed : colorGrey),
                    right: BorderSide(
                        width: 1.0, color: specIndex == i ? colorRed : colorGrey),
                    bottom: BorderSide(
                        width: 1.0, color: specIndex == i ? colorRed : colorGrey),
                  ),
                  color: Color(0xFFf7f7f7),
                  borderRadius: BorderRadius.all(Radius.circular(2))),
            ),
          ));
        }
        return spectListWidget;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    二、GridView.count

    控件网格的主轴方向是它滚动的方向( scrollDirection)。 最常用的网格布局是GridView.count,它创建了一个在横轴上具有固定数量 网格块 的平铺的布局

    GridView源码

    GridView({
        Key key,
        Axis scrollDirection = Axis.vertical, 
        bool reverse = false,  
        ScrollController controller,
        bool primary,
        ScrollPhysics physics,
        bool shrinkWrap = false, 
        EdgeInsetsGeometry padding,  
        @required this.gridDelegate, 
        bool addAutomaticKeepAlives = true,
        bool addRepaintBoundaries = true,
        bool addSemanticIndexes = true,
        double cacheExtent,
        List<Widget> children = const <Widget>[],
        int semanticChildCount,
      })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    属性值类型说明
    scrollDirectionAxis设置滚动的方向,horizontal(水平)或vertical(垂直)
    reverse bool是否翻转
    controllerScrollController用来控制滚动位置及监听滚动事件
    shrinkWrapbool是否根据子widget的总长度来设置GridView的长度
    paddingEdgeInsetsGeometry间距
    gridDelegateSliverGridDelegate控制子Widget如何进行布局
    childrenList子控件

    gridDelegate

    该属性接收一个SliverGridDelegate类型的值,主要是用来控制子Widget如何进行布局。

    在这里插入图片描述
    他有如下两个实现类
    SliverGridDelegateWithMaxCrossAxisExtent和SliverGridDelegateWithFixedCrossAxisCount
    构造方法

     const SliverGridDelegateWithMaxCrossAxisExtent({
        @required this.maxCrossAxisExtent, //子控件的最大宽度,实际宽度是根据交叉轴的值进行平分,也就是说最大宽度并不一定是实际宽度,很有可能子控件的实际宽度要小于设置的最大宽度
        this.mainAxisSpacing = 0.0, //主轴之间的间距
        this.crossAxisSpacing = 0.0,//交叉轴之间的间距
        this.childAspectRatio = 1.0,//子控件的宽高比
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    GridView(
          scrollDirection: Axis.vertical,
          gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
            maxCrossAxisExtent: 100, //子控件最大宽度为100
            childAspectRatio: 0.5,//宽高比为1:2
            crossAxisSpacing: 10,
            mainAxisSpacing: 10,
          ),
          padding: EdgeInsets.all(10),
          children: List.generate(
            20,
            (index) {
              return Box(index + 1);
            },
          ),
        );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    List.generate()

    List.generate(images.length, (i){
      return Container(
        margin: EdgeInsets.only(left:20.0),
          child: 
            Image.network("${images[i]}",
              width: 375.0,
              fit: BoxFit.fitWidth,
            ),
       );
    }),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    实战举例

    GridView.count(
            shrinkWrap: true,
            physics: NeverScrollableScrollPhysics(),
            crossAxisSpacing: 20,
            crossAxisCount: 3,
            children: menuList.map((e) {
              return InkWell(
                onTap: () {
                  jumpToTargetPage(e.tag);
                },
                child: Column(
                  children: [
                    Image.asset(
                      e.icon,
                      height: 36,
                      width: 36,
                    ),
                    SizedBox(height: 10),
                    Text(
                      e.title,
                      style: TextStyle(fontSize: 12, color: Colours.textBlack32),
                    ),
                  ],
                ),
              );
            }).toList(),
          )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
      void jumpToTargetPage(String tag) {
        String pageName;
        Map arguments;
        switch (tag) {
          case "1":
            pushNamedPage(PublishRoute, arg: arguments);
            break;
          case "2":
            break;
        }
        if (pageName != null) {
          pushNamedPage(pageName, arg: arguments);
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    三、ListView.builder

    ListView.builder 是一种构建列表的方法,其中的子 Widget 可以按需构建。但是,与返回静态 Widget 不同的是,它会多次调用(基于 itemCount)一个生成函数,并可在每次调用时返回不同的 Widget。

    无限循环列表

    ListView.builder(
      itemBuilder: (context, index) {
        return ListTile(
          leading: Icon(Icons.shopping_cart),
          title: Text('product $index'),
          subtitle: Text('price: ${Random().nextInt(100)} USD'),    
        );
      }
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    ListView.builder(
                shrinkWrap: true,
                physics: const NeverScrollableScrollPhysics(),
                itemBuilder: (ctx, index) {
                  NewsModel news = accountViewModel.newsList[index];
                  return InkWell(
                    onTap: () {
                    },
                    child: Padding(
                      padding: const EdgeInsets.symmetric(vertical: 10),
                      child: Row(
                        children: [
                          Image.asset(
                            "resource/images/temp_news.png",
                            width: 110,
                            height: 70,
                            fit: BoxFit.fill,
                          ),
                          SizedBox(width: 10),
                          Expanded(
                            child: Container(
                              height: 70,
                              child: Column(
                                mainAxisSize: MainAxisSize.max,
                                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    news.title,
                                    style: TextStyle(
                                        fontSize: 14, color: Colours.textBlack32),
                                    overflow: TextOverflow.ellipsis,
                                    softWrap: true,
                                    maxLines: 2,
                                  ),
                                  Text(
                                    news.pubDate,
                                    style: TextStyle(
                                        fontSize: 12, color: Color(0xFFA3A0A0)),
                                  ),
                                ],
                              ),
                            ),
                          )
                        ],
                      ),
                    ),
                  );
                },
                itemCount: accountViewModel.newsList.length,
              )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    解决Flutter ListView 或者SingleChildScrollView 嵌套 ListView.builder滑动冲突

    原因
    SingleChildScrollView 和 ListView 都有滚动属性physics 他们默认是都是可以滚动的,
    ListView 嵌套 ListView.builder 需要后者shrinkWrap = true,不然报错;
    解决方式
    禁用 ListView 的滚动physics 保留 SingleChildScrollView 的滚动
    Listview 执行 physics 属性 new NeverScrollableScrollPhysics(), //禁用

    new ListView.builder(
                  shrinkWrap: true,
                  physics: new NeverScrollableScrollPhysics(),
    )
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    实现高值医疗耗材智能化管理的RFID医疗柜解决方案
    MySQL数据类型:字符串类型详解
    在Spring Boot微服务使用RedisTemplate操作Redis集群
    Java学习第一课
    langchain(1):使用LangChain 调用 openai 的 text/chat model
    热管理系统研发服务
    大数据精准营销一站式解决你的获客难题
    经典模型——AlexNet
    LeetCode654.最大二叉树——java
    Python 自动化(十五)请求和响应
  • 原文地址:https://blog.csdn.net/qq_43547255/article/details/126256476