Flutter學習(8)——CheckBox多選框使用及動態更改多選框資料

one發表於2021-08-10

原文地址:Flutter學習(8)——CheckBox多選框使用及動態更改多選框資料 | Stars-One的雜貨小窩

最近專案需求需要調整頁面,記錄一下實現過程

這次主要是要實現個評價頁面,選擇不同的星級顯示不同的多選框資料,加上之前也沒有使用過CheckBox,今天便是一起講吧

本篇程式碼github

效果預覽

Flutter學習(8)——CheckBox多選框使用及動態更改多選框資料

效果看似有點複雜,我們一步步來實現吧 ?

1.星級元件使用

首先,我們有使用到星級評分元件pubspec.yaml檔案中新增下面依賴

flutter_simple_rating_bar: ^0.0.3

使用的話頁面也是有個例子介紹,我們直接拿來即可使用

RatingBar(
		//預設選擇5星
		rating: 5,
        //總星星數目
        starCount: 5,
        //星星圖示
        icon: Icon(
            Icons.star,
            size: 40,
            color: Colors.grey,
        ),
        spacing: 5.0,
        size: 40,
        isIndicator: false,
        //是否可選半星
        allowHalfRating: true,
        onRatingCallback:(double value, ValueNotifier<bool> isIndicator) {
	        //value是點選後選中的星星數(即評分),範圍為1-rating(我們上面rating為5)
            
        },
        color: Colors.amber,
)

現在我們要根據選擇⭐的數目不同,從而顯示不同的文字,這過程是動態的,所以我們選擇使用StatefulWidget作為頁面繼承的基類

我們先在State類中加上相應的資料,如下所示

//顯示的文字資訊
List typeList = ["非常不滿意", "不滿意", "基本滿意", "滿意", "非常滿意"];

//當前選擇type的下標
int selectType = 4;

我們文字顯示內容是typeList[selectType],我們在星級評分選擇的回撥函式,更改selecType即可實現更改UI的功能

Column(
    children: [
        RatingBar(
            rating: 5,
            //預設選擇5星
            starCount: 5,
            //總星星數目
            //星星圖示
            icon: Icon(
                Icons.star,
                size: 40,
                color: Colors.grey,
            ),
            spacing: 5.0,
            size: 40,
            isIndicator: false,
            //是否可選半星
            allowHalfRating: true,
            onRatingCallback:
            (double value, ValueNotifier<bool> isIndicator) {
                //更改資料同時也要更改UI,所以使用setState方法
                setState(() {
                    //注意 星級從1開始,selectType是下標,從0開始,需要減1
                    selectType = value.toInt() - 1;
                });
            },
            color: Colors.amber,
        ),
        //取指定下標的文字內容展示
        Text(typeList[selectType])
    ],
)
    

2.多選框使用及資料更改

CheckBox多選框的使用需要定義一個bool型別的資料,每次點選需要更改此資料來改變CheckBox的選擇狀態

由於我們是使用的列表資料,所以每個資料寫個變數不太現實,而且繁瑣,所以可以考慮在實體類中加個bool型別的欄位,用來儲存CheckBox選擇的狀態

這裡,資料我就暫且寫死,實際上是要通過呼叫介面獲取的

void initTagData() {
    var data = """{
    "code":200,
    "error":"",
    "ReturnValue":[{"generalstars":"1","tagid":"109759610348409856","tagname":"無理由超過法定辦理時間","updatetime":"2020-06-12 17:38:00"},{"generalstars":"1","tagid":"6176177900787350","tagname":"在辦事指南之外增加新的審批條件","updatetime":"2020-08-27 00:00:00"},{"generalstars":"1","tagid":"6176177900787351","tagname":"需提供辦事指南之外的申報材料","updatetime":"2020-08-27 00:00:00"},{"generalstars":"1","tagid":"7176177890787535","tagname":"無理由超過法定辦理時間","updatetime":"2020-08-27 00:00:00"},{"generalstars":"2","tagid":"6176177900787349","tagname":"未在承諾時間內辦結","updatetime":"2020-08-27 00:00:00"},{"generalstars":"2","tagid":"6176177900787347","tagname":"沒有提供材料樣本","updatetime":"2020-08-27 00:00:00"},{"generalstars":"2","tagid":"6176177900787348","tagname":"沒有提供材料清單","updatetime":"2020-08-27 00:00:00"},{"generalstars":"3","tagid":"119596473292723200","tagname":"22222","updatetime":"2020-06-12 17:38:00"},{"generalstars":"3","tagid":"6176177900787346","tagname":"在承諾的時間內辦結","updatetime":"2020-08-27 00:00:00"},{"generalstars":"3","tagid":"6176177900787345","tagname":"提供申報材料樣本","updatetime":"2020-08-27 00:00:00"},{"generalstars":"3","tagid":"6176177900787344","tagname":"一次性告知需要補正的材料","updatetime":"2020-08-27 00:00:00"},{"generalstars":"4","tagid":"6176177900787340","tagname":"填寫一張表單就可以完成申報","updatetime":"2020-08-27 00:00:00"},{"generalstars":"4","tagid":"6176177900787341","tagname":"線上提交材料視窗核驗","updatetime":"2020-08-27 00:00:00"},{"generalstars":"4","tagid":"6176177900787342","tagname":"一張清單告知全部申報材料","updatetime":"2020-08-27 00:00:00"},{"generalstars":"5","tagid":"7176177890787335","tagname":"一窗受理一次辦結","updatetime":"2020-08-27 00:00:00"},{"generalstars":"5","tagid":"7176177890787235","tagname":"可以先受理後補材料","updatetime":"2020-08-27 00:00:00"},{"generalstars":"5","tagid":"7176177890787435","tagname":"不用提交證明","updatetime":"2020-08-27 00:00:00"}]
    }""";
    Map<String, dynamic> responseData = jsonDecode(data);
    var tagListModel = TagListModel.fromJson(responseData);
    if (tagListModel.code == 200) {
      //注意要使用setState,才會使UI發生變化(因為網路請求是耗時任務,可能介面渲染完畢後才能請求到資料)
      setState(() {
        tagList.addAll(tagListModel.tagItems);
      });
    }
  }

TagListModel是生成的實體類,生成的步驟已經在上篇文章講解到,這裡就不在贅述了

按照上面說的思路,我們在TagListModel的實體類中(其實是在TagItem中),新增一個bool型別的欄位,用來作為儲存CheckBox選擇的狀態,

這裡使用外掛生成的實體類(TagListModel裡面還包含有個實體類),原本應該是ReturnValue,但為了方便,我將其改了個名字TagItem

Flutter中的CheckBox是隻有一個框,並不包含文字,所以我們得組合一個文字,拼成一個Widget,_tagView()這個方法就是每個選項

Widget _tagView(item) {
    var str = item.tagname;
    return InkWell(
        child: Row(
            children: <Widget>[
                Checkbox(
                    value: item.flag,
                    activeColor: Colors.blue,
                    onChanged: (bool val) {
                        // val 是布林值
                        this.setState(() {
                            item.flag = val;
                        });
                    },
                ),
                Text(str),
            ],
        ),
        onTap: () {
            setState(() {
                item.flag = !item.flag;
            });
        },
   );
          

這裡最外層使用了InkWell,是因為其有onTag的點選事件方法 點選整行都可以去改變CheckBox的狀態,使用者體驗比較友好,如果沒有的話,只能點選CheckBox的框來改變狀態

有了上面的一個子項,現在我們需要構建一個多選選項列表(列表包含N個_tagView元件)

先宣告兩個陣列,用來存放資料

//標籤資料(存放所有資料)
List<TagItem> tagList = [];

//臨時存放的資料,後面評價提交需要
List<TagItem> tempTagList = [];

這裡為什麼需要兩個陣列呢?

主要是考慮到後面需要獲取勾選的資料,所以才考慮再多加一個臨時的列表

每次改變星級的時候,列表選項的資料就會改變,之後我們只需要去這個臨時列表中篩選狀態為true的資料即可

List<Widget> _tagList() {
    List<Widget> widgetList = [];
    var list = this.tagList;
    tempTagList.clear();

    var star = selectType.toInt() + 1;
    //匹配相同星級資料
    for (var i = 0; i < list.length; i++) {
      var item = list[i];
      if (item.generalstars == star.toString()) {
        //存在臨時表中,之後可以快速查詢使用者的選擇
        tempTagList.add(item);
        var row = _tagView(item);
        widgetList.add(row);
      }
    }
    return widgetList;
}

由於資料較少,可以使用Column用來包裹上面的元件_tagList

//標籤選項
Container(
    decoration: BoxDecoration(color: Colors.white),
    padding: EdgeInsets.fromLTRB(10, 0, 15, 5),
    child: Column(
    	children: _tagList(),
    )
)

由於提交標籤需要傳標籤的id,而且需要逗號隔開,所以需要遍歷臨時選項列表,處理一下即可

//處理標籤
var tagId = "";
for (var i = 0; i < tempTagList.length; i++) {
    var item = tempTagList[i];
    //查詢選擇的標籤資料
    if (item.flag) {
        tagId += item.tagid + ",";
    }
}
if (tagId.length>0) {
	tagId = tagId.substring(0, tagId.length - 1);
}
print(tagId);

原始碼

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_demo/model/tag_list_model.dart';
import 'package:flutter_simple_rating_bar/flutter_simple_rating_bar.dart';

class CheckBoxPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new CheckBoxState();
  }
}

class CheckBoxState extends State<CheckBoxPage> {
  //標籤資料
  List<TagItem> tagList = [];

  //臨時標籤,後面評價提交需要
  List<TagItem> tempTagList = [];

  List typeList = ["非常不滿意", "不滿意", "基本滿意", "滿意", "非常滿意"];

  //當前選擇type的下標
  int selectType = 4;

  @override
  void initState() {
    //初始化資料(這裡是暫時寫死,實際中這裡是呼叫介面)
    initTagData();
  }

