iOS Metaball#

#

Added in version 2.0.1.

Enabling dynamic fluid merging, separation animations, and surface wobble interactions between glass elements.

https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/metaball-behavior-preview.png
  • Enables smooth fluid morphing, merging, and separation between adjacent UI elements;

  • Supports interactive surface wobble and jelly-like distortion effects on touch;

  • Powered by real-time GLSL distance-field shaders for organic liquid dynamics;

  • Styled with frosted glass optics, soft shadows, and dynamic refraction effects;

  • Ideal for flexible toolbars, expanding action buttons, and dynamic status badges;

Anatomy#

https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/metaball-behavior-anatomy.png

Example#

from kivy.lang import Builder

from kivymd.app import MDApp

KV = '''
MDScreen:
    id: screen

    FitImage:
        id: bg_image
        source: "https://picsum.photos/800/600?random=1"

    IOSMetaballContainer:
        size_hint: 1, None
        height: dp(80)
        pos_hint: {"center_x": 0.5, "center_y": 0.5}
        merge_distance: dp(1)
        merge_target: 1
        viscosity: dp(80)

        IOSButton:
            size: dp(56), dp(56)
            adaptive_size: False
            border_radius: [dp(28)] * 4
            target_background: bg_image
            pos_hint: {"center_y": 0.5}
            x: dp(300)
            on_release: self.parent.toggle_state(sender=self)

            IOSIconButton:
                icon: "filter-variant"
                theme_icon_color: "Custom"
                icon_color: "white"

        IOSButton:
            spacing: dp(12)
            adaptive_height: False
            adaptive_width: True
            height: dp(56)
            target_background: bg_image
            pos_hint: {"center_y": 0.5}
            x: dp(400)
            border_radius: [dp(28)] * 4
            on_release: self.parent.toggle_state(sender=self)

            IOSIconButton:
                icon: "filter-variant"
                theme_icon_color: "Custom"
                icon_color: "white"

            IOSIconButton:
                icon: "dots-horizontal"
                theme_icon_color: "Custom"
                icon_color: "white"
'''


class LiquidGlassDemo(MDApp):
    def build(self):
        return Builder.load_string(KV)


if __name__ == "__main__":
    LiquidGlassDemo().run()
https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/metaball-behavior-example.gif

Example with various glass objects#

from kivy.lang import Builder

from kivymd.app import MDApp

KV = '''
MDScreen:

    FitImage:
        id: bg_image
        source: "https://picsum.photos/800/600?random=1"

    IOSMetaballContainer:
        size_hint: 1, None
        height: dp(80)
        pos_hint: {"center_x": 0.5, "center_y": 0.5}
        merge_distance: dp(1)
        merge_target: 0
        viscosity: dp(80)

        IOSTextField:
            blur_amount: 10
            target_background: bg_image
            hint_text: "Message"
            size_hint_x: 0.3
            size_hint_y: None
            height: dp(56)
            pos_hint: {"center_y": 0.5}
            x: dp(320)

            IOSTextFieldLeadingIcon:
                icon: "gmail"
                theme_icon_color: "Custom"
                icon_color: "white"

            IOSTextFieldTrailingIcon:
                icon: "account"
                theme_icon_color: "Custom"
                icon_color: "white"
                on_release: self.parent.parent.toggle_state(sender=self)

        IOSButton:
            spacing: dp(12)
            adaptive_height: False
            adaptive_width: True
            height: dp(56)
            target_background: bg_image
            pos_hint: {"center_y": 0.5}
            x: dp(240)
            border_radius: [dp(28)] * 4
            on_release: self.parent.toggle_state(sender=self)

            IOSIconButton:
                icon: "filter-variant"
                theme_icon_color: "Custom"
                icon_color: "white"
'''


class LiquidGlassDemo(MDApp):
    def build(self):
        return Builder.load_string(KV)


if __name__ == "__main__":
    LiquidGlassDemo().run()
https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/metaball-behavior-example-with-various-glass-objects.gif

API - kivymd.uix.behaviors.ios.metaball_behavior#

class kivymd.uix.behaviors.ios.metaball_behavior.IOSMetaballBehavior(*args, **kwargs)#

