Flutter移动应用:MDC

原文地址:https://siques.cn/p/38
这个章节咱们继续介绍一些重要的 Material 风格的小部件app

Chip 是小碎片,咱们会学习建立各类不一样类型的 Chipide

好比带删除功能的 Chip,像按钮的 Chip ,像复选框同样的 Chip , 还有像单选按钮同样的 Chippost

而后再了解一下使用 DataTable 小部件去建立一个数据表,了解一下数据表的排序功能,还有数据行的选择功能,咱们还会去建立一个能够分页显示数据的数据表学习

接着咱们会再了解一下卡片小部件,添加一组卡片,还有一个步骤小部件,能够用它展现用户要完成的一系列的步骤=ui

Chip

在这里插入图片描述

Chip:就是一种像小标签,小碎片的东西。好比给内容选择的内容标签,过滤的条件这些东西,均可以使用 Chip 小部件来展现它们。

Chip:小碎片

几种chip的展现
imgspa

代码块:.net

Chip(
                  label: Text("Life"),
                ),
                SizedBox(width: 8.0),
                Chip(
                  label: Text("Sunset"),
                  backgroundColor: Colors.orange,
                ),
                SizedBox(width: 8.0),
                Chip(
                  label: Text("Shenghao"),
                  avatar: CircleAvatar(
                    backgroundColor: Colors.grey,
                    child: Text('豪'),
                  ),
                ),
                SizedBox(width: 8.0),
                Chip(
                  label: Text("Shenghao"),
                  avatar: CircleAvatar(
                    backgroundImage: NetworkImage(<图片地址>),
                  ),
                )

Wrap:换行显示小部件

如今界面的右边会出现一个警告,提示咱们有一部份内容超出了屏幕的显示 .. 这里咱们能够用一个 Wrap 小部件去包装一下这些 Chip .. 这样若是内容超出了屏幕的显示,会另起一行显示它插件

结构:
img3d

代码块:rest

import "package:flutter/material.dart";

class ChipDemo extends StatefulWidget {
  ChipDemo({Key key}) : super(key: key);

  @override
  _ChipDemoState createState() => _ChipDemoState();
}
~开始~
class _ChipDemoState extends State<ChipDemo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ChipDemo'),
        elevation: 0,
      ),
      body: Container(
        padding: EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Wrap(
              spacing: 8.0,
              // 行间隔
              runSpacing: 8.0,
              children: <Widget>[
                Chip(
                  label: Text("Life"),
                ),
                Chip(
                  label: Text("Sunset"),
                  backgroundColor: Colors.orange,
                ),
                Chip(
                  label: Text("Shenghao"),
                  avatar: CircleAvatar(
                    backgroundColor: Colors.grey,
                    child: Text('豪'),
                  ),
                ),
                Chip(
                  label: Text("Shenghao"),
                  avatar: CircleAvatar(
                    backgroundImage: NetworkImage(""),
                  ),
                )
              ],
            ),
          ],
        ),
      ),
    );
  }
}
~结束~

Divider:分隔符

Divider 这个小部件能够在界面上添加一个分隔符 .. 在这组 Chip 小部件的下面,添加一个 Divider .. 保存一下 .. 你会发现界面上会显示一条浅灰色的分隔线 ..

img

Chip:带删除功能的小碎片

在这个分隔符的上面,再添加一个 Chip ..

设置一下它的 label .. 一个 Text 小部件 .. 文字是 City ... 在 Chip 上面能够添加一个删除小图标 ..

按下这个删除小图标会执行 onDeleted ,先添
加一个 onDeleted .. 给它一个空白的方法 .. 如今这个 Chip 的右边会出现一个默认的删除按钮 .. 按一它就会执行 onDeleted 指定的动做 ..

img

Chip:用列表生成带删除功能的小碎片

先在类里面添加一个列表数据 .. 列表项目的值是 String .. 列表的名字叫 _tags .. 里面添加几个字符串类型的列表数据 .. 苹果 .. 香蕉 .. 还有柠檬 ..

