webviewmaui

MAUI: How to update the webview height after loading new content


I want to implement the below UI in my MAUI application (header and footer sections are not added):

UI Requirement

Below is my code (header and footer sections are not added): Webview to show HTML content(with a background image), Audio frame with previous, play and next options and finally a footer image.

<Frame 
    Margin="10,0,10,0"
    x:Name="android_uwp_layout"
    IsVisible="False"
    Padding="0"
    CornerRadius="20"
    Grid.Row="1">

    <ScrollView 
        x:Name="android_scroll_view"
        Orientation="Vertical" 
        VerticalOptions="FillAndExpand">
        <VerticalStackLayout Spacing="10">

            <!-- Background image with WebView -->
            <Grid>
                <Image Aspect="Fill" HorizontalOptions="FillAndExpand" Source="ic_daily_reading_new_bg_xx.png"/>

                <WebView  
                    x:Name="mywebview"
                    BackgroundColor="Transparent"
                    HorizontalOptions="FillAndExpand"
                    VerticalOptions="FillAndExpand"
                    Navigated="OnWebViewNavigated"/>
            </Grid>

            <!-- Audio frame (Appears after WebView content) -->
            <Frame
                x:Name="android_audio_frame"
                BackgroundColor="#666664"
                Padding="0"
                CornerRadius="{OnIdiom Phone=30, Tablet=45, Desktop=30}"
                Margin="0,10,0,15"
                HorizontalOptions="CenterAndExpand"
                VerticalOptions="EndAndExpand">

                //Audio frame UI with previous, play and next options
            </Frame>

            <!-- Footer image (Appears at the bottom) -->
            <ffimageloading:CachedImage 
                Aspect="AspectFit"
                HorizontalOptions="Fill"
                VerticalOptions="End"
                Source="ic_jesus_xx.png"/>
        </VerticalStackLayout>
    </ScrollView>
</Frame>

I integrated the HTML content to webview like below: (Added custom font and added a different color for header contents)

string description = Details.description;
string htmlContent = $@"
        <html>
        <head>
            <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'/>
            <style>
                @font-face {{
                    font-family: 'PoppinsLight';
                    src: url('file:///android_asset/fonts/Poppins-Light.ttf');
                }}
                html, body {{
                    font-family: 'PoppinsLight', Arial, sans-serif;
                    margin: 0;
                    padding: 5;
                    line-height: 1.6;
                    overflow-x: hidden;
                    height: 100%;
                }}
                h4 {{
                    color: #679E18;
                }}
            </style>
        </head>
        <body>
            {description}
        </body>
        </html>";
var htmlSource = new HtmlWebViewSource
{
    Html = htmlContent
};
mywebview.Source = htmlSource;

Everything is working fine when load data initially. My Problem is when refreshing the content via API either blank space is showing in webview bottom or the entire webview content is not showing on the UI. The initially loaded height is not changing when loading the new content.

I have added Navigated event for webview, but it only fires when loading the data on initial time.

private async void OnWebViewNavigated(object sender, WebNavigatedEventArgs e)
{
    await Task.Delay(500); // Allow time for content to load

    string js = "document.body.scrollHeight";
    var height = await mywebview.EvaluateJavaScriptAsync(js);

    if (double.TryParse(height, out double newHeight) && newHeight > 0)
    {
        mywebview.HeightRequest = newHeight;  // Set WebView height dynamically
    }
}

Please check this video for a clear understanding.

I tried creating a new function with the OnWebViewNavigated logic like below:

//setting html data to webview
mywebview.Source = htmlSource;
await Task.Delay(500);
await AdjustWebViewHeight();
                                    
private async Task AdjustWebViewHeight()
{
    string js = "document.body.scrollHeight";
    var height = await mywebview.EvaluateJavaScriptAsync(js);

    if (double.TryParse(height, out double newHeight) && newHeight > 0)
   {
        mywebview.HeightRequest = newHeight;
   }
}

But at that time the UI completely breaks. Showing blank space on top and font styles are changing with that approach. In this video I have added what kind of issues with the new logic I tried.

Please suggest a solution to update the webview height based on the content without affecting the font family and blank spaces.


Solution

  • I share a workaround to adjust height when changing the WebView Source using Maui Handler.

    In code behind, first we customize a ModifyWebView Handler in constructor.

    We create a CustomWebViewClient class and set it to platformWebView.

        public DailyReadingPage()
        {
            InitializeComponent();
    
            ModifyWebView();
            ...
            CallDailyReading(htmldata);
        }
    
        void ModifyWebView()
        {
                Microsoft.Maui.Handlers.WebViewHandler.Mapper.AppendToMapping("MyCustomization", (handler, view) =>
            {
    #if ANDROID
                if (handler.PlatformView is global::Android.Webkit.WebView platformWebView)
                {
                     platformWebView.SetWebViewClient(new CustomWebViewClient(this.mywebview));
                }
    #endif
            });
        }
    

    Here is the code for CustomWebViewClient class, you can place it just in code behind,

    #if ANDROID
        public class CustomWebViewClient : WebViewClient
        {
    
            Microsoft.Maui.Controls.WebView mywebview;
            public CustomWebViewClient(Microsoft.Maui.Controls.WebView webView)
            {
                
                mywebview = webView;
                //mywebview.HeightRequest = 0;
            }
    
            public async override void OnPageFinished(Android.Webkit.WebView view, string url)
            {
                mywebview.HeightRequest = 0;
                await Task.Delay(500); // Allow time for content to load
    
                string js = "document.body.scrollHeight";
                var height = await mywebview.EvaluateJavaScriptAsync(js);
    
                if (double.TryParse(height, out double newHeight) && newHeight > 0)
                {
                    mywebview.HeightRequest = newHeight;  // Set WebView height dynamically
                }
    
                base.OnPageFinished(view, url);
    
    
            }
    
        }
    #endif
    

    Just set the height: 100% is okay.

    It will adjust the size then.


    By the way, though it seems not necessary, you could also try adding the PropertyChanged event for the WebView and implement it. The code is shown below for your reference, hope it helps!

     <WebView  PropertyChanged="mywebview_PropertyChanged"
         x:Name="mywebview"
    

    and implement it in code behind,

        private void mywebview_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
    #if ANDROID
            if (e.PropertyName == "Source")
            {
                if (this.mywebview.Handler != null)
                    (this.mywebview.Handler.PlatformView as Android.Webkit.WebView).SetWebViewClient(new CustomWebViewClient(this.mywebview));
            }
    #endif
        }