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:步驟的行爲

相關文章
相關標籤/搜索