Design a Mobile UI with Kivy

Design a mobile UI with Kivy in this hands-on lesson. Learn core concepts, step-by-step implementation, and troubleshooting tips to build cross-platform mobile interfaces with Python.

Focus: design a mobile ui with kivy

Sponsored

You have a working Python idea, but every time you open your app on your phone, the buttons are cramped, text overflows, and tapping the wrong thing is far too easy. That is the moment design a mobile UI with Kivy becomes non-negotiable. In this lesson, you’ll learn why Kivy treats layout as a first-class concern, how to structure screens with BoxLayout and GridLayout, and how to make your buttons and labels respond gracefully to any screen size. By the end, you’ll have a clean, finger-friendly login screen that works on Android and iOS — without writing a single line of platform-specific code.

The Problem This Lesson Solves

Think about the last time you used an app designed for a desktop or a web page. Tiny links, overlapping text, and inputs that shift when the keyboard pops up. Now imagine you’re the developer who shipped that experience. Mobile screens are small, touch-driven, and come in wildly different aspect ratios — a 320px-wide budget phone and a 430px-wide flagship are both common. Hardcoding positions or sizes (e.g., x=200, y=400) is a recipe for disaster: every device becomes another bug report.

The deeper issue is that desktop and web developers are used to thinking in absolute coordinates or CSS pixels. Kivy throws that away with a declarative layout system — you describe relationships (“the button is below the label, centered horizontally”) and Kivy computes pixel positions for you. That shift is the heart of design a mobile UI with Kivy: you stop micromanaging pixels and start designing behavior.

Without these skills, your app will suffer from:

  • Unreadable text — labels that clip or overlap on smaller screens.
  • Accidental taps — buttons under 48×48 px are nearly impossible to hit reliably.
  • Broken layouts — UI that looks fine on your test emulator but falls apart on a real device.
  • Needless rewrites — redoing every screen when you add a tablet or change orientation.

This lesson gives you the patterns to avoid all that pain.

Core Concept / Mental Model

Think of a Kivy UI as a nesting doll — you start with a root widget, then tuck containers inside containers, each one responsible for arranging its children. The three key ideas to hold onto:

  • Widgets are the visual elements: Button, Label, TextInput, Image. They know how to draw themselves.
  • Layouts are invisible widgets that size and position their children. BoxLayout stacks items in a line, GridLayout puts them in a table, FloatLayout lets you position by proportions.
  • The rule for designing a mobile UI with Kivy is simple: every widget must have a parent layout, and every layout must define how it fills its space. Let the engine do the math.

A diagram in words

Imagine a login screen as three nested boxes:

Screen
└── BoxLayout (vertical)
    ├── Label ("Welcome")
    ├── TextInput (username)
    ├── TextInput (password)
    └── BoxLayout (horizontal)
        ├── Button ("Login")
        └── Button ("Cancel")

The root BoxLayout divides vertical space equally among its children. Inside it, the bottom BoxLayout splits horizontally. Every widget fills its assigned cell — you never set absolute x or y. This is how Kivy adapts to any screen size.

Key mechanics involved

  • size_hint — a fraction (0 to 1) of the parent’s size. For example, size_hint=(0.5, 0.3) makes a widget half the width and 30% of the height of its parent.
  • pos_hint — the same idea for position, used in FloatLayout or AnchorLayout (e.g., pos_hint={'center_x': 0.5, 'top': 1}).
  • The kv language — a declarative, YAML-like syntax that describes the widget tree. It’s the preferred way to design a mobile UI with Kivy because it keeps layout separate from logic.
  • The ScreenManager — for multi-screen apps, it handles navigation and maintains a stack of screens.

That mental model — nesting layouts, proportional sizing, separating structure from logic — is the core of Kivy UI design.

How It Works Step by Step

Let’s walk through the process of building a mobile UI from scratch, using Kivy’s building blocks.

Step 1: Choose your root layout

Every screen needs a container that defines the overall flow. Start with a BoxLayout set to vertical if you want items stacked top-to-bottom, or horizontal for side-by-side. For more complex alignments, use GridLayout.

from kivy.uix.boxlayout import BoxLayout

root = BoxLayout(orientation='vertical')

Step 2: Add widgets and set their size_hint

Each widget you add will occupy a proportional slice of the layout. Use size_hint to give your design a sense of hierarchy — a title label might take 20% of the height, a button area 40%.

from kivy.uix.label import Label
from kivy.uix.button import Button

title = Label(text='Welcome', size_hint=(1, 0.2))
btn = Button(text='Tap me', size_hint=(1, 0.4))

root.add_widget(title)
root.add_widget(btn)

Step 3: Nest layouts for complex designs

Rarely will a single layout suffice. Nest a BoxLayout inside another for control. For example, a row of buttons at the bottom of a vertical layout.

button_row = BoxLayout(orientation='horizontal')
button_row.add_widget(Button(text='OK'))
button_row.add_widget(Button(text='Cancel'))

root.add_widget(button_row)

Step 4: Use the kv language for clean separation

The kv language lets you declare the whole UI in one file — it’s easier to read and maintain. You load it with Builder.load_file('main.kv') or use a string.

