Plugin.MAUI.GoogleChartsView 2.0.1

dotnet add package Plugin.MAUI.GoogleChartsView --version 2.0.1
                    
NuGet\Install-Package Plugin.MAUI.GoogleChartsView -Version 2.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Plugin.MAUI.GoogleChartsView" Version="2.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Plugin.MAUI.GoogleChartsView" Version="2.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Plugin.MAUI.GoogleChartsView" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Plugin.MAUI.GoogleChartsView --version 2.0.1
                    
#r "nuget: Plugin.MAUI.GoogleChartsView, 2.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Plugin.MAUI.GoogleChartsView@2.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Plugin.MAUI.GoogleChartsView&version=2.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Plugin.MAUI.GoogleChartsView&version=2.0.1
                    
Install as a Cake Tool

Plugin.MAUI.GoogleChartsView

MAUI.GoogleChartsView is a user-friendly charting control designed for .NET MAUI applications. By harnessing the capabilities of Google Charts, it offers customizable data visualizations to suit a wide range of data types. This library is an ideal choice for developers who aim to create visually engaging and informative charts in their .NET MAUI applications while avoiding the complexities associated with building charting solutions from the ground up.

Note: An internet connection is required to render charts. The Google Charts library is loaded from Google's CDN (per Google's terms it cannot be bundled for offline use); your chart data itself never leaves the device. Loader failures (e.g. offline devices) are reported through the ChartError event, so you can show a fallback UI or retry.

What's New in v2.0.0

  • Events are no longer dropped - callbacks are queued and acknowledged one at a time, so a tap that raises both click and select delivers both
  • Incremental updates - changing Data or Options redraws the live chart instead of reloading the page, so the loader isn't re-fetched, animations don't replay and scroll/zoom position survives. New UpdateChartAsync()
  • Zoom actually works - ZoomEnabled now drives the viewport meta and the native WebView (Android WebSettings, WKWebView pinch recogniser, WebView2 pinch/zoom settings, including Ctrl+wheel on Windows)
  • Typed options - LegendPosition, LegendAlignment, LineCurveType, ChartOrientation, StackMode, SeriesType, TrendlineType; string overloads still work
  • Richer options - Series(...), VAxes(...) for dual axes, Trendline(...), Annotations(...), MapsApiKey(...), and HAxis/VAxis with Format/LogScale
  • Fractional axis bounds - HAxis/VAxis take double? (breaking: was int?)
  • Richer selection data - ChartSelectionEventArgs.Value and .ColumnLabel
  • Cancellable export - GetChartImageAsync accepts a timeout and a CancellationToken
  • Clear data errors - ChartDataBuilder.Build() rejects rows whose cell count doesn't match the column count, naming the row
  • Leak fix - the auto-refresh timer stops when the view is unloaded and resumes when it returns
  • Gallery fixes - Android 21-28 requests storage permission, Apple platforms request add-only photo access, Windows falls back to Pictures for unpackaged apps
  • RequestChartImage is obsolete; use GetChartImageAsync

What's New in v1.1.0

  • Native date support in builders - DateTime, DateOnly, TimeOnly values are automatically converted to the Google Charts date format
  • Reliable PNG export - Export uses direct JavaScript evaluation (no more URL-length limits), with correct support detection (corechart types + GeoChart)
  • Resize handling - Charts redraw automatically on rotation and window resize
  • Offline detection - Loader failures raise ChartError instead of failing silently
  • Localization - New Language property for locale-aware chart formatting
  • ChartTitle now works - Applied to the chart when options don't already define a title
  • Hardened rendering - Chart type validation, batched re-renders, user options are never silently overwritten

Features

  • Supports all Google Charts types including Line, Bar, Pie, Area, Sankey, Timeline, Gauge, and more
  • Type-safe ChartType enum with automatic package loading
  • Fluent API builders for easy data and options construction
  • Customizable chart options to fit your design needs
  • Seamless integration with .NET MAUI
  • Interactive features like tooltips and animations
  • Export charts as images (PNG format) on all platforms
  • Save charts directly to device gallery (Android, iOS, MacCatalyst, Windows)
  • Responsive chart rendering for different screen sizes
  • Event-driven architecture for chart interactions