下面咱们能够基于这个列表数据去生成一组 Chip .. 能够把它们放在一个 Wrap 里面 .. 添加一个 spacing 设置一下项目之间的间隔 .. 它的 children 属性能够用一下_tags .. map ,迭代处理一下 _tags 里的项目 .. 处理的结果转换成一个 List .. 用一下 toList .
img

代码块:

import "package:flutter/material.dart";

class ChipDemo extends StatefulWidget {
  ChipDemo({Key key}) : super(key: key);

  @override
  _ChipDemoState createState() => _ChipDemoState();
}
~开始~
class _ChipDemoState extends State<ChipDemo> {
  List<String> _tags = ['Apple', 'Banana', "Lemon"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ChipDemo'),
        elevation: 0,
      ),
      body: Container(
        padding: EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Wrap(
              spacing: 8.0,
              // 行间隔
              runSpacing: 8.0,
              children: <Widget>[

                Divider(
                  color: Colors.grey,
                  height: 32.0,
                  indent: 32.0,
                ),
                Wrap(
                  spacing: 8.0,
                  children: _tags.map((tag) {
                    return Chip(
                      label: Text(tag),
                      onDeleted: () {
                        setState(() {
                          _tags.remove(tag);
                        });
                      },
                    );
                  }).toList(),
                )
              ],
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.restore),
        onPressed: () {
          setState(() {
            _tags = ['Apple', 'Banana', "Lemon"];
          });
        },
      ),
    );
  }
}
~结束~

ActionChip:动做碎片

ActionChip 能够带一个动做,因此它的功能有点像是按钮 ... 先复制一份上面这个 Divider 还有这个 Wrap 小部件 ..

生成的这组 Chip 能够换成 ActionChip .. 这样它里面就不能再用 onDelete 了 .. ActionChip 里提供了一个 onPressed .. 点按 Chip 会执行这个属性指定的动做 .. 先给它一个空白的方法 ..
img

FilterChip:过滤碎片

FilterChip 的功能跟 Checkbox 或者 Switch 有点像 .. 它会有选中还有未选中两种状态 .. 先复制一份以前添加的 Divider ... Container .. 还有这个 Wrap ..

修改一下标题文字 .. FilterChip .. 后面加上一个 _selected.toString() .. 这个 _selected 是一个列表,表示的就是被选中的项目,toString 这个方法能够把列表转换成字符串 ..
img

ChoiceChip:选择碎片

ChoiceChip 有点像是单选按钮 .. 表示在一组东西里面的一个惟一的选择 .. 先在这个类里面添加一个新的数据 .. 类型是 String .. 名字能够叫 _choice .. 先让它等于 Lemon ..

img

DataTable

DataTable 小部件能够建立数据表格 .. 咱们能够把它放在一个 ListView 视图里面 .. 把这个 Column 换成一个 ListView ... 再去掉 mainAxisAlignment 属性 ..

DataTable:数据表格

在 ListView 的 children 里面,添加一个 DataTable .. 这个小部件有两个必须的属性,columns 还有 rows .. columns 里面是数据表格的栏目 ..

每一个栏目能够用一个 DataColumn 小部件建立 .. 这个小部件里面须要一个 label 属性 .. 一个 Text ,显示的文字是 Title ..

复制一份,再添加一个栏目 .. 栏目的标签是 Author ..

img

如今模拟器上显示的就是一个 DataTable .. 数据表格 .. 这个表格里面有两个 DataColumn ... Title 还有 Author ..

表格里面有两行具体的内容 .. 每一行内容都是一个 DataRow .. 这行内容里面,每一个单元格用的是 DataCell ...

DataTable:用列表生成数据表格

下面咱们能够一个列表去生成数据表格里的内容 .. 在项目的 model 目录的下面,有个 post.dart ... 这个文件里面定义了一个列表数据叫 posts .. 每一个项目都是一个 Post .. 里面有 title .. author .. description 还有 imageUrl 这几个属性 ..