<LoginScreen>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'Welcome'
            size_hint_y: 0.3
        TextInput:
            hint_text: 'Username'
        TextInput:
            hint_text: 'Password'
        BoxLayout:
            orientation: 'horizontal'
            Button:
                text: 'Login'
            Button:
                text: 'Cancel'

Step 5: Bind events and run the app

Wire up button presses using the on_press event. Then create an App subclass and call run().

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

class LoginScreen(BoxLayout):
    def on_login(self):
        print("Login pressed")

class MyApp(App):
    def build(self):
        return LoginScreen()

if __name__ == '__main__':
    MyApp().run()

The engine handles resizing for you. Rotate the device? The layout recalculates based on the new aspect ratio — as long as you avoided absolute positions, you’re good.

Pro tip: Always test on multiple screen sizes (e.g., an Android emulator with a small phone preset and a tablet preset). One layout that looks great on your dev machine will not automatically look great everywhere.

Hands-On Walkthrough

Let’s build a complete, production-ready login screen in Python. I’ll use the kv language because it’s the cleanest approach for designing a mobile UI with Kivy.

Step-by-step implementation

1. Create the project structure — one Python file main.py and one app.kv file.

2. Write the Python code (main.py)

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.label import Label

class LoginScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.orientation = 'vertical'
        self.spacing = 10
        self.padding = 20

        # Title
        self.add_widget(Label(text='Welcome!', font_size=32, size_hint_y=0.3))

        # Username input
        username = TextInput(hint_text='Username', multiline=False, size_hint_y=0.2)
        self.add_widget(username)

        # Password input
        password = TextInput(hint_text='Password', multiline=False, password=True, size_hint_y=0.2)
        self.add_widget(password)

        # Button row
        btn_row = BoxLayout(orientation='horizontal', spacing=10, size_hint_y=0.3)
        login_btn = Button(text='Login', size_hint=(1, 1))
        cancel_btn = Button(text='Cancel', size_hint=(1, 1))
        btn_row.add_widget(login_btn)
        btn_row.add_widget(cancel_btn)
        self.add_widget(btn_row)

        # Bind events
        login_btn.bind(on_press=lambda *args: print("Logged in"))
        cancel_btn.bind(on_press=lambda *args: print("Cancelled"))

class MyApp(App):
    def build(self):
        return LoginScreen()

if __name__ == '__main__':
    MyApp().run()

3. Expected output — run python main.py, and a window (or phone screen on Android) appears with a title, two inputs, and two buttons. Tap each button and you’ll see the print statements in the console.

4. Refine with touch-friendly sizes — the recommended minimum touch target is 48×48 dp. Use Kivy’s dp() helper to convert density-independent pixels:

from kivy.metrics import dp
login_btn = Button(text='Login', size_hint=(1, None), height=dp(48))

Now your buttons will be consistently tappable across devices.

5. Enhance with a screen manager — for a multi-screen app, wrap your screen in a ScreenManager.

from kivy.uix.screenmanager import ScreenManager, Screen

class LoginScreen(Screen):
    pass

class HomeScreen(Screen):
    pass

class MyApp(App):
    def build(self):
        sm = ScreenManager()
        sm.add_widget(LoginScreen(name='login'))
        sm.add_widget(HomeScreen(name='home'))
        return sm

This lets you switch screens with sm.current = 'home' when a login succeeds.

Interactive example

Here’s a small kv-based version that demonstrates real-time resizing — run it and drag the window edge to see the UI adapt.

from kivy.app import App
from kivy.lang import Builder

kv = '''
BoxLayout:
    orientation: 'vertical'
    padding: dp(20)
    spacing: dp(10)
    Label:
        text: 'Responsive'
        font_size: '24sp'
    TextInput:
        hint_text: 'Type here'
    Button:
        text: 'Press'
        size_hint_y: 0.3
'''

class ResponsiveApp(App):
    def build(self):
        return Builder.load_string(kv)

if __name__ == '__main__':
    ResponsiveApp().run()

Try resizing the window: everything scales proportionally, and the text input stretches to fill available space.

Compare Options / When to Choose What

You don’t have to use Kivy; there are several ways to build a mobile UI in Python. Here’s a comparison to help you decide.

Framework Pros Cons Best for
Kivy Cross-platform (Android, iOS, desktop), declarative kv language, rich widget set Larger APK size, UI may not look 100% native Rapid prototyping, apps that need full Python control
BeeWare / Toga Uses native widgets, smaller app size Less mature, fewer widgets, limited layout flexibility Apps that must feel native and are simple
Flutter (Dart) Excellent performance, native feel, hot reload Requires learning Dart, not Python High-quality production apps with large teams
React Native Huge ecosystem, native-like JavaScript/TypeScript, not Python Apps targeting both stores with a web background

When to choose Kivy — if you want to stay in Python, need cross-platform support quickly, and your UI is moderately complex. Kivy’s layout system is arguably more flexible than Toga’s, though Toga has a smaller footprint.