Supported Platforms

  • Android 5.0+ (API 21)
  • iOS 15.0+
  • MacCatalyst 15.0+
  • Windows 10.0.17763+

Getting Started

Installation

Install the Plugin.MAUI.GoogleChartsView NuGet package in your .NET MAUI project:

dotnet add package Plugin.MAUI.GoogleChartsView

Usage Examples

1. Basic Chart Setup

Add the GoogleChartsView control to your page:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:charts="clr-namespace:MAUI.GoogleChartsView;assembly=MAUI.GoogleChartsView"
             x:Class="MyApp.MainPage">

    <StackLayout>
        <charts:GoogleChartsView x:Name="MyChart"
                                 WidthRequest="400"
                                 HeightRequest="300"
                                 ChartReady="OnChartReady"
                                 ChartSelected="OnChartSelected"
                                 ChartError="OnChartError" />

        <Button Text="Export to Gallery" Clicked="OnExportClicked" />
    </StackLayout>

</ContentPage>

2. Configure Chart Data and Options (Using Builders)

using MAUI.GoogleChartsView;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        SetupChart();
    }

    private void SetupChart()
    {
        // Use ChartDataBuilder for easy data construction
        var data = ChartDataBuilder.Create()
            .AddStringColumn("Year")
            .AddNumberColumn("Sales")
            .AddNumberColumn("Expenses")
            .AddRow("2021", 1000, 400)
            .AddRow("2022", 1170, 460)
            .AddRow("2023", 660, 1120)
            .AddRow("2024", 1030, 540)
            .Build();

        // Use ChartOptionsBuilder for easy options configuration
        var options = ChartOptionsBuilder.Create()
            .Title("Company Performance")
            .SmoothLines()
            .Legend("bottom")
            .BackgroundColor("#f5f5f5")
            .Colors("#2196F3", "#FF5722")
            .HAxis(title: "Year")
            .VAxis(title: "Amount")
            .Build();

        MyChart.Data = data;
        MyChart.SetChartType(ChartType.LineChart);  // Type-safe!
        MyChart.Options = options;
        MyChart.EnableAnimation = true;
        MyChart.AnimationDuration = 1000;
    }

    private void OnChartReady(object sender, EventArgs e)
    {
        Console.WriteLine("Chart is ready!");
    }

    private void OnChartSelected(object sender, ChartSelectionEventArgs e)
    {
        Console.WriteLine($"Selected: Row {e.Row}, Column {e.Column}");
    }

    private void OnChartError(object sender, ChartErrorEventArgs e)
    {
        Console.WriteLine($"Error: {e.ErrorType} - {e.Message}");
    }

    private async void OnExportClicked(object sender, EventArgs e)
    {
        try
        {
            // Save to device gallery
            var path = await ChartImageSaver.SaveToGalleryAsync(
                await MyChart.GetChartImageAsync(),
                "MyChart.png");

            await DisplayAlert("Success", $"Chart saved to: {path}", "OK");
        }
        catch (Exception ex)
        {
            await DisplayAlert("Error", ex.Message, "OK");
        }
    }
}

3. Export Charts as Images

PNG export via getImageURI() is supported for corechart types (Line, Bar, Pie, Area, Column, Scatter, Combo, SteppedArea, Bubble, Histogram, Candlestick) and GeoChart only (Google docs). Wait for the ChartReady event before exporting.

// Method 1: Get image as data URI
var imageDataUri = await myChart.GetChartImageAsync();

// Method 2: Save to specific file path
await myChart.SaveChartImageAsync("/path/to/chart.png");

// Method 3: Save to device gallery (platform-specific)
var savedPath = await ChartImageSaver.SaveToGalleryAsync(imageDataUri, "chart.png");

// Method 4: Get raw bytes for custom handling
byte[] imageBytes = ChartImageSaver.GetImageBytes(imageDataUri);

