[Flutter]flutter基础之组件基础(十九)

一、AboutListTile Widget

AboutListTile 是显示关于框的 ListTile ,继承自 StatelessWidgetAboutListTile 点击时显示一个带有 showAboutDialog 的关于对话框。此 Widget 常用在 Drawer 中,Drawer 在前述文章介绍过。如果不是在 Drawer 中使用,则应选择调用 showAboutDialogshowLicensePage

构造方法如下:

1
const AboutListTile({
2
  Key key,
3
  //Widget类型可选命名参数,显示的图标
4
  this.icon,
5
  //Widget类型可选命名参数,子Widget
6
  this.child,
7
  //String类型可选命名参数,应用程序名称
8
  this.applicationName,
9
  //String类型可选命名参数,此应用程序版本的版本
10
  this.applicationVersion,
11
  //Widget类型可选命名参数,在AboutDialog中显示在应用程序名称旁边的图标
12
  this.applicationIcon,
13
  //String类型可选命名参数,在AboutDialog中以小字体显示的字符串
14
  this.applicationLegalese,
15
  //List<Widget>类型可选命名参数,在名称,版本和legalese之后添加到AboutDialog的小部件
16
  this.aboutBoxChildren,
17
  //bool类型可选命名参数,此列表图块是否是垂直密集列表的一部分
18
  this.dense,
19
})

使用如下:

1
import 'package:flutter/material.dart';
2
3
void main() => runApp(MyApp());
4
5
class MyApp extends StatelessWidget {
6
  @override
7
  Widget build(BuildContext context) {
8
    return MaterialApp(
9
      home: MyHomePage(),
10
    );
11
  }
12
}
13
14
class MyHomePage extends StatefulWidget {
15
  @override
16
  _MyHomePageState createState() => _MyHomePageState();
17
}
18
19
class _MyHomePageState extends State<MyHomePage> {
20
  @override
21
  Widget build(BuildContext context) {
22
    return Scaffold(
23
      appBar: AppBar(
24
        backgroundColor: Colors.blue,
25
        title: Text("HomePage"),
26
      ),
27
      endDrawer: Drawer(
28
        child: ListView(
29
          children: <Widget>[
30
            AboutListTile(
31
              icon: Icon(Icons.home),
32
              child: Text("关于"),
33
              applicationName: "标题",
34
              applicationVersion: "2.1",
35
              applicationIcon: Icon(Icons.map),
36
              applicationLegalese: "说明文本",
37
              aboutBoxChildren: <Widget>[
38
                Text("about"),
39
              ],
40
            ),
41
          ],
42
        ),
43
      ),
44
      body:Center(
45
        child: Container(
46
        ),
47
      ),
48
    );
49
  }
50
}

效果如下:

2020421454

二、AnimatedIcon Widget

AnimatedIcon 是显示动画图标的 Widget ,继承自 StatelessWidget 。构造方法如下:

1
const AnimatedIcon({
2
  Key key,
3
  //AnimatedIconData类型必传参数,显示的图标,可用图标在AnimatedIcons中列出
4
  @required this.icon,
5
  //Animation<double>类型必传参数,动画图标的动画进度
6
  @required this.progress,
7
  //Color类型可选命名参数,绘制图标使用的颜色
8
  this.color,
9
  //double类型可选命名参数,图标的大小(以逻辑像素为单位)
10
  this.size,
11
  //String类型可选命名参数,图标的语义标签
12
  this.semanticLabel,
13
  //TextDirection类型可选命名参数,用于呈现图标的文本方向
14
  this.textDirection,
15
})

Animation 是动画的基类,在后续文章的动画部分会做详细介绍。

AnimatedIcons 中提供了很多可用的 AnimatedIconData 类型图标,查看地址:https://api.flutter.dev/flutter/material/AnimatedIcons-class.html

使用如下:

1
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin{
2
  AnimationController _controller;
3
  @override
4
  void initState() {
5
    super.initState();
6
    _controller = AnimationController(vsync: this, duration: Duration(seconds: 2))
7
      ..addListener((){
8
        setState(() {
9
        });
10
      })
11
      ..forward();
12
  }
13
  @override
14
  Widget build(BuildContext context) {
15
    return Scaffold(
16
      appBar: AppBar(
17
        backgroundColor: Colors.blue,
18
        title: Text("HomePage"),
19
      ),
20
      body:AnimatedIcon(   					//AnimatedIcon
21
        icon: AnimatedIcons.arrow_menu,
22
        progress: _controller,
23
        color: Colors.red,
24
        size: 100,
25
      ),
26
    );
27
  }
28
}

