Skip to content
v2.0.2Reliable haptics, clean logs & 130+ widgets

Build native Android apps with Python.

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.

No XML layoutsNo Kotlin UI codeJust Python
$pip install pydrud
MIT licensedPython 3.11 embeddedFirst APK in ~2 minutes
9:41

Pydrud

Native · Python powered

7

Tap the button

Native View

Rendered by Android

live
Overview
Widgets
Settings

Built on a real native stack

Python 3.11
Android Views
JVM toolchain
Gradle 8.13
JDK 17
SQLite
NDJSON bridge
Material 3
Chaquopy 17
Python 3.11
Android Views
JVM toolchain
Gradle 8.13
JDK 17
SQLite
NDJSON bridge
Material 3
Chaquopy 17
0+Widgets & presets
0Verified PyPI packages
0Built-in icon vectors
0+Tests in CI
Everything you need

A complete framework, not a demo

From declarative UI and reactive state to a full CLI, data layer and native hardware access — Pydrud ships the pieces a real product needs.

UI

Declarative UI

130+ widgets and presets: layout, Material 3 components, form fields, chips, charts, media, gestures and Flutter-style compositions.

State

Reactive State

State<T> auto-triggers UI re-renders on value change. Keyed diffing sends tiny patches instead of megabytes over the bridge.

Theme

Design System

Theme + Tokens — colours, radii, sizes, depth, motion and type live in Python and drive the native renderer directly.

Android

Native Rendering

Every widget becomes a real Android View — not a WebView, not a canvas. Real ripples, real focus, real accessibility.

DX

Full CLI Toolchain

init, dev, run, sync, build, watch, analyze, doctor, clean, pip, icons, keygen, permissions, capabilities, docs and inspect.

Data

Batteries Data Layer

SQLite Database with migrations and transactions, a Model ORM with lookups and pagination, plus a TTL Cache and @cached decorator.

Packages

PyPI on Android

119 verified Android-compatible packages — openai, pandas, numpy, pillow, yt-dlp and more — wired into Chaquopy automatically.

Native

Background & Hardware

WorkManager jobs, foreground services, push, camera preview and capture, sensors, biometrics, Bluetooth, NFC and audio.

Layout

Responsive by Default

Live device metrics — rotation, split screen, insets, font scale — drive breakpoints, percent units and adaptive widgets.

Routing

URL Navigation

Pattern routes, query strings, guards, redirects, nested navigators, deep links and seven screen transitions.

Async

Async by Default

Thread pool, timers and a non-blocking HTTP client mean the UI never freezes while your Python does real work.

Testing

Testable in CI

pydrud.testing.AppTester boots your whole app, taps widgets and stubs native answers — without a device or emulator.

How it works

Python describes it. Android renders it.

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

Declarative widget tree

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

NDJSON over TCP

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

Real native Views

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)

Build keyed tree
Diff vs confirmed
Send patch tx
Native render ACK
Coalesce next
Write Python, get Android

Familiar Python, real device output

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.

src/app/state.py
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()
130+ widgets & presets

A component for every screen

Layout, Material 3, lists, forms, media, feedback, navigation and motion — every constructor backed by a real native Android renderer.

ContainerColumnRowCenterStackPositionedSizedBoxPaddingCardWrapExpandedFlexibleScaffoldAppBarButtonChipBadgeAvatarBannerTooltipTabsDrawerStepperListViewGridViewListTileExpansionTileReorderableListInfiniteListSkeletonTextFieldSearchFieldPasswordFieldEmailFieldFormFormFieldSliderSwitchCheckboxContainerColumnRowCenterStackPositionedSizedBoxPaddingCardWrapExpandedFlexibleScaffoldAppBarButtonChipBadgeAvatarBannerTooltipTabsDrawerStepperListViewGridViewListTileExpansionTileReorderableListInfiniteListSkeletonTextFieldSearchFieldPasswordFieldEmailFieldFormFormFieldSliderSwitchCheckbox
RadioDropdownImageVideoPlayerWebViewMapViewCameraPreviewQRScannerChartCanvasRichTextMarkdownProgressBarCircularProgressRefreshIndicatorDialogSnackBarToastRatingsEmptyStateErrorStateLoadingStateBottomNavigationBarNavigationRailRouterNavigatorDeepLinkSegmentedButtonAnimationTweenSequence_AnimatedContainerAnimatedOpacityFadeInSlideInScaleInHeroGestureDetectorRadioDropdownImageVideoPlayerWebViewMapViewCameraPreviewQRScannerChartCanvasRichTextMarkdownProgressBarCircularProgressRefreshIndicatorDialogSnackBarToastRatingsEmptyStateErrorStateLoadingStateBottomNavigationBarNavigationRailRouterNavigatorDeepLinkSegmentedButtonAnimationTweenSequence_AnimatedContainerAnimatedOpacityFadeInSlideInScaleInHeroGestureDetector

Layout

12
ContainerColumnRowCenterStackPositionedSizedBoxPaddingCardWrapExpandedFlexible

Material 3

11
ScaffoldAppBarButtonChipBadgeAvatarBannerTooltipTabsDrawerStepper

Lists

7
ListViewGridViewListTileExpansionTileReorderableListInfiniteListSkeleton

Forms

11
TextFieldSearchFieldPasswordFieldEmailFieldFormFormFieldSliderSwitchCheckboxRadioDropdown

Media

10
ImageVideoPlayerWebViewMapViewCameraPreviewQRScannerChartCanvasRichTextMarkdown

Feedback

10
ProgressBarCircularProgressRefreshIndicatorDialogSnackBarToastRatingsEmptyStateErrorStateLoadingState

