Flutter计算器增强版


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

项目概述

运行效果图
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、项目背景与目标

计算器是最基础也是最常用的工具类应用之一。从简单的四则运算到复杂的科学计算,计算器在日常生活、学习、工作中扮演着重要角色。本项目基于Flutter框架开发一款计算器增强版应用,不仅提供基本的四则运算功能,更集成了科学计算、程序员计算、进制转换等高级功能,打造一款功能全面、界面现代的计算工具。

项目的核心目标涵盖多个维度:构建完整的计算引擎系统,实现多种计算模式切换,设计直观的用户界面,打造流畅的交互体验,以及确保计算的准确性和性能表现。通过本项目的开发,不仅能够深入理解Flutter在工具类应用中的应用,更能掌握表达式解析、状态管理、键盘布局等核心技术要点。

二、技术选型与架构设计

技术栈分析

本项目选用Flutter作为开发框架,主要基于以下考量:Flutter的跨平台特性能够同时支持Android和iOS平台,降低开发成本;声明式UI编程范式能够高效构建复杂的键盘布局;丰富的Widget组件库为应用UI开发提供了坚实基础;优秀的性能表现确保了计算的流畅性。

Dart语言作为Flutter的开发语言,具备强类型、异步编程支持、优秀的性能表现等特性。项目采用单文件架构,将所有应用逻辑集中在main.dart文件中,这种设计既便于代码管理,又利于理解应用整体架构。

架构层次划分

应用架构采用分层设计思想,主要分为以下几个层次:

数据模型层:定义应用中的核心数据结构,包括CalculationHistory(计算历史)、CalculatorMode(计算模式)、NumberBase(进制类型)等类和枚举。这些模型类封装了计算器的状态和行为,构成了应用逻辑的基础。

业务逻辑层:实现应用的核心功能逻辑,包括表达式解析、数值计算、进制转换、内存操作等。这一层是应用的心脏,决定了应用的功能性和可用性。

渲染表现层:负责应用界面的绘制和UI展示,使用Flutter的Material Design组件库实现现代化的界面设计,通过动态键盘布局实现不同计算模式的切换。

状态管理层:管理应用的各种状态,包括当前显示值、表达式、计算模式、内存值等,确保应用状态的一致性和可预测性。

核心功能模块详解

一、表达式解析系统

解析器设计原理

表达式解析是计算器的核心功能,采用递归下降解析算法实现:

class _ExpressionParser {
  int _pos = 0;
  String _expr = '';

  double parse(String expr) {
    _expr = expr.replaceAll(' ', '');
    _pos = 0;
    return _parseExpression();
  }
}

解析器维护当前位置指针_pos,从左到右扫描表达式。解析过程分为三个层次:表达式层处理加减法、项层处理乘除法、因子层处理数字和括号。

表达式层解析

表达式层处理加减运算:

double _parseExpression() {
  double result = _parseTerm();
  while (_pos < _expr.length) {
    String op = _expr[_pos];
    if (op == '+') {
      _pos++;
      result += _parseTerm();
    } else if (op == '-') {
      _pos++;
      result -= _parseTerm();
    } else {
      break;
    }
  }
  return result;
}

首先解析第一个项,然后循环处理后续的加减运算。这种设计确保了加减法的左结合性。

项层解析

项层处理乘除运算:

double _parseTerm() {
  double result = _parseFactor();
  while (_pos < _expr.length) {
    String op = _expr[_pos];
    if (op == '*') {
      _pos++;
      result *= _parseFactor();
    } else if (op == '/') {
      _pos++;
      double divisor = _parseFactor();
      if (divisor == 0) throw Exception('Division by zero');
      result /= divisor;
    } else {
      break;
    }
  }
  return result;
}

项层优先级高于表达式层,确保乘除法先于加减法执行。除法运算时检查除数是否为零,避免运行时错误。

因子层解析

因子层处理数字、括号和正负号:

double _parseFactor() {
  if (_pos >= _expr.length) throw Exception('Unexpected end');

  if (_expr[_pos] == '(') {
    _pos++;
    double result = _parseExpression();
    if (_pos >= _expr.length || _expr[_pos] != ')') {
      throw Exception('Missing )');
    }
    _pos++;
    return result;
  }

  if (_expr[_pos] == '-') {
    _pos++;
    return -_parseFactor();
  }

  if (_expr[_pos] == '+') {
    _pos++;
    return _parseFactor();
  }

  return _parseNumber();
}

遇到左括号时递归解析括号内的表达式,遇到正负号时处理一元运算符。这种递归设计优雅地处理了嵌套括号的情况。

