ChatGPT解决这个技术问题 Extra ChatGPT

Flutter:如何将标题行添加到 ListView

对 Flutter 非常陌生。我已经能够利用 HTTP 数据请求、构建 ListView、编辑该列表中的行和其他基础知识。环境极佳。

我已经设法为 ListView 拼凑了一个结构糟糕的标题,但我知道这是不对的。我无法让标题文本正确排列。

我看到 Drawer 类有一个 DrawerHeader 类,但看不到 ListView 有一个 ListViewHeader

@override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Contacts'),
          actions: <Widget>[
            IconButton(icon: Icon(Icons.add_circle),
                onPressed: getCustData
            ),
          ],
        ),
        //body:
        body: Column(
            children: <Widget>[
              Row(
                  children: <Widget>[
                    Expanded(child: Text('', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
                    Expanded(child: Text('First Name', style:  TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
                    Expanded(child: Text('Last Name', style:  TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
                    Expanded(child: Text('City', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
                    Expanded(child: Text('Customer Id', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
                    Expanded(child: Text('', style: TextStyle(height: 3.0, fontSize: 15.2, fontWeight: FontWeight.bold,))),
                  ]
              ),

              Expanded(child:Container(
                child: ListView.builder(

                  itemCount: data == null ? 0 : data.length,
                  itemBuilder: (BuildContext context, int index) {

                    return InkWell(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                              builder: (context) => APIDetailView(data[index])),
                        );
                      },

                      child: ListTile(                //return new ListTile(
                          onTap: null,
                          leading: CircleAvatar(
                            backgroundColor: Colors.blue,
                            child: Text(data[index]["FirstName"][0]),
                          ),
                          title: Row(
                              children: <Widget>[
                                Expanded(child: Text(data[index]["FirstName"])),
                                Expanded(child: Text(data[index]["LastName"])),
                                Expanded(child: Text(data[index]["Bill_City"])),
                                Expanded(child: Text(data[index]["Customer_Id"])),
                              ]
                          )
                      ),

                    );
                  }, //itemBuilder

                ),
              ),
            ),
          ]
        )
    );
  }
}

谢谢。

https://i.stack.imgur.com/00bbc.png

考虑使用 DataTable
根据提供的代码,您的标题有 6 个子元素(列标题);第一个和最后一个是空的。第一个空标题元素由 ListTile 中的 leading 属性表示,但没有关联的 trailing 属性与第 6 个空标题列相匹配。因此,标题显示 6 个元素,但您的列表仅使用 5 列(1 个 leading 和一个 title w/4 个子项)。因此,添加 trailing 将有助于将它们排列起来,但将标题设为 ListItem 并带有 leadingtrailing 和带有 4 个元素的 title 使其完美;正如您在回答中所做的那样。

n
najeira

通过 itemBuilder 将标题作为第一行返回:

ListView.builder(
    itemCount: data == null ? 1 : data.length + 1,
    itemBuilder: (BuildContext context, int index) {
        if (index == 0) {
            // return the header
            return new Column(...);
        }
        index -= 1;

        // return row
        var row = data[index];
        return new InkWell(... with row ...);
    },
);

谢谢...我觉得这比我拥有的要干净得多。我仍在努力让 Header 标题与 ListView 列对齐。
你能让每一列都有固定的宽度吗?
我不愿意。这是一个移动应用程序......永远不知道我正在处理的屏幕宽度。
您可以从 MediaQuery 获取屏幕宽度。用百分比计算宽度怎么样?
好的。我会试一试并回帖。
d
davidk

这是我解决这个问题的方法。感谢@najeira 让我思考其他解决方案。

在第一个正文列中,我对标题使用了与 ListTile 相同的布局。

因为我的数据 ListTile,在这种情况下,包括一个 CircleAvatar,所有的水平间距都偏离了一点......呈现 CircleAvatar 的 5 列......然后是 4 个均匀间隔的列。

所以...我在第一个正文 Column 中添加了一个 ListTile,一个带有透明 backgroundColorCircleAvatar,然后是我的 4 个标题中的 Row

        ListTile(
        onTap: null,
        leading: CircleAvatar(
          backgroundColor: Colors.transparent,
        ),
        title: Row(
            children: <Widget>[
              Expanded(child: Text("First Name")),
              Expanded(child: Text("Last Name")),
              Expanded(child: Text("City")),
              Expanded(child: Text("Id")),
            ]
        ),
      ),

https://i.stack.imgur.com/sNGrPl.png


你会知道如何让整个东西水平滚动吗?除了我的列表项更大之外,我面临着与标题相同的问题,我在表格中有 10 列,并且想让整个内容也可以横向滚动
您可以将整个内容包装在 ListView 中,并将滚动方向设置为水平。
@Abbas.M 用 ```SingleChildScrollView`` 包装标题: Row(...) ,它将滚动
B
Byaku

您可以在 Column 中添加 ContainerListView

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: Text("Demo App1"),
        ),
        body: Column(
          children: <Widget>[
            Container(
              height: 40.0,
              child: Row(
                children: <Widget>[
                  Container(
                      padding: EdgeInsets.all(4.0),
                      width: 100.0,
                      child: Text(
                        "Name",
                        style: TextStyle(fontSize: 18),
                      )),
                  Container(
                      padding: EdgeInsets.all(4.0),
                      width: 100.0,
                      child: Text(
                        "Age",
                        style: TextStyle(fontSize: 18),
                      )),
                ],
              ),
            ),
            Expanded(
              child: ListView.builder(
                itemCount: 100,
                itemBuilder: (BuildContext context, int index) {
                  return Row(
                    children: <Widget>[
                      Container(
                          padding: EdgeInsets.all(4.0),
                          width: 100.0,
                          child: Text(
                            "Name $index",
                            style: TextStyle(fontSize: 18),
                          )),
                      Container(
                        padding: EdgeInsets.all(4.0),
                        width: 100.0,
                        child: Text(
                          "Age $index",
                          style: TextStyle(fontSize: 18),
                        ),
                      )
                    ],
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

我在使用 ListView 时遇到了“A RenderFlex 在底部溢出 69 像素”的问题,这个问题对我有帮助。这里的技巧是将 ListView 包装在 Expanded 小部件中。当我使用 SingleChildScrollView 时不起作用,因为我想要不可滚动的标题和可滚动的列表。
B
Byaku

您可以像这样向项目列表中的第一项添加一列

  new ListView.builder(
    itemCount: litems.length,
    itemBuilder: (BuildContext ctxt, int index) {
      if (index == 0) {
        return Column(
          children: <Widget>[
            Header(),
            rowContent(index),
          ],
        );
      } else {
        return rowContent(index);
      }
    },
  )

是的,这非常简单直接。不过,我会说,现在这让我想尝试让它成为一个粘性标题。 :-)
好主意。简单易用。
u
user12208004

najeira 的解决方案简单易行,但您可以在不触及索引的情况下获得相同且更灵活的结果。

您可以使用与 listView 功能相同的 CustomScrollView 和 SliverList,而不是使用 listView。

   return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverToBoxAdapter(
            // you could add any widget
            child: ListTile(
              leading: CircleAvatar(
                backgroundColor: Colors.transparent,
              ),
              title: Row(
                children: <Widget>[
                  Expanded(child: Text("First Name")),
                  Expanded(child: Text("Last Name")),
                  Expanded(child: Text("City")),
                  Expanded(child: Text("Id")),
                ],
              ),
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) {
                return InkWell(
                  onTap: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) => APIDetailView(data[index])),
                    );
                  },
                  child: ListTile(
                    //return  ListTile(
                    leading: CircleAvatar(
                      backgroundColor: Colors.blue,
                      child: Text(data[index]["FirstName"][0]),
                    ),
                    title: Row(
                      children: <Widget>[
                        Expanded(child: Text(data[index]["FirstName"])),
                        Expanded(child: Text(data[index]["LastName"])),
                        Expanded(child: Text(data[index]["Bill_City"])),
                        Expanded(child: Text(data[index]["Customer_Id"])),
                      ],
                    ),
                  ),
                );
              },
              childCount: data == null ? 0 : data.length,
            ),
          ),
        ],
      ),
    );

如果列表项具有固定高度,我建议使用 SliverFixedExtentList 而不是 SliverList。只需指定 itemExtent,滚动效果会更好。
K
Kab Agouda

使用 DataTable 小部件!
该小部件允许您构建表格。 代码: DataTable(columns: [], rows: [],)

例子 :

  DataTable(
   columns: [
     DataColumn(label: Text('Lang.')),
     DataColumn(label: Text('Year')),
   ],
   rows: [
     DataRow(cells: [DataCell(Text('Dart')), DataCell(Text('2010'))]),
     DataRow(cells: [DataCell(Text('Go')), DataCell(Text('2009'))]),
     DataRow(cells: [DataCell(Text('PHP')), DataCell(Text('1994'))]),
     DataRow(cells: [DataCell(Text('Java')), DataCell(Text('1995'))]),
   ],
)

输出:

https://i.stack.imgur.com/oJ0Xj.png

您可以通过观看此 official video 或访问 flutter.dev 了解有关 DataTable 的更多信息


d
derZorn

看来您真正要寻找的是 DataTable 小部件而不是 ListView。它有一个可定制的标题,包括排序选项。

https://i.stack.imgur.com/ZSoDW.png


T
TheMisir

我创建了 listview_utils 包以减少构建页眉和页脚列表项所需的样板代码。这是使用该包的示例代码:

import 'package:listview_utils/listview_utils.dart';

CustomListView(
  header: Container(
    child: Text('Header'),
  ),
  itemCount: items.length,
  itemBuilder: (BuildContext context, int index, _) {
    return ListTile(
      title: Text(item['title']),
    );
  },
);

我正在维护这个包。


B
Bilal Şimşek

根据您的 api 数据查找动态部分标题。将此类添加到您的项目中。

class _FlutterSectionListViewState extends State<FlutterSectionListView> {
  /// List of total number of rows and section in each group
  var itemList = [];
  int itemCount = 0;
  int sectionCount = 0;

  @override
  void initState() {
    /// ----#4
    sectionCount = widget.numberOfSection();

    /// ----#5
    itemCount = listItemCount();
    super.initState();
  }

  /// ----#6
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: itemCount,
      itemBuilder: (context, index) {
        return buildItemWidget(index);
      },
      key: widget.key,
    );
  }

  /// Get the total count of items in list(including both row and sections)
  int listItemCount() {
    itemList = [];
    int rowCount = 0;

    for (int i = 0; i < sectionCount; i++) {
      /// Get the number of rows in each section using callback
      int rows = widget.numberOfRowsInSection(i);

      /// Here 1 is added for each section in one group
      rowCount += rows + 1;
      itemList.insert(i, rowCount);
    }
    return rowCount;
  }

  /// ----#7
  /// Get the widget for each item in list
  Widget buildItemWidget(int index) {
    /// ----#8
    IndexPath indexPath = sectionModel(index);

    /// ----#9
    /// If the row number is -1 of any indexPath it will represent a section else row
    if (indexPath.row < 0) {
      /// ----#10
      return widget.sectionWidget != null
          ? widget.sectionWidget!(indexPath.section)
          : SizedBox(
              height: 0,
            );
    } else {
      return widget.rowWidget!(indexPath.section, indexPath.row);
    }
  }

  /// Calculate/Map the indexPath for an item Index
  IndexPath sectionModel(int index) {
    int? row = 0;
    int section = 0;
    for (int i = 0; i < sectionCount; i++) {
      int item = itemList[i];
      if (index < item) {
        row = (index - (i > 0 ? itemList[i - 1] : 0) - 1) as int?;
        section = i;
        break;
      }
    }
    return IndexPath(section: section, row: row!);
  }
}

/// Helper class for indexPath of each item in list
class IndexPath {
  IndexPath({required this.section, required this.row});

  int section = 0;
  int row = 0;
}

根据您的 api 数据创建您的列表

 List<List<Operator>> ops = [];
        List<String> sections = [];
        if(c.operatorStatuses.value!.availableOperators.length>0){
           ops.add(c.operatorStatuses.value!.availableOperators);
           sections.add("Müsait Operatörler");
        }

         if(c.operatorStatuses.value!.busyOperators.length>0){
           ops.add(c.operatorStatuses.value!.busyOperators);
           sections.add("Meşgul Operatörler");
        }
         if(c.operatorStatuses.value!.breakOperators.length>0){
             ops.add(c.operatorStatuses.value!.breakOperators);
           sections.add("Moladaki Operatörler");
        }
         if(c.operatorStatuses.value!.closedOperators.length>0){
             ops.add(c.operatorStatuses.value!.closedOperators);
           sections.add("Kapalı Operatörler");
        }
       

在ui中显示;

   FlutterSectionListView(
          numberOfSection: () => ops.length,
          numberOfRowsInSection: (section) {
            return ops[section].length;
          },
          sectionWidget: (section) {
            if(section<ops.length){
               return Container(
              child: Padding(
                padding: const EdgeInsets.all(8),
                child: Text(sections[section]),
              ),
              color: Colors.grey,
            );
            }else{
              return SizedBox();
            }
           
          },
          rowWidget: (section, row) {

            if(row < ops[section].length){
Operator? op = ops[section][row];
          
          
            return card(op);
            }else{
              return SizedBox();
            }
           
             
          },
        )

感谢[这篇文章][1]。

注意:代码块根据更新的数据有时会产生错误。[1]:https://medium.com/@dharmendra_yadav/ios-like-sectioned-listview-widget-in-flutter-7cf9dab2dd1a


S
Shadros

我用这个:

body: Column(
      children: [
        Container(
          // The header will be here
        ),
        Expanded(
          // The ListView
          child: ListView.builder(
              itemCount: // The length,
              itemBuilder: (_, index) {
                return //List Item Widget Here
              }),
        ),
      ],

)