效果如下:

20202421753

三、BackButton Widget

BackButton 是 Material 设计风格的回退按钮,继承自 StatelessWidgetBackButton 是一个图标按钮,内部基于 IconButton 实现。其内部的 BackButtonIcon 会根据不同的平台环境显示适合特定平台的回退图标。默认情况下,在不设置 onPressed 回调的情况,回退按钮调用 Navigator.maybePop 返回上一条路线。

决定显示 BackButton 时,请考虑使用 ModalRoute.of(context)?.canPop 检查是否可以弹出当前路由。如果该值为假(例如,因为当前路径是初始路径),则按下 BackButton 不会有任何效果。

构造方法如下:

1
const BackButton({
2
  Key key, 
3
  //Color类型可选命名参数,使用的图标的颜色
4
  this.color,
5
  //VoidCallback类型可选命名参数,要执行的替代回调,而不是弹出导航器的默认行为
6
  this.onPressed 
7
})

onPressedVoidCallback 类型,是一个无返回值的方法,定义为:void Function();

使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body: BackButton(
10
        color: Colors.red,
11
        onPressed: (){
12
          if(ModalRoute.of(context).canPop){
13
            print("回退");
14
          }else{
15
            print("不可回退");
16
          }
17
        },
18
      ),
19
    );
20
  }
21
}

效果如下:

20204221043

四、Banner Widget

Banner 是在另一个 Widget 的角上方显示一条对角线消息的 Widget ,继承自 StatelessWidget 。此 Widget 对于显示应用程序的执行模式很有用。构造方法如下:

1
const Banner({
2
  Key key,
3
  //Widget类型可选命名参数,显示在横幅后面的小部件
4
  this.child,
5
  //String类型必传参数,显示在横幅中的消息
6
  @required this.message,
7
  //TextDirection类型可选命名参数,文本方向
8
  this.textDirection,
9
  //BannerLocation类型必传参数,显示横幅的位置(例如,右上角)
10
  @required this.location,
11
  //TextDirection类型可选命名参数,布局方向
12
  this.layoutDirection,
13
  //Color类型可选命名参数,Banner的颜色
14
  this.color = _kColor,
15
  //TextStyle类型可选命名参数,标语上显示的文字样式
16
  this.textStyle = _kTextStyle,
17
})

其中 locationBannerLocation 类型,是一个枚举类型值,定义如下:

1
enum BannerLocation {
2
  //当环境Directionality(或Banner.layoutDirection)为TextDirection.rtl时,在右上角显示横幅;
3
  //当环境Directionality为TextDirection.ltr时,在左上角显示横幅
4
  topStart,
5
  //当环境Directionality(或Banner.layoutDirection)为TextDirection.rtl时,在左上角显示横幅;
6
  //当环境Directionality为TextDirection.ltr时,在右上角显示横幅
7
  topEnd,
8
  //当环境Directionality(或Banner.layoutDirection)为TextDirection.rtl时,在右下角显示横幅;
9
  //当环境Directionality为TextDirection.ltr时,在左下角显示横幅
10
  bottomStart,
11
  //当环境Directionality(或Banner.layoutDirection)为TextDirection.rtl时,在左下角显示横幅;
12
  //当环境Directionality为TextDirection.ltr时,在右下角显示横幅
13
  bottomEnd,
14
}

Banner 使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body: Container(
10
        width: 200,
11
        height: 200,
12
        color: Colors.red,
13
        child: Banner(
14
          child: Text("子Widget"),
15
          layoutDirection: TextDirection.rtl,
16
          color: Colors.blue,
17
          message: "Banner",
18
          location: BannerLocation.topStart,
19
        ),
20
      ),
21
    );
22
  }
23
}

效果如下:

20204221124

五、CheckedModeBanner Widget

CheckedModeBanner 是在检查模式下运行时显示 “DEBUG” 的横幅,继承自 StatelessWidgetMaterialApp 默认会构建其中一个。在发布模式中什么都不做。构造方法如下:

1
const CheckedModeBanner({
2
  Key key,
3
  //Widget类型必传参数,显示在横幅后面的小部件
4
  @required this.child,
5
})

使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body: Container(
10
        width: 200,
11
        height: 200,
12
        color: Colors.red,
13
        child: CheckedModeBanner(
14
          child: Text("子Widget"),
15
        ),
16
      ),