二、科学计算功能

三角函数实现

三角函数支持角度制输入:

case 'sin':
  result = math.sin(value * math.pi / 180);
  break;
case 'cos':
  result = math.cos(value * math.pi / 180);
  break;
case 'tan':
  result = math.tan(value * math.pi / 180);
  break;

用户输入的角度值需要转换为弧度制才能使用dart:math库的三角函数。转换公式为:弧度 = 角度 × π / 180。

对数函数实现

提供常用对数和自然对数:

case 'log':
  result = math.log(value) / math.ln10;
  break;
case 'ln':
  result = math.log(value);
  break;

dart:math库只提供自然对数log函数,常用对数通过换底公式计算:log₁₀(x) = ln(x) / ln(10)。

其他科学函数
case 'sqrt':
  result = math.sqrt(value);
  break;
case 'abs':
  result = value.abs();
  break;
case 'exp':
  result = math.exp(value);
  break;
case 'floor':
  result = value.floorToDouble();
  break;
case 'ceil':
  result = value.ceilToDouble();
  break;

平方根、绝对值、指数、取整等函数直接使用dart:math库实现,确保计算精度。

三、程序员计算功能

进制转换实现

进制转换使用Dart内置的进制转换函数:

String _convertBase(String value, NumberBase from, NumberBase to) {
  try {
    int decimal = int.parse(value, radix: _getRadix(from));
    return decimal.toRadixString(_getRadix(to)).toUpperCase();
  } catch (e) {
    return 'Error';
  }
}

int _getRadix(NumberBase base) {
  switch (base) {
    case NumberBase.binary:
      return 2;
    case NumberBase.octal:
      return 8;
    case NumberBase.decimal:
      return 10;
    case NumberBase.hexadecimal:
      return 16;
  }
}

int.parse函数支持指定进制解析字符串,toRadixString方法支持将整数转换为指定进制的字符串表示。

位运算支持

程序员模式支持多种位运算:

case 'AND':
case 'OR':
case 'XOR':
case 'NOT':
case 'MOD':
  _insertOperator(value);
  break;
case '<<':
case '>>':
  _insertOperator(value);
  break;

位运算符包括:与运算(AND)、或运算(OR)、异或运算(XOR)、非运算(NOT)、取模(MOD)、左移(<<)、右移(>>)。

四、内存操作功能

内存存储设计

计算器提供四个内存操作功能:

String _memory = '';

void _memoryClear() {
  _memory = '';
}

void _memoryRecall() {
  if (_memory.isNotEmpty) {
    _display = _memory;
    _isNewInput = true;
  }
}

void _memoryAdd() {
  try {
    double current = double.parse(_display);
    double mem = _memory.isEmpty ? 0 : double.parse(_memory);
    _memory = _formatResult(mem + current);
  } catch (e) {
    _showError();
  }
}

void _memorySubtract() {
  try {
    double current = double.parse(_display);
    double mem = _memory.isEmpty ? 0 : double.parse(_memory);
    _memory = _formatResult(mem - current);
  } catch (e) {
    _showError();
  }
}

内存操作包括:MC(清除内存)、MR(调用内存)、M+(添加到内存)、M-(从内存减去)。内存值显示在屏幕底部,方便用户查看。

五、计算历史功能

历史记录存储

每次计算结果保存到历史列表:

class CalculationHistory {
  final String expression;
  final String result;
  final DateTime timestamp;
}

_history.insert(0, CalculationHistory(
  expression: _expression + _display,
  result: formattedResult,
));

历史记录包含表达式、结果和时间戳,新记录插入到列表开头,确保最新的记录显示在最前面。

历史记录展示

历史页面使用列表展示所有记录:

ListView.builder(
  padding: const EdgeInsets.all(16),
  itemCount: _history.length,
  itemBuilder: (context, index) {
    final item = _history[index];
    return Card(
      child: ListTile(
        title: Text(item.expression),
        subtitle: Text('= ${item.result}'),
        trailing: Text(_formatTime(item.timestamp)),
        onTap: () {
          setState(() {
            _display = item.result;
            _isNewInput = true;
            _currentIndex = 0;
          });
        },
      ),
    );
  },
)

点击历史记录可以将结果复制到当前显示,方便继续计算。

UI界面开发

一、主界面布局

主界面采用底部导航栏设计,包含三个主要页面:

BottomNavigationBar(
  currentIndex: _currentIndex,
  onTap: (index) => setState(() => _currentIndex = index),
  selectedItemColor: Colors.teal,
  unselectedItemColor: Colors.grey,
  items: const [
    BottomNavigationBarItem(icon: Icon(Icons.calculate), label: '计算器'),
    BottomNavigationBarItem(icon: Icon(Icons.history), label: '历史'),
    BottomNavigationBarItem(icon: Icon(Icons.transform), label: '转换'),
  ],
)

三个页面分别是计算器、历史记录和进制转换,覆盖了应用的主要功能入口。

二、显示区域设计

显示区域展示当前输入和表达式:

Widget _buildDisplay() {
  return Container(
    padding: const EdgeInsets.all(20),
    color: Colors.grey.shade850,
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.end,
      children: [
        Text(_expression, style: TextStyle(color: Colors.grey.shade500, fontSize: 18)),
        const SizedBox(height: 8),
        Text(_display, style: TextStyle(color: _hasError ? Colors.red : Colors.white, fontSize: 48)),
        if (_memory.isNotEmpty)
          Row(
            children: [
              Icon(Icons.memory, size: 14, color: Colors.teal.shade300),
              Text('M: $_memory', style: TextStyle(color: Colors.teal.shade300, fontSize: 12)),
            ],
          ),
      ],
    ),
  );
}

显示区域分为三层:表达式层显示当前计算过程,结果层显示当前数值,内存层显示存储的内存值。错误时结果显示为红色。

三、键盘布局设计

基本模式键盘

基本模式提供四则运算和内存操作:

Widget _buildBasicKeyboard() {
  return Container(
    child: Column(
      children: [
        _buildKeyboardRow(['MC', 'MR', 'M+', 'M-']),
        _buildKeyboardRow(['C', '⌫', '%', '÷']),
        _buildKeyboardRow(['7', '8', '9', '×']),
        _buildKeyboardRow(['4', '5', '6', '-']),
        _buildKeyboardRow(['1', '2', '3', '+']),
        _buildKeyboardRow(['±', '0', '.', '=']),
      ],
    ),
  );
}

键盘采用网格布局,每行四个按钮。数字键使用灰色背景,运算符使用主题色背景,清除键使用红色背景。

科学模式键盘

科学模式扩展了科学计算功能:

Widget _buildScientificKeyboard() {
  return SingleChildScrollView(
    child: Column(
      children: [
        _buildKeyboardRow(['sin', 'cos', 'tan', '÷']),
        _buildKeyboardRow(['log', 'ln', 'sqrt', '×']),
        _buildKeyboardRow(['π', 'e', '^', '-']),
        _buildKeyboardRow(['(', ')', '!', '+']),
        _buildKeyboardRow(['C', '⌫', '%', '=']),
        _buildKeyboardRow(['7', '8', '9', 'abs']),
        _buildKeyboardRow(['4', '5', '6', 'exp']),
        _buildKeyboardRow(['1', '2', '3', 'floor']),
        _buildKeyboardRow(['±', '0', '.', 'ceil']),
      ],
    ),
  );
}

科学模式键盘使用SingleChildScrollView包裹,支持滚动查看所有按钮。科学函数按钮使用紫色背景,与基本按钮区分。

程序员模式键盘

程序员模式提供进制选择和位运算:

Widget _buildProgrammerKeyboard() {
  return Column(
    children: [
      _buildBaseSelector(),
      _buildKeyboardRow(['AND', 'OR', 'XOR', 'NOT']),
      _buildKeyboardRow(['<<', '>>', 'MOD', '÷']),
      _buildKeyboardRow(['A', 'B', 'C', '×']),
      _buildKeyboardRow(['D', 'E', 'F', '-']),
      _buildKeyboardRow(['7', '8', '9', '+']),
      _buildKeyboardRow(['4', '5', '6', '⌫']),
      _buildKeyboardRow(['1', '2', '3', 'C']),
      _buildKeyboardRow(['±', '0', '.', '=']),
    ],
  );
}

程序员模式顶部显示进制选择器,支持二进制、八进制、十进制、十六进制切换。A-F按钮用于输入十六进制数字。

四、按钮样式设计

按钮根据功能类型使用不同颜色:

Widget _buildButton(String text) {
  Color bgColor;

  if (['÷', '×', '-', '+', '='].contains(text)) {
    bgColor = Colors.teal;
  } else if (['C', '⌫'].contains(text)) {
    bgColor = Colors.red.shade400;
  } else if (RegExp(r'^[0-9]$').hasMatch(text) || text == '.') {
    bgColor = Colors.grey.shade800;
  } else if (['sin', 'cos', 'tan', ...].contains(text)) {
    bgColor = Colors.indigo.shade600;
  } else if (['AND', 'OR', 'XOR', ...].contains(text)) {
    bgColor = Colors.purple.shade600;
  } else if (['MC', 'MR', 'M+', 'M-'].contains(text)) {
    bgColor = Colors.orange.shade600;
  } else {
    bgColor = Colors.grey.shade700;
  }

  return Material(
    color: bgColor,
    borderRadius: BorderRadius.circular(12),
    child: InkWell(
      onTap: () => _onButtonPress(text),
      child: Container(height: 60, child: Text(text)),
    ),
  );
}

