[- Flutter 數據&狀態篇 -] setState

上篇: 又不是不能用-篇 本文源碼Github

0.1:對我而言,一個產品有四層境界
1.造都造不出來
2.它又不是不能用    <----
3.用的時候你們都不說話
4.如絲般順滑,易拓展,易修改,易複用
複製代碼


0.2:要說的話

注意:本篇是對狀態最基本的使用。雖然比較糙,可是並不表明不重要
後面兩篇是基於此篇的優化,因此這篇必定要看懂,才能跟上個人思惟。
效果以下,單從界面上來看,我仍是比較滿意的。git


0.3: 簡介一下

本項目主要包括如下幾點:github

1. 輸入一個待辦事項,下面的ListView動態更新  
2. 條目的複選框選中,條目的文字自動添加下劃線
3. 條目的複選框非選中,條目的文字自動取消下劃線
4. 三個按鈕會根據是否完成而過濾數據,顯示相應條目
複製代碼

1.靜態界面的實現

萬里長征第一步,固然是先把靜態界面搞出了。bash

import 'package:flutter/material.dart';

class TodoList extends StatefulWidget {
  @override
  _TodoListState createState() => _TodoListState();
}

class _TodoListState extends State<TodoList> {
  @override
  Widget build(BuildContext context) {
        return Container();
  }
}
複製代碼

1.1.輸入框的組件

經過一個TextField和RaisedButton進行拼合,樣式什麼的本身看,就不廢話了。
我感受這樣挺好看的,不枉我精心調試一番。喜歡的話,能夠本身抽個組件。微信

var textField = TextField(
  controller: new TextEditingController(text: this.text),
  keyboardType: TextInputType.text,
  textAlign: TextAlign.start,
  maxLines: 1,
  cursorColor: Colors.black,
  cursorWidth: 3,
  style: TextStyle(
      fontSize: 16, color: Colors.lightBlue, backgroundColor: Colors.white),
  decoration: InputDecoration(
    filled: true,
    fillColor: Colors.white,
    hintText: '添加一個待辦項',
    hintStyle: TextStyle(color: Colors.black26, fontSize: 14),
    contentPadding: EdgeInsets.only(left: 14.0, bottom: 8.0, top: 8.0),
    focusedBorder: OutlineInputBorder(
      borderSide: BorderSide(color: Colors.white),
      borderRadius: BorderRadius.only(
          topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
    ),
    enabledBorder: UnderlineInputBorder(
      borderSide: BorderSide(color: Colors.white),
      borderRadius: BorderRadius.only(
          topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
    ),
  ),
  onChanged: (str) {
    //輸入時的回調
  },
);
var btn = RaisedButton(
  child: Icon(Icons.add),
  padding: EdgeInsets.zero,
  onPressed: () {
    //按鈕點擊回調
  },
);

var inputBtn = Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    Container(
      child: textField,
      width: 200,
    ),
    ClipRRect(
      borderRadius: BorderRadius.only(
          topRight: Radius.circular(10), bottomRight: Radius.circular(10)),
      child: Container(
        child: btn,
        width: 36,
        height: 36,
      ),
    ),
  ],
);
複製代碼

1.2.三個按鈕

三個按鈕,比較簡單ide

var op = Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: <Widget>[
    RaisedButton(
      color: Colors.blue,
      onPressed: () {
      },
      child: Text("所有"),
    ),
    RaisedButton(
      onPressed: () {
      },
      child: Text("已完成"),
    ),
    RaisedButton(
      onPressed: () {
      },
      child: Text("未完成"),
    ),
  ],
);
複製代碼

1.3.待準備的數據
  • 用一個Map盛放文字和是否選中的
var todo = <String, bool>{};
複製代碼
  • 定義一個狀態枚舉
enum ShowType { 
    all, 
    todo, 
    done 
}
複製代碼
  • 類中設置初始變量
class _TodoListState extends State<TodoList> {
  var todo = <String, bool>{};//列表數據
  var text;//當前輸入文字
  var showType = ShowType.all;//顯示類型
}
複製代碼

