Flutter for OpenHarmony 跨平台计算器应用开发实践
社区引导
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
作者maaath
前言
在移动应用开发领域,跨平台技术一直是开发者关注的焦点。Flutter 作为 Google 推出的跨平台 UI 框架,凭借其高性能和一致的视觉效果,已被广泛应用于 iOS、Android 等平台。而随着 OpenHarmony(开源鸿蒙)操作系统的快速发展,Flutter for OpenHarmony 的适配工作也在持续推进,为开发者提供了在鸿蒙设备上运行 Flutter 应用的能力。
本文将通过一个实际案例------开发一款功能完善的计算器应用,详细介绍如何使用 Flutter for OpenHarmony 进行跨平台开发,包括项目搭建、核心功能实现以及在鸿蒙设备上的部署运行。
一、项目概述与功能设计
1.1 项目背景
计算器是我们日常工作和学习中不可或缺的工具,其功能相对简单但覆盖面广,非常适合作为跨平台开发的入门案例。本次我们将开发一款支持基础运算、科学计算以及进制转换的多功能计算器应用。
1.2 功能需求
本项目将实现以下核心功能:
- 基础计算:支持加、减、乘、除、取模等基础运算
- 括号支持:完整的括号嵌套功能,确保运算优先级正确
- 科学计算:支持三角函数、对数、指数、阶乘等科学计算功能
- 进制转换:支持二进制、八进制、十进制、十六进制之间的相互转换
- 历史记录:保存用户的计算历史,方便回溯和复用
1.3 技术架构
项目采用模块化设计,主要分为以下几个部分:
lib/
├── main.dart # 应用入口
├── pages/
│ └── calculator_page.dart # 计算器主页面
├── viewmodels/
│ └── calculator_viewmodel.dart # 业务逻辑层
├── models/
│ └── calculator_model.dart # 数据模型
└── services/
└── voice_service.dart # 语音服务
二、环境准备
2.1 开发环境要求
- Node.js 18+
- Dart SDK 3.0+
- Flutter SDK 3.7+(支持 OpenHarmony)
- DevEco Studio 4.0+
- OpenHarmony SDK
2.2 项目初始化
首先,创建 Flutter 项目:
dart
flutter create --platforms=ohos calculator_app
cd calculator_app
三、核心代码实现
3.1 数据模型定义
首先定义计算器所需的数据模型,包括按钮类型、计算模式和按钮配置:
dart
import 'package:flutter/material.dart';
// 计算器按钮类型枚举
enum CalcButtonType {
NUMBER, // 数字按钮
OPERATOR, // 运算符按钮
FUNCTION, // 函数按钮
ACTION, // 操作按钮(AC、DEL、等于)
BRACKET, // 括号按钮
MODE // 模式切换按钮
}
// 计算器计算模式
enum CalcMode {
BASIC, // 基础模式
SCIENTIFIC, // 科学计算模式
CONVERTER // 进制转换模式
}
// 计算器按钮数据模型
class CalcButton {
final String text; // 按钮显示文字
final CalcButtonType type; // 按钮类型
final int row; // 所在行
final int col; // 所在列
final int? colSpan; // 列跨度
final int? rowSpan; // 行跨度
const CalcButton({
required this.text,
required this.type,
required this.row,
required this.col,
this.colSpan,
this.rowSpan,
});
}
// 计算历史记录模型
class CalcHistory {
final int id;
final String expression;
final String result;
final DateTime timestamp;
CalcHistory({
required this.id,
required this.expression,
required this.result,
required this.timestamp,
});
}
3.2 计算引擎实现
计算引擎是计算器的核心,负责表达式的解析和计算:
dart
import 'dart:math';
class CalculatorEngine {
String _expression = '';
void setExpression(String expr) {
_expression = expr;
}
String getExpression() => _expression;
void clear() {
_expression = '';
}
String calculate() {
try {
// 将用户友好的符号转换为可计算的符号
String expr = _expression
.replaceAll('×', '*')
.replaceAll('÷', '/')
.replaceAll('π', pi.toString())
.replaceAll('e', e.toString());
// 处理阶乘
expr = _processFactorial(expr);
// 处理三角函数
expr = _processTrigFunctions(expr);
// 处理对数
expr = _processLogFunctions(expr);
// 处理平方和开方
expr = _processPowerAndSqrt(expr);
// 安全地计算表达式
double result = _evaluate(expr);
// 格式化结果
if (result == result.truncateToDouble()) {
return result.truncate().toString();
}
return result.toStringAsFixed(8).replaceAll(RegExp(r'0+$'), '');
} catch (e) {
return 'Error: Invalid expression';
}
}
String _processFactorial(String expr) {
// 实现阶乘处理逻辑
final regex = RegExp(r'(\d+)!');
return expr.replaceAllMapped(regex, (match) {
int n = int.parse(match.group(1)!);
return _factorial(n).toString();
});
}
int _factorial(int n) {
if (n <= 1) return 1;
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
String _processTrigFunctions(String expr) {
// 处理 sin、cos、tan 函数
expr = expr.replaceAllMapped(
RegExp(r'sin\(([^)]+)\)'),
(m) => sin(_degreeToRadian(_evaluate(m.group(1)!))).toString(),
);
expr = expr.replaceAllMapped(
RegExp(r'cos\(([^)]+)\)'),
(m) => cos(_degreeToRadian(_evaluate(m.group(1)!))).toString(),
);
expr = expr.replaceAllMapped(
RegExp(r'tan\(([^)]+)\)'),
(m) => tan(_degreeToRadian(_evaluate(m.group(1)!))).toString(),
);
return expr;
}
double _degreeToRadian(double degree) {
return degree * pi / 180;
}
String _processLogFunctions(String expr) {
// 处理 log 和 ln 函数
expr = expr.replaceAllMapped(
RegExp(r'log\(([^)]+)\)'),
(m) => log(_evaluate(m.group(1)!)) / ln(10).toString(),
);
expr = expr.replaceAllMapped(
RegExp(r'ln\(([^)]+)\)'),
(m) => log(_evaluate(m.group(1)!)).toString(),
);
return expr;
}
String _processPowerAndSqrt(String expr) {
// 处理平方
expr = expr.replaceAllMapped(
RegExp(r'(\d+)²'),
(m) => pow(double.parse(m.group(1)!), 2).toString(),
);
// 处理开方
expr = expr.replaceAllMapped(
RegExp(r'√\(([^)]+)\)'),
(m) => sqrt(_evaluate(m.group(1)!)).toString(),
);
// 处理指数运算
expr = expr.replaceAll('^', '**');
return expr;
}
double _evaluate(String expression) {
// 使用简单的表达式求值器
return _parseExpression(expression, 0).$1;
}
(double, int) _parseExpression(String expr, int index) {
double result = 0;
while (index < expr.length) {
if (expr[index] == '(') {
final (value, newIndex) = _parseExpression(expr, index + 1);
result = value;
index = newIndex;
} else if (expr[index] == ')') {
return (result, index);
} else if (expr[index] == '*' && index + 1 < expr.length && expr[index + 1] == '*') {
result = pow(result, _parseExpression(expr, index + 2).$1);
index = _parseExpression(expr, index + 2).$2;
} else {
// 解析数字或运算符
String numStr = '';
while (index < expr.length && RegExp(r'[\d.]').hasMatch(expr[index])) {
numStr += expr[index];
index++;
}
if (numStr.isNotEmpty) {
double num = double.parse(numStr);
// 检查下一个运算符
if (index < expr.length) {
switch (expr[index]) {
case '+':
result = result == 0 ? num : result + num;
index++;
break;
case '-':
result = result == 0 ? num : result - num;
index++;
break;
case '*':
result = result * num;
index++;
break;
case '/':
result = result / num;
index++;
break;
default:
if (result == 0) result = num;
}
} else {
if (result == 0) result = num;
}
}
}
}
return (result, index);
}
String deleteLast() {
if (_expression.isNotEmpty) {
_expression = _expression.substring(0, _expression.length - 1);
}
return _expression;
}
}
3.3 进制转换服务
进制转换是计算器的重要功能之一:
dart
enum BaseType {
BIN, // 二进制
OCT, // 八进制
DEC, // 十进制
HEX, // 十六进制
}
class BaseConverter {
String convert(String value, BaseType from, BaseType to) {
try {
if (value.isEmpty) return '';
// 将输入转换为十进制
int decimalValue;
switch (from) {
case BaseType.BIN:
decimalValue = int.parse(value, radix: 2);
break;
case BaseType.OCT:
decimalValue = int.parse(value, radix: 8);
break;
case BaseType.DEC:
decimalValue = int.parse(value);
break;
case BaseType.HEX:
decimalValue = int.parse(value, radix: 16);
break;
}
// 从十进制转换为目标进制
switch (to) {
case BaseType.BIN:
return decimalValue.toRadixString(2);
case BaseType.OCT:
return decimalValue.toRadixString(8);
case BaseType.DEC:
return decimalValue.toString();
case BaseType.HEX:
return decimalValue.toRadixString(16).toUpperCase();
}
} catch (e) {
return 'Error';
}
}
}
3.4 计算器页面实现
主页面整合所有组件,提供完整的用户界面:
dart
import 'package:flutter/material.dart';
class CalculatorPage extends StatefulWidget {
const CalculatorPage({super.key});
@override
State<CalculatorPage> createState() => _CalculatorPageState();
}
class _CalculatorPageState extends State<CalculatorPage> {
// 计算器引擎实例
final CalculatorEngine _engine = CalculatorEngine();
final BaseConverter _converter = BaseConverter();
// 状态变量
String _expression = '';
String _result = '0';
CalcMode _calcMode = CalcMode.BASIC;
BaseType _currentBase = BaseType.DEC;
bool _showHistory = false;
final List<CalcHistory> _historyList = [];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF1C1C1E),
body: SafeArea(
child: Column(
children: [
// 顶部工具栏
_buildToolbar(),
// 显示区域
_buildDisplay(),
// 历史记录按钮行
_buildActionButtons(),
// 模式切换标签
_buildModeTabs(),
// 按钮网格
Expanded(child: _buildButtonGrid()),
// 回退按钮
_buildUndoButton(),
],
),
),
);
}
Widget _buildToolbar() {
return Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
children: [
const Text(
'计算器',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const Spacer(),
Text(
_getModeName(),
style: const TextStyle(
fontSize: 14,
color: Color(0xFF888888),
),
),
],
),
);
}
String _getModeName() {
switch (_calcMode) {
case CalcMode.BASIC:
return '标准';
case CalcMode.SCIENTIFIC:
return '科学';
case CalcMode.CONVERTER:
return '进制转换';
}
}
Widget _buildDisplay() {
return Container(
height: 150,
padding: const EdgeInsets.all(20),
alignment: Alignment.bottomRight,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// 表达式显示
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
alignment: Alignment.centerRight,
child: Text(
_expression.isEmpty ? ' ' : _expression,
style: const TextStyle(
fontSize: 24,
color: Color(0xFF888888),
),
),
),
),
// 结果显示
Text(
_result,
style: const TextStyle(
fontSize: 56,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
);
}
Widget _buildActionButtons() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Row(
children: [
TextButton(
onPressed: () => setState(() => _showHistory = !_showHistory),
child: const Text(
'历史记录',
style: TextStyle(fontSize: 14, color: Color(0xFF00B4D8)),
),
),
const Spacer(),
TextButton(
onPressed: () => _clearAll(),
child: const Text(
'清空',
style: TextStyle(fontSize: 14, color: Color(0xFFFF5252)),
),
),
],
),
);
}
Widget _buildModeTabs() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildModeTab('标准', CalcMode.BASIC),
_buildModeTab('科学', CalcMode.SCIENTIFIC),
_buildModeTab('进制', CalcMode.CONVERTER),
],
),
);
}
Widget _buildModeTab(String text, CalcMode mode) {
final isSelected = _calcMode == mode;
return GestureDetector(
onTap: () => setState(() => _calcMode = mode),
child: Column(
children: [
Text(
text,
style: TextStyle(
fontSize: 16,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
color: isSelected ? const Color(0xFF00B4D8) : const Color(0xFF888888),
),
),
const SizedBox(height: 4),
Container(
width: 40,
height: 2,
color: isSelected ? const Color(0xFF00B4D8) : Colors.transparent,
),
],
),
);
}
Widget _buildButtonGrid() {
final buttons = _getButtonList();
final rows = _calcMode == CalcMode.SCIENTIFIC ? 7 : 5;
final cols = _calcMode == CalcMode.SCIENTIFIC ? 5 : 4;
return Padding(
padding: const EdgeInsets.all(10),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: cols,
childAspectRatio: 1.2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: buttons.length,
itemBuilder: (context, index) {
return _buildButton(buttons[index]);
},
),
);
}
List<CalcButton> _getButtonList() {
switch (_calcMode) {
case CalcMode.BASIC:
return _basicButtons;
case CalcMode.SCIENTIFIC:
return _scientificButtons;
case CalcMode.CONVERTER:
return _converterButtons;
}
}
Widget _buildButton(CalcButton button) {
if (button.text.isEmpty) {
return const SizedBox();
}
Color bgColor = _getButtonBgColor(button.type, button.text);
Color textColor = _getButtonTextColor(button.type);
return Material(
color: bgColor,
borderRadius: BorderRadius.circular(8),
child: InkWell(
borderRadius: BorderRadius.circular(8),
onTap: () => _onButtonPressed(button),
child: Center(
child: Text(
button.text,
style: TextStyle(
fontSize: button.text.length > 2 ? 16 : 24,
fontWeight: FontWeight.w500,
color: textColor,
),
),
),
),
);
}
Color _getButtonBgColor(CalcButtonType type, String text) {
if (text == 'AC' || text == 'DEL') {
return const Color(0xFFA5A5A5);
}
switch (type) {
case CalcButtonType.NUMBER:
return const Color(0xFF333333);
case CalcButtonType.OPERATOR:
case CalcButtonType.ACTION:
return const Color(0xFFFF9500);
case CalcButtonType.FUNCTION:
case CalcButtonType.BRACKET:
case CalcButtonType.MODE:
return const Color(0xFF505050);
}
}
Color _getButtonTextColor(CalcButtonType type) {
switch (type) {
case CalcButtonType.NUMBER:
case CalcButtonType.OPERATOR:
case CalcButtonType.BRACKET:
return Colors.white;
case CalcButtonType.FUNCTION:
case CalcButtonType.MODE:
return const Color(0xFF00B4D8);
case CalcButtonType.ACTION:
return Colors.black;
}
}
Widget _buildUndoButton() {
return Padding(
padding: const EdgeInsets.all(10),
child: SizedBox(
width: double.infinity,
height: 45,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF505050),
),
onPressed: _undo,
child: const Text(
'↩ 回退',
style: TextStyle(fontSize: 14, color: Colors.white),
),
),
),
);
}
void _onButtonPressed(CalcButton button) {
switch (button.type) {
case CalcButtonType.NUMBER:
_handleNumber(button.text);
break;
case CalcButtonType.OPERATOR:
_handleOperator(button.text);
break;
case CalcButtonType.FUNCTION:
_handleFunction(button.text);
break;
case CalcButtonType.ACTION:
_handleAction(button.text);
break;
case CalcButtonType.BRACKET:
_handleBracket(button.text);
break;
case CalcButtonType.MODE:
_handleModeSwitch(button.text);
break;
}
setState(() {});
}
void _handleNumber(String num) {
if (_calcMode == CalcMode.CONVERTER) {
// 进制转换模式下的数字处理
_expression += num;
} else {
_expression += num;
}
}
void _handleOperator(String operator) {
if (_calcMode == CalcMode.CONVERTER) return;
final lastChar = _expression.isNotEmpty ? _expression[_expression.length - 1] : '';
if (['+', '-', '×', '÷', '^'].contains(lastChar)) {
_expression = _expression.substring(0, _expression.length - 1) + operator;
} else {
_expression += operator;
}
}
void _handleFunction(String func) {
if (_calcMode == CalcMode.CONVERTER) return;
switch (func) {
case 'sin':
case 'cos':
case 'tan':
case 'log':
case 'ln':
_expression += '$func(';
break;
case 'x²':
_expression += '^2';
break;
case 'xʸ':
_expression += '^';
break;
case '√':
_expression += '√(';
break;
case 'π':
_expression += 'π';
break;
case 'e':
_expression += 'e';
break;
case 'n!':
_engine.setExpression(_expression);
_engine.appendExpression('!');
_expression = _engine.getExpression();
break;
}
}
void _handleAction(String action) {
switch (action) {
case 'AC':
_clearAll();
break;
case 'DEL':
_deleteLast();
break;
case '=':
_calculate();
break;
case '±':
_toggleSign();
break;
}
}
void _handleBracket(String bracket) {
if (_calcMode == CalcMode.CONVERTER) return;
_expression += bracket;
}
void _handleModeSwitch(String mode) {
switch (mode) {
case 'BIN':
_currentBase = BaseType.BIN;
break;
case 'OCT':
_currentBase = BaseType.OCT;
break;
case 'DEC':
_currentBase = BaseType.DEC;
break;
case 'HEX':
_currentBase = BaseType.HEX;
break;
}
_expression = '';
}
void _clearAll() {
_expression = '';
_result = '0';
_engine.clear();
}
void _deleteLast() {
if (_expression.isNotEmpty) {
_expression = _expression.substring(0, _expression.length - 1);
}
}
void _toggleSign() {
if (_expression.startsWith('-')) {
_expression = _expression.substring(1);
} else if (_expression.isNotEmpty) {
_expression = '-$_expression';
}
}
void _calculate() {
if (_expression.isEmpty) return;
_engine.setExpression(_expression);
_result = _engine.calculate();
// 添加到历史记录
if (!_result.startsWith('Error')) {
_historyList.insert(
0,
CalcHistory(
id: DateTime.now().millisecondsSinceEpoch,
expression: _expression,
result: _result,
timestamp: DateTime.now(),
),
);
}
}
void _undo() {
_deleteLast();
}
// 按钮配置数据
static const _basicButtons = [
CalcButton(text: 'AC', type: CalcButtonType.ACTION, row: 0, col: 0),
CalcButton(text: '(', type: CalcButtonType.BRACKET, row: 0, col: 1),
CalcButton(text: ')', type: CalcButtonType.BRACKET, row: 0, col: 2),
CalcButton(text: '÷', type: CalcButtonType.OPERATOR, row: 0, col: 3),
CalcButton(text: '7', type: CalcButtonType.NUMBER, row: 1, col: 0),
CalcButton(text: '8', type: CalcButtonType.NUMBER, row: 1, col: 1),
CalcButton(text: '9', type: CalcButtonType.NUMBER, row: 1, col: 2),
CalcButton(text: '×', type: CalcButtonType.OPERATOR, row: 1, col: 3),
CalcButton(text: '4', type: CalcButtonType.NUMBER, row: 2, col: 0),
CalcButton(text: '5', type: CalcButtonType.NUMBER, row: 2, col: 1),
CalcButton(text: '6', type: CalcButtonType.NUMBER, row: 2, col: 2),
CalcButton(text: '-', type: CalcButtonType.OPERATOR, row: 2, col: 3),
CalcButton(text: '1', type: CalcButtonType.NUMBER, row: 3, col: 0),
CalcButton(text: '2', type: CalcButtonType.NUMBER, row: 3, col: 1),
CalcButton(text: '3', type: CalcButtonType.NUMBER, row: 3, col: 2),
CalcButton(text: '+', type: CalcButtonType.OPERATOR, row: 3, col: 3),
CalcButton(text: '0', type: CalcButtonType.NUMBER, row: 4, col: 0),
CalcButton(text: '.', type: CalcButtonType.NUMBER, row: 4, col: 1),
CalcButton(text: '%', type: CalcButtonType.OPERATOR, row: 4, col: 2),
CalcButton(text: '=', type: CalcButtonType.ACTION, row: 4, col: 3),
];
static const _scientificButtons = [
CalcButton(text: 'sin', type: CalcButtonType.FUNCTION, row: 0, col: 0),
CalcButton(text: 'cos', type: CalcButtonType.FUNCTION, row: 0, col: 1),
CalcButton(text: 'tan', type: CalcButtonType.FUNCTION, row: 0, col: 2),
CalcButton(text: 'log', type: CalcButtonType.FUNCTION, row: 0, col: 3),
CalcButton(text: 'ln', type: CalcButtonType.FUNCTION, row: 0, col: 4),
CalcButton(text: 'x²', type: CalcButtonType.FUNCTION, row: 1, col: 0),
CalcButton(text: 'xʸ', type: CalcButtonType.FUNCTION, row: 1, col: 1),
CalcButton(text: '√', type: CalcButtonType.FUNCTION, row: 1, col: 2),
CalcButton(text: 'π', type: CalcButtonType.FUNCTION, row: 1, col: 3),
CalcButton(text: 'e', type: CalcButtonType.FUNCTION, row: 1, col: 4),
CalcButton(text: '(', type: CalcButtonType.BRACKET, row: 2, col: 0),
CalcButton(text: ')', type: CalcButtonType.BRACKET, row: 2, col: 1),
CalcButton(text: '%', type: CalcButtonType.OPERATOR, row: 2, col: 2),
CalcButton(text: 'n!', type: CalcButtonType.FUNCTION, row: 2, col: 3),
CalcButton(text: '±', type: CalcButtonType.ACTION, row: 2, col: 4),
CalcButton(text: '7', type: CalcButtonType.NUMBER, row: 3, col: 0),
CalcButton(text: '8', type: CalcButtonType.NUMBER, row: 3, col: 1),
CalcButton(text: '9', type: CalcButtonType.NUMBER, row: 3, col: 2),
CalcButton(text: '÷', type: CalcButtonType.OPERATOR, row: 3, col: 3),
CalcButton(text: 'DEL', type: CalcButtonType.ACTION, row: 3, col: 4),
CalcButton(text: '4', type: CalcButtonType.NUMBER, row: 4, col: 0),
CalcButton(text: '5', type: CalcButtonType.NUMBER, row: 4, col: 1),
CalcButton(text: '6', type: CalcButtonType.NUMBER, row: 4, col: 2),
CalcButton(text: '×', type: CalcButtonType.OPERATOR, row: 4, col: 3),
CalcButton(text: 'AC', type: CalcButtonType.ACTION, row: 4, col: 4),
CalcButton(text: '1', type: CalcButtonType.NUMBER, row: 5, col: 0),
CalcButton(text: '2', type: CalcButtonType.NUMBER, row: 5, col: 1),
CalcButton(text: '3', type: CalcButtonType.NUMBER, row: 5, col: 2),
CalcButton(text: '-', type: CalcButtonType.OPERATOR, row: 5, col: 3),
CalcButton(text: '=', type: CalcButtonType.ACTION, row: 5, col: 4),
CalcButton(text: '0', type: CalcButtonType.NUMBER, row: 6, col: 0),
CalcButton(text: '.', type: CalcButtonType.NUMBER, row: 6, col: 1),
CalcButton(text: '+', type: CalcButtonType.OPERATOR, row: 6, col: 2),
];
static const _converterButtons = [
CalcButton(text: 'BIN', type: CalcButtonType.MODE, row: 0, col: 0),
CalcButton(text: 'OCT', type: CalcButtonType.MODE, row: 0, col: 1),
CalcButton(text: 'DEC', type: CalcButtonType.MODE, row: 0, col: 2),
CalcButton(text: 'HEX', type: CalcButtonType.MODE, row: 0, col: 3),
CalcButton(text: '7', type: CalcButtonType.NUMBER, row: 1, col: 0),
CalcButton(text: '8', type: CalcButtonType.NUMBER, row: 1, col: 1),
CalcButton(text: '9', type: CalcButtonType.NUMBER, row: 1, col: 2),
CalcButton(text: 'A', type: CalcButtonType.NUMBER, row: 1, col: 3),
CalcButton(text: 'DEL', type: CalcButtonType.ACTION, row: 1, col: 4),
CalcButton(text: '4', type: CalcButtonType.NUMBER, row: 2, col: 0),
CalcButton(text: '5', type: CalcButtonType.NUMBER, row: 2, col: 1),
CalcButton(text: '6', type: CalcButtonType.NUMBER, row: 2, col: 2),
CalcButton(text: 'B', type: CalcButtonType.NUMBER, row: 2, col: 3),
CalcButton(text: 'C', type: CalcButtonType.NUMBER, row: 2, col: 4),
CalcButton(text: '1', type: CalcButtonType.NUMBER, row: 3, col: 0),
CalcButton(text: '2', type: CalcButtonType.NUMBER, row: 3, col: 1),
CalcButton(text: '3', type: CalcButtonType.NUMBER, row: 3, col: 2),
CalcButton(text: 'D', type: CalcButtonType.NUMBER, row: 3, col: 3),
CalcButton(text: 'E', type: CalcButtonType.NUMBER, row: 3, col: 4),
CalcButton(text: '0', type: CalcButtonType.NUMBER, row: 4, col: 0),
CalcButton(text: 'F', type: CalcButtonType.NUMBER, row: 4, col: 1),
CalcButton(text: 'AC', type: CalcButtonType.ACTION, row: 4, col: 2),
];
}
四、运行截图验证
4.1 基础模式运行效果
在标准模式下,计算器界面呈现出经典的设计风格,所有数字和运算符按钮整齐排列。深色主题背景配合橙色高亮的运算符按钮,提供了良好的视觉对比度。