17
    );
18
  }
19
}

效果与 Banner 类似,不同的只是不能设置特定的属性。

六、ButtonBar Widget

ButtonBar 是一个可以包含多个子 Widget 并在水平方向末端对齐的 Widget ,继承自 StatelessWidget 。其子 Widget 通常为按钮,可以通过其属性的设置来调整按钮的宽高等,其他非按钮类 Widget 则不受属性约束。其默认为水平布局,当水平布局无法容纳所有子 Widget 的宽度时,则会变为纵向布局。

可以使用 ButtonBarTheme 配置 ButtonBar 。对于 ButtonBar 上的任何 null 属性,将改用周围的 ButtonBarTheme 属性。如果 ButtonBarTheme 的属性也为 null ,则该属性将默认为以下字段文档中描述的值。

children 中的子 Widget 可以包装在 ButtonTheme 中,对特定的子 Widget 进行属性设置。

ButtonBar 构造方法如下:

1
const ButtonBar({
2
  Key key,
3
  //MainAxisAlignment类型可选命名参数,如何沿水平轴放置孩子
4
  this.alignment,
5
  //MainAxisSize类型可选命名参数,有多少水平空间可用
6
  this.mainAxisSize,
7
  //ButtonTextTheme类型可选命名参数,重写周围的ButtonTheme.textTheme,以定义按钮的基本颜色,大小,
8
  //内部填充和形状
9
  this.buttonTextTheme,
10
  //double类型可选命名参数,按钮的最小宽度
11
  this.buttonMinWidth,
12
  //double类型可选命名参数,按钮的最小高度
13
  this.buttonHeight,
14
  //EdgeInsetsGeometry类型可选命名参数,覆盖周围的ButtonThemeData.padding,以定义按钮子级的填充
15
  //(通常是按钮的标签)
16
  this.buttonPadding,
17
  //bool类型可选命名参数,重写周围的ButtonThemeData.alignedDropdown来定义DropdownButton菜单的宽
18
  //度是否与按钮的宽度匹配
19
  this.buttonAlignedDropdown,
20
  //ButtonBarLayoutBehavior类型可选命名参数,定义ButtonBar应该使用最小尺寸限制还是填充来调整自身大小
21
  this.layoutBehavior,
22
  //List<Widget>类型可选命名参数,水平排列的按钮
23
  this.children = const <Widget>[],
24
})

其中,buttonTextThemeButtonTextTheme 类型,是一个枚举类型值,定义如下:

1
enum ButtonTextTheme {
2
  //按钮文本是黑色还是白色,具体取决于ThemeData.brightness
3
  normal,
4
  //按钮文本为ThemeData.accentColor
5
  accent,
6
  //按钮文本基于ThemeData.primaryColor
7
  primary,
8
}

layoutBehaviorButtonBarLayoutBehavior 类型,用于与 ButtonThemeButtonThemeData 一起使用以定义按钮栏应如何使用约束或内部填充来调整自身大小。是一个枚举类型值,如下:

1
enum ButtonBarLayoutBehavior {
2
  //按钮栏将被限制为最小高度52。需要进行此设置才能创建符合材料规格的按钮条。
3
  constrained,
4
  //按钮栏将从按钮主题填充中计算其填充
5
  padded,
6
}

ButtonBar 使用如下:

1
import 'package:flutter/material.dart';
2
3
void main() => runApp(MyApp());
4
5
class MyApp extends StatelessWidget {
6
  @override
7
  Widget build(BuildContext context) {
8
    return MaterialApp(
9
      theme: ThemeData(
10
        accentColor: Colors.red,    //配合ButtonTextTheme.accent设置按钮文本颜色
11
        buttonTheme: ButtonThemeData(
12
          disabledColor: Colors.red,   //按钮不可用状态颜色
13
          buttonColor: Colors.greenAccent,  //按钮启用状态颜色
14
        ),
15
        buttonBarTheme: ButtonBarThemeData(
16
            mainAxisSize: MainAxisSize.min,
17
        ),
18
      ),
19
      home: MyHomePage(),
20
    );
21
  }
22
}
23
24
class MyHomePage extends StatefulWidget {
25
  @override
26
  _MyHomePageState createState() => _MyHomePageState();
27
}
28
29
class _MyHomePageState extends State<MyHomePage> {
30
  @override
31
  Widget build(BuildContext context) {
32
    return Scaffold(
33
      appBar: AppBar(
34
        backgroundColor: Colors.blue,
35
        title: Text("HomePage"),
36
      ),
37
      body: Container(
38
        color: Colors.yellow,
39
        child: ButtonBar(
40
          buttonTextTheme: ButtonTextTheme.accent,
41
          buttonMinWidth: 100,
42
          buttonHeight: 200,
43
          children: <Widget>[
44
            RaisedButton(
45
              child: Text("按钮1"),
46
              onPressed: (){},
47
            ),
48
            RaisedButton(
49
              child: Text("按钮2"),
50
            ),
51
            ButtonTheme(
52
              disabledColor: Colors.green,
53
              height: 150,
54
              child:  RaisedButton(
55
                child: Text("按钮3"),
56
              ),
57
            ),
58
          ],
59
        ),
60
      ),
61
    );
62
  }
63
}