  void initTagData() {
    var data = """{
    "code":200,
    "error":"",
    "ReturnValue":[{"generalstars":"1","tagid":"109759610348409856","tagname":"無理由超過法定辦理時間","updatetime":"2020-06-12 17:38:00"},{"generalstars":"1","tagid":"6176177900787350","tagname":"在辦事指南之外增加新的審批條件","updatetime":"2020-08-27 00:00:00"},{"generalstars":"1","tagid":"6176177900787351","tagname":"需提供辦事指南之外的申報材料","updatetime":"2020-08-27 00:00:00"},{"generalstars":"1","tagid":"7176177890787535","tagname":"無理由超過法定辦理時間","updatetime":"2020-08-27 00:00:00"},{"generalstars":"2","tagid":"6176177900787349","tagname":"未在承諾時間內辦結","updatetime":"2020-08-27 00:00:00"},{"generalstars":"2","tagid":"6176177900787347","tagname":"沒有提供材料樣本","updatetime":"2020-08-27 00:00:00"},{"generalstars":"2","tagid":"6176177900787348","tagname":"沒有提供材料清單","updatetime":"2020-08-27 00:00:00"},{"generalstars":"3","tagid":"119596473292723200","tagname":"22222","updatetime":"2020-06-12 17:38:00"},{"generalstars":"3","tagid":"6176177900787346","tagname":"在承諾的時間內辦結","updatetime":"2020-08-27 00:00:00"},{"generalstars":"3","tagid":"6176177900787345","tagname":"提供申報材料樣本","updatetime":"2020-08-27 00:00:00"},{"generalstars":"3","tagid":"6176177900787344","tagname":"一次性告知需要補正的材料","updatetime":"2020-08-27 00:00:00"},{"generalstars":"4","tagid":"6176177900787340","tagname":"填寫一張表單就可以完成申報","updatetime":"2020-08-27 00:00:00"},{"generalstars":"4","tagid":"6176177900787341","tagname":"線上提交材料視窗核驗","updatetime":"2020-08-27 00:00:00"},{"generalstars":"4","tagid":"6176177900787342","tagname":"一張清單告知全部申報材料","updatetime":"2020-08-27 00:00:00"},{"generalstars":"5","tagid":"7176177890787335","tagname":"一窗受理一次辦結","updatetime":"2020-08-27 00:00:00"},{"generalstars":"5","tagid":"7176177890787235","tagname":"可以先受理後補材料","updatetime":"2020-08-27 00:00:00"},{"generalstars":"5","tagid":"7176177890787435","tagname":"不用提交證明","updatetime":"2020-08-27 00:00:00"}]
    }""";
    Map<String, dynamic> responseData = jsonDecode(data);
    var tagListModel = TagListModel.fromJson(responseData);
    if (tagListModel.code == 200) {
      //注意要使用setState,才會使UI發生變化(因為網路請求是耗時任務,可能介面渲染完畢後才能請求到資料)
      setState(() {
        tagList.addAll(tagListModel.tagItems);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("評價"),
      ),
      body: Column(
        children: [
          Container(
            decoration: BoxDecoration(color: Colors.white),
            padding: EdgeInsets.fromLTRB(15, 0, 15, 10),
            child: Wrap(
              spacing: 10,
              children: [
                Center(
                  child: Column(
                    children: [
                      RatingBar(
                        rating: 5,
                        //預設選擇5星
                        starCount: 5,
                        //總星星數目
                        //星星圖示
                        icon: Icon(
                          Icons.star,
                          size: 40,
                          color: Colors.grey,
                        ),
                        spacing: 5.0,
                        size: 40,
                        isIndicator: false,
                        //是否可選半星
                        allowHalfRating: true,
                        onRatingCallback:
                            (double value, ValueNotifier<bool> isIndicator) {
                          //更改資料同時也要更改UI,所以使用setState方法
                          setState(() {
                            //注意 星級從1開始,selectType是下標,從0開始,需要減1
                            selectType = value.toInt() - 1;
                          });
                        },
                        color: Colors.amber,
                      ),
                      _tipText()
                    ],
                  ),
                )
              ],
            ),
          ),
          //標籤選項
          Container(
              decoration: BoxDecoration(color: Colors.white),
              padding: EdgeInsets.fromLTRB(10, 0, 15, 5),
              child: Column(
                children: _tagList(),
              )),
          Container(
              decoration: BoxDecoration(color: Colors.white),
              padding: EdgeInsets.fromLTRB(10, 0, 15, 5),
              child: FlatButton(
                child: Text("提交"),
                onPressed: () {
                  //處理標籤
                  var tagId = "";
                  for (var i = 0; i < tempTagList.length; i++) {
                    var item = tempTagList[i];
                    //查詢選擇的標籤資料
                    if (item.flag) {
                      tagId += item.tagid + ",";
                    }
                  }
                  if (tagId.length>0) {
                    tagId = tagId.substring(0, tagId.length - 1);
                  }
                  print(tagId);
                },
              )),
        ],
      ),
    );
  }

  Widget _tipText() {
    //根據selectType變更UI
    return Text(typeList[selectType]);
  }

  List<Widget> _tagList() {
    List<Widget> widgetList = [];
    var list = this.tagList;
    tempTagList.clear();

    var star = selectType.toInt() + 1;
    for (var i = 0; i < list.length; i++) {
      var item = list[i];
      if (item.generalstars == star.toString()) {
        //存在臨時表中,之後可以快速查詢使用者的選擇
        tempTagList.add(item);
        var row = _tagView(item);
        widgetList.add(row);
      }
    }
    return widgetList;
  }

  Widget _tagView(item) {
    var str = item.tagname;
    return InkWell(
      child: Row(
        children: <Widget>[
          Checkbox(
            value: item.flag,
            activeColor: Colors.blue,
            onChanged: (bool val) {
              // val 是布林值
              this.setState(() {
                item.flag = val;
              });
            },
          ),
          Text(str),
        ],
      ),
      onTap: () {
        setState(() {
          item.flag = !item.flag;
        });
      },
    );
  }
}

相關文章