Calculator Tutorial

This tutorial demonstrates building a multi-platform Calculator app using the Flet framework in Python. It covers basic Flet concepts including page layout, control addition, reusable UI components, event handling, and app publishing.

Requirements

  • Python 3.10 or above
  • flet package installed (pip install flet)
  • Core Concepts & Implementation Steps

    1. Getting Started

  • Import flet as ft.
  • Define a main function that accepts page: ft.Page.
  • Use page.add() to add controls to the page.
  • Run the app using ft.run(main).
  • Example hello.py:

    import flet as ft

    def main(page: ft.Page): page.add(ft.Text(value="Hello, world!"))

    ft.run(main)

    2. Adding Page Controls

  • Initialize a ft.Text control for displaying results.
  • Add ft.Button controls for numbers and operations.
  • 3. Building Page Layout

  • Use ft.Row to arrange controls horizontally.
  • Use ft.Column to stack rows vertically.
  • Wrap the entire calculator UI in a ft.Container for styling.
  • #### Container Styling

  • width: Sets the width of the container.
  • bgcolor: Sets the background color (e.g., ft.Colors.BLACK).
  • border_radius: Applies rounded corners (e.g., ft.BorderRadius.all(20)).
  • padding: Adds internal spacing.
  • content: Takes a control (typically ft.Column) to hold the calculator's rows.
  • #### Styled Controls (Custom Buttons)

    To achieve specific visual styles for buttons, custom controls inheriting from ft.Button are created:

  • **CalcButton (Base Class):**
  • * expand: int = 1: Controls button sizing within its row.
  • **DigitButton:**
  • * bgcolor: ft.Colors.WHITE_24 * color: ft.Colors.WHITE
  • **ActionButton:**
  • * bgcolor: ft.Colors.ORANGE * color: ft.Colors.WHITE
  • **ExtraActionButton:**
  • * bgcolor: ft.Colors.BLUE_GREY_100 * color: ft.Colors.BLACK

    These custom buttons are then used within ft.Row controls inside the main ft.Container's ft.Column.

    4. Reusable UI Components

  • Encapsulate UI logic and state into custom classes (e.g., CalculatorApp class) for reusability.
  • Instantiate and add multiple instances of these components to the page.
  • 5. Handling Events

  • Define event handler functions (e.g., button_clicked).
  • Assign these handlers to the on_click property of button controls.
  • Use e.control.content to identify the clicked button's label.
  • Update control values (e.g., result.value) and call page.update() to refresh the UI.
  • 6. Publishing Your App

  • Use the flet build command to package the application.
  • Refer to Flet documentation for detailed instructions on building for desktop, mobile, and web platforms.
  • Full Code Example (Calculator App)

    from dataclasses import field
    import flet as ft

    def main(page: ft.Page): page.title = "Calc App" result = ft.Text(value="0", color=ft.Colors.WHITE, size=20)

    # Custom button classes @ft.control class CalcButton(ft.Button): expand: int = field(default_factory=lambda: 1)

    @ft.control class DigitButton(CalcButton): bgcolor: ft.Colors = ft.Colors.WHITE_24 color: ft.Colors = ft.Colors.WHITE

    @ft.control class ActionButton(CalcButton): bgcolor: ft.Colors = ft.Colors.ORANGE color: ft.Colors = ft.Colors.WHITE

    @ft.control class ExtraActionButton(CalcButton): bgcolor: ft.Colors = ft.Colors.BLUE_GREY_100 color: ft.Colors = ft.Colors.BLACK

    # Event handler def button_clicked(e): data = e.control.content print(f"Button clicked with data = {data}") if data == "AC": result.value = "0" # TODO: Implement calculator logic here page.update()

    # Helper to create buttons with event handler def create_button(button_type, text, **kwargs): btn = button_type(content=text, on_click=button_clicked, **kwargs) return btn

    page.add( ft.Container( width=350, bgcolor=ft.Colors.BLACK, border_radius=ft.BorderRadius.all(20), padding=20, content=ft.Column( controls=[ ft.Row(controls=[result], alignment=ft.MainAxisAlignment.END), ft.Row( controls=[ create_button(ExtraActionButton, "AC"), create_button(ExtraActionButton, "+/-"), create_button(ExtraActionButton, "%", expand=1), # Adjusted expand for consistency create_button(ActionButton, "/"), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN ), ft.Row( controls=[ create_button(DigitButton, "7"), create_button(DigitButton, "8"), create_button(DigitButton, "9"), create_button(ActionButton, "*"), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN ), ft.Row( controls=[ create_button(DigitButton, "4"), create_button(DigitButton, "5"), create_button(DigitButton, "6"), create_button(ActionButton, "-"), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN ), ft.Row( controls=[ create_button(DigitButton, "1"), create_button(DigitButton, "2"), create_button(DigitButton, "3"), create_button(ActionButton, "+"), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN ), ft.Row( controls=[ create_button(DigitButton, "0", expand=2), create_button(DigitButton, "."), create_button(ActionButton, "="), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN ), ] ) ) )

    if __name__ == "__main__": ft.run(main)

    Further Exploration

  • [Flet Controls](https://docs.flet.dev/controls/)
  • [Flet Examples](https://github.com/flet-dev/flet/tree/main/sdk/python/examples)