Declarative UI
130+ widgets and presets: layout, Material 3 components, form fields, chips, charts, media, gestures and Flutter-style compositions.
Pydrud is a Flutter-inspired framework that turns declarative Python widget trees into real native Android Views at runtime — via Chaquopy and a lightweight TCP bridge.
Pydrud
Native · Python powered
7
Tap the button
Native View
Rendered by Android
Built on a real native stack
From declarative UI and reactive state to a full CLI, data layer and native hardware access — Pydrud ships the pieces a real product needs.
130+ widgets and presets: layout, Material 3 components, form fields, chips, charts, media, gestures and Flutter-style compositions.
State<T> auto-triggers UI re-renders on value change. Keyed diffing sends tiny patches instead of megabytes over the bridge.
Theme + Tokens — colours, radii, sizes, depth, motion and type live in Python and drive the native renderer directly.
Every widget becomes a real Android View — not a WebView, not a canvas. Real ripples, real focus, real accessibility.
init, dev, run, sync, build, watch, analyze, doctor, clean, pip, icons, keygen, permissions, capabilities, docs and inspect.
SQLite Database with migrations and transactions, a Model ORM with lookups and pagination, plus a TTL Cache and @cached decorator.
119 verified Android-compatible packages — openai, pandas, numpy, pillow, yt-dlp and more — wired into Chaquopy automatically.
WorkManager jobs, foreground services, push, camera preview and capture, sensors, biometrics, Bluetooth, NFC and audio.
Live device metrics — rotation, split screen, insets, font scale — drive breakpoints, percent units and adaptive widgets.
Pattern routes, query strings, guards, redirects, nested navigators, deep links and seven screen transitions.
Thread pool, timers and a non-blocking HTTP client mean the UI never freezes while your Python does real work.
pydrud.testing.AppTester boots your whole app, taps widgets and stubs native answers — without a device or emulator.
A first-class Element/ElementTree layer sits between your widgets and the native Views, so Pydrud knows both the desired state and what Android actually confirmed.
Python
You compose widgets with State<T>, Theme tokens and async services. The framework builds a keyed tree and diffs it against the last confirmed revision.
from pydrud import App, State, Column, Button
counter = State(0)
page.add(Column(children=[...]))
Bridge
Protocol v2 adds a transaction id, desired revision and patch base revision. Only changed widgets travel — kilobytes, not megabytes.
{"op":"patch","tx":42,"base":7,...}
{"ack":true,"revision":8}
Android
The generated Java renderer maps each widget to an Android View, owns the Element/ElementTree identity and reports an explicit render ACK or NACK.
onCreateView(widget, parent)
view.setBackground(...)
sendAck(tx, revision)
No new language to learn. If you can write Python, you can ship an Android app — state, navigation, data, networking and theming all speak the same idiom.
State<T> lives outside the builder, so it survives re-renders. Mutating it and calling page.update() sends a tiny keyed patch, not the whole tree.
1from pydrud import App, State, Text, Button2 3count = State(0)4 5def main(page):6 def inc(event):7 count.value += 18 page.update()9 10 page.add(11 Text(str(count.value), size=72),12 Button("+").on_click(inc),13 )14 15App(target=main).run()Layout, Material 3, lists, forms, media, feedback, navigation and motion — every constructor backed by a real native Android renderer.
Scaffold, run, sync, build, analyze, doctor, package, sign and inspect — Pydrud manages the generated Android project so you never hand-edit XML or Gradle.
During pydrud run press single keys for hot reload, hot restart and live inspection — changes land on device in 30–50ms.
File "src/app/screens/home.py", line 42, in home_screen
total = 100 / count.value
ZeroDivisionError: division by zero
One palette, one 4dp spacing scale, one set of motion curves. Every token is a Python value sent to the device before the first frame — so there is no flash of unstyled UI.
from pydrud import Theme, Colors, Spacing, Radius Theme.configure( radius_card=24, radius_button=20, # shape app_bar_height=64, nav_height=72, # size elevation_card=2, press_scale=0.96, # depth font_family="serif", font_scale=1.1,) Theme.seed(Colors.TEAL) # rebuild palettepage.set_theme_mode("dark") # repaint Python + nativePalette
Radius scale
Elevation
Spacing
Radius
Motion
Type
Pydrud checks Android compatibility, installs into the embedded runtime and syncs Gradle for you. Packages that cannot work on Android — server frameworks, desktop GUI toolkits — are rejected with an explanation.
Showing 119 of 119 verified packages
Both examples live in the repository and run against the test suite — real patterns you can copy into your own project.
Store actions, keyed lists, swipe-to-delete, filtering, persistence and a confirm dialog.
store = Store({"items": []}) def add(todo): store.dispatch( {"type": "add", "todo": todo}) db.save(store.state) ListView(children=[ Card(child=ListTile( title=t.title, on_click=toggle, )) for t in visible()])HTTP client, pull-to-refresh, permissions + GPS, Chart, Skeleton loading and bottom navigation.
def load(event=None): page.location.get().then(fetch) def fetch(pos): page.http.get( API, params={"lat": pos.lat} ).then(render) RefreshIndicator( on_refresh=load, child=Chart(data, kind="line"),)Connect an Android device over USB or ADB over TCP — Pydrud handles the build, install and launch for you.
One pure-Python package. Python 3.11 is embedded on device via Chaquopy.
Generates a complete Android project, runtime bridge and starter widget tree.
Connect a device over USB or ADB over TCP. First APK in about two minutes.
Edit Python under src/ and see changes on device in 30–50ms without rebuilding.
1from pydrud import App, Theme, Colors2from pydrud import State, Column, Center, Text, Button3 4counter = State(0)5 6def main(page):7 page.title = "My Pydrud App"8 9 def on_press(event):10 counter.value += 111 page.update()12 13 page.add(14 Column(15 spacing=16,16 children=[17 Center(child=Text(18 str(counter.value), size=72,19 weight=200,20 color=Colors.PRIMARY,21 )),22 Center(child=Text(23 "Tap the button", size=14,24 color="#FF9CA3AF",25 )),26 Center(child=Button(27 "+", pill=True,28 on_click=on_press,29 )),30 ],31 )32 )33 34Theme.seed(Colors.INDIGO)35App(target=main).run()No XML, no Java, no Gradle files to hand-edit. YAML and Python stay the source of truth.
The things developers ask before their first Pydrud build.
Neither. Every Pydrud widget is mapped to a real native Android View through the generated renderer. You get genuine Material ripples, focus handling, native text fields and accessibility — not HTML in a browser or pixels on a canvas.
One install, one command, one widget tree. Join the developers building native Android apps without writing a line of XML or Kotlin UI code.