// Method 5: Get as stream
Stream imageStream = ChartImageSaver.GetImageStream(imageDataUri);

Properties

Property Type Default Description
ChartTypeName string null Google Charts type as string (LineChart, PieChart, BarChart, etc.). Prefer SetChartType(ChartType) for type safety
CurrentChartType ChartType? null Read-only: the enum value set via SetChartType
Data object null Chart data in Google Charts DataTable format
Options object null Chart configuration options
EnableAnimation bool true Apply a startup animation (unless Options already define one)
AnimationDuration int 1000 Animation duration in milliseconds
ChartTitle string null Chart title (applied unless Options already define a title)
RefreshIntervalMinutes int 10 Auto-refresh interval (must be greater than 0)
ZoomEnabled bool true Enable/disable zoom functionality
AdditionalPackages string[] null Extra Google Charts packages to load when using ChartTypeName
Language string null Locale for chart formatting (e.g. "de", "fr", "ja")
IsChartReady bool false Read-only: indicates if chart has finished rendering

Events

Event EventArgs Description
ChartReady EventArgs Fired when chart finishes rendering
ChartSelected ChartSelectionEventArgs Fired when user selects a chart element
ChartClicked ChartClickEventArgs Fired when user clicks on chart
ChartError ChartErrorEventArgs Fired when an error occurs
ChartImageReady ChartImageEventArgs Fired when image export completes

Supported Chart Types

Use the ChartType enum for type-safe chart selection with automatic package loading:

// Type-safe chart selection
myChart.SetChartType(ChartType.LineChart);
myChart.SetChartType(ChartType.Sankey);    // Auto-loads sankey package
myChart.SetChartType(ChartType.Timeline);  // Auto-loads timeline package

CoreChart Package (corechart)

Standard chart types - all loaded with a single package:

Chart Type Description
LineChart Line graphs with optional smooth curves
AreaChart Filled area under lines
ColumnChart Vertical bar charts
BarChart Horizontal bar charts
PieChart Pie/donut charts (use PieHole for donut)
ScatterChart X-Y scatter plots
ComboChart Mix of bars and lines
SteppedAreaChart Stepped/staircase area charts
BubbleChart Scatter with bubble sizes
Histogram Distribution frequency charts
CandlestickChart Financial OHLC charts

Individual Package Chart Types

Each loads its own package automatically:

Chart Type Package Description
Gauge gauge Speedometer-style gauges
Sankey sankey Flow diagrams showing relationships
Table table Interactive sortable data tables
TreeMap treemap Hierarchical data as nested rectangles
Calendar calendar Heat map calendar visualization
Gantt gantt Project timeline with dependencies
OrgChart orgchart Organizational hierarchy diagrams
GeoChart geochart Geographic data on maps
Timeline timeline Event timelines
WordTree wordtree Word relationship trees

Chart Options Reference

Option Description Supported Types
title Chart title displayed above All
curveType Line curve style ("function" for smooth) LineChart
legend Legend configuration All
backgroundColor Chart background color All
colors Array of series colors All
fontSize Default font size All
fontName Default font family All
lineWidth Line width in pixels LineChart, AreaChart
pointSize Point diameter in pixels LineChart, AreaChart
hAxis Horizontal axis configuration All
vAxis Vertical axis configuration All
chartArea Drawing area size/position All
tooltip Tooltip configuration All
animation Animation settings All
is3D Enable 3D rendering PieChart, BarChart
pieHole Donut chart hole size (0-1) PieChart
orientation Chart orientation BarChart
explorer Pan/zoom configuration LineChart, AreaChart
trendlines Trendline configuration ScatterChart, ColumnChart

Platform Permissions

Android

Add to AndroidManifest.xml for gallery saving:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="28" />

iOS / MacCatalyst

Add to Info.plist for photo library access:

<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app saves chart images to your photo library.</string>

Data Builders

Use the fluent API builders for easy chart construction:

ChartDataBuilder

// Simple usage
var data = ChartDataBuilder.Create()
    .AddStringColumn("Category")
    .AddNumberColumn("Value")
    .AddRow("A", 100)
    .AddRow("B", 200)
    .Build();

