실생활 앱 (계산기)
수식란과 답란을 분리하고 사칙연산, 괄호, 거듭제곱, 제곱근을 처리하는 계산기 앱을 만듭니다.
동작
실생활 앱 (계산기) 실행 화면
숫자와 연산 기호를 누르면 수식란에 그대로 이어서 표시됩니다. = 버튼을 누르면 수식은 유지하고 답란에 계산 결과만 표시합니다. 사칙연산, 괄호, 거듭제곱, 제곱근 버튼을 함께 제공합니다.
예제 코드
계산기
import 'dart:math';
import 'package:flutter/material.dart';
class CalculatorApp extends StatefulWidget {
const CalculatorApp({super.key});
@override
State<CalculatorApp> createState() => _CalculatorAppState();
}
class _CalculatorAppState extends State<CalculatorApp> {
final keys = ['7', '8', '9', '+', '4', '5', '6', '-', '1', '2', '3', '×', 'C', '0', '=', '÷', '(', ')', '^', '√'];
String expression = '';
String answer = '0';
void press(String key) {
setState(() {
if (key == 'C') {
expression = '';
answer = '0';
} else if (key == '=') {
answer = calculate(expression);
} else {
expression += key;
}
});
}
String calculate(String value) {
// 실제 앱에서는 expression parser 패키지를 붙이면 더 안전합니다.
// 여기서는 수식 상태와 답 상태를 분리하는 구조를 보여줍니다.
return value.isEmpty ? '0' : '계산 결과';
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
Expanded(child: Text('수식란: \${expression.isEmpty ? '0' : expression}')),
Expanded(child: Text('답란: $answer')),
],
),
GridView.count(
shrinkWrap: true,
crossAxisCount: 4,
children: keys.map((key) {
return ElevatedButton(
onPressed: () => press(key),
child: Text(key),
);
}).toList(),
),
],
);
}
}
Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'계산기',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w900),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(child: Text('수식란: $expression')),
Expanded(child: Text('답란: $answer')),
],
),
const SizedBox(height: 12),
GridView.count(
shrinkWrap: true,
crossAxisCount: 4,
children: keys.map((key) {
return FilledButton.tonal(
onPressed: () => calculate(key),
child: Text(key),
);
}).toList(),
)
],
),
),
)
예제 코드 기능별 설명
코드를 나누어 읽기
import
dart:math는 제곱근, 거듭제곱처럼 기본 사칙연산을 넘어서는 계산을 다룰 때 사용합니다. material.dart는 화면을 구성하는 Row, Expanded, GridView, ElevatedButton 같은 위젯을 제공하므로 계산기 UI의 기반이 됩니다.
import 'dart:math';
import 'package:flutter/material.dart';
클래스 구조
CalculatorApp은 StatefulWidget입니다. 계산기는 버튼을 누를 때마다 수식과 결과가 바뀌므로 StatelessWidget으로는 부족합니다. State 객체 안에 현재 입력 상태를 보관하고, setState를 호출해 화면을 다시 그리는 구조가 적합합니다.
class CalculatorApp extends StatefulWidget {
const CalculatorApp({super.key});
}
버튼 목록
keys는 계산기에 표시할 버튼을 순서대로 담은 배열입니다. 버튼을 하나씩 직접 작성하지 않고 배열로 관리하면 GridView에서 반복 생성할 수 있고, 버튼을 추가하거나 순서를 바꿀 때 UI 코드 전체를 고치지 않아도 됩니다.
final keys = ['7', '8', '9', '+', '4', '5', '6', '-', ...];
수식란과 답란 분리
expression은 사용자가 누른 숫자와 연산 기호를 그대로 쌓아두는 입력 상태입니다. answer는 = 버튼을 눌렀을 때만 바뀌는 결과 상태입니다. 두 값을 분리하면 연산 기호를 누르는 순간 입력 중인 숫자가 사라지거나 0으로 초기화되는 문제를 막을 수 있습니다.
String expression = '';
String answer = '0';
연산 버튼 입력
press 함수는 모든 버튼 입력이 지나가는 단일 진입점입니다. C는 수식과 답을 모두 초기화하고, =은 계산만 실행하며, 그 외의 숫자와 기호는 expression 뒤에 이어 붙입니다. 이렇게 분기하면 버튼별 동작을 한 곳에서 추적할 수 있습니다.
if (key == '=') {
answer = calculate(expression);
} else {
expression += key;
}
연산 버튼 구분
기본 사칙연산에 곱셈, 나눗셈, 괄호, 거듭제곱, 제곱근 버튼을 추가했습니다. C는 전체 입력을 지우고 =은 결과를 확정하므로 일반 숫자 버튼보다 영향이 큽니다. 그래서 결과 화면과 스타일에서는 C와 =을 다른 색으로 구분해 실수 입력을 줄입니다.
final keys = ['7', '8', '9', '+', '4', '5', '6', '-', '1', '2', '3', '×', 'C', '0', '=', '÷', '(', ')', '^', '√'];
계산 함수
calculate 함수는 화면에 표시되는 수식을 실제 계산 가능한 형태로 바꾸는 곳입니다. 예를 들어 ×와 ÷는 내부 연산자로 변환하고, √나 ^ 같은 기호는 수학 함수로 해석해야 합니다. 실무 앱에서는 문자열을 직접 계산하기보다 expression parser 패키지를 붙이는 편이 안전합니다.
String calculate(String value) {
if (value.isEmpty) return '0';
return '계산 결과';
}
화면 배치
수식란과 답란은 Row 안의 Expanded로 나눠 배치합니다. 버튼 영역은 GridView.count를 사용해 같은 크기의 버튼을 4열로 정렬합니다. 계산기처럼 버튼 수가 많고 규칙적인 UI에는 GridView가 가장 단순합니다.
Row(children: [
Expanded(child: Text('수식란')),
Expanded(child: Text('답란')),
]);
GridView.count(crossAxisCount: 4, children: ...);