老孟导读:这是 【Flutter 实战】组件系列文章的最后一篇,其他组件地址:http://laomengit.com/guide/widgets/Text.html,接下来将会讲解动画系列,关注老孟,精彩不断。

先看一下效果:

大家学习UI编程语言时喜欢用哪个 App 当作第一个练手的项目呢?,我喜欢使用 计算器 ,可能是习惯了吧,学习 Android 和 React Native 都用此 App 当作练手的项目。

下面我会一步一步的教大家如何实现此项目。

整个项目的 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 组件用于两个组件之间的间隔。

_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 按钮代码如下:

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});@overrideWidget 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);@overrideWidget 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个变量:

  • _text:显示当前输入的数字和计算结果。
  • _beforeText:用于保存被加数,比如输入 5+1,保存 5 ,用于后面的计算。
  • _isResult:表示当前值是否为计算的结果,true:新输入数字直接显示,false:新输入数字和当前字符串相加,比如当前显示 5,如果是计算的结果,点击 1 时,直接显示1,否则显示 51。
  • _operateText:保存加减乘除。

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也是有不足的地方:

  1. 不足之一:计算结果逻辑,上面计算结果的逻辑是不完美的,当增加一个操作符(比如 取余),计算逻辑复杂度将会以指数级方式增加,那为什么还要用此方式?最重要的原因是计算结果逻辑不是此项目的重点,作为一个Flutter的入门项目重点是熟悉组件的使用,计算器的计算逻辑有一个比较著名的方式:后缀表达式的计算过程,然而此方式偏向于算法,对初学者非常不友好,因此,我采用了一种不完美但适合初学者的逻辑。
  2. 不足之二:此App没有考虑横屏的情况,为什么?因为横屏很可能导致整体布局发生变化,横屏时按钮是变大还是拉伸,或者拉伸间隙?不同的方式使用的布局会发生变化,因此,目前只考虑了竖屏的布局,实际项目中要考虑横屏情况吗?其实这是一个用户体验的问题,首先问问自己,为什么要横屏?横屏可以显著的提升用户体验吗?如果不能,为什么要花费大力气适配横屏呢?

交流

老孟Flutter博客地址(330个控件用法):http://laomengit.com

欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:

【Flutter 实战】简约而不简单的计算器相关推荐

  1. Flutter实战 | 从 0 搭建「网易云音乐」APP(三、每日推荐、推荐歌单)

    本系列可能会伴随大家很长时间,这里我会从0开始搭建一个「网易云音乐」的APP出来. 下面是该APP 功能的思维导图: 前期回顾: •Flutter实战 | 从 0 搭建「网易云音乐」APP(一.创建项 ...

  2. 【Flutter实战】定位装饰权重组件及柱状图案例

    老孟导读:Flutter中有这么一类组件,用于定位.装饰.控制子组件,比如 Container (定位.装饰).Expanded (扩展).SizedBox (固定尺寸).AspectRatio (宽 ...

  3. Flutter实战 | 从 0 搭建「网易云音乐」APP(五、播放功能逻辑)

    本系列可能会伴随大家很长时间,这里我会从0开始搭建一个「网易云音乐」的APP出来. 下面是该APP 功能的思维导图: 前期回顾: 1.Flutter实战 | 从 0 搭建「网易云音乐」APP(一.创建 ...

  4. Flutter实战之(Clubhouse App)

    flutter_ClubHouse 课程安排 每日一更, 最后达到的效果是1:1 项目简介 "Clubhouse"(俱乐部会馆)的音频聊天应用程序日前火了.这款社交软件的功能相对单 ...

  5. Flutter实战5 -- 天气查询APP重构之状态管理(ScopedModel)

    0x00 前言 前面四篇文章: Flutter实战1 --- 写一个天气查询的APP Flutter实战2 --- 写一个天气查询的APP FFlutter实战3 --- PC上运行Flutter A ...

  6. Android 实战项目:简单计算器

    文章目录 实战项目:简易计算器 1.需求分析 2.界面设计 3.关键代码 1.输入按键的合法性校验 2.执行运算并显示计算结果 简单计算器 - 详细操作步骤 总结 实战项目:简易计算器 1.需求分析 ...

  7. 【Flutter实战】移动技术发展史

    老孟导读:大家好,这是[Flutter实战]系列文章的第一篇,这并不是一篇Flutter技术文章,而是介绍智能手机操作系统.跨平台技术的演进以及我对各种跨平台技术看法的文章. 智能手机操作系统 塞班( ...

  8. 暗黑风java战棋游戏_简约而不简单的类暗黑战棋游戏

    评测:<符石守护者>--简约而不简单 一句话总结:国产战棋游戏精品 引入了大量暗黑因素的回合制游戏,在零零散散诸多设定下,竟然呈现出一种暗黑的游戏感,不得不令人叹服. <符石守护者& ...

  9. 国内首本《Flutter 实战》中文电子书正式开源了

    来源公众号:开发者技术前线| 作者:白哥 Flutter是Google开发的新一代跨平台方案,Flutter可以实现写一份代码同时运行在iOS和Android设备上,并且提供很好的性能体验.Flutt ...

最新文章

  1. matlab getstart,matlab帮助文件(matlab get start.pdf)
  2. autoware使用激光雷达进行目标检测(五)
  3. 数组-合并两个有序数组(双指针,从后往前)
  4. python使用界面-用python制作用户图形界面
  5. [LeetCode] Remove Duplicates from Sorted Array II
  6. Android之基于BaseAdapter和SimpleAdapter的GridView
  7. 如何基于OSS和MTS,快速搭建音视频文件上传服务?
  8. STM32那点事(3)_中断(下)
  9. c语言程序设计 江宝钏 实验九,c语言程序设计,江宝钏著,实验九
  10. POJ 2229 Sumsets(递推,找规律)
  11. 严重: Exception sending context initialized event to listener instance of class
  12. 魔兽世界稳定服务器,魔兽世界美服服务器趋于稳定 排队新技术将实装
  13. 网络扫描及安全评估实验实验报告
  14. java 国际象棋_java – 自我项目:使用GUI创建国际象棋游戏
  15. 金山陈飞舟:《剑网3》3D引擎重生记
  16. 解析Hander消息处理机制
  17. ADS 常见问题及解决方法
  18. The 2021 Sichuan Provincial 四川省赛 L. Spicy Restaurant(多源bfs)
  19. 14nm服务器芯片,Intel最后一代14nm服务器平台仍然杳无踪影 全新10nm Ice Lake具体特性一直未公布...
  20. 快递分拣的计算机技术是那些,RFID物流分拣系统方案

热门文章

  1. 正确的慢跑姿势是怎样的?
  2. cocos2d-x碰撞检测学习笔记
  3. 中文汉字和常见英文数字等的unicode编码范围
  4. 文件系统目录结构(Linux)
  5. Python 爬虫实例(6)—— 爬取蚂蚁免费代理
  6. python 中使用breast_cancer数据画图
  7. 140版本 boost_看完这篇,相信对UB4.0 boost这双鞋你会有新的感受!
  8. opengl的配置和太阳-地球-月球演示
  9. Java日志框架-SLF4J入门
  10. 微淘百课微信万群直播定制机好用么?