1.4:根據數據造成列表

注意:如何Map獲取對應索引處的鍵,值。根據值的true/fase來控制decoration的有無post

Widget formList(Map<String, bool> todo) {
    return ListView.builder(
      itemCount: todo.length,
      padding: EdgeInsets.all(8.0),
      itemExtent: 50.0,
      itemBuilder: (BuildContext context, int index) {
        var key = todo.keys.toList()[index];//鍵
        var value = todo.values.toList()[index];//值
        var text = Align(
          child: Text(
            todo.keys.toList()[index],
            style: TextStyle(
              decorationThickness: 3,
                decoration: value
                    ? TextDecoration.lineThrough
                    : TextDecoration.none,
                decorationColor: Colors.blue),
          ),
          alignment: Alignment.centerLeft,
        );

        return Card(
          child: Row(
            children: <Widget>[
              Checkbox(
                onChanged: (b) {
                 //Checkbox點擊
                },
                value: todo.values.toList()[index],
              ),
              text
            ],
          ),
        );
      },
    );
  }
複製代碼

1.5:拼組

這裏要注意,用Expanded包一下,ListView才能自延展本身的尺寸
直接寫的話啊,因爲高度未知,會崩掉。優化

return Column(
  children: <Widget>[inputBtn, op, Expanded(child: formList(todo))],
);
複製代碼

2.狀態的更新

2.1:鳥瞰全局

這裏狀態有點亂,我畫了幅圖說明一下:ui

狀態量有三個:text 輸入框的文字,todo列表數據,showType展示類型
1.輸入框經過監聽,改變text的值
2.在添加按鈕點擊時,將加入到狀態值todo中 
3.todo用來渲染Todo列表,根據key和value展示數據和複選框狀態
4.複選框經過點擊,改變todo的狀態,來顯示對勾以及文字下劃線
5.根據showType的不一樣,選擇過濾的方式。
6.在適宜的狀態值改變時,調用老夫的setState來更新
複製代碼

2.2:輸入框監聽
onChanged: (str) {
  text = str;
},
複製代碼

2.3:點擊按鈕監聽

注意收起鍵盤的操做FocusScope.of(context).requestFocus(FocusNode());this

onPressed: () {
  FocusScope.of(context).requestFocus(FocusNode()); //收起鍵盤
  if (text != null && text != "") {
    todo[text] = false;//爲Map添加數據
    text = "";//輸入框文字清空
    setState(() {});
  }
},
複製代碼

2.4:複選框點擊
onChanged: (b) {
  todo[key] = b;
  setState(() {});
},
複製代碼

2.5:過濾操做

想了好一會,纔想到該如何過濾出想要的元素spa

showList(ShowType showType) {
  switch (showType) {
    case ShowType.all:
      return formList(todo);
      break;
    case ShowType.todo:
      return formList(Map.fromEntries(todo.entries.where((e) => !e.value)));
      break;
    case ShowType.done:
      return formList(Map.fromEntries(todo.entries.where((e) => e.value)));
      break;
  }
}
複製代碼

2.6:拼合
return Column(
  children: <Widget>[inputBtn, op, Expanded(child: showList(showType))],
);
複製代碼

3. 代碼全覽

import 'package:flutter/material.dart';

class TodoList extends StatefulWidget {
  @override
  _TodoListState createState() => _TodoListState();
}

enum ShowType { all, todo, done }

class _TodoListState extends State<TodoList> {
  var todo = <String, bool>{};//列表數據
  var text;//當前輸入文字
  var showType = ShowType.all;//顯示類型

