上篇: 又不是不能用-篇 本文源码Github
0.1:对我而言,一个产品有四层境界
1.造都造不出来 2.它又不是不能用 <---- 3.用的时候大家都不说话 4.如丝般顺滑,易拓展,易修改,易复用 复制代码
0.2:要说的话
注意:本篇是对状态最基本的使用。虽然比较糙,但是并不代表不重要
后面两篇是基于此篇的优化,所以这篇一定要看懂,才能跟上我的思维。
效果如下,单从界面上来看,我还是比较满意的。
0.3: 简介一下
本项目主要包括以下几点:
1. 输入一个待办事项,下面的ListView动态更新 2. 条目的复选框选中,条目的文字自动添加下划线 3. 条目的复选框非选中,条目的文字自动取消下划线 4. 三个按钮会根据是否完成而过滤数据,显示相应条目 复制代码
1.静态界面的实现
万里长征第一步,当然是先把静态界面搞出了。
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.三个按钮
三个按钮,比较简单
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的有无
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:鸟瞰全局
这里状态有点乱,我画了幅图说明一下:
状态量有三个: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());
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:过滤操作
想了好一会,才想到该如何过滤出想要的元素
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 ], ), ); }, ); } } 复制代码
到这里效果就已经实现了,但是状态值四溢,看着感觉有些难看。
坏的代码就相当于你有个女友,又丑又乱,又凶又恶,有事没事给你找茬。
然而你还不得不一直面对她,问了你一句为什么这么傻,你含着泪说:"又不是不..."
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