Flutter 框架跨平台鸿蒙开发-鸿蒙计算器开发教程

目录

项目简介

[1. 运行效果图](#1. 运行效果图)

2.完整代码预览

一、开发环境准备

[1. 安装Flutter SDK](#1. 安装Flutter SDK)

[2. 安装鸿蒙开发环境](#2. 安装鸿蒙开发环境)

[3. 配置Flutter鸿蒙支持](#3. 配置Flutter鸿蒙支持)

二、项目结构

三、核心代码实现

[1. 应用入口](#1. 应用入口)

[2. 状态管理](#2. 状态管理)

[3. 数字输入处理](#3. 数字输入处理)

功能说明:

[4. 运算符处理](#4. 运算符处理)

功能说明:

[5. 计算逻辑](#5. 计算逻辑)

功能说明:

[6. 功能按钮实现](#6. 功能按钮实现)

清除功能

百分比计算

正负号切换

[7. UI界面设计](#7. UI界面设计)

按钮组件

设计要点:

界面布局

设计特点:

四、总结

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


​​​​​​​项目简介

本文介绍如何使用Flutter开发一个功能完善的计算器应用,该应用可以部署在鸿蒙系统上运行。计算器具备以下功能:

  • 基本运算:加、减、乘、除

  • 百分比计算

  • 正负号切换

  • 清除功能

  • 小数点支持

1. 运行效果图

2.完整代码预览

Dart 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(const CalculatorApp());
}

class CalculatorApp extends StatelessWidget {
  const CalculatorApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter鸿蒙计算器',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(useMaterial3: true),
      home: const Calculator(),
    );
  }
}

class Calculator extends StatefulWidget {
  const Calculator({super.key});

  @override
  State<Calculator> createState() => _CalculatorState();
}

class _CalculatorState extends State<Calculator> {
  String _display = '0';
  String _previousValue = '';
  String _operator = '';
  bool _shouldResetDisplay = false;
  bool _justCalculated = false;

  String _formatResult(double value) {
    if (value.isInfinite || value.isNaN) return '错误';
    if (value == value.toInt() && value.abs() < 1e10) {
      return value.toInt().toString();
    }
    String str = value.toStringAsFixed(8);
    // 移除末尾的0和小数点
    while (str.contains('.') && (str.endsWith('0') || str.endsWith('.'))) {
      str = str.substring(0, str.length - 1);
    }
    return str;
  }

  void _onNumberPressed(String number) {
    setState(() {
      if (_justCalculated) {
        _display = number;
        _previousValue = '';
        _operator = '';
        _justCalculated = false;
        _shouldResetDisplay = false;
        return;
      }

      if (_shouldResetDisplay) {
        _display = number;
        _shouldResetDisplay = false;
      } else {
        if (number == '.' && _display.contains('.')) return;
        if (_display == '0' && number != '.') {
          _display = number;
        } else {
          _display += number;
        }
      }
    });
  }

  void _onOperatorPressed(String op) {
    setState(() {
      _justCalculated = false;

      if (_operator.isNotEmpty &&
          !_shouldResetDisplay &&
          _previousValue.isNotEmpty) {
        _performCalculation();
      }

      _previousValue = _display;
      _operator = op;
      _shouldResetDisplay = true;
    });
  }

  void _performCalculation() {
    if (_previousValue.isEmpty || _operator.isEmpty) return;

    try {
      double num1 = double.parse(_previousValue);
      double num2 = double.parse(_display);
      double result = 0;

      switch (_operator) {
        case '+':
          result = num1 + num2;
        case '-':
          result = num1 - num2;
        case '×':
          result = num1 * num2;
        case '÷':
          if (num2 == 0) {
            _display = '错误';
            _operator = '';
            _previousValue = '';
            return;
          }
          result = num1 / num2;
      }

      _display = _formatResult(result);
      _previousValue = _display;
    } catch (e) {
      _display = '错误';
      _operator = '';
      _previousValue = '';
    }
  }

  void _onEqualPressed() {
    if (_operator.isEmpty || _previousValue.isEmpty) return;
    setState(() {
      _performCalculation();
      _operator = '';
      _previousValue = '';
      _shouldResetDisplay = true;
      _justCalculated = true;
    });
  }

  void _onClearPressed() {
    setState(() {
      _display = '0';
      _previousValue = '';
      _operator = '';
      _shouldResetDisplay = false;
      _justCalculated = false;
    });
  }

  void _onPercentagePressed() {
    setState(() {
      try {
        double value = double.parse(_display);
        _display = _formatResult(value / 100);
      } catch (e) {
        _display = '错误';
      }
    });
  }

  void _onToggleSignPressed() {
    setState(() {
      if (_display != '0' && _display != '错误') {
        if (_display.startsWith('-')) {
          _display = _display.substring(1);
        } else {
          _display = '-$_display';
        }
      }
    });
  }

  Widget _buildButton({
    required String text,
    required VoidCallback onPressed,
    Color? bgColor,
    Color? textColor,
    int flex = 1,
  }) {
    final isOperator = ['÷', '×', '-', '+', '='].contains(text);
    final isSelected =
        isOperator && text != '=' && _operator == text && _shouldResetDisplay;

    return Expanded(
      flex: flex,
      child: Padding(
        padding: const EdgeInsets.all(6),
        child: Material(
          color:
              isSelected ? Colors.white : (bgColor ?? const Color(0xFF333333)),
          borderRadius: BorderRadius.circular(100),
          child: InkWell(
            onTap: onPressed,
            borderRadius: BorderRadius.circular(100),
            child: AspectRatio(
              aspectRatio: flex.toDouble(),
              child: Center(
                child: Text(
                  text,
                  style: TextStyle(
                    fontSize: 32,
                    fontWeight: FontWeight.w400,
                    color: isSelected
                        ? const Color(0xFFFF9500)
                        : (textColor ?? Colors.white),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    const orange = Color(0xFFFF9500);
    const gray = Color(0xFFA5A5A5);

    return Scaffold(
      backgroundColor: Colors.black,
      appBar: AppBar(
        title:
            const Text('Flutter鸿蒙计算器', style: TextStyle(color: Colors.white)),
        backgroundColor: Colors.black,
        centerTitle: true,
      ),
      body: SafeArea(
        child: Column(
          children: [
            Expanded(
              flex: 2,
              child: Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
                alignment: Alignment.bottomRight,
                child: FittedBox(
                  fit: BoxFit.scaleDown,
                  child: Text(
                    _display,
                    style: const TextStyle(
                        fontSize: 80,
                        color: Colors.white,
                        fontWeight: FontWeight.w300),
                  ),
                ),
              ),
            ),
            Expanded(
              flex: 5,
              child: Padding(
                padding: const EdgeInsets.all(8),
                child: Column(
                  children: [
                    Expanded(
                        child: Row(children: [
                      _buildButton(
                          text: 'C',
                          onPressed: _onClearPressed,
                          bgColor: gray,
                          textColor: Colors.black),
                      _buildButton(
                          text: '±',
                          onPressed: _onToggleSignPressed,
                          bgColor: gray,
                          textColor: Colors.black),
                      _buildButton(
                          text: '%',
                          onPressed: _onPercentagePressed,
                          bgColor: gray,
                          textColor: Colors.black),
                      _buildButton(
                          text: '÷',
                          onPressed: () => _onOperatorPressed('÷'),
                          bgColor: orange),
                    ])),
                    Expanded(
                        child: Row(children: [
                      _buildButton(
                          text: '7', onPressed: () => _onNumberPressed('7')),
                      _buildButton(
                          text: '8', onPressed: () => _onNumberPressed('8')),
                      _buildButton(
                          text: '9', onPressed: () => _onNumberPressed('9')),
                      _buildButton(
                          text: '×',
                          onPressed: () => _onOperatorPressed('×'),
                          bgColor: orange),
                    ])),
                    Expanded(
                        child: Row(children: [
                      _buildButton(
                          text: '4', onPressed: () => _onNumberPressed('4')),
                      _buildButton(
                          text: '5', onPressed: () => _onNumberPressed('5')),
                      _buildButton(
                          text: '6', onPressed: () => _onNumberPressed('6')),
                      _buildButton(
                          text: '-',
                          onPressed: () => _onOperatorPressed('-'),
                          bgColor: orange),
                    ])),
                    Expanded(
                        child: Row(children: [
                      _buildButton(
                          text: '1', onPressed: () => _onNumberPressed('1')),
                      _buildButton(
                          text: '2', onPressed: () => _onNumberPressed('2')),
                      _buildButton(
                          text: '3', onPressed: () => _onNumberPressed('3')),
                      _buildButton(
                          text: '+',
                          onPressed: () => _onOperatorPressed('+'),
                          bgColor: orange),
                    ])),
                    Expanded(
                        child: Row(children: [
                      _buildButton(
                          text: '0',
                          onPressed: () => _onNumberPressed('0'),
                          flex: 2),
                      _buildButton(
                          text: '.', onPressed: () => _onNumberPressed('.')),
                      _buildButton(
                          text: '=',
                          onPressed: _onEqualPressed,
                          bgColor: orange),
                    ])),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

一、开发环境准备

1. 安装Flutter SDK

  • 下载Flutter SDK并配置环境变量
  • 运行 `flutter doctor` 检查开发环境

2. 安装鸿蒙开发环境

  • 安装DevEco Studio
  • 配置鸿蒙SDK和模拟器

3. 配置Flutter鸿蒙支持

  • 启用Flutter鸿蒙平台支持
  • 配置编译工具链

二、项目结构

项目采用单文件结构,主要代码集中在 `main.dart` 文件中:

lib/

└── main.dart # 主应用文件

三、核心代码实现

1. 应用入口

Dart 复制代码
void main() {
  runApp(const CalculatorApp());
}

class CalculatorApp extends StatelessWidget {
  const CalculatorApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter鸿蒙计算器',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const Calculator(),
    );
  }
}

2. 状态管理

计算器使用StatefulWidget来管理状态:

Dart 复制代码
class Calculator extends StatefulWidget {
  const Calculator({super.key});

  @override
  State<Calculator> createState() => _CalculatorState();
}

class _CalculatorState extends State<Calculator> {
  String _display = '0';              // 显示的内容
  String _previousValue = '';        // 之前的数值
  String _operator = '';             // 操作符
  bool _shouldResetDisplay = false; // 是否重置显示

3. 数字输入处理

Dart 复制代码
void _onNumberPressed(String number) {
  setState(() {
    if (_shouldResetDisplay) {
      _display = number;
      _shouldResetDisplay = false;
    } else {
      if (_display == '0' && number != '.') {
        _display = number;
      } else if (number == '.' && _display.contains('.')) {
        return;
      } else {
        _display += number;
      }
    }
  });
}
功能说明:
  • 当输入新数字时,如果需要重置则直接替换显示内容
  • 防止多个小数点输入
  • 自动处理0开头的数字

4. 运算符处理

Dart 复制代码
void _onOperatorPressed(String operator) {
  setState(() {
    if (_previousValue.isEmpty) {
      _previousValue = _display;
    } else if (_operator.isNotEmpty && !_shouldResetDisplay) {
      _calculate();
    }
    _operator = operator;
    _shouldResetDisplay = true;
  });
}
功能说明:
  • 首次按下运算符时保存当前数值
  • 连续运算时自动计算中间结果
  • 设置运算符并标记下一次输入需要重置

5. 计算逻辑

Dart 复制代码
void _calculate() {
  if (_previousValue.isEmpty || _operator.isEmpty) return;

  double num1 = double.parse(_previousValue);
  double num2 = double.parse(_display);
  double result = 0;

  switch (_operator) {
    case '+':
      result = num1 + num2;
      break;
    case '-':
      result = num1 - num2;
      break;
    case '×':
      result = num1 * num2;
      break;
    case '÷':
      if (num2 != 0) {
        result = num1 / num2;
      } else {
        _display = '错误';
        _operator = '';
        _previousValue = '';
        return;
      }
      break;
  }

  setState(() {
    _display = result.toStringAsFixed(10).replaceAll(RegExp(r'\.?0+$'), '');
    _previousValue = '';
    _operator = '';
    _shouldResetDisplay = true;
  });
}
功能说明:
  • 支持四则运算
  • 除零错误处理
  • 自动去除结果末尾多余的0
  • 计算完成后重置状态

6. 功能按钮实现

清除功能
Dart 复制代码
void _onClearPressed() {
  setState(() {
    _display = '0';
    _previousValue = '';
    _operator = '';
    _shouldResetDisplay = false;
  });
}
百分比计算
Dart 复制代码
void _onPercentagePressed() {
  setState(() {
    double value = double.parse(_display);
    _display = (value / 100).toString();
  });
}
正负号切换
Dart 复制代码
void _onToggleSignPressed() {
  setState(() {
    if (_display != '0') {
      if (_display.startsWith('-')) {
        _display = _display.substring(1);
      } else {
        _display = '-$_display';
      }
    }
  });
}

7. UI界面设计

按钮组件
Dart 复制代码
Widget _buildButton({
  required String text,
  required VoidCallback onPressed,
  Color? backgroundColor,
  Color? textColor,
}) {
  return Container(
    margin: const EdgeInsets.all(8),
    child: ElevatedButton(
      onPressed: onPressed,
      style: ElevatedButton.styleFrom(
        backgroundColor: backgroundColor ?? Colors.grey[300],
        foregroundColor: textColor ?? Colors.black87,
        elevation: 2,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(20),
        ),
        minimumSize: const Size(80, 80),
      ),
      child: Text(
        text,
        style: const TextStyle(
          fontSize: 32,
          fontWeight: FontWeight.w500,
        ),
      ),
    ),
  );
}
设计要点:
  • 圆角按钮设计
  • 统一的按钮尺寸
  • 可自定义的背景色和文字颜色
  • 合适的边距和字体大小

界面布局

Dart 复制代码
@override
Widget build(BuildContext context) {
  return Scaffold(
    backgroundColor: Colors.black,
    appBar: AppBar(
      title: const Text(
        'Flutter鸿蒙计算器',
        style: TextStyle(color: Colors.white),
      ),
      backgroundColor: Colors.black,
      elevation: 0,
      centerTitle: true,
    ),
    body: Column(
      children: [
        // 显示屏
        Expanded(
          child: Container(
            padding: const EdgeInsets.all(24),
            alignment: Alignment.bottomRight,
            child: Text(
              _display,
              style: TextStyle(
                fontSize: _display.length > 9 ? 48 : 72,
                color: Colors.white,
                fontWeight: FontWeight.w300,
              ),
            ),
          ),
        ),
        // 键盘区域
        Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            children: [
              // 5行按钮布局
              // 第一行: C ± % ÷
              // 第二行: 7 8 9 ×
              // 第三行: 4 5 6 -
              // 第四行: 1 2 3 +
              // 第五行: 0 . =
            ],
          ),
        ),
      ],
    ),
  );
}
设计特点:
  • 黑色主题,符合现代计算器设计风格
  • 自适应字体大小,长数字自动缩小
  • 运算符使用橙色突出显示
  • 功能键使用灰色区分
  • 数字键使用深灰色,白字显示

四、总结

通过本教程,我们成功开发了一个功能完善的Flutter鸿蒙计算器应用。该项目展示了:

  • Flutter在鸿蒙平台的强大支持
  • Stateful Widget的状态管理
  • Material Design的UI实现
  • 基本算法逻辑的封装

项目代码结构清晰,易于理解和扩展,适合作为Flutter学习的入门项目。

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

相关推荐
不如喫茶去1 小时前
VScode突然打不开,点击图标没反应
ide·vscode·编辑器
qiqiliuwu1 小时前
VUE3+TS+ElementUI项目中监测页面滚动scroll事件以及滚动高度不生效问题的解决方案(window.addEventListener)
前端·javascript·elementui·typescript·vue
LawrenceLan2 小时前
16.Flutter 零基础入门(十六):Widget 基础概念与第一个 Flutter 页面
开发语言·前端·flutter·dart
南村群童欺我老无力.2 小时前
Flutter 框架跨平台鸿蒙开发 - 从零开发经典推箱子游戏
flutter·游戏·华为·typescript·harmonyos
哈哈你是真的厉害2 小时前
React Native 鸿蒙跨平台开发:TemperatureConverter 温度换算器
react native·react.js·harmonyos
LawrenceLan2 小时前
17.Flutter 零基础入门(十七):StatelessWidget 与 State 的第一次分离
开发语言·前端·flutter·dart
大雷神2 小时前
Harmony应用中 HAR 包开发与发布到openharmony中的完整指南
harmonyos
柒儿吖2 小时前
Flutter跨平台三方库image_picker在鸿蒙中的使用指南
flutter·华为·harmonyos
世人万千丶2 小时前
鸿蒙跨端框架Flutter学习day 2、常用UI组件-折行布局 Wrap & Chip
学习·flutter·ui·华为·harmonyos·鸿蒙