Skip to Content
Flet🧮 Calculator

Mini Project: Calculator 🧮

Xây dựng máy tính với giao diện đẹp. ⏱️ 30 phút

🎯 Mục tiêu

Tạo ứng dụng Calculator với:

  • Các phép tính cơ bản (+, -, ×, ÷)
  • Giao diện grid buttons
  • Hiển thị expression và kết quả
  • Clear và delete chức năng

🚀 Bắt đầu

Bước 1: Cấu trúc cơ bản

Tạo file calculator_app.py:

import flet as ft @ft.observable class CalcState: expression: str = "" result: str = "0" def input(self, value: str): self.expression += value def clear(self): self.expression = "" self.result = "0" def delete(self): self.expression = self.expression[:-1] def calculate(self): try: # Thay thế ký hiệu để eval expr = self.expression.replace("×", "*").replace("÷", "/") self.result = str(eval(expr)) except: self.result = "Error" state = CalcState() def main(page: ft.Page): page.title = "Calculator" page.add(ft.Text("Calculator")) ft.run(main)

Bước 2: Tạo Display

@ft.component def Display(): return ft.Container( content=ft.Column([ ft.Text( state.expression or "0", size=24, color=ft.Colors.GREY_500, text_align=ft.TextAlign.RIGHT, ), ft.Text( state.result, size=48, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.RIGHT, ), ], horizontal_alignment=ft.CrossAxisAlignment.END, spacing=5), padding=20, bgcolor=ft.Colors.GREY_100, border_radius=ft.border_radius.only(top_left=20, top_right=20), )

Bước 3: Tạo Button Component

@ft.component def CalcButton(text: str, expand: int = 1, bg_color=None, text_color=None): def on_click(_): if text == "C": state.clear() elif text == "⌫": state.delete() elif text == "=": state.calculate() else: state.input(text) return ft.Container( content=ft.Text(text, size=24, weight=ft.FontWeight.W_500, color=text_color), bgcolor=bg_color or ft.Colors.WHITE, border_radius=10, alignment=ft.alignment.center, expand=expand, height=70, on_click=on_click, )

Bước 4: Tạo Button Grid

@ft.component def ButtonGrid(): # Định nghĩa các nút buttons = [ ["C", "⌫", "÷"], ["7", "8", "9", "×"], ["4", "5", "6", "-"], ["1", "2", "3", "+"], ["0", ".", "="], ] return ft.Column([ ft.Row([ CalcButton( btn, expand=2 if btn == "0" else 1, bg_color=ft.Colors.ORANGE if btn in ["÷", "×", "-", "+", "="] else ft.Colors.GREY_300 if btn in ["C", "⌫"] else None, text_color=ft.Colors.WHITE if btn in ["÷", "×", "-", "+", "="] else None, ) for btn in row ], spacing=10) for row in buttons ], spacing=10)

Bước 5: Code hoàn chỉnh

import flet as ft @ft.observable class CalcState: expression: str = "" result: str = "0" def input(self, value: str): self.expression += value def clear(self): self.expression = "" self.result = "0" def delete(self): self.expression = self.expression[:-1] if not self.expression: self.result = "0" def calculate(self): try: expr = self.expression.replace("×", "*").replace("÷", "/") if expr: result = eval(expr) # Format số đẹp if isinstance(result, float) and result.is_integer(): self.result = str(int(result)) else: self.result = str(round(result, 10)) except: self.result = "Error" state = CalcState() @ft.component def Display(): return ft.Container( content=ft.Column([ ft.Text( state.expression or " ", size=22, color=ft.Colors.GREY_600, text_align=ft.TextAlign.RIGHT, ), ft.Text( state.result, size=52, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.RIGHT, ), ], horizontal_alignment=ft.CrossAxisAlignment.END, spacing=5), padding=ft.padding.only(left=20, right=20, top=30, bottom=20), bgcolor=ft.Colors.GREY_100, border_radius=ft.border_radius.only(top_left=20, top_right=20), width=320, ) @ft.component def CalcButton(text: str, expand: int = 1, bg_color=None, text_color=None): def on_click(_): if text == "C": state.clear() elif text == "⌫": state.delete() elif text == "=": state.calculate() else: state.input(text) return ft.Container( content=ft.Text( text, size=26, weight=ft.FontWeight.W_500, color=text_color or ft.Colors.BLACK87 ), bgcolor=bg_color or ft.Colors.WHITE, border_radius=15, alignment=ft.alignment.center, expand=expand, height=65, shadow=ft.BoxShadow(blur_radius=3, color=ft.Colors.BLACK12), on_click=on_click, ) @ft.component def ButtonGrid(): return ft.Container( content=ft.Column([ ft.Row([ CalcButton("C", bg_color=ft.Colors.RED_100, text_color=ft.Colors.RED), CalcButton("⌫", bg_color=ft.Colors.GREY_200), CalcButton("÷", bg_color=ft.Colors.ORANGE, text_color=ft.Colors.WHITE), ], spacing=10), ft.Row([ CalcButton("7"), CalcButton("8"), CalcButton("9"), CalcButton("×", bg_color=ft.Colors.ORANGE, text_color=ft.Colors.WHITE), ], spacing=10), ft.Row([ CalcButton("4"), CalcButton("5"), CalcButton("6"), CalcButton("-", bg_color=ft.Colors.ORANGE, text_color=ft.Colors.WHITE), ], spacing=10), ft.Row([ CalcButton("1"), CalcButton("2"), CalcButton("3"), CalcButton("+", bg_color=ft.Colors.ORANGE, text_color=ft.Colors.WHITE), ], spacing=10), ft.Row([ CalcButton("0", expand=2), CalcButton("."), CalcButton("=", bg_color=ft.Colors.ORANGE, text_color=ft.Colors.WHITE), ], spacing=10), ], spacing=10), padding=15, width=320, ) @ft.component def Calculator(): return ft.Container( content=ft.Column([ Display(), ButtonGrid(), ], spacing=0), bgcolor=ft.Colors.GREY_200, border_radius=20, shadow=ft.BoxShadow(blur_radius=20, color=ft.Colors.BLACK26), ) def main(page: ft.Page): page.title = "Calculator" page.vertical_alignment = ft.MainAxisAlignment.CENTER page.horizontal_alignment = ft.CrossAxisAlignment.CENTER page.bgcolor = ft.Colors.BLUE_GREY_100 page.add(Calculator()) ft.run(main)

✅ Kết quả

Calculator hoàn chỉnh với:

  • ✅ Các phép tính +, -, ×, ÷
  • ✅ Clear (C) và Delete (⌫)
  • ✅ Hiển thị expression và result
  • ✅ Giao diện đẹp với shadows

🎯 Thử thách mở rộng

  1. Percentage (%) - Thêm phép tính phần trăm
  2. Parentheses - Thêm dấu ngoặc ()
  3. History - Lưu lịch sử tính toán
  4. Keyboard support - Nhập từ bàn phím
  5. Scientific mode - Thêm sin, cos, sqrt…

📝 Kiến thức ôn tập

  • Grid layout với Row/Column
  • Conditional styling - màu sắc theo loại nút
  • State management - expression và result
  • Shadow và border radius

⏭️ Tiếp theo

Tuyệt! Tiếp tục với Mini Project: Theme Switcher.

Last updated on