老孟导读:这是 【Flutter 实战】组件系列文章的最后一篇,其余组件地址:http://laomengit.com/guide/widgets/Text.html,接下来将会讲解动画系列,关注老孟,精彩不断。html
先看一下效果:git
你们学习UI编程语言时喜欢用哪一个 App 看成第一个练手的项目呢?,我喜欢使用 计算器 ,多是习惯了吧,学习 Android 和 React Native 都用此 App 看成练手的项目。算法
下面我会一步一步的教你们如何实现此项目。编程
整个项目的 UI 分为两大部分,一部分是顶部显示数字和计算结果,另外一部分是底部的输入按钮。微信
因此总体布局使用 Column,在不一样分辨率的手机上,规定底部固定大小,剩余空间都由顶部组件填充,因此顶部组件使用 Expanded 扩充,代码以下:less
Container( padding: EdgeInsets.symmetric(horizontal: 18), child: Column( children: <Widget>[ Expanded( child: Container( alignment: Alignment.bottomRight, padding: EdgeInsets.only(right: 10), child: Text( '$_text', maxLines: 1, style: TextStyle( color: Colors.white, fontSize: 48, fontWeight: FontWeight.w400), ), ), ), SizedBox( height: 20, ), _CalculatorKeyboard( onValueChange: _onValueChange, ), SizedBox( height: 80, ) ], ), )
SizedBox 组件用于两个组件之间的间隔。编程语言
_CalculatorKeyboard 是底部的输入按钮组件,也是此项目的重点,除了 0 这个按钮外,其他都是圆形按钮,不一样之处是 高亮颜色(按住时颜色)、背景颜色、按钮文本、文本颜色不一样,所以先实现一个按钮组件,代码以下:ide
Ink( decoration: BoxDecoration( color: Color(0xFF363636), borderRadius: BorderRadius.all(Radius.circular(200))), child: InkWell( borderRadius: BorderRadius.all(Radius.circular(200)), highlightColor: Color(0xFF363636), child: Container( width: 70, height: 70, alignment: Alignment.center, child: Text( '1', style: TextStyle(color: Colors.white, fontSize: 24), ), ), ), )
而 0 这个按钮的宽度是两个按钮的宽度 + 两个按钮的间隙,因此 0 按钮代码以下:布局
Ink( decoration: BoxDecoration( color: Color(0xFF363636), borderRadius: BorderRadius.all(Radius.circular(200))), child: InkWell( borderRadius: BorderRadius.all(Radius.circular(200)), highlightColor: Color(0xFF363636), child: Container( width: 158, height: 70, alignment: Alignment.center, child: Text( '0', style: TextStyle(color: Colors.white, fontSize: 24), ), ), ), )
将按钮组件进行封装,其中高亮颜色(按住时颜色)、背景颜色、按钮文本、文本颜色属性做为参数,封装以下:学习
class _CalculatorItem extends StatelessWidget { final String text; final Color textColor; final Color color; final Color highlightColor; final double width; final ValueChanged<String> onValueChange; _CalculatorItem( {this.text, this.textColor, this.color, this.highlightColor, this.width, this.onValueChange}); @override Widget build(BuildContext context) { return Ink( decoration: BoxDecoration( color: color, borderRadius: BorderRadius.all(Radius.circular(200))), child: InkWell( onTap: () { onValueChange('$text'); }, borderRadius: BorderRadius.all(Radius.circular(200)), highlightColor: highlightColor ?? color, child: Container( width: width ?? 70, height: 70, padding: EdgeInsets.only(left: width == null ? 0 : 25), alignment: width == null ? Alignment.center : Alignment.centerLeft, child: Text( '$text', style: TextStyle(color: textColor ?? Colors.white, fontSize: 24), ), ), ), ); } }
输入按钮的布局使用 Wrap 布局组件,若是没有 0 这个组件也能够使用 GridView组件,按钮的数据:
final List<Map> _keyboardList = [ { 'text': 'AC', 'textColor': Colors.black, 'color': Color(0xFFA5A5A5), 'highlightColor': Color(0xFFD8D8D8) }, { 'text': '+/-', 'textColor': Colors.black, 'color': Color(0xFFA5A5A5), 'highlightColor': Color(0xFFD8D8D8) }, { 'text': '%', 'textColor': Colors.black, 'color': Color(0xFFA5A5A5), 'highlightColor': Color(0xFFD8D8D8) }, { 'text': '÷', 'color': Color(0xFFE89E28), 'highlightColor': Color(0xFFEDC68F) }, {'text': '7', 'color': Color(0xFF363636)}, {'text': '8', 'color': Color(0xFF363636)}, {'text': '9', 'color': Color(0xFF363636)}, { 'text': 'x', 'color': Color(0xFFE89E28), 'highlightColor': Color(0xFFEDC68F) }, {'text': '4', 'color': Color(0xFF363636)}, {'text': '5', 'color': Color(0xFF363636)}, {'text': '6', 'color': Color(0xFF363636)}, { 'text': '-', 'color': Color(0xFFE89E28), 'highlightColor': Color(0xFFEDC68F) }, {'text': '1', 'color': Color(0xFF363636)}, {'text': '2', 'color': Color(0xFF363636)}, {'text': '3', 'color': Color(0xFF363636)}, { 'text': '+', 'color': Color(0xFFE89E28), 'highlightColor': Color(0xFFEDC68F) }, {'text': '0', 'color': Color(0xFF363636), 'width': 158.0}, {'text': '.', 'color': Color(0xFF363636)}, { 'text': '=', 'color': Color(0xFFE89E28), 'highlightColor': Color(0xFFEDC68F) }, ];
整个输入按钮组件:
class _CalculatorKeyboard extends StatelessWidget { final ValueChanged<String> onValueChange; const _CalculatorKeyboard({Key key, this.onValueChange}) : super(key: key); @override Widget build(BuildContext context) { return Wrap( runSpacing: 18, spacing: 18, children: List.generate(_keyboardList.length, (index) { return _CalculatorItem( text: _keyboardList[index]['text'], textColor: _keyboardList[index]['textColor'], color: _keyboardList[index]['color'], highlightColor: _keyboardList[index]['highlightColor'], width: _keyboardList[index]['width'], onValueChange: onValueChange, ); }), ); } }
onValueChange 是点击按钮的回调,参数是当前按钮的文本,用于计算,下面说下计算逻辑:
这里有4个变量:
AC 按钮表示清空当前输入,显示 0,同时初始化其余变量:
case 'AC': _text = '0'; _beforeText = '0'; _isResult = false; break;
+/- 按钮表示对当前数字取反,好比 5->-5:
case '+/-': if (_text.startsWith('-')) { _text = _text.substring(1); } else { _text = '-$_text'; } break;
% 按钮表示当前数除以100:
case '%': double d = _value2Double(_text); _isResult = true; _text = '${d / 100.0}'; break;
+、-、x、÷ 按钮,保存当前 操做符号:
case '+': case '-': case 'x': case '÷': _isResult = false; _operateText = value;
0-9 和 . 按钮根据是不是计算结果和是否有操做符号进行显示:
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': if (_isResult) { _text = value; } if (_operateText.isNotEmpty && _beforeText.isEmpty) { _beforeText = _text; _text = ''; } _text += value; if (_text.startsWith('0')) { _text = _text.substring(1); } break;
= 按钮计算结果:
case '=': double d = _value2Double(_beforeText); double d1 = _value2Double(_text); switch (_operateText) { case '+': _text = '${d + d1}'; break; case '-': _text = '${d - d1}'; break; case 'x': _text = '${d * d1}'; break; case '÷': _text = '${d / d1}'; break; } _beforeText = ''; _isResult = true; _operateText = ''; break; double _value2Double(String value) { if (_text.startsWith('-')) { String s = value.substring(1); return double.parse(s) * -1; } else { return double.parse(value); } }
回过头来,发现代码仅仅只有250多行,固然App也是有不足的地方:
老孟Flutter博客地址(330个控件用法):http://laomengit.com
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
![]() |
![]() |