pythonkivykivymd

How do I display a scrollview with an image and text inside of an MDDialog?


I want to scroll through the contents of my MDDialog using a scrollview but it is not displayed inside the widget at all. Here's the code:

from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDButton, MDButtonText
from kivymd.uix.dialog import (
    MDDialog, MDDialogIcon,
    MDDialogHeadlineText, MDDialogSupportingText,
    MDDialogButtonContainer, MDDialogContentContainer,
)
from kivymd.uix.divider import MDDivider
from kivymd.uix.fitimage import FitImage
from kivymd.uix.imagelist import MDSmartTile, MDSmartTileImage
from kivymd.uix.label import MDLabel
from kivymd.uix.list import (MDListItem,
                             MDListItemLeadingIcon, MDListItemSupportingText,
                             )
from kivymd.uix.scrollview import MDScrollView

KV = '''
MDScreen:
    md_bg_color: self.theme_cls.backgroundColor
    MDButton:        
        pos_hint: {'center_x': .5, 'center_y': .5}
        on_release: app.show_alert_dialog()
        MDButtonText:            
            text: "Show dialog"
'''


class MDDialogCustomContentContainer(MDDialogContentContainer):
    def __init__(self, **kwargs):
        super(MDDialogCustomContentContainer, self).__init__(**kwargs)
        self.size_hint_y = None
        self.adaptive_height = True
        layout_root = MDScrollView(do_scroll_x=False, do_scroll_y=True)
        layout = MDBoxLayout(orientation="vertical", size_hint=(1, None), adaptive_height=True,
                             padding="10dp", spacing="10dp")
        layout.bind(minimum_height=layout.setter('height')) 
        img = FitImage(source="bear.png")
        layout.add_widget(img)
        for i in range(10):
            layout.add_widget(MDLabel(text=f"text №{i}", adaptive_height=True))
        self.add_widget(MDDivider())
        layout_root.add_widget(layout)
        self.add_widget(layout_root)
        self.add_widget(MDDivider())


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

    def show_alert_dialog(self):
        MDDialog(
            # ----------------------------Icon-----------------------------
            MDDialogIcon(icon="refresh",
                         ),
            # -----------------------Headline text-------------------------
            MDDialogHeadlineText(text="Reset settings?",
                                 ),
            # -----------------------Supporting text-----------------------
            MDDialogSupportingText(text="This will reset your app preferences back to their "
                                        "default settings. The following accounts will also "                
                                        "be signed out:",
                                   ),
            # -----------------------Custom content------------------------
            MDDialogCustomContentContainer(),
            # ---------------------Button container------------------------
            MDDialogButtonContainer(
                Widget(), MDButton(
                    MDButtonText(text="Cancel"), style="text",
                ), MDButton(
                    MDButtonText(text="Accept"), style="text",
                ), spacing="8dp",
            ),  # -------------------------------------------------------------
        ).open()


Example().run()

I tried to give layout and the MDDialogCustomContentContainer adaptive_height=True, but that didn't work out. I have also thought of giving all widgets a fixed height, but since there was an image that I want to display, I didn't know (and I still don't know) how to make that happen


Solution

  • I think you just need to specify some sizes (or perhaps do some linking to get sizes to adjust). Also, the MDDialogContentContainer is a BoxLayout, so there is no need to add another BoxLayout to it. Here is a modified version of your MDDialogCustomContentContainer:

    class MDDialogCustomContentContainer(MDDialogContentContainer):
        def __init__(self, **kwargs):
            super(MDDialogCustomContentContainer, self).__init__(**kwargs)
            self.size_hint_y = None
            self.adaptive_height = True
            layout_root = MDScrollView(do_scroll_x=False, do_scroll_y=True, size_hint_y=None, height=200)
            img = FitImage(source='bear.png', size_hint=(None, None), size=(100, 1000), keep_ratio=False, allow_stretch=True)
            self.add_widget(MDDivider())
            layout_root.add_widget(img)
            self.add_widget(layout_root)
            self.add_widget(MDDivider())
    

    Since you are setting adaptive_height to True in the MDDialogCustomContentContainer, it will try to use the smallest height that will contain its children. If the MDScrollView does not get assigned a height, the MDDialogCustomContentContainer will assume it has 0 height, so there will be nothing to scroll. So the above code specifies a height for the MDScrollView and for the FitImage.