  @override
  Widget build(BuildContext context) {
    var textField = TextField(
      controller: new TextEditingController(text: this.text),
      keyboardType: TextInputType.text,
      textAlign: TextAlign.start,
      maxLines: 1,
      cursorColor: Colors.black,
      cursorWidth: 3,
      style: TextStyle(
          fontSize: 16, color: Colors.lightBlue, backgroundColor: Colors.white),
      decoration: InputDecoration(
        filled: true,
        fillColor: Colors.white,
        hintText: '添加一個待辦項',
        hintStyle: TextStyle(color: Colors.black26, fontSize: 14),
        contentPadding: EdgeInsets.only(left: 14.0, bottom: 8.0, top: 8.0),
        focusedBorder: OutlineInputBorder(
          borderSide: BorderSide(color: Colors.white),
          borderRadius: BorderRadius.only(
              topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
        ),
        enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: Colors.white),
          borderRadius: BorderRadius.only(
              topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
        ),
      ),
      onChanged: (str) {
        text = str;
      },
    );

    var btn = RaisedButton(
      child: Icon(Icons.add),
      padding: EdgeInsets.zero,
      onPressed: () {
        FocusScope.of(context).requestFocus(FocusNode()); //收起鍵盤
        if (text != null && text != "") {
          todo[text] = false;
          text = "";
          setState(() {});
        }
      },
    );

    var inputBtn = Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Container(
          child: textField,
          width: 200,
        ),
        ClipRRect(
          borderRadius: BorderRadius.only(
              topRight: Radius.circular(10), bottomRight: Radius.circular(10)),
          child: Container(
            child: btn,
            width: 36,
            height: 36,
          ),
        ),
      ],
    );

    var op = Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        RaisedButton(
          color: Colors.blue,
          textTheme: ButtonTextTheme.primary,
          onPressed: () {
            showType = ShowType.all;
            setState(() {});
          },
          child: Text("所有"),
        ),
        RaisedButton(
          onPressed: () {
            showType = ShowType.done;
            setState(() {});
          },
          child: Text("已完成"),
        ),
        RaisedButton(
          onPressed: () {
            showType = ShowType.todo;
            setState(() {});
          },
          child: Text("未完成"),
        ),
      ],
    );

    return Column(
      children: <Widget>[inputBtn, op, Expanded(child: showList(showType))],
    );
  }

  showList(ShowType showType) {
    switch (showType) {
      case ShowType.all:
        return formList(todo);
        break;
      case ShowType.todo:
        return formList(Map.fromEntries(todo.entries.where((e) => !e.value)));
        break;
      case ShowType.done:
        return formList(Map.fromEntries(todo.entries.where((e) => e.value)));
        break;
    }
  }

  Widget formList(Map<String, bool> todo) {
    return ListView.builder(
      itemCount: todo.length,
      padding: EdgeInsets.all(8.0),
      itemExtent: 50.0,
      itemBuilder: (BuildContext context, int index) {
        var key = todo.keys.toList()[index];
        var value = todo.values.toList()[index];
        var text = Align(
          child: Text(
            todo.keys.toList()[index],
            style: TextStyle(
              decorationThickness: 3,
                decoration: value
                    ? TextDecoration.lineThrough
                    : TextDecoration.none,
                decorationColor: Colors.blue),
          ),
          alignment: Alignment.centerLeft,
        );

        return Card(
          child: Row(
            children: <Widget>[
              Checkbox(
                onChanged: (b) {
                  todo[key] = b;
                  setState(() {});
                },
                value: todo.values.toList()[index],
              ),
              text
            ],
          ),
        );
      },
    );
  }
}

複製代碼

到這裏效果就已經實現了,可是狀態值四溢,看着感受有些難看。
壞的代碼就至關於你有個女朋友,又醜又亂,又兇又惡,有事沒事給你找茬。
然而你還不得不一直面對她,問了你一句爲何這麼傻,你含着淚說:"又不是不..."


結語

本文到此接近尾聲了,若是想快速嚐鮮Flutter,《Flutter七日》會是你的必備佳品;若是想細細探究它,那就跟隨個人腳步,完成一次Flutter之旅。
另外本人有一個Flutter微信交流羣,歡迎小夥伴加入,共同探討Flutter的問題,本人微信號:zdl1994328,期待與你的交流與切磋。

下一篇,將爲你帶來如何對當前代碼進行優化,讓狀態量更容易管理,敬請期待。

相關文章
相關標籤/搜索