Navigation

6
BottomNavigationBarNavigationRailRouterNavigatorDeepLinkSegmentedButton

Motion

10
AnimationTweenSequence_AnimatedContainerAnimatedOpacityFadeInSlideInScaleInHeroGestureDetector
CLI toolchain

A whole toolkit in one command

Scaffold, run, sync, build, analyze, doctor, package, sign and inspect — Pydrud manages the generated Android project so you never hand-edit XML or Gradle.

Flutter-style interactive dev runner

During pydrud run press single keys for hot reload, hot restart and live inspection — changes land on device in 30–50ms.

rHot reload — sync Python, keep state
RHot restart — reset state & router
t / pDump the live widget tree
cClear terminal & refresh banner
h / ?Interactive key menu
dDetach, leave the app running
qQuit the app on device
Python Runtime Error

File "src/app/screens/home.py", line 42, in home_screen

total = 100 / count.value

ZeroDivisionError: division by zero

Last good UI stays visible on device
pydrud — zsh
$pydrud init my_app --org com.example# Scaffold a new project
$pydrud run# Build, install, launch + interactive dev runner
$pydrud dev# Host-driven live preview over the LAN
$pydrud sync# Regenerate the managed native runtime
$pydrud build --release# Signed, R8-shrunk APK
$pydrud watch# Rebuild, reinstall, relaunch loop
$pydrud analyze# Static analysis for common mistakes
$pydrud doctor# Diagnose your build environment
$pydrud pip add yt-dlp requests# Install verified packages
$pydrud icons --source logo.png# Every density, round + adaptive
$pydrud keygen# Upload keystore + keystore.properties
$pydrud permissions add camera location# Declare native permissions
$pydrud capabilities add haptics notifications# Enable capabilities
$pydrud inspect# Live widget inspector
$pydrud docs# Offline API reference
$
Design system

The Java renderer makes zero design decisions

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.

src/app/theme.py
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 + native

Palette

primary
violet
cyan
python
android

Radius scale

4
12
20
28

Elevation

124

Spacing

XS 4SM 8MD 16LG 24HUGE 48

Radius

XS 4SM 8MD 12LG 20XL 28

Motion

fast 120msbase 220msslow 360ms

Type

displaytitlebodylabelmono
PyPI on Android

119 verified packages, wired automatically

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.

$pydrud pip add yt-dlp requests

ai

8 shown
anthropicgoogle-generativeaigroqhuggingface-hubopenaisentencepiecetiktokentransformers

database

8 shown
firebase-adminpeeweepymongopysqlcipher3redissqlalchemysupabasetinydb

documents

5 shown
et-xmlfileopenpyxlpypdfpython-docxreportlab

media

16 shown
ffmpeg-pythongttsimageioinstaloadermoviepymutagenopencv-pythonpillowpydubpython-barcodepytubepyzbarqrcodespeechrecognitionyoutube-search-pythonyt-dlp

network

19 shown
aiohttpcertificharset-normalizerdeep-translatorfeedparsergeopygoogletranshttpxidnapaho-mqttprawrequestssseclient-pytelethontweepyurllib3websocket-clientwebsocketswikipedia

parsing

15 shown
beautifulsoup4chardetcsvkithtml5libjsonschemalxmlmarkdownmarkdownifyorjsonpyyamlsoupsievetomltomliujsonxmltodict

science

10 shown
matplotlibmpmathnetworkxnumpypandasqiskitscikit-learnscipystatsmodelssympy

security

8 shown
bcryptcryptographykeyringpasslibpycryptodomepyjwtpyotppython-jose

utility

30 shown
arrowattrscachetoolschevroncroniteremojifakerfuzzywuzzyhumanizejinja2markupsafemore-itertoolsphonenumberspsutilpydanticpython-dateutilpython-dotenvpython-slugifypytzqrcode-terminalrapidfuzzregexrichscheduleshortuuidtabulatetenacitytyping-extensionstzdatavalidators

Showing 119 of 119 verified packages

See it in action

Two complete apps, covered by tests

Both examples live in the repository and run against the test suite — real patterns you can copy into your own project.

Todo app

examples/todo_app.py

Store actions, keyed lists, swipe-to-delete, filtering, persistence and a confirm dialog.

examples/todo_app.py
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()])

Weather app

examples/weather_app.py

HTTP client, pull-to-refresh, permissions + GPS, Chart, Skeleton loading and bottom navigation.

examples/weather_app.py
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"),)
Quickstart

From zero to a running APK in about two minutes

Connect an Android device over USB or ADB over TCP — Pydrud handles the build, install and launch for you.

01

Install the framework

One pure-Python package. Python 3.11 is embedded on device via Chaquopy.

$pip install pydrud
02

Scaffold a project

Generates a complete Android project, runtime bridge and starter widget tree.

$pydrud init my_app --org com.example
03

Build & launch

Connect a device over USB or ADB over TCP. First APK in about two minutes.

$cd my_app && pydrud run
04

Iterate live

Edit Python under src/ and see changes on device in 30–50ms without rebuilding.

$press r # hot reload
src/app/main.py
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()

Everything is generated for you

No XML, no Java, no Gradle files to hand-edit. YAML and Python stay the source of truth.

  • Complete Android project generated
  • Native bridge & Chaquopy runtime wired up
  • themes.xml generated from your palette
  • Hot reload dev runner ready
  • Icons, permissions & signing scaffolded
$pydrud init my_app --org com.example
FAQ

Questions, answered

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.

Open source · MIT

Ship your first Python-built Android app today

One install, one command, one widget tree. Join the developers building native Android apps without writing a line of XML or Kotlin UI code.

$pip install pydrud