老孟導讀:這是 【Flutter 實戰】組件系列文章的最後一篇,其餘組件地址: http://laomengit.com/guide/widgets/Text.html,接下來將會講解動畫系列,關注老孟,精彩不斷。
先看一下效果:html
你們學習UI編程語言時喜歡用哪一個 App 看成第一個練手的項目呢?,我喜歡使用 計算器 ,多是習慣了吧,學習 Android 和 React Native 都用此 App 看成練手的項目。git
下面我會一步一步的教你們如何實現此項目。算法
整個項目的 UI 分爲兩大部分,一部分是頂部顯示數字和計算結果,另外一部分是底部的輸入按鈕。編程
因此總體佈局使用 Column,在不一樣分辨率的手機上,規定底部固定大小,剩餘空間都由頂部組件填充,因此頂部組件使用 Expanded 擴充,代碼以下:微信
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 組件用於兩個組件之間的間隔。less
_CalculatorKeyboard 是底部的輸入按鈕組件,也是此項目的重點,除了 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: 70, height: 70, alignment: Alignment.center, child: Text( '1', style: TextStyle(color: Colors.white, fontSize: 24), ), ), ), )
而 0 這個按鈕的寬度是兩個按鈕的寬度 + 兩個按鈕的間隙,因此 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: 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】: