From e23266270bc7545b7acecc9a82fc74bd752f76a0 Mon Sep 17 00:00:00 2001 From: Diwakar Ray Yadav Date: Sat, 29 Nov 2025 11:34:05 +0545 Subject: [PATCH 1/2] docs: Add Todo App tutorial for FletX - Create comprehensive step-by-step tutorial for beginners - Includes TodoController with state management - Demonstrates reactive UI updates with FletX - Shows proper separation of concerns - Includes best practices for FletX development Resolves: Issue for adding example tutorials Tutorial covers all FletX fundamentals with real code examples. This tutorial provides a step-by-step guide to building a simple Todo application using FletX. It covers project setup, state management, UI creation, and best practices. --- docs/examples/todo-app.md | 214 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 docs/examples/todo-app.md diff --git a/docs/examples/todo-app.md b/docs/examples/todo-app.md new file mode 100644 index 0000000..f63aa11 --- /dev/null +++ b/docs/examples/todo-app.md @@ -0,0 +1,214 @@ +# Build a Todo App with FletX + +## Overview +In this tutorial, we'll build a simple but complete Todo application using FletX. You'll learn how to manage state, handle user input, and structure a FletX application. + +## What You'll Learn +- Setting up a FletX page +- Managing reactive state with FletX controllers +- Handling user events (input, button clicks) +- Building a list of items dynamically +- Implementing add and delete functionality + +## Prerequisites +- Python 3.8+ +- FletX installed (`pip install fletx`) +- Basic Python knowledge + +## Step 1: Project Setup + +Create a new Python file called `todo_app.py`: + +```python +from flet import icons +from fletx import FletXPage, FletXController + +class TodoController(FletXController): + def __init__(self): + super().__init__() + self.todos = [] +``` + +This creates a basic controller that will hold our todo list state. + +## Step 2: Create the TodoController + +Extend the TodoController to manage adding and removing todos: + +```python +class TodoController(FletXController): + def __init__(self): + super().__init__() + self.todos = [] + + def add_todo(self, title): + """Add a new todo to the list""" + if title.strip(): + self.todos.append({ + "id": len(self.todos) + 1, + "title": title, + "completed": False + }) + self.update() + + def remove_todo(self, todo_id): + """Remove a todo by ID""" + self.todos = [t for t in self.todos if t["id"] != todo_id] + self.update() + + def toggle_todo(self, todo_id): + """Toggle completion status of a todo""" + for todo in self.todos: + if todo["id"] == todo_id: + todo["completed"] = not todo["completed"] + self.update() +``` + +## Step 3: Build the UI + +Now create the main view: + +```python +from flet import ( + Column, Row, TextField, IconButton, Text, + ListView, Container, Icon, icons, colors +) +from fletx import FletXPage, FletXController + +class TodoPage(FletXPage): + def __init__(self): + super().__init__() + self.controller = TodoController() + self.title = "Todo App" + self.vertical_alignment = "start" + self.horizontal_alignment = "center" + + def build(self): + # Input field for new todos + self.input_field = TextField( + label="What needs to be done?", + width=300, + on_submit=self.add_todo + ) + + # Button to add todos + add_button = IconButton( + icon=icons.ADD, + on_click=self.add_todo, + tooltip="Add todo" + ) + + # Container for the input row + input_row = Row( + [self.input_field, add_button], + alignment="center", + spacing=10 + ) + + # Container for todos list + self.todos_container = Column( + spacing=10, + scroll="auto" + ) + + # Main layout + return Column( + [ + Text("My Todo App", size=28, weight="bold"), + input_row, + self.todos_container + ], + spacing=20, + padding=20 + ) + + def add_todo(self, e=None): + """Handle adding a new todo""" + title = self.input_field.value.strip() + if title: + self.controller.add_todo(title) + self.input_field.value = "" + self.input_field.focus() + self.update_todos_view() + + def remove_todo(self, todo_id): + """Handle removing a todo""" + self.controller.remove_todo(todo_id) + self.update_todos_view() + + def toggle_todo(self, todo_id): + """Handle toggling todo completion""" + self.controller.toggle_todo(todo_id) + self.update_todos_view() + + def update_todos_view(self): + """Update the todos list display""" + self.todos_container.clean() + + for todo in self.controller.todos: + # Create a row for each todo + todo_text = Text( + todo["title"], + size=16, + color=colors.GREY if todo["completed"] else colors.BLACK, + style="strikethrough" if todo["completed"] else None + ) + + delete_btn = IconButton( + icon=icons.DELETE, + icon_color=colors.RED_400, + on_click=lambda e, tid=todo["id"]: self.remove_todo(tid), + tooltip="Delete todo" + ) + + todo_item = Row( + [todo_text, delete_btn], + alignment="space_between", + spacing=10 + ) + + # Add container styling + todo_container = Container( + content=todo_item, + padding=10, + border_radius=5 + ) + + self.todos_container.controls.append(todo_container) + + self.update() +``` + +## Step 4: Run Your App + +Create the main entry point: + +```python +if __name__ == "__main__": + app = TodoPage() + app.run() +``` + +## What's Happening? + +- **TodoController**: Manages the app state (the list of todos) +- **TodoPage**: Renders the UI and handles user interactions +- **Reactive Updates**: When the controller state changes, we call `update_todos_view()` to refresh the display +- **Event Handling**: Button clicks and text submissions trigger state changes + +## Next Steps + +To enhance this app, you could: +1. Add due dates to todos +2. Persist todos to a file or database +3. Add categories or tags +4. Implement search/filter functionality +5. Add animations when items are added/removed + +## Best Practices Demonstrated + +✅ **State Management**: Controller handles all business logic +✅ **Separation of Concerns**: UI logic separate from state management +✅ **Reactive Updates**: UI automatically reflects state changes +✅ **Clean Code**: Well-organized, readable structure +✅ **User Experience**: Clear feedback for user actions From 9edd7b44b246c2115172afa7b8ef1d5872096884 Mon Sep 17 00:00:00 2001 From: Diwakar Ray Yadav Date: Sun, 30 Nov 2025 16:41:40 +0545 Subject: [PATCH 2/2] fix: Address all review comments - Use FletX project structure, Python 3.12, RxList, and reactive components Addressed all review comments from @Wgoeh: - Fixed Python version requirement from 3.8+ to 3.12+ (only version currently supported) - Updated project setup to use 'fletx new' command for proper project structure - Changed todos from regular list to RxList (Reactive List) for proper UI subscriptions - Removed manual self.update() calls - now uses reactive objects that auto-update UI - Implemented @reactive_list decorator for proper dynamic list handling - Fixed main entry point to use FletXApp instead of TodoPage.run() - Added proper project structure and component generation instructions - Referenced fake-shop example repository for best practices --- docs/examples/todo-app.md | 276 +++++++++++++++++--------------------- 1 file changed, 122 insertions(+), 154 deletions(-) diff --git a/docs/examples/todo-app.md b/docs/examples/todo-app.md index f63aa11..e90f0f2 100644 --- a/docs/examples/todo-app.md +++ b/docs/examples/todo-app.md @@ -1,214 +1,182 @@ # Build a Todo App with FletX ## Overview -In this tutorial, we'll build a simple but complete Todo application using FletX. You'll learn how to manage state, handle user input, and structure a FletX application. +In this tutorial, we'll build a simple but complete Todo application using FletX. You'll learn how to manage state, handle user input, structure a FletX application using the proper project setup, and use reactive components. ## What You'll Learn -- Setting up a FletX page -- Managing reactive state with FletX controllers +- Creating a FletX project using `fletx new` +- Building reactive state management using RxList +- Managing reactive components with `@reactive_list` decorator - Handling user events (input, button clicks) - Building a list of items dynamically - Implementing add and delete functionality ## Prerequisites -- Python 3.8+ -- FletX installed (`pip install fletx`) +- Python 3.12+ +- FletX installed (`pip install fletx` or `pip install FletX --pre` for pre-release) - Basic Python knowledge ## Step 1: Project Setup -Create a new Python file called `todo_app.py`: +Create a new FletX project: + +```bash +fletx new todo_app +cd todo_app +``` + +This creates the proper FletX project structure with all necessary configuration. + +## Step 2: Create the Todo Controller + +In your controller file, create a `TodoController` that manages the application state using reactive objects: ```python -from flet import icons -from fletx import FletXPage, FletXController +from fletx import FletXController class TodoController(FletXController): def __init__(self): super().__init__() - self.todos = [] + # Use RxList instead of regular list so UI can subscribe to changes + self.todos = self.create_rx_list([]) + + def add_todo(self, title: str): + """Add a new todo item""" + self.todos.append({ + "title": title, + "completed": False + }) + + def delete_todo(self, index: int): + """Delete a todo item by index""" + if 0 <= index < len(self.todos): + self.todos.pop(index) + + def toggle_todo(self, index: int): + """Toggle completed status of a todo""" + if 0 <= index < len(self.todos): + todo = self.todos[index] + todo["completed"] = not todo["completed"] + # Update the list to trigger UI refresh + self.todos[index] = todo ``` -This creates a basic controller that will hold our todo list state. +## Step 3: Create a Reactive Todo List Component -## Step 2: Create the TodoController +Generate a reactive component: -Extend the TodoController to manage adding and removing todos: +```bash +fletx generate component todo_list +``` + +Then update your component file with the `@reactive_list` decorator: ```python -class TodoController(FletXController): - def __init__(self): +from fletx import FletXComponent, reactive_list +from flet import Row, Checkbox, IconButton, icons, Column, Text + +@reactive_list +class TodoList(FletXComponent): + def __init__(self, controller): super().__init__() - self.todos = [] + self.controller = controller - def add_todo(self, title): - """Add a new todo to the list""" - if title.strip(): - self.todos.append({ - "id": len(self.todos) + 1, - "title": title, - "completed": False - }) - self.update() - - def remove_todo(self, todo_id): - """Remove a todo by ID""" - self.todos = [t for t in self.todos if t["id"] != todo_id] - self.update() - - def toggle_todo(self, todo_id): - """Toggle completion status of a todo""" - for todo in self.todos: - if todo["id"] == todo_id: - todo["completed"] = not todo["completed"] - self.update() + def build(self): + return Column( + controls=[ + Row( + controls=[ + Checkbox( + label=todo.get("title", ""), + value=todo.get("completed", False), + on_change=lambda e, idx=i: self.controller.toggle_todo(idx) + ), + IconButton( + icon=icons.DELETE, + on_click=lambda e, idx=i: self.controller.delete_todo(idx) + ) + ] + ) + for i, todo in enumerate(self.controller.todos) + ] + ) ``` -## Step 3: Build the UI +## Step 4: Create the Main Page -Now create the main view: +Create a `TodoPage` that brings everything together: ```python -from flet import ( - Column, Row, TextField, IconButton, Text, - ListView, Container, Icon, icons, colors -) -from fletx import FletXPage, FletXController +from fletx import FletXPage, FletXApp +from flet import Column, Row, TextField, IconButton, icons, Container, padding +from todo_list import TodoList +from controller import TodoController class TodoPage(FletXPage): def __init__(self): super().__init__() self.controller = TodoController() - self.title = "Todo App" - self.vertical_alignment = "start" - self.horizontal_alignment = "center" def build(self): - # Input field for new todos - self.input_field = TextField( - label="What needs to be done?", - width=300, - on_submit=self.add_todo + input_field = TextField( + label="Add a new task", + expand=True ) - # Button to add todos - add_button = IconButton( - icon=icons.ADD, - on_click=self.add_todo, - tooltip="Add todo" - ) + def add_todo(): + if input_field.value.strip(): + self.controller.add_todo(input_field.value.strip()) + input_field.value = "" + self.update() - # Container for the input row - input_row = Row( - [self.input_field, add_button], - alignment="center", - spacing=10 - ) - - # Container for todos list - self.todos_container = Column( - spacing=10, - scroll="auto" - ) - - # Main layout return Column( - [ - Text("My Todo App", size=28, weight="bold"), - input_row, - self.todos_container + controls=[ + Row( + controls=[ + input_field, + IconButton( + icon=icons.ADD, + on_click=lambda e: add_todo() + ) + ] + ), + TodoList(self.controller) ], - spacing=20, - padding=20 + padding=padding.all(20) ) - - def add_todo(self, e=None): - """Handle adding a new todo""" - title = self.input_field.value.strip() - if title: - self.controller.add_todo(title) - self.input_field.value = "" - self.input_field.focus() - self.update_todos_view() - - def remove_todo(self, todo_id): - """Handle removing a todo""" - self.controller.remove_todo(todo_id) - self.update_todos_view() - - def toggle_todo(self, todo_id): - """Handle toggling todo completion""" - self.controller.toggle_todo(todo_id) - self.update_todos_view() - - def update_todos_view(self): - """Update the todos list display""" - self.todos_container.clean() - - for todo in self.controller.todos: - # Create a row for each todo - todo_text = Text( - todo["title"], - size=16, - color=colors.GREY if todo["completed"] else colors.BLACK, - style="strikethrough" if todo["completed"] else None - ) - - delete_btn = IconButton( - icon=icons.DELETE, - icon_color=colors.RED_400, - on_click=lambda e, tid=todo["id"]: self.remove_todo(tid), - tooltip="Delete todo" - ) - - todo_item = Row( - [todo_text, delete_btn], - alignment="space_between", - spacing=10 - ) - - # Add container styling - todo_container = Container( - content=todo_item, - padding=10, - border_radius=5 - ) - - self.todos_container.controls.append(todo_container) - - self.update() ``` -## Step 4: Run Your App +## Step 5: Run Your Application -Create the main entry point: +Update your `main.py` file to use `FletXApp` instead of running the page directly: ```python +from fletx import FletXApp +from pages.todo_page import TodoPage + if __name__ == "__main__": - app = TodoPage() + app = FletXApp(page_class=TodoPage) app.run() ``` -## What's Happening? +## Key Concepts Explained -- **TodoController**: Manages the app state (the list of todos) -- **TodoPage**: Renders the UI and handles user interactions -- **Reactive Updates**: When the controller state changes, we call `update_todos_view()` to refresh the display -- **Event Handling**: Button clicks and text submissions trigger state changes +### Reactive List (RxList) +The `RxList` created with `self.create_rx_list([])` automatically notifies the UI when items change. This means you don't need to manually call `self.update()`. The UI subscribes to these changes and updates automatically. -## Next Steps +### @reactive_list Decorator +This decorator makes your component automatically rebuild whenever the reactive list changes. This is the proper way to handle dynamic lists in FletX. + +### Project Structure +Using `fletx new` provides the correct project structure and configuration. This is required for FletX applications to work properly. -To enhance this app, you could: -1. Add due dates to todos -2. Persist todos to a file or database -3. Add categories or tags -4. Implement search/filter functionality -5. Add animations when items are added/removed +## Complete Example -## Best Practices Demonstrated +For a complete working example with more features, check out the [fake-shop](https://github.com/AllDotPy/fake-shop) repository which demonstrates best practices for FletX development. + +## Next Steps -✅ **State Management**: Controller handles all business logic -✅ **Separation of Concerns**: UI logic separate from state management -✅ **Reactive Updates**: UI automatically reflects state changes -✅ **Clean Code**: Well-organized, readable structure -✅ **User Experience**: Clear feedback for user actions +- Add persistence by saving todos to a file or database +- Implement todo categories or tags +- Add filtering options (show all, completed, pending) +- Style the application with custom themes