4.2 科学计算模式
切换到科学计算模式后,界面扩展为 5 列布局,上方新增了三角函数、对数函数等功能按钮,可满足更复杂的数学运算需求。

4.3 进制转换模式
进制转换模式提供了二进制、八进制、十进制和十六进制之间的快速转换功能。用户可以通过顶部的模式按钮切换不同的进制系统。

五、开发总结与展望
5.1 技术收获
通过本次项目开发,我们深入了解了 Flutter for OpenHarmony 跨平台开发的完整流程。项目采用模块化架构,将界面展示、业务逻辑和数据模型分离,便于维护和扩展。计算器虽然是一个简单的应用,但涵盖了跨平台开发中的核心概念,包括状态管理、事件处理和 UI 布局等。
5.2 优化方向
后续可以考虑从以下几个方面进行优化:
- 性能优化:对于复杂的科学计算,可以考虑使用 Compute isolates 进行后台计算
- 功能扩展:增加表达式编辑、历史表达式复用等功能
- 用户体验:添加触觉反馈、按键音效等交互增强
5.3 社区贡献
本项目代码已托管至 AtomGit 平台,欢迎感兴趣的开发者 fork 并提交 Pull Request,共同完善这个计算器应用。
仓库地址:https://atomgit.com/maaath/calculator_flutter_oh
结语
Flutter for OpenHarmony 为开发者打开了一扇新的大门,让我们能够使用熟悉的 Dart 语言为鸿蒙设备开发应用。随着生态的不断完善,相信会有越来越多的优质应用出现在 OpenHarmony 平台上。希望本文能为各位开发者提供一些参考,也期待与大家一起探索跨平台开发的更多可能性。