// Dates are converted automatically to the Google Charts format
var series = ChartDataBuilder.Create()
    .AddDateColumn("Date")
    .AddNumberColumn("Value")
    .AddRow(new DateTime(2024, 1, 1), 3)
    .AddRow(new DateTime(2024, 1, 2), 7)
    .Build();

// With formatted values
var row = new ChartRow()
    .AddCell(1000, "$1,000")
    .AddCell(2000, "$2,000");

// From collection
var salesData = ChartDataBuilder.Create()
    .AddStringColumn("Product")
    .AddNumberColumn("Sales")
    .AddRows(products, p => new object[] { p.Name, p.Sales })
    .Build();

ChartOptionsBuilder

var options = ChartOptionsBuilder.Create()
    .Title("Sales Report")
    .Subtitle("Q1 2024")
    .BackgroundColor("#ffffff")
    .Colors("#4285F4", "#EA4335", "#FBBC05", "#34A853")
    .FontSize(14)
    .FontName("Arial")
    .Legend("bottom", "center")
    .HAxis(title: "Month", minValue: 0, maxValue: 12)
    .VAxis(title: "Revenue ($)")
    .SmoothLines()           // For line charts
    .LineWidth(3)
    .PointSize(5)
    .PieHole(0.4)            // For donut charts
    .Is3D()                  // For 3D pie charts
    .Stacked()               // For stacked bar/column
    .ChartArea("80%", "70%", "10%", "10%")
    .Tooltip(showColorCode: true, trigger: "focus")
    .Explorer()              // Enable pan/zoom
    .Build();

Special Package Chart Examples

Some chart types require specific data formats. Here are examples for each:

Gauge Chart

var data = ChartDataBuilder.Create()
    .AddStringColumn("Label")
    .AddNumberColumn("Value")
    .AddRow("Memory", 80)
    .AddRow("CPU", 55)
    .AddRow("Network", 68)
    .Build();

var options = ChartOptionsBuilder.Create()
    .Set("width", 400)
    .Set("height", 300)
    .Set("redFrom", 90)
    .Set("redTo", 100)
    .Set("yellowFrom", 75)
    .Set("yellowTo", 90)
    .Set("minorTicks", 5)
    .Build();

MyChart.Data = data;
MyChart.SetChartType(ChartType.Gauge);
MyChart.Options = options;

Sankey Diagram

var data = ChartDataBuilder.Create()
    .AddStringColumn("From")
    .AddStringColumn("To")
    .AddNumberColumn("Weight")
    .AddRow("A", "X", 5)
    .AddRow("A", "Y", 7)
    .AddRow("A", "Z", 6)
    .AddRow("B", "X", 2)
    .AddRow("B", "Y", 9)
    .AddRow("B", "Z", 4)
    .Build();

var options = ChartOptionsBuilder.Create()
    .Set("width", 600)
    .Set("height", 400)
    .Build();

MyChart.Data = data;
MyChart.SetChartType(ChartType.Sankey);
MyChart.Options = options;

Organization Chart

var data = ChartDataBuilder.Create()
    .AddStringColumn("Name")
    .AddStringColumn("Manager")
    .AddStringColumn("Tooltip")
    .AddRow("Mike", "", "The President")
    .AddRow("Jim", "Mike", "VP")
    .AddRow("Alice", "Mike", "VP")
    .AddRow("Bob", "Jim", "Manager")
    .AddRow("Carol", "Alice", "Manager")
    .Build();

var options = ChartOptionsBuilder.Create()
    .Set("allowHtml", true)
    .Set("size", "large")
    .Build();

MyChart.Data = data;
MyChart.SetChartType(ChartType.OrgChart);
MyChart.Options = options;

TreeMap