颜色编码帮助用户快速识别按钮功能:青色表示运算符,红色表示清除,灰色表示数字,紫色表示科学函数,橙色表示内存操作。

性能优化方案

一、表达式解析优化

解析器使用位置指针避免字符串复制:

double parse(String expr) {
  _expr = expr.replaceAll(' ', '');
  _pos = 0;
  return _parseExpression();
}

只进行一次空格移除操作,后续解析直接在原字符串上进行,避免频繁的字符串复制。

二、状态管理优化

状态更新采用增量方式:

void _insertDigit(String digit) {
  if (_isNewInput) {
    _display = digit;
    _isNewInput = false;
  } else {
    _display += digit;
  }
}

数字输入时只在现有字符串后追加,避免重建整个字符串。状态标志位_isNewInput避免了不必要的判断。

三、列表渲染优化

历史列表使用ListView.builder实现按需渲染:

ListView.builder(
  itemCount: _history.length,
  itemBuilder: (context, index) {
    return Card(...);
  },
)

只有可见区域的历史记录才会被创建和渲染,大幅降低了内存占用。

测试方案与步骤

一、功能测试

基本运算测试:验证加减乘除四则运算是否正确;测试连续运算的优先级;检查括号运算是否正确。

科学计算测试:验证三角函数计算结果;测试对数函数计算精度;检查阶乘运算边界条件。

程序员模式测试:验证进制转换结果;测试位运算正确性;检查十六进制输入。

内存操作测试:验证内存存储和调用;测试内存加减运算;检查内存清除功能。

二、边界测试

除零测试:验证除以零时的错误处理。

溢出测试:测试大数计算的结果处理。

精度测试:验证浮点数计算的精度。

三、用户体验测试

响应速度测试:测试按钮响应的及时性。

界面美观测试:评估界面设计和颜色搭配。

操作便捷性测试:评估键盘布局的合理性。

项目总结与展望

一、项目成果总结

本项目成功实现了一款功能完整、界面现代的计算器增强版应用,涵盖了工具类应用开发的核心要素。通过Flutter框架的应用,实现了跨平台的应用体验,证明了Flutter在工具类应用开发领域的可行性。

项目采用模块化设计思想,将应用功能划分为表达式解析、科学计算、程序员计算、内存操作、历史记录等独立模块,各模块职责明确,耦合度低,便于维护和扩展。

二、技术亮点总结

递归下降解析器:实现了完整的表达式解析,支持括号嵌套和运算符优先级。

多模式切换:支持基本、科学、程序员三种计算模式,满足不同用户需求。

进制转换:支持二进制、八进制、十进制、十六进制的相互转换。

内存操作:提供完整的内存存储和操作功能。

计算历史:记录计算历史,支持快速调用历史结果。

三、未来优化方向

单位换算:添加长度、重量、温度等常用单位换算功能。

汇率转换:集成实时汇率API,支持货币转换。

语音输入:支持语音输入表达式,提升便捷性。

主题切换:支持多种主题颜色和布局风格。

小部件支持:添加桌面小部件,快速访问计算功能。

云同步:实现计算历史云端同步,多设备共享。

四、开发经验总结

通过本项目的开发,积累了宝贵的Flutter应用开发经验:

解析算法的重要性:表达式解析是计算器的核心,理解递归下降算法的原理,掌握运算符优先级处理,是开发此类应用的基础。

状态管理的简洁性:计算器涉及大量状态变化,简洁的状态管理设计能够确保数据的一致性和可预测性。

用户体验的细节:工具类应用最终服务于用户,从按钮的颜色编码到键盘的布局设计,每个细节都需要精心打磨。

功能扩展的灵活性:良好的架构设计使得功能扩展变得简单,从基本计算到科学计算再到程序员计算,模块化的设计确保了代码的可维护性。

本项目为Flutter工具类应用开发提供了一个完整的实践案例,展示了如何实现表达式解析、多模式切换、进制转换等核心功能,希望能够为相关开发者提供参考和启发,推动Flutter在工具类应用开发领域的应用和发展。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