Behavior that implements an iOS-style liquid glass metaball effect, enabling dynamic fluid merging, separation animations, and surface wobble interactions between glass elements.

active_button_merge_transition#

Animation type for the active button during merging.

active_button_merge_transition is a StringProperty and defaults to ‘out_cubic’.

active_button_merge_duration#

Duration of the active button animation during merging.

active_button_merge_duration is a NumericProperty and defaults to 0.85.

passive_button_merge_transition#

Animation type for the passive button during merging.

passive_button_merge_transition is a StringProperty and defaults to ‘out_cubic’.

passive_button_merge_duration#

Duration of the passive button animation during merging.

passive_button_merge_duration is a NumericProperty and defaults to 0.85.

viscosity_merge_transition#

Animation type for smoothness of blending during merging.

viscosity_merge_transition is a StringProperty and defaults to ‘out_cubic’.

viscosity_merge_duration#

Duration of smoothness of blending during merging.

viscosity_merge_duration is a NumericProperty and defaults to 0.85.

viscosity_separate_transition#

Animation type for smoothness of blending during merging.

viscosity_separate_transition is a StringProperty and defaults to ‘out_cubic’.

viscosity_separate_duration#

Duration of smoothness of blending during merging.

viscosity_separate_duration is a NumericProperty and defaults to 0.85.

buttons_separate_transition#

Button animation type during separate.

buttons_separate_transition is a StringProperty and defaults to ‘out_cubic’.

buttons_separate_duration#

Button animation duration during separate.

buttons_separate_duration is a NumericProperty and defaults to 0.85.

buttons_reset_press_state_transition#

Animation type for the pressed button state return.

buttons_reset_press_state_transition is a StringProperty and defaults to ‘out_cubic’.

buttons_reset_press_state_duration#

Duration of the animation for returning the button from the pressed state.

buttons_reset_press_state_duration is a NumericProperty and defaults to 0.2.

merge_target#

Index of the target metaball widget towards which others merge.

IOSMetaballContainer:
    merge_target: 0
https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/metaball-behavior-merge-target-0.gif
IOSMetaballContainer:
    merge_target: 1
https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/metaball-behavior-merge-target-1.gif

merge_target is a NumericProperty and defaults to 0.

merge_distance#

Additional threshold distance added to touch detection before triggering a merge connection event.

merge_distance is an NumericProperty and defaults to dp(12).

viscosity#

Smooth union strength (u_k) governing fluid blending intensity between objects.

viscosity is an NumericProperty and defaults to dp(55).

on_merge(*args) → None#

Fired when metaball objects successfully finish merging.

on_separate(*args) → None#

Fired when metaball objects successfully finish separating.

toggle_state(sender=None) → None | bool#

Toggles state between merged metaballs and separated widgets.

animate_merge(sender=None) → None#

Animates child widgets into a single unified metaball fluid shape.

animate_separate() → None#

Animates merged metaball shapes back to their individual initial states.

class kivymd.uix.behaviors.ios.metaball_behavior.IOSMetaballContainer(*args, **kwargs)#

Layout container managing liquid metaball UI components, layout bindings, and widget life cycles.

For more information, see in the IOSMetaballBehavior and MDFloatLayout classes documentation.

on_parent(instance, parent) → None#

Stops the schedule when the container is deleted.

add_widget(widget, *args, **kwargs) → None#

Add a new widget as a child of this widget.

Parameters:
widget: Widget

Widget to add to our list of children.

index: int, defaults to 0

Index to insert the widget in the list. Notice that the default of 0 means the widget is inserted at the beginning of the list and will thus be drawn on top of other sibling widgets. For a full discussion of the index and widget hierarchy, please see the Widgets Programming Guide.

Added in version 1.0.5.

canvas: str, defaults to None

Canvas to add widget’s canvas to. Can be ‘before’, ‘after’ or None for the default canvas.

Added in version 1.9.0.

>>> from kivy.uix.button import Button
>>> from kivy.uix.slider import Slider
>>> root = Widget()
>>> root.add_widget(Button())
>>> slider = Slider()
>>> root.add_widget(slider)