var data = ChartDataBuilder.Create()
    .AddStringColumn("Location")
    .AddStringColumn("Parent")
    .AddNumberColumn("Size")
    .AddNumberColumn("Color")
    .AddRow("Global", null, 0, 0)
    .AddRow("Americas", "Global", 0, 0)
    .AddRow("Europe", "Global", 0, 0)
    .AddRow("USA", "Americas", 52, 31)
    .AddRow("Mexico", "Americas", 24, 12)
    .AddRow("France", "Europe", 42, -11)
    .AddRow("Germany", "Europe", 31, -2)
    .Build();

var options = ChartOptionsBuilder.Create()
    .Set("minColor", "#f00")
    .Set("midColor", "#ddd")
    .Set("maxColor", "#0d0")
    .Set("headerHeight", 15)
    .Set("showScale", true)
    .Build();

MyChart.Data = data;
MyChart.SetChartType(ChartType.TreeMap);
MyChart.Options = options;

Table Chart

var data = ChartDataBuilder.Create()
    .AddStringColumn("Name")
    .AddNumberColumn("Salary")
    .AddBooleanColumn("Full Time")
    .AddRow("Mike", 10000, true)
    .AddRow("Jim", 8000, false)
    .AddRow("Alice", 12500, true)
    .Build();

var options = ChartOptionsBuilder.Create()
    .Set("showRowNumber", true)
    .Set("page", "enable")
    .Set("pageSize", 10)
    .Build();

MyChart.Data = data;
MyChart.SetChartType(ChartType.Table);
MyChart.Options = options;

Calendar Chart (Using Anonymous Object)

Calendar charts require JavaScript Date objects. Use anonymous objects for date handling:

var data = new
{
    cols = new object[]
    {
        new { id = "", label = "Date", type = "date" },
        new { id = "", label = "Value", type = "number" }
    },
    rows = new object[]
    {
        new { c = new object[] { new { v = "Date(2024, 0, 1)" }, new { v = 3 } } },
        new { c = new object[] { new { v = "Date(2024, 0, 2)" }, new { v = 7 } } },
        new { c = new object[] { new { v = "Date(2024, 0, 15)" }, new { v = 10 } } },
        new { c = new object[] { new { v = "Date(2024, 1, 1)" }, new { v = 5 } } },
        new { c = new object[] { new { v = "Date(2024, 1, 14)" }, new { v = 8 } } }
    }
};

var options = ChartOptionsBuilder.Create()
    .Title("Daily Activity")
    .Set("height", 350)
    .Set("calendar", new { cellSize = 15 })
    .Set("colorAxis", new { minValue = 0, colors = new[] { "#ffffff", "#4285F4" } })
    .Build();

MyChart.Data = data;
MyChart.SetChartType(ChartType.Calendar);
MyChart.Options = options;

Gantt Chart (Using Anonymous Object)

Gantt charts require a specific 7-column format with dates:

var data = new
{
    cols = new object[]
    {
        new { id = "Task ID", label = "Task ID", type = "string" },
        new { id = "Task Name", label = "Task Name", type = "string" },
        new { id = "Start", label = "Start", type = "date" },
        new { id = "End", label = "End", type = "date" },
        new { id = "Duration", label = "Duration", type = "number" },
        new { id = "Percent Complete", label = "Percent Complete", type = "number" },
        new { id = "Dependencies", label = "Dependencies", type = "string" }
    },
    rows = new object[]
    {
        new { c = new object[] {
            new { v = "Research" },
            new { v = "Find sources" },
            new { v = "Date(2024, 0, 1)" },
            new { v = "Date(2024, 0, 5)" },
            new { v = (object?)null },
            new { v = 100 },
            new { v = (object?)null }
        }},
        new { c = new object[] {
            new { v = "Write" },
            new { v = "Write paper" },
            new { v = (object?)null },
            new { v = "Date(2024, 0, 9)" },
            new { v = 3 * 24 * 60 * 60 * 1000 }, // 3 days in milliseconds
            new { v = 25 },
            new { v = "Research" }
        }},
        new { c = new object[] {
            new { v = "Complete" },
            new { v = "Hand in paper" },
            new { v = (object?)null },
            new { v = "Date(2024, 0, 10)" },
            new { v = 1 * 24 * 60 * 60 * 1000 },
            new { v = 0 },
            new { v = "Write" }
        }}
    }
};

