""" Components/Hero =============== .. versionadded:: 1.0.0 .. rubric:: Use the :class:`~MDHeroFrom` widget to animate a widget from one screen to the next. - The hero refers to the widget that flies between screens. - Create a hero animation using KivyMD’s :class:`~MDHeroFrom` widget. - Fly the hero from one screen to another. - Animate the transformation of a hero’s shape from circular to rectangular while flying it from one screen to another. - The :class:`~MDHeroFrom` widget in KivyMD implements a style of animation commonly known as shared element transitions or shared element animations. .. raw:: html
The widget that will move from screen A to screen B will be a hero. To move a widget from one screen to another using hero animation, you need to do the following: - On screen **A**, place the :class:`~MDHeroFrom` container. - Sets a tag (string) for the :class:`~MDHeroFrom` container. - Place a hero in the :class:`~MDHeroFrom` container. - On screen **B**, place the :class:`~MDHeroTo` container - our hero from screen **A **will fly into this container. .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/hero-base.png :align: center .. warning:: :class:`~MDHeroFrom` container cannot have more than one child widget. Base example ------------ .. code-block:: python from kivy.lang import Builder from kivymd.app import MDApp KV = ''' MDScreenManager: MDScreen: name: "screen A" md_bg_color: "lightblue" MDHeroFrom: id: hero_from tag: "hero" size_hint: None, None size: "120dp", "120dp" pos_hint: {"top": .98} x: 24 FitImage: source: "https://github.com/kivymd/internal/raw/main/logo/kivymd_logo_blue.png" size_hint: None, None size: hero_from.size MDRaisedButton: text: "Move Hero To Screen B" pos_hint: {"center_x": .5} y: "36dp" on_release: root.current_hero = "hero" root.current = "screen B" MDScreen: name: "screen B" hero_to: hero_to md_bg_color: "cadetblue" MDHeroTo: id: hero_to size_hint: None, None size: "220dp", "220dp" pos_hint: {"center_x": .5, "center_y": .5} MDRaisedButton: text: "Move Hero To Screen A" pos_hint: {"center_x": .5} y: "36dp" on_release: root.current_hero = "hero" root.current = "screen A" ''' class Test(MDApp): def build(self): return Builder.load_string(KV) Test().run() .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/hero-usage.gif :align: center Note that the child of the :class:`~MDHeroFrom` widget must have the size of the parent: .. code-block:: kv MDHeroFrom: id: hero_from FitImage: size_hint: None, None size: hero_from.size To enable hero animation before setting the name of the current screen for the screen manager, you must specify the name of the tag of the :class:`~MDHeroFrom` container in which the hero is located: .. code-block:: kv MDRaisedButton: text: "Move Hero To Screen B" on_release: root.current_hero = "hero" root.current = "screen 2" If you need to switch to a screen that does not contain heroes, set the `current_hero` attribute for the screen manager as "" (empty string): .. code-block:: kv MDRaisedButton: text: "Go To Another Screen" on_release: root.current_hero = "" root.current = "another screen" Example ------- .. code-block:: python from kivy.lang import Builder from kivymd.app import MDApp KV = ''' MDScreenManager: MDScreen: name: "screen A" md_bg_color: "lightblue" MDHeroFrom: id: hero_from tag: "hero" size_hint: None, None size: "120dp", "120dp" pos_hint: {"top": .98} x: 24 FitImage: source: "https://github.com/kivymd/internal/raw/main/logo/kivymd_logo_blue.png" size_hint: None, None size: hero_from.size MDRaisedButton: text: "Move Hero To Screen B" pos_hint: {"center_x": .5} y: "36dp" on_release: root.current_hero = "hero" root.current = "screen B" MDScreen: name: "screen B" hero_to: hero_to md_bg_color: "cadetblue" MDHeroTo: id: hero_to size_hint: None, None size: "220dp", "220dp" pos_hint: {"center_x": .5, "center_y": .5} MDRaisedButton: text: "Go To Screen C" pos_hint: {"center_x": .5} y: "52dp" on_release: root.current_hero = "" root.current = "screen C" MDRaisedButton: text: "Move Hero To Screen A" pos_hint: {"center_x": .5} y: "8dp" on_release: root.current_hero = "hero" root.current = "screen A" MDScreen: name: "screen C" MDLabel: text: "Screen C" halign: "center" MDRaisedButton: text: "Back To Screen B" pos_hint: {"center_x": .5} y: "36dp" on_release: root.current = "screen B" ''' class Test(MDApp): def build(self): return Builder.load_string(KV) Test().run() .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/hero-switch-another-screen.gif :align: center Events ------ Two events are available for the hero: - `on_transform_in` - when the hero flies from screen **A** to screen **B**. - `on_transform_out` - when the hero back from screen **B** to screen **A**. The `on_transform_in`, `on_transform_out` events relate to the :class:`~MDHeroFrom` container. For example, let's change the radius and background color of the hero during the flight between the screens: .. code-block:: python from kivy import utils from kivy.animation import Animation from kivy.lang import Builder from kivy.utils import get_color_from_hex from kivymd.app import MDApp from kivymd.uix.hero import MDHeroFrom from kivymd.uix.relativelayout import MDRelativeLayout KV = ''' MDScreenManager: MDScreen: name: "screen A" md_bg_color: "lightblue" MyHero: id: hero_from tag: "hero" size_hint: None, None size: "120dp", "120dp" pos_hint: {"top": .98} x: 24 MDRelativeLayout: size_hint: None, None size: hero_from.size md_bg_color: "blue" radius: [24, 12, 24, 12] FitImage: source: "https://github.com/kivymd/internal/raw/main/logo/kivymd_logo_blue.png" MDRaisedButton: text: "Move Hero To Screen B" pos_hint: {"center_x": .5} y: "36dp" on_release: root.current_hero = "hero" root.current = "screen B" MDScreen: name: "screen B" hero_to: hero_to md_bg_color: "cadetblue" MDHeroTo: id: hero_to size_hint: None, None size: "220dp", "220dp" pos_hint: {"center_x": .5, "center_y": .5} MDRaisedButton: text: "Move Hero To Screen A" pos_hint: {"center_x": .5} y: "36dp" on_release: root.current_hero = "hero" root.current = "screen A" ''' class Test(MDApp): def build(self): return Builder.load_string(KV) class MyHero(MDHeroFrom): def on_transform_in( self, instance_hero_widget: MDRelativeLayout, duration: float ): ''' Called when the hero flies from screen **A** to screen **B**. :param instance_hero_widget: dhild widget of the `MDHeroFrom` class. :param duration of the transition animation between screens. ''' Animation( radius=[12, 24, 12, 24], duration=duration, md_bg_color=(0, 1, 1, 1), ).start(instance_hero_widget) def on_transform_out( self, instance_hero_widget: MDRelativeLayout, duration: float ): '''Called when the hero back from screen **B** to screen **A**.''' Animation( radius=[24, 12, 24, 12], duration=duration, md_bg_color=get_color_from_hex(utils.hex_colormap["blue"]), ).start(instance_hero_widget) Test().run() .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/hero-events.gif :align: center Usage with ScrollView --------------------- .. code-block:: python from kivy.animation import Animation from kivy.clock import Clock from kivy.lang import Builder from kivy.properties import StringProperty, ObjectProperty from kivymd.app import MDApp from kivymd.uix.hero import MDHeroFrom KV = '''