老孟導讀:這是 【Flutter 實戰】組件系列文章的最後一篇,其餘組件地址:laomengit.com/guide/widge…,接下來將會講解動畫系列,關注老孟,精彩不斷。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個控件用法):laomengit.com
歡迎加入Flutter交流羣(微信:laomengit)、關注公衆號【老孟Flutter】:
![]() |
![]() |