var options = ChartOptionsBuilder.Create()
    .Set("height", 300)
    .Set("gantt", new
    {
        trackHeight = 30,
        criticalPathEnabled = true,
        criticalPathStyle = new { stroke = "#e64a19", strokeWidth = 2 }
    })
    .Build();

MyChart.Data = data;
MyChart.SetChartType(ChartType.Gantt);
MyChart.Options = options;

Auto-Refresh

// Start auto-refreshing every 5 minutes
myChart.RefreshIntervalMinutes = 5;
myChart.StartAutoRefresh();

// Stop auto-refresh
myChart.StopAutoRefresh();

Resource Cleanup

The control implements IDisposable. For proper cleanup:

// In your page's OnDisappearing or cleanup code
myChart.Dispose();

Contributing

Contributions are always welcome!

License

Plugin.MAUI.GoogleChartsView is licensed under the MIT license.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-android35.0 is compatible.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-ios18.0 is compatible.  net9.0-maccatalyst was computed.  net9.0-maccatalyst18.0 is compatible.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net9.0-windows10.0.19041 is compatible.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.1 106 7/30/2026
2.0.0 104 7/29/2026
1.1.0 101 7/29/2026
1.0.0 155 2/4/2026
0.0.1.2 453 5/8/2024
0.0.1.1 229 5/8/2024
0.0.0.6 251 5/4/2024
0.0.0.5 255 4/25/2024
0.0.0.4 452 4/11/2023
0.0.0.3 308 4/10/2023

v2.0.0:
- BREAKING: HAxis/VAxis bounds are now double? instead of int?, so fractional axis limits are expressible
- BREAKING: RequestChartImage is obsolete; use the awaitable GetChartImageAsync
- Fixed event loss: chart events are queued and acknowledged one at a time, so 'click' and 'select' raised in the same tick both arrive
- Fixed ZoomEnabled being CSS-only; the viewport meta now matches it and the platform WebView is configured directly (Android WebSettings, WKWebView pinch recogniser, WebView2 pinch/zoom settings)
- Fixed the auto-refresh timer outliving the view; it stops on Unloaded and resumes on Loaded
- Fixed empty column id/pattern being sent to Google Charts
- Fixed gallery saving: Android 21-28 now requests storage permission, iOS/MacCatalyst request add-only photo access, Windows falls back to Pictures when StorageLibrary is unavailable (unpackaged apps)
- Added incremental updates: changing Data or Options redraws in place instead of reloading the page, so the loader is not re-fetched and animations do not replay
- Added UpdateChartAsync
- Added ChartSelectionEventArgs.Value and .ColumnLabel
- Added CancellationToken and timeout overloads to GetChartImageAsync/SaveChartImageAsync
- Added typed options: LegendPosition, LegendAlignment, LineCurveType, ChartOrientation, StackMode, SeriesType, TrendlineType
- Added Series, VAxes (dual axis), Trendline, Annotations and MapsApiKey option builders
- Added row/column arity validation in ChartDataBuilder.Build() with an actionable message
- Added a unit test project covering builders, chart-type metadata and data-URI decoding; tests now gate publishing

v1.1.0:
- Fixed DateTime/DateOnly/TimeOnly serialization to Google Charts "Date(...)" format in ChartDataBuilder
- Fixed image export support detection (getImageURI only supports corechart types and GeoChart)
- Fixed event handler leaks in GetChartImageAsync; export now uses direct JavaScript evaluation (no URL-length limits)
- Fixed ChartTitle and EnableAnimation silently overwriting user-supplied options
- Added chart redraw on WebView resize/rotation
- Added Google Charts loader failure detection (offline scenarios raise ChartError)
- Added Language property for localized chart formatting
- Added chart type name validation (prevents script injection)
- Batched property changes into a single render
- Accept iOS 14+ "Limited" photo library permission when saving to gallery
- Enabled SourceLink, deterministic builds, symbol packages and XML documentation