再回到咱们的 DataTable 的演示 .. 先在这个文件的顶部导入刚才的文件 .. 位置是上一级目录下面的 model 里面的 post.dart ..

找到以前添加的 DataTable 里的 rows 属性 .. 去掉手工给它设置的这个 DataRow 列表 .. 这个 DataRow 列表如今咱们可使用一个列表数据生成 ..

用一下 posts .. map ,把迭代的结果转换成一个列表 .. 用一下 toList 方法 ..

img

DataTable:数据表格的排序

在 DataTable 小部件上添加一个 sortColumnIndex .. 它的值应该是排序的栏的索引号 .. 好比表示按第一栏内容排序,它的值应该设置成 0 ... 你会发现这个栏目的标签旁边会出现一个表示排序的符号 ..

默认是升序排列 .. 表示表格当前用的排序方法,可使用 sortAscending 属性控制 .. 若是是 true,就表示数据表格正在按栏目升序排列 .. 若是把它的值设置成 false,就表示当前表格是降序排列 ..

这两个属性的值应该动态去设置 .. 用 _sortColumnIndex 的值表示 sortColumnIndex ... 下面用 _sortAscending 的值表示 sortAscending ..

img

代码块:

import "package:flutter/material.dart";
import 'package:shenghao_flutter/model/post.dart';

class DataTableDemo extends StatefulWidget {
  DataTableDemo({Key key}) : super(key: key);

  @override
  _DataTableDemoState createState() => _DataTableDemoState();
}

~开始~
class _DataTableDemoState extends State<DataTableDemo> {
  int _sortColumnIndex;
  bool _sortAscending = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('DataTableDemo'),
        elevation: 0,
      ),
      body: Container(
        padding: EdgeInsets.all(16.0),
        child: ListView(children: <Widget>[
          DataTable(
              sortColumnIndex: _sortColumnIndex,
              sortAscending: _sortAscending,
              columns: [
                DataColumn(
                  label: Text('Title'),
                  onSort: (int index, bool ascend) {
                    setState(() {
                      _sortColumnIndex = index;
                      _sortAscending = ascend;
                      posts.sort((a, b) {
                        if (!ascend) {
                          final c = a;
                          a = b;
                          b = c;
                        }

                        return a.title.length.compareTo(b.title.length);
                      });
                    });
                  },
                ),
                DataColumn(label: Text('Author')),
                // DataColumn(label: Text('Image'))
              ],
              rows: posts.map((post) {
                return DataRow(cells: [
                  DataCell(Text(post.title)),
                  DataCell(Text(post.author)),
                  // DataCell(Image.network(post.imageUrl)),
                ]);
              }).toList())
        ]),
      ),
    );
  }
}
~结束~

DataTable:选择数据表格行

在数据表格的 DataRow 里面,selected 属性的值表示的是行的选择状态 .. 这个状态咱们可使用 post 里的 selected 属性来表示 .. 而后找到 model 下面的这个 post.dart .. 按住 alt 键,点一下 posts,能够打开定义这个 posts 的地方 .
img
在 Post 里面,添加一个 bool 类型的 selected .. 默认让它的值等于 false ...

img

按下行 .. 能够切换行的选择状态 ...

表格栏目名字的左边有一个复选框能够切换全部行的选择状态 ... 若是你想定制这个全选的动做,能够在 DataTable 里面添加一个 onSelectAll 方法 .. 它也接收一个 bool 类型的 value 参数 .. 这个 value 表示的就是全选的状态 ..

PaginatedDataTable:分页显示表格数据

先找到这个 DataTable .. 把它换成一个 PaginatedDataTable .. 这样以前咱们在 DataTable 里面添加的 rows 这个属性就再也不须要了 .. 表格里的具体的数据要使用一个 source 来设置 .. 先复制一下这个 cells 里面添加的几个单元格 .. 一下子会用到 ..
img

代码块:

import "package:flutter/material.dart";
import 'package:shenghao_flutter/model/post.dart';
import "../model/post.dart";

class PostsDataSource extends DataTableSource {
  final List<Post> _posts = posts;
  int _selectedCount = 0;

  @override
  int get rowCount => _posts.length;

  @override
  // TODO: implement isRowCountApproximate
  bool get isRowCountApproximate => false;

  @override
  // TODO: implement selectedRowCount
  int get selectedRowCount => _selectedCount;

  @override
  DataRow getRow(int index) {
    final Post post = _posts[index];

    return DataRow.byIndex(index: index, cells: <DataCell>[
      DataCell(Text(post.title)),
      DataCell(Text(post.author)),
    ]);
  }
}
~开始~
class PaginatedDataTableDemo extends StatefulWidget {
  PaginatedDataTableDemo({Key key}) : super(key: key);

  @override
  _DataTableDemoState createState() => _DataTableDemoState();
}

class _DataTableDemoState extends State<PaginatedDataTableDemo> {
  int _sortColumnIndex;
  bool _sortAscending = true;
  final PostsDataSource _postsDataSource = PostsDataSource();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('PaginatedDataTableDemo'),
        elevation: 0,
      ),
      body: Container(
        padding: EdgeInsets.all(16.0),
        child: ListView(children: <Widget>[
          PaginatedDataTable(
            header: Text('Posts'),
            rowsPerPage: 2,
            source: _postsDataSource,
            sortColumnIndex: _sortColumnIndex,
            sortAscending: _sortAscending,
            // onSelectAll: (bool value) {},
            columns: [
              DataColumn(
                label: Text('Title'),
                onSort: (int index, bool ascend) {
                  setState(() {
                    _sortColumnIndex = index;
                    _sortAscending = ascend;

                    posts.sort((a, b) {
                      if (!ascend) {
                        final c = a;
                        a = b;
                        b = c;
                      }

                      return a.title.length.compareTo(b.title.length);
                    });
                  });
                },
              ),
              DataColumn(label: Text('Author')),
              // DataColumn(label: Text('Image'))
            ],
          )
        ]),
      ),
    );
  }
}
~结束~

PaginatedDataTable:排序

表格里面还有个排序的功能,咱们须要再修改一下 .. 先找到这个能够排序的栏目 .. 它上面添加了 onSort ,表示这个栏目是能够排序的 ..

这个方法接收两个参数,一个是 int 类型的 index,表示栏目的索引号,这个名字能够换成 columnIndex ,这样更容易明白它表示的东西 ..

img

下面再到 PostDataSoruce 里面,去添加一个方法 .. 名字是 _sort .. 第一个参数是个方法参数,名字能够叫它 getField .. 这个方法参数接受一个 post 参数 .. 这样在这个 _sort 里面,使用这个 getField 参数的时候,咱们须要给它提供一个 post ,就是内容列表里面的一个项目 ..

第二个参数是 bool 类型的 ascending .. 里面用一下 _posts 上的 sort 方法,一个方法 ,两个参数,a 还有 b ,表示的就是列表里面有两个项目 .. 先判断一下,若是 !ascending .. 咱们可让 a 还有 b 的值调换一下 .. 先让 c 等于 a .. a 等于 b ,再让 b 等于 c ..

下面再添加一个 aValue .. 它的值用一下 getField ,把 a 交给它 ... 对于数据表里面的 Title 这栏的排序来讲,这个 aValue 应该就是 a 这个文章内容的 title 的长度,也就是它的标题字符长度 .. 由于在使用 _sort 这个方法的时候,给它的 getField 方法参数设置的就是让它返回 post 里的 title 的 length 属性的值 ..

下面再添加一个 bValue ,用一下 getField,把 b 交给它 .

方法 return 的是,用一下 Comparable 上面的 compare 方法,比较一下 aValue .. 还有 bValue ..

排序完成之后,须要再执行一下 notifyListeners() ...

import "package:flutter/material.dart";
import 'package:shenghao_flutter/model/post.dart';
import "../model/post.dart";

class PostsDataSource extends DataTableSource {
  final List<Post> _posts = posts;
  int _selectedCount = 0;

  @override
  int get rowCount => _posts.length;

  @override
  // TODO: implement isRowCountApproximate
  bool get isRowCountApproximate => false;

  @override
  // TODO: implement selectedRowCount
  int get selectedRowCount => _selectedCount;

  @override
  DataRow getRow(int index) {
    final Post post = _posts[index];

    return DataRow.byIndex(index: index, cells: <DataCell>[
      DataCell(Text(post.title)),
      DataCell(Text(post.author)),
    ]);
  }

  void _sort(getField(post), bool ascending) {
    _posts.sort((a, b) {
      if (!ascending) {
        final c = a;
        a = b;
        b = c;
      }
      final aValue = getField(a);
      final bValue = getField(b);
      return Comparable.compare(aValue, bValue);
    });
    notifyListeners();
  }
}

~开始~
class PaginatedDataTableDemo extends StatefulWidget {
  PaginatedDataTableDemo({Key key}) : super(key: key);

  @override
  _DataTableDemoState createState() => _DataTableDemoState();
}

class _DataTableDemoState extends State<PaginatedDataTableDemo> {
  int _sortColumnIndex;
  bool _sortAscending = true;
  final PostsDataSource _postsDataSource = PostsDataSource();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('PaginatedDataTableDemo'),
        elevation: 0,
      ),
      body: Container(
        padding: EdgeInsets.all(16.0),
        child: ListView(children: <Widget>[
          PaginatedDataTable(
            header: Text('Posts'),
            rowsPerPage: 2,
            source: _postsDataSource,
            sortColumnIndex: _sortColumnIndex,
            sortAscending: _sortAscending,
            // onSelectAll: (bool value) {},
            columns: [
              DataColumn(
                label: Text('Title'),
                onSort: (int columnindex, bool ascending) {
                  _postsDataSource._sort(
                      (post) => post.title.length, ascending);
                  setState(() {
                    _sortColumnIndex = columnindex;
                    _sortAscending = ascending;
                  });
                },
              ),
              DataColumn(label: Text('Author')),
              // DataColumn(label: Text('Image'))
            ],
          )
        ]),
      ),
    );
  }
}
~结束~

Card

下面咱们试一下 Flutter 的 Card 小部件 .. 咱们能够把一组卡片小部件放在一个 ListView 里面 .. 这个 ListView 的孩子们可使用一个 map 方法来生成 .. 用一下 posts.map .. 结果执行一下 toList 转换成一个列表 ..

卡片

也许这是最丑的卡片:

img

ClipRRect:圆角(给图像添加圆角效果)

卡片小部件默认有一个大小是 4.0 的圆角效果 .. 不过这里卡片的上面这个圆角被图片挡住了 .. 咱们能够给图像的左上角还有右上角添加一个大小跟卡片小部件同样的圆角效果 ..

能够这样 .. 先找到卡片上的图像 .. 剪切一下 .. 而后用一个 ClipRRect .. 它会给它的孩子裁出来一个圆角 .. 添加一个 borderRadius 属性 .. 对应的值用一下 BorderRadius.only .. 先设置一下 topLeft 这个角的圆角 .. Radius.circular .. 大小是 4.0 ... 复制一份 .. 再设置一下 topRight 这个角的圆角 .. 大小也是 4.0 ..

img

Stepper

Stepper:步骤

Stepper,步骤小部件 .. 能够用它展现用户须要完成一系列的步骤 .. 在这个 Stepper 演示里面 .. 咱们能够先用一个 Theme .. 覆盖掉应用的部分主题的配置 .. 一个 data 属性 .. 它的值用一下 Theme.of context .. copyWith .. 里面从新设置一下主题的 primaryColor .. 颜色能够设置成黑色 ..
img
安装一个生成随机内容的插件
img

Stepper:步骤的行为

相关文章
相关标签/搜索