iOS Metaball#
#
Added in version 2.0.1.
Enabling dynamic fluid merging, separation animations, and surface wobble interactions between glass elements.
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#
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()
from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.behaviors import IOSMetaballContainer
from kivymd.uix.button import IOSButton, IOSIconButton
from kivymd.uix.screen import MDScreen
from kivymd.uix.fitimage import FitImage
class LiquidGlassDemo(MDApp):
def build(self):
bg_image = FitImage(source="https://picsum.photos/800/600?random=1")
return MDScreen(
bg_image,
IOSMetaballContainer(
IOSButton(
IOSIconButton(
icon="filter-variant",
theme_icon_color="Custom",
icon_color="white",
),
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=lambda x: x.parent.toggle_state(sender=x),
),
IOSButton(
IOSIconButton(
icon="filter-variant",
theme_icon_color="Custom",
icon_color="white",
),
IOSIconButton(
icon="dots-horizontal",
theme_icon_color="Custom",
icon_color="white",
),
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=lambda x: x.parent.toggle_state(sender=x)
),
size_hint=(1, None),
height=dp(80),
pos_hint={"center_x": .5, "center_y": .5},
merge_distance=dp(1),
merge_target=1,
viscosity=dp(80),
),
)
if __name__ == "__main__":
LiquidGlassDemo().run()
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()
from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.behaviors import IOSMetaballContainer
from kivymd.uix.button import IOSButton, IOSIconButton
from kivymd.uix.screen import MDScreen
from kivymd.uix.fitimage import FitImage
from kivymd.uix.textfield import (
IOSTextField, IOSTextFieldLeadingIcon, IOSTextFieldTrailingIcon
)
class LiquidGlassDemo(MDApp):
def build(self):
bg_image = FitImage(source="https://picsum.photos/800/600?random=1")
return MDScreen(
bg_image,
IOSMetaballContainer(
IOSTextField(
IOSTextFieldLeadingIcon(
icon="gmail",
theme_icon_color="Custom",
icon_color="white",
),
IOSTextFieldTrailingIcon(
icon="account",
theme_icon_color="Custom",
icon_color="white",
on_release=lambda x: x.parent.parent.toggle_state(sender=x),
),
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),
),
IOSButton(
IOSIconButton(
icon="filter-variant",
theme_icon_color="Custom",
icon_color="white",
),
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=lambda x: x.parent.toggle_state(sender=x)
),
size_hint=(1, None),
height=dp(80),
pos_hint={"center_x": .5, "center_y": .5},
merge_distance=dp(1),
merge_target=0,
viscosity=dp(80),
),
)
if __name__ == "__main__":
LiquidGlassDemo().run()
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_transitionis aStringPropertyand defaults to ‘out_cubic’.
- active_button_merge_duration#
Duration of the active button animation during merging.
active_button_merge_durationis aNumericPropertyand defaults to 0.85.
- passive_button_merge_transition#
Animation type for the passive button during merging.
passive_button_merge_transitionis aStringPropertyand defaults to ‘out_cubic’.
- passive_button_merge_duration#
Duration of the passive button animation during merging.
passive_button_merge_durationis aNumericPropertyand defaults to 0.85.
- viscosity_merge_transition#
Animation type for smoothness of blending during merging.
viscosity_merge_transitionis aStringPropertyand defaults to ‘out_cubic’.
- viscosity_merge_duration#
Duration of smoothness of blending during merging.
viscosity_merge_durationis aNumericPropertyand defaults to 0.85.
- viscosity_separate_transition#
Animation type for smoothness of blending during merging.
viscosity_separate_transitionis aStringPropertyand defaults to ‘out_cubic’.
- viscosity_separate_duration#
Duration of smoothness of blending during merging.
viscosity_separate_durationis aNumericPropertyand defaults to 0.85.
- buttons_separate_transition#
Button animation type during separate.
buttons_separate_transitionis aStringPropertyand defaults to ‘out_cubic’.
- buttons_separate_duration#
Button animation duration during separate.
buttons_separate_durationis aNumericPropertyand defaults to 0.85.
- buttons_reset_press_state_transition#
Animation type for the pressed button state return.
buttons_reset_press_state_transitionis aStringPropertyand 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_durationis aNumericPropertyand defaults to 0.2.
- merge_target#
Index of the target metaball widget towards which others merge.
IOSMetaballContainer: merge_target: 0
IOSMetaballContainer: merge_target: 1
merge_targetis aNumericPropertyand defaults to 0.
- merge_distance#
Additional threshold distance added to touch detection before triggering a merge connection event.
merge_distanceis anNumericPropertyand defaults to dp(12).
- viscosity#
Smooth union strength (u_k) governing fluid blending intensity between objects.
viscosityis anNumericPropertyand defaults to dp(55).
- 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
IOSMetaballBehaviorandMDFloatLayoutclasses documentation.- 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.
- widget:
>>> from kivy.uix.button import Button >>> from kivy.uix.slider import Slider >>> root = Widget() >>> root.add_widget(Button()) >>> slider = Slider() >>> root.add_widget(slider)