When to avoid Kivy — if you need pixel-perfect native widgets (e.g., iOS design language) or have a tiny app where the APK size matters more than development speed.

Given this track is about Python mobile development, Kivy is the most pragmatic choice — you’ll learn patterns that also apply to other layout systems.

Troubleshooting & Edge Cases

Early on, your Kivy UI will misbehave. Here are the most common pitfalls I see and how to fix them.

1. Layouts collapse to zero size

Symptom: Nothing appears, or widgets stack on top of each other.

Cause: You forgot to set a size_hint or used a FloatLayout without pos_hint. If a child never gets a size or position, Kivy may default to (0, 0).

Fix: Give every widget a size_hint (or set size explicitly for non-layout widgets) and use pos_hint in FloatLayout.

box = BoxLayout()
label = Label(text='Hi', size_hint=(1, 1))  # Always visible
box.add_widget(label)

2. Text gets cut off on small screens

Symptom: Labels clip their text when the screen is short.

Cause: The layout is too tight, or you’re using fixed pixel heights.

Fix: Allow the label to grow: set size_hint_y=None and use a minimum height in dp. Or enable text truncation with text_size and shrink.

Label:
    text: 'This is a long description'
    text_size: self.width, None
    size_hint_y: None
    height: dp(80)
    halign: 'center'
    valign: 'middle'

3. Keyboard pushes inputs out of view

Symptom: The text input disappears behind the on-screen keyboard.

Cause: Your root layout doesn’t react to the keyboard resize.

Fix: In the App class, set self.root.top = 0 when the keyboard opens, or use Kivy’s Window.keyboard_height property to adjust the layout. A simple approach: make your screen a ScrollView so the user can scroll up.

from kivy.core.window import Window

def on_keyboard_open(self, *args):
    self.root.y = Window.keyboard_height

4. The UI looks fine on desktop but broken on Android

Cause: You used px instead of dp. Pixels vary with density.

Fix: Always use dp() for sizes and sp() for font sizes.

from kivy.metrics import dp, sp
widget.height = dp(56)
label.font_size = sp(16)

5. Buttons are too small to tap

Cause: Default button height is around 36 dp, below the 48 dp target.

Fix: Set a minimum height on all buttons using size_hint_y=None, height=dp(48).

What You Learned & What's Next

You now know design a mobile UI with Kivy at a practical level. Let’s recap:

  • You can explain why layout management is critical for mobile (the problem this lesson solves).
  • You understand the mental model of nested layouts and size_hint/pos_hint (core concept).
  • You can implement a responsive screen step by step using BoxLayout, GridLayout, and the kv language.
  • You completed a hands-on login screen and refined it with touch targets and a screen manager.
  • You can compare Kivy to other frameworks and decide when it’s the right tool.
  • You can troubleshoot common UI failures like collapsed layouts, text clipping, and keyboard overlap.

Your next lesson in the Mobile App Development track will likely cover handling device APIs (camera, sensors, geolocation) or packaging and deployment — both of which build on the UI foundation you just created. A responsive, well-structured UI makes those integrations much easier to test and maintain.

Pro tip: Revisit your layout decisions after you add your app’s business logic. You’ll often find that a ScrollView or an AnchorLayout better matches how users actually interact with your content.

Practice recap

Now build a simple profile screen using a GridLayout with two input fields and a save button. Run it on an Android emulator (or desktop) and resize the window to verify it stays well-proportioned. Then, add a ScreenManager with a second screen that appears when you tap save — this will prepare you for the next lesson on app navigation.

Common mistakes

  • Forgetting to set a size_hint on children — widgets collapse to zero size and disappear.
  • Using absolute pixel positions instead of layouts — the UI breaks on different screen sizes.
  • Using px instead of dp or sp, making touch targets too small on high-density screens.
  • Nesting BoxLayouts without specifying orientation — default is horizontal, which surprises many beginners.
  • Forgetting to handle the keyboard — text inputs get hidden when the on-screen keyboard pushes the screen up.

Variations

  1. Use kv files instead of Python code to define the UI — cleaner separation of design and logic.
  2. Explore GridLayout for form-like layouts where you want equal column widths and predictable rows.
  3. Consider AnchorLayout for pinning widgets to corners or center without complex nesting.

Real-world use cases

  • A login/registration screen for a fitness app that needs to be tappable and readable on all Android phones.
  • An inventory management app where a GridLayout displays products in a scrollable grid for warehouse staff.
  • A customer feedback form on iOS/Android with a smooth ScrollView to avoid keyboard overlap during input.

Key takeaways

  • Always use layouts (BoxLayout, GridLayout, FloatLayout) instead of absolute positioning to get responsive mobile UIs.
  • Master the size_hint and pos_hint properties — they are the foundation of flexible Kivy layout design.
  • Prefer the kv language for separating UI structure from Python logic, making UIs easier to maintain.
  • Respect touch-target best practices: use dp units and aim for at least 48x48 dp buttons.
  • Test your UI on multiple screen sizes and handle keyboard overlap with scroll views or layout adjustments.
  • Compare Kivy to alternatives like BeeWare to ensure you pick the right framework for your project.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.