效果如下:

2020422346

七、DataTable Widget

DataTable 是一个 Material 设计风格的数据表,继承自 StatelessWidget 。在表中显示数据非常昂贵,因为要对表进行布局,必须对所有数据进行两次测量,一次要协商用于每一列的维,一次要根据协商的结果实际对表进行布局。因此,如果您有很多数据(比如,超过12行,有12列,但是精确的限制取决于目标设备),建议您使用 PaginatedDataTable ,它会自动将数据分割成多个页面。

构造方法如下:

1
DataTable({
2
  Key key,
3
  //List<DataColumn>类型必传参数,表中各列的配置和标签
4
  @required this.columns,
5
  //int类型可选命名参数,当前主排序键的列
6
  this.sortColumnIndex,
7
  //bool类型可选命名参数,sortColumnIndex中提到的列(如果有)是否按升序排序
8
  this.sortAscending = true,
9
  //ValueSetter<bool>类型可选命名参数,当用户使用标题行中的复选框选择或取消选择每一行时调用
10
  this.onSelectAll,
11
  //double类型可选命名参数,每行的高度(不包括包含列标题的行)
12
  this.dataRowHeight = kMinInteractiveDimension,
13
  //double类型可选命名参数,标题行的高度
14
  this.headingRowHeight = 56.0,
15
  //double类型可选命名参数,表格边缘与每行第一个和最后一个单元格中的内容之间的水平边距
16
  this.horizontalMargin = 24.0,
17
  //double类型可选命名参数,每个数据列内容之间的水平边距
18
  this.columnSpacing = 56.0,
19
  //List<DataRow>类型必传参数,每行要显示的数据(不包括包含列标题的行)
20
  @required this.rows,
21
})

其中,DataColumn 用于配置数据表列数据。必须为每一列提供一个列配置。构造方法如下:

1
const DataColumn({
2
  //Widget类型必传参数,列标题
3
  @required this.label,
4
  //String类型可选命名参数,列标题的工具提示
5
  this.tooltip,
6
  //bool类型可选命名参数,此列是否代表数字数据
7
  this.numeric = false,
8
  //DataColumnSortCallback类型可选命名参数,当用户要求使用此列对表格进行排序时调用
9
  this.onSort,
10
})

DataRow 用于配置行数据,一个 DataRow 代表一行。必须为每一行提供一个行配置。构造方法如下:

1
//为数据表的一行创建配置
2
const DataRow({
3
  this.key,
4
  //bool类型可选命名参数,是否选择该行
5
  this.selected = false,
6
  //ValueChanged<bool>类型可选命名参数,当用户选择或取消选择可选行时调用
7
  this.onSelectChanged,
8
  //List<DataCell>类型必传参数,该行的数据
9
  @required this.cells,
10
}) 
11
  
12
//从行索引派生键,为数据表的行创建配置
13
DataRow.byIndex({
14
  //int类型可选命名参数,索引值
15
  int index,
16
  //bool类型可选命名参数,是否选择该行
17
  this.selected = false,
18
  //ValueChanged<bool>类型可选命名参数,当用户选择或取消选择可选行时调用
19
  this.onSelectChanged,
20
  //List<DataCell>类型必传参数,该行的数据
21
  @required this.cells,
22
})

DataCell 用于配置每个单元格数据。必须在新的 DataRow 构造函数的 cells 参数中为 DataTable 中的每个 DataRow 提供一个 DataCell 对象列表。构造方法如下:

1
const DataCell(
2
  //Widget类型必传参数,该行的数据  
3
  this.child, {
4
  //bool类型可选命名参数,孩子实际上是否是占位符
5
  this.placeholder = false,
6
  //bool类型可选命名参数,是否在单元格末尾显示编辑图标
7
  this.showEditIcon = false,
8
  //VoidCallback类型可选命名参数,如果单元格被点击则调用
9
  this.onTap,
10
  })

DataTable 的基本使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  bool _glAscending = true;
3
  List<UserInfo> students = [
4
    UserInfo(stuNum: 1, stuName: "张三", stuAge: 20, stuSex: "男"),
5
    UserInfo(stuNum: 2, stuName: "李四", stuSex: "男", isSelected: true),
6
    UserInfo(stuNum: 3, stuName: "王五", stuAge: 15, stuSex: "女"),
7
    UserInfo(stuNum: 4, stuName: "赵六", stuAge: 18, stuSex: "男"),
8
  ];
9
  @override
10
  Widget build(BuildContext context) {
11
    return Scaffold(
12
      appBar: AppBar(
13
        backgroundColor: Colors.blue,
14
        title: Text("HomePage"),
15
      ),
16
      body: Container(
17
        color: Colors.yellow,
18
        child: DataTable(
19
          sortColumnIndex: 0,
20
          sortAscending: _glAscending,
21
          onSelectAll: (bool select){
22
            setState(() {
23
              for(UserInfo info in students){
24
                info.isSelected = select;
25
              }
26
            });
27
          },
28
          columns: <DataColumn>[
29
            DataColumn(
30
              label: Text("编号"),
31
              numeric: true,
32
              onSort: (int columnIndex, bool ascending){
33
                setState(() {
34
                  _glAscending = ascending;
35
                  if(ascending){
36
                    students.sort((a, b)=> a.stuNum.compareTo(b.stuNum));
37
                  }else{
38
                    students.sort((a, b)=> b.stuNum.compareTo(a.stuNum));
39
                  }
40
                });
41
              },
42
            ),
43
            DataColumn(
44
              label: Text("姓名"),
45
            ),
46
            DataColumn(
47
              label: Text("年龄"),
48
            ),
49
            DataColumn(
50
              label: Text("性别"),
51
            ),
52
          ],
53
          rows: students.map((UserInfo userInfo){
54
            return DataRow(
55
              selected: userInfo.isSelected,
56
              onSelectChanged: (bool selected){
57
                setState(() {
58
                  userInfo.isSelected = selected;
59
                });
60
              },
61
              cells: <DataCell>[
62
                DataCell(Text("${userInfo.stuNum}")),
63
                DataCell(Text("${userInfo.stuName}")),
64
                DataCell(Text("${userInfo.stuAge}")),
65
                DataCell(Text("${userInfo.stuSex}"), onTap: (){
66
                  print(userInfo.stuSex);
67
                }),
68
              ],
69
            );
70
          }).toList(),
71
        ),
72
      ),
73
    );
74
  }
75
}
76
77
class UserInfo {
78
  UserInfo({this.stuNum, this.stuName, this.stuAge = 0, this.stuSex, this.isSelected = false});
79
  int stuNum;
80
  String stuName;
81
  int stuAge;
82
  String stuSex;
83
  bool isSelected;
84
}

效果如下:

2020423321

DataTable 本身是不提供滚动功能的,如果数据在横向或纵向超出可视范围,需要移动,可将 DataTable 封装在 SingleChildScrollView 等可滚动 Widget 中即可。

八、PaginatedDataTable Widget

PaginatedDataTable 是一个 Material 设计风格的数据表,其使用多个页面显示数据,继承自 StatefulWidget 。它惰性的读取数据。构造方法如下:

1
PaginatedDataTable({
2
  Key key,
3
  //Widget类型必传参数,头部Widget
4
  @required this.header,
5
  //List<Widget>类型可选命名参数,图标按钮显示在表格的右上方
6
  this.actions,
7
  //List<DataColumn>类型必传参数,表中各列的配置和标签
8
  @required this.columns,
9
  //int类型可选命名参数,当前主排序键的列
10
  this.sortColumnIndex,
11
  //bool类型可选命名参数,sortColumnIndex中提到的列(如果有)是否按升序排序
12
  this.sortAscending = true,
13
  //ValueSetter<bool>类型可选命名参数,当用户使用标题行中的复选框选择或取消选择每一行时调用
14
  this.onSelectAll,
15
  //double类型可选命名参数,每行的高度(不包括包含列标题的行)
16
  this.dataRowHeight = kMinInteractiveDimension,
17
  //double类型可选命名参数,标题行的高度
18
  this.headingRowHeight = 56.0,
19
  //double类型可选命名参数,表的边缘与每行的第一个和最后一个单元格中的内容之间的水平边距
20
  this.horizontalMargin = 24.0,
21
  //double类型可选命名参数,每个数据列内容之间的水平边距
22
  this.columnSpacing = 56.0,
23
  //int类型可选命名参数,第一次创建窗口小部件时要显示的第一行的索引
24
  this.initialFirstRowIndex = 0,
25
  //ValueChanged<int>类型可选命名参数,当用户切换到另一个页面时调用
26
  this.onPageChanged,
27
  //int类型可选命名参数,每页上显示的行数,默认为10行
28
  this.rowsPerPage = defaultRowsPerPage,
29
  //List<int>类型可选命名参数,为rowsPerPage提供的选项
30
  this.availableRowsPerPage = const <int>[defaultRowsPerPage, defaultRowsPerPage * 2, defaultRowsPerPage * 5, defaultRowsPerPage * 10],
31
  //ValueChanged<int>类型可选命名参数,当用户选择每页不同的行数时调用
32
  this.onRowsPerPageChanged,
33
  //DragStartBehavior类型可选命名参数,确定处理拖动开始行为的方式
34
  this.dragStartBehavior = DragStartBehavior.start,
35
  //DataTableSource类型必传参数,提供数据以显示在每一行中的数据源。必须为非null
36
  @required this.source,
37
})

其中 DataTableSource 用于提供每一行的数据源,继承自 ChangeNotifier ,是一个抽象类。数据源提供两个主要信息:数据表中的行数( rowCount ) 和每行的数据。因为其为一个抽象类,且没有提供任何的可用子类,所以需要自定义类并继承 DataTableSource ,必须重写 DataRow getRow(int index) 方法和 isRowCountApproximaterowCountselectedRowCount 属性。

实现如下:

1
import 'package:flutter/material.dart';
2
3
void main() => runApp(MyApp());
4
5
class MyApp extends StatelessWidget {
6
  @override
7
  Widget build(BuildContext context) {
8
    return MaterialApp(
9
      home: MyHomePage(),
10
    );
11
  }
12
}
13
14
class MyHomePage extends StatefulWidget {
15
  @override
16
  _MyHomePageState createState() => _MyHomePageState();
17
}
18
19
class _MyHomePageState extends State<MyHomePage> {
20
  List<UserInfo> students = [
21
    UserInfo(stuNum: 1, stuName: "张三", stuAge: 20, stuSex: "男"),
22
    UserInfo(stuNum: 2, stuName: "李四", stuSex: "男"),
23
    UserInfo(stuNum: 3, stuName: "王五", stuAge: 15, stuSex: "女"),
24
    UserInfo(stuNum: 4, stuName: "赵六", stuAge: 18, stuSex: "男"),
25
    UserInfo(stuNum: 5, stuName: "张三", stuAge: 20, stuSex: "男"),
26
    UserInfo(stuNum: 6, stuName: "李四", stuSex: "男"),
27
    UserInfo(stuNum: 7, stuName: "王五", stuAge: 15, stuSex: "女"),
28
    UserInfo(stuNum: 4, stuName: "赵六", stuAge: 18, stuSex: "男"),
29
    UserInfo(stuNum: 5, stuName: "张三", stuAge: 20, stuSex: "男"),
30
    UserInfo(stuNum: 6, stuName: "李四", stuSex: "男"),
31
    UserInfo(stuNum: 7, stuName: "王五", stuAge: 15, stuSex: "女"),
32
    UserInfo(stuNum: 8, stuName: "赵六", stuAge: 18, stuSex: "男"),
33
  ];
34
35
  MyDataTableSource tableSource;
36
  int perPageValue;
37
  bool _glAscending = true;
38
  int valueIndex;
39
40
  @override
41
  void initState() {
42
    super.initState();
43
    tableSource = MyDataTableSource(students);
44
    //每页显示多少条数据
45
    perPageValue = 6;
46
  }
47
48
  //排序
49
  void sort(int columnIndex, bool ascending) {
50
    tableSource._sort(columnIndex, ascending);
51
    print(columnIndex);
52
    setState(() {
53
      valueIndex = columnIndex;
54
      _glAscending = ascending;
55
    });
56
  }
57
58
  @override
59
  Widget build(BuildContext context) {
60
    return Scaffold(
61
      appBar: AppBar(
62
        backgroundColor: Colors.blue,
63
        title: Text("HomePage"),
64
      ),
65
      body: Container(
66
        color: Colors.yellow,
67
        child: PaginatedDataTable(
68
          header: Text("学生信息记录表"),
69
          //每页显示行数
70
          rowsPerPage: perPageValue,
71
          //全选时的回调方法
72
          onSelectAll: tableSource._selectAll,
73
          //升序或降序
74
          sortAscending: _glAscending,
75
          //排序的主键列
76
          sortColumnIndex: valueIndex,
77
          //点击翻页时的回调
78
          onPageChanged: (int value){
79
          },
80
          //改变每页显示的行数时的回调
81
          onRowsPerPageChanged: (int value){
82
            setState(() {
83
              perPageValue = value;
84
            });
85
          },
86
          //每页可以选择的显示行数选项
87
          availableRowsPerPage: [
88
            6, 12
89
          ],
90
          columns: <DataColumn>[
91
            DataColumn(
92
              label: Text("编号"),
93
              numeric: true,
94
              onSort: sort,
95
            ),
96
            DataColumn(
97
              label: Text("姓名"),
98
            ),
99
            DataColumn(
100
              label: Text("年龄"),
101
            ),
102
            DataColumn(
103
              label: Text("性别"),
104
            ),
105
          ],
106
          source: tableSource,
107
        ),
108
      ),
109
    );
110
  }
111
}
112
113
class MyDataTableSource extends DataTableSource {
114
  MyDataTableSource(this.stuList);
115
  final List<UserInfo> stuList;
116
  int _selectRowCount = 0;
117
118
  @override
119
  DataRow getRow(int index) {
120
    if(index > stuList.length || index < 0) {
121
      return null;
122
    }
123
    final UserInfo info = stuList[index];
124
    return DataRow.byIndex(
125
      index: index,
126
      selected: info.isSelected,
127
      onSelectChanged: (bool selected){
128
        info.isSelected = selected;
129
        _selectRowCount += selected ? 1 : -1;
130
        notifyListeners();
131
      },
132
      cells: <DataCell>[
133
        DataCell(Text("${info.stuNum}")),
134
        DataCell(Text("${info.stuName}")),
135
        DataCell(Text("${info.stuAge}")),
136
        DataCell(Text("${info.stuSex}"), onTap: (){
137
          print(info.stuSex);
138
        }),
139
      ],
140
    );
141
  }
142
143
  @override
144
  //行数书否不确定
145
  bool get isRowCountApproximate => false;
146
147
  @override
148
  //总行数
149
  int get rowCount => stuList.length;
150
151
  @override
152
  //已经选择的行数
153
  int get selectedRowCount => _selectRowCount;
154
155
  //排序
156
  void _sort(int columnIndex, bool ascending) {
157
    if(ascending){
158
      stuList.sort((a, b)=> a.stuNum.compareTo(b.stuNum));
159
    }else{
160
      stuList.sort((a, b)=> b.stuNum.compareTo(a.stuNum));
161
    }
162
    notifyListeners();
163
  }
164
165
  //选择所有
166
  void _selectAll(bool isSelected){
167
    for(UserInfo uInfo in stuList) {
168
      uInfo.isSelected = isSelected;
169
    }
170
    _selectRowCount = isSelected ? stuList.length : 0;
171
    notifyListeners();
172
  }
173
}
174
175
class UserInfo {
176
  UserInfo({this.stuNum, this.stuName, this.stuAge = 0, this.stuSex, this.isSelected = false});
177
  int stuNum;
178
  String stuName;
179
  int stuAge;
180
  String stuSex;
181
  bool isSelected;
182
}

显示效果如下:

2020427709

上述代码中,在自定义继承 DataTableSource 的类中使用了 notifyListeners(); 方法。notifyListeners();DataTableSource 的父类 ChangeNotifier 提供的方法,当数据更改时,调用此方法可以发送通知。

九、Divider Widget

Divider 是一条细的水平线,两侧可设置填充宽度,继承自 StatelessWidget 。其可用于列表、抽屉和其他位置以分割内容。要在 ListTile 项之间创建分隔线,考虑使用针对该情况进行了优化的 ListTile.divideTiles 。 构造方法如下:

1
const Divider({
2
  Key key,
3
  //double类型可选命名参数,分割线的高度范围
4
  this.height,
5
  //double类型可选命名参数,分隔线内绘制的线的粗细
6
  this.thickness,
7
  //double类型可选命名参数,分割线前的空白空间
8
  this.indent,
9
  //double类型可选命名参数,分隔线后缘的空白空间量
10
  this.endIndent,
11
  //Color类型可选命名参数,分割线的颜色
12
  this.color,
13
})

使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body: Container(
10
        color: Colors.yellow,
11
        child: Divider(    //Divider
12
          height: 100,
13
          thickness: 10,
14
          indent: 40,
15
          endIndent: 40,
16
          color: Colors.red,
17
        ),
18
      ),
19
    );
20
  }
21
}

效果如下:

2020427833

十、DraggableScrollableSheet Widget

DraggableScrollableSheet 是一个可滚动的容器 Widget ,通过调整可滚动的大小直到达到限制,然后滚动来响应拖动手势,继承自 StatefulWidget 。此 Widget 最初显示为 initialChildSize 设置的比例,如果在 builder 中构建的 Widget 使用了提供的 ScrollController 滚动控制器,则其显示范围为 minChildSizemaxChildSize ,如果未使用 ScrollController ,则显示范围始终为 initialChildSize ,不可滚动。

构造方法如下:

1
const DraggableScrollableSheet({
2
  Key key,
3
  //double类型可选命名参数,显示窗口小部件时要使用的父容器高度的初始小数值
4
  this.initialChildSize = 0.5,
5
  //double类型可选命名参数,显示的子Widget占用父级的最小百分比,其比例不能大于initialChildSize
6
  this.minChildSize = 0.25,
7
  //double类型可选命名参数,显示的子Widget占用父级的最大百分比
8
  this.maxChildSize = 1.0,
9
  //bool类型可选命名参数,小部件是否应该扩展以填充其父级中的可用空间
10
  this.expand = true,
11
  //ScrollableWidgetBuilder类型必传参数,该构建器创建一个要显示在此小部件中的子项,该子项将使用提供
12
  //的ScrollController启用内容的拖动和滚动
13
  @required this.builder,
14
})

其中,builderScrollableWidgetBuilder 类型,是一个返回值为 Widget 的方法,定义为:Widget Function(BuildContext context, ScrollController scrollController);

使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body: Container(
10
        color: Colors.yellow,
11
        child: DraggableScrollableSheet(
12
          initialChildSize: 0.8,
13
          minChildSize: 0.5,
14
          maxChildSize: 0.8,
15
          builder: (BuildContext context, ScrollController scrollController) {
16
            return Container(
17
              color: Colors.red,
18
              child: ListView.builder(
19
                  controller: scrollController,
20
                itemCount: 20,
21
                itemBuilder: (BuildContext context, int index) {
22
                  return ListTile(title: Text('Item $index'));
23
                },
24
              ),
25
            );
26
          },
27
        ),
28
      ),
29
    );
30
  }
31
}

效果如下:

2020428104

十一、DraggableScrollableActuator Widget

DraggableScrollableActuator 是一个可以通知其后代 DraggableScrollableSheet 将其位置重置为初始化状态的 Widget ,继承自 StatelessWidget 。构造方法如下:

1
DraggableScrollableActuator({
2
  Key key,
3
  //Widget类型必传参数,子Widget,应为DraggableScrollableSheet
4
  @required this.child,
5
})

使用如下:

1
class _MyHomePageState extends State<MyHomePage> {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        backgroundColor: Colors.blue,
7
        title: Text("HomePage"),
8
      ),
9
      body: Container(
10
        color: Colors.yellow,
11
        child: DraggableScrollableActuator(
12
          child: DraggableScrollableSheet(
13
            initialChildSize: 0.8,
14
            minChildSize: 0.5,
15
            maxChildSize: 0.8,
16
            builder: (BuildContext context, ScrollController scrollController) {
17
              return Container(
18
                color: Colors.red,
19
                child: ListView.builder(
20
                    controller: scrollController,
21
                  itemCount: 20,
22
                  itemBuilder: (BuildContext context, int index) {
23
                    return ListTile(
24
                      title: Text('Item $index'),
25
                      onTap: (){
26
                        DraggableScrollableActuator.reset(context);
27
                        },
28
                    );
29
                  },
30
                ),
31
              );
32
            },
33
          ),
34
        ),
35
      ),
36
    );
37
  }
38
}

效果如下:

2020428118
陌问.MW wechat
欢迎关注微信公众号,及时获取知识!