The Spring Boot of Python for Flutter
Project description
๐ Sagnos
The Spring Boot of Python for Flutter.
Write Python. Get a Flutter app. Zero manual API code.
What is Sagnos?
Building a Flutter app with a Python backend normally means:
- Manually writing HTTP calls in Dart
- Manually parsing JSON
- Manually keeping Python and Dart models in sync
- Silent runtime crashes when you rename a field
Sagnos eliminates all of that.
You write Python functions with type hints. Sagnos automatically generates the entire Dart client โ models, HTTP calls, error handling, everything.
The 30-Second Demo
You write this Python:
from sagnos import expose, model, SagnosApp
@model
class User:
id: int
name: str
email: str
@expose
async def get_user(id: int) -> User:
return User(id=id, name="Ada Lovelace", email="ada@dev.com")
@expose
async def list_users() -> list[User]:
return [User(id=1, name="Ada", email="ada@dev.com")]
app = SagnosApp()
app.run()
Sagnos generates this Dart automatically:
// You never write this โ Sagnos generates it
class User {
final int id;
final String name;
final String email;
// fromJson, toJson, copyWith all included
}
class SagnosClient {
Future<User> getUser(int id) async { ... }
Future<List<User>> listUsers() async { ... }
}
You use it in Flutter like this:
final client = SagnosClient();
// That's it. No HTTP. No JSON. Full type safety.
final user = await client.getUser(1);
Text(user.name)
Why Sagnos?
| Plain FastAPI + Flutter | Firebase | gRPC | Sagnos | |
|---|---|---|---|---|
| Write Python backend | โ | โ | โ | โ |
| Keep real Flutter UI | โ | โ | โ | โ |
| Auto-generate Dart client | โ | โ | โ (via .proto) | โ |
| Python-native (just decorators) | โ | โ | โ | โ |
| Works with Python ML/AI | โ | โ | โ | โ |
| Zero boilerplate both sides | โ | โ | โ | โ |
Installation
pip install sagnos
Flutter side โ add to pubspec.yaml:
dependencies:
http: ^1.2.0
Quick Start
1. Write your Python backend
# backend.py
from datetime import datetime
from typing import Optional
from sagnos import expose, model, SagnosApp, NotFoundError
@model
class Product:
id: int
title: str
price: float
in_stock: bool
PRODUCTS = {
1: Product(id=1, title="Keyboard", price=49.99, in_stock=True),
2: Product(id=2, title="Monitor", price=299.99, in_stock=False),
}
@expose(method="GET")
async def list_products() -> list[Product]:
"""Get all products"""
return list(PRODUCTS.values())
@expose(method="GET")
async def get_product(id: int) -> Product:
"""Get product by ID"""
product = PRODUCTS.get(id)
if not product:
raise NotFoundError(f"Product {id} not found")
return product
@expose
async def create_product(title: str, price: float) -> Product:
"""Create a new product"""
new_id = max(PRODUCTS.keys()) + 1
product = Product(id=new_id, title=title, price=price, in_stock=True)
PRODUCTS[new_id] = product
return product
if __name__ == "__main__":
app = SagnosApp(title="My Shop")
app.run()
2. Run the backend
python backend.py
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๐ Sagnos v0.1.0 โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ Server โ http://127.0.0.1:8000 โ
โ Docs โ http://127.0.0.1:8000/docs โ
โ Schema โ http://127.0.0.1:8000/sagnos/schema
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
3. Generate Dart bindings
python -c "
from sagnos.codegen import generate
generate(
'http://127.0.0.1:8000/sagnos/schema',
'./my_flutter_app/lib/sagnos'
)
"
This generates 4 files into your Flutter project:
my_flutter_app/lib/sagnos/
โโโ models.dart โ Product class, fromJson, toJson
โโโ sagnos_client.dart โ listProducts(), getProduct(), createProduct()
โโโ sagnos_exception.dart โ typed error handling
โโโ sagnos_stream.dart โ WebSocket streaming support
4. Use in Flutter
import 'sagnos/sagnos_client.dart';
import 'sagnos/models.dart';
import 'sagnos/sagnos_exception.dart';
final client = SagnosClient(baseUrl: 'http://127.0.0.1:8000');
// List products
final products = await client.listProducts();
// Get one product
final product = await client.getProduct(1);
print(product.title); // Keyboard
// Create a product
final newProduct = await client.createProduct('USB Hub', 29.99);
// Typed error handling
try {
await client.getProduct(999);
} on SagnosException catch (e) {
if (e.isNotFound) print('Product not found');
}
Decorators
@model
Registers a Python class as a Sagnos data model.
Auto-generates the Dart class with fromJson, toJson, copyWith.
@model
class User:
id: int
name: str
created_at: datetime # โ DateTime in Dart
bio: Optional[str] # โ String? in Dart (null safe)
@expose
Registers a function as a REST endpoint.
Auto-generates a typed Dart method in SagnosClient.
@expose(method="GET") # GET or POST
async def get_user(id: int) -> User:
...
@expose(auth_required=True) # Protected endpoint
async def delete_user(id: int) -> bool:
...
@expose(deprecated=True) # Marks as deprecated in Dart
async def old_endpoint() -> str:
...
@stream
Registers an async generator as a WebSocket stream.
Auto-generates SagnosStream<T> usage in Flutter.
@stream
async def live_updates() -> AsyncGenerator[User, None]:
while True:
yield get_latest_user()
await asyncio.sleep(1)
// Flutter side
final stream = SagnosStream<User>(
url: 'ws://127.0.0.1:8000/ws/live-updates',
fromJson: User.fromJson,
);
stream.stream.listen((user) => setState(() => _user = user));
Type Mapping
Sagnos automatically converts Python types to Dart:
| Python | Dart |
|---|---|
int |
int |
float |
double |
str |
String |
bool |
bool |
datetime |
DateTime |
UUID |
String |
Decimal |
double |
Optional[X] |
X? |
list[X] |
List<X> |
dict[K, V] |
Map<K, V> |
@model class |
Dart class |
Error Handling
Define typed errors in Python:
from sagnos import NotFoundError, ValidationError_, AuthError
@expose
async def get_user(id: int) -> User:
user = db.get(id)
if not user:
raise NotFoundError(f"User {id} not found")
return user
Catch them by type in Flutter:
try {
final user = await client.getUser(999);
} on SagnosException catch (e) {
if (e.isNotFound) print('User not found');
if (e.isUnauthorized) print('Please login');
if (e.isValidation) print('Bad input: ${e.message}');
}
Real World Use Cases
AI Mobile App
import torch
from sagnos import expose, model, SagnosApp
@model
class Prediction:
label: str
confidence: float
@expose
async def predict(text: str) -> Prediction:
result = my_ml_model(text)
return Prediction(label=result.label, confidence=result.score)
Flutter sends text, Python ML model processes it, result shows in UI. Zero HTTP code written.
IoT Dashboard
@stream
async def sensor_data() -> AsyncGenerator[Reading, None]:
while True:
yield read_sensor()
await asyncio.sleep(0.5)
Real-time sensor readings streamed to Flutter UI via WebSocket.
Project Structure
my_app/
โโโ backend.py โ your Python code (@expose, @model)
โโโ requirements.txt
โโโ flutter_app/
โโโ lib/
โ โโโ main.dart โ your Flutter UI
โ โโโ sagnos/ โ AUTO-GENERATED, never edit
โ โโโ models.dart
โ โโโ sagnos_client.dart
โ โโโ sagnos_exception.dart
โ โโโ sagnos_stream.dart
โโโ pubspec.yaml
Rule: Edit backend.py for logic. Edit main.dart for UI. Never touch the sagnos/ folder โ it gets regenerated.
API Docs
When your backend is running, visit:
http://localhost:8000/docsโ Interactive Swagger UIhttp://localhost:8000/sagnos/schemaโ Raw schema JSONhttp://localhost:8000/sagnos/healthโ Health check
Contributing
Pull requests welcome. For major changes please open an issue first.
git clone https://github.com/siddhardh-codemonk/sagnos
cd sagnos
python -m venv .venv
.venv\Scripts\activate
pip install -e .
pytest tests/ -v
License
MIT
Author
Built by Siddhardh โ an 18-year-old CS student who wanted Python to work seamlessly with Flutter.
"Write Python. Get Flutter. That's it."
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sagnos-0.2.0.tar.gz.
File metadata
- Download URL: sagnos-0.2.0.tar.gz
- Upload date:
- Size: 22.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
561297cd1eb64511bc4d8e996a5b45c29db40bc2aad6a416e70dc93d33ad5865
|
|
| MD5 |
7c1c4bf86e2e23c863ceef14a292c788
|
|
| BLAKE2b-256 |
a4ae113ac9d47330f48e3f226a3d3ca903d6a7d964383df4c837ed2279882444
|
File details
Details for the file sagnos-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sagnos-0.2.0-py3-none-any.whl
- Upload date:
- Size: 21.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2118142e04b63cc014c2f68b13fa07122c7601a5c51b6a380691709cf16bd96d
|
|
| MD5 |
d169e97cd54c9ac0917aa57ecc0de809
|
|
| BLAKE2b-256 |
67eaa5c729b3afa74dc15d9e0b012f9796fa4a36230fc2993e65e35aa4846360
|