TL;DR: With .NET MAUI Tab View, you can create a sleek, responsive dashboard where switching between cities feels instant, real-time data from Open‑Meteo loads smoothly in the background, and multi-day forecasts are presented in a clean, visually intuitive layout that’s easy to explore and rely on.
A weather app should do more than just show numbers; it should feel smooth and effortless to use. Imagine tapping once to switch cities, seeing updates instantly, and checking a clean, easy-to-follow forecast for the week ahead.
That’s exactly what you can build with Syncfusion® .NET MAUI Tab View.
In this guide, we’ll create a responsive weather dashboard that fetches live data from a remote API and presents it in a clean, modern way. By combining Tab View with smart asynchronous loading, we’ll build an app that feels fast, polished, and easy to use.
What you will learn
Here, we’ll learn to:
- Implement the .NET MAUI Tab View for effortless navigation through cities.
- Fetch and parse real-time weather data from a reliable API.
- Display current conditions and multi-day forecasts with a clean design.
- Keep the UI smooth and responsive, even when switching tabs.
Designing a weather dashboard with .NET MAUI Tab View
Let’s follow these steps to build a weather dashboard with the .NET MAUI Tab View:
Note: Before proceeding, refer to our .NET MAUI Tab View getting started documentation.
Step 1: Adding city tabs
Start by making the dashboard interactive with SfTabView. Each tab represents a city, and the header automatically shows the city name. This lets users switch between locations with a single tap, making navigation simple and intuitive.
<tabview:SfTabView x:Name="LocationTabView" ItemsSource="{Binding Cities}" IndicatorBackground="White">
<!-- Header: city title -->
<tabview:SfTabView.HeaderItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}" TextColor="White"/>
</DataTemplate>
</tabview:SfTabView.HeaderItemTemplate>
</tabview:SfTabView>Step 2: Customizing background views to improve the look and feel
Give your dashboard a modern touch by adding a radial gradient background to the page. This simple design choice instantly elevates the look, making the interface sleek, vibrant, and visually appealing.
<ContentPage.Background>
<RadialGradientBrush Center="0.5,0.35" Radius="0.9">
<GradientStop Color="#3A91F7" Offset="0.0" />
<GradientStop Color="#0D2C4D" Offset="0.55" />
<GradientStop Color="#071421" Offset="1.0" />
</RadialGradientBrush>
</ContentPage.Background>Refer to the following images.

Step 3: Define data models for weather API response
To keep things structured, create data models that mirror the API response. This makes parsing JSON simple and keeps your app maintainable.
public class WeatherApiResponse
{
[JsonPropertyName("hourly")]
public HourlyData? Hourly { get; set; }
[JsonPropertyName("daily")]
public DailyData? Daily { get; set; }
}
public class HourlyData
{
[JsonPropertyName("temperature_2m")]
public List<double>? TemperatureData { get; set; }
[JsonPropertyName("weathercode")]
public List<int>? Weather { get; set; }
}
public class DailyData
{
[JsonPropertyName("time")]
public List<string>? Time { get; set; }
[JsonPropertyName("temperature_2m_max")]
public List<double>? TemperatureMax { get; set; }
[JsonPropertyName("temperature_2m_min")]
public List<double>? TemperatureMin { get; set; }
[JsonPropertyName("weathercode")]
public List<int>? WeatherData { get; set; }
}Now, let’s turn raw weather data into a friendly forecast model. This simple model wraps each day’s details into a neat package, ready to display in your app with clarity and style.
public class DailyForecastItem
{
public string DayText { get; set; } = ""; // e.g., "Tue • Jan 30"
public string HighText { get; set; } = ""; // e.g., "High : 25°"
public string LowText { get; set; } = ""; // e.g., "Low : 12°"
public string Icon { get; set; } = "cloudy.png"; // Icon
}Step 4: Initialize the dashboard and load weather data instantly
When the app launches, it binds the dashboard to the ViewModel. Then, it automatically selects the first city and starts loading its weather data. Meanwhile, other cities are prepared in the background, so that switching tabs feels instant and smooth, keeping the experience fast and responsive.
private readonly WeatherMainViewModel viewmodel = new();
public MainPage()
{
InitializeComponent();
BindingContext = viewmodel;
LocationTabView.SelectionChanged += (_, e) =>
{
int index = (int)e.NewIndex;
if (index >= 0 && index < viewmodel.Cities.Count)
{
_ = viewmodel.Cities[index].EnsureLoadedAsync();
}
};
}
protected override async void OnAppearing()
{
base.OnAppearing();
if (viewmodel.Cities.Count > 0)
{
LocationTabView.SelectedIndex = 0;
await viewmodel.WarmUpAsync();
}
}Step 5: Display the key weather details clearly
Below the Tab View, let’s add a scrollable layout to display:
- Current date,
- Weather icon,
- Condition and temperature, and
- Horizontal scroll view for multi-day
This layout keeps the dashboard clean, organized, and easy to navigate, while giving users all the information they need at a glance.
<tabview:SfTabView.ContentItemTemplate>
<DataTemplate>
<ScrollView>
<VerticalStackLayout>
<!-- current date -->
<Label
Text="{Binding DateText}"
TextColor="White"
HorizontalOptions="Center" />
<!-- current weather icon -->
<Image
Source="{Binding Icon}"
HorizontalOptions="Center" />
<!-- current weather condition -->
<Label
Text="{Binding ConditionText}"
HorizontalOptions="Center"
TextColor="White" />
<!-- current temperature -->
<Label
Text="{Binding TempText}"
TextColor="White"
HorizontalOptions="Center" />
<!-- Daily Forecast -->
<ScrollView Orientation="Horizontal">
<CollectionView
ItemsSource="{Binding NextDays}"
ItemsLayout="HorizontalList"
HorizontalOptions="Center"
SelectionMode="None">
<CollectionView.ItemTemplate>
<!-- Scrollable view for daily weather forecast -->
</CollectionView.ItemTemplate>
</CollectionView>
</ScrollView>
</VerticalStackLayout>
</ScrollView>
</DataTemplate>
</tabview:SfTabView.ContentItemTemplate>Step 6: Set up city coordinates and prefetch weather data
The ViewModel takes care of retrieving city coordinates, loading the first city immediately, and prefetching the others in the background. Outdated requests are canceled automatically, while intuitive weather icons keep updates clear and reliable. This ensures the dashboard stays fast, error‑free, and seamless every time you switch tabs.
Note: For complete implementation details, refer to the GitHub demo.
private readonly Dictionary<string, (double Lat, double Lon)> CityCoordinates = new()
{
{ "Phoenix", (33.45, -112.07) },
{ "Seattle", (47.61, -122.33) },
{ "San Francisco", (37.77, -122.42) },
{ "Miami", (25.76, -80.19) },
{ "Denver", (39.74, -104.99) },
{ "Chicago", (41.88, -87.63) },
{ "New York", (40.71, -74.01) },
};
public WeatherMainViewModel()
{
foreach (var coordinates in CityCoordinates)
{
Cities.Add(new CityWeatherViewModel(
coordinates.Key,
coordinates.Value.Lat,
coordinates.Value.Lon
));
}
}
// Load the first city, then prefetch the rest
public async Task WarmUpAsync()
{
if (Cities.Count == 0)
return;
await Cities[0].EnsureLoadedAsync();
_ = Task.WhenAll(
Cities
.Skip(1)
.Select(c => c.EnsureLoadedAsync())
);
}Step 7: Mapping weather codes into meaningful insights
This step involves converting raw numeric weather codes into user-friendly visuals. This mapping ensures each Open-Meteo code is translated into an intuitive label (e.g., “Clear,” “Rain”) with a corresponding icon, making forecasts easy to understand at a glance.
public static (string Icon, string Label) Map(int weatherValue) =>
weatherValue switch
{
0 => ("sunny.png", "Clear"),
1 or 2 => ("partly_cloudy.png", "Partly cloudy"),
3 => ("cloudy.png", "Cloudy"),
// weather codes to icons and labels
_ => ("unknown.png", "Unknown")
};Step 8: Building an interactive weather forecast
Behind the scenes, the app keeps your forecast up to date. It cancels outdated requests, grabs the latest data from Open-Meteo, and instantly updates today’s temperature and conditions. Then, it builds a smooth weekly view, so you can scroll and plan with ease.
public Task EnsureLoadedAsync()
{
if (isLoaded)
return Task.CompletedTask;
return loadTask ??= LoadCoreAsync();
}
private async Task LoadCoreAsync()
{
// Cancel any in-flight request and create a fresh token
try
{
string apiUrl =
"https://api.open-meteo.com/v1/forecast" +
$"?latitude={Latitude.ToString(CultureInfo.InvariantCulture)}" +
$"&longitude={Longitude.ToString(CultureInfo.InvariantCulture)}" +
"&hourly=temperature_2m,weathercode" + // Request hourly data
"&daily=temperature_2m_max,temperature_2m_min,weathercode" +
"&timezone=auto";
// Deserialize the response
// Compute today's temperature and weather code
// Map to label and icon
await MainThread.InvokeOnMainThreadAsync(() =>
{
// Update today's date, temperature, condition, and icon in the UI
// Prepare and populate a scrollable weekly view
// (dates, highs, lows, icons)
});
isLoaded = true;
}
catch (OperationCanceledException)
{
// Ignore cancellation
}
finally
{
// Reset load task if not completed
if (!isLoaded)
loadTask = null;
}
}Step 9: Finding the right forecast day
Accuracy matters when showing future weather. Let’s implement the logic that scans a list of date strings, safely parses each one, and returns the index of the first date that is later than today. If no future date is found, it falls back to a default index. In this method, the invalid dates are skipped to avoid causing errors.
private static int GetFirstFutureDayIndex(System.Collections.Generic.List<string> dates)
{
if (dates == null || dates.Count == 0)
return 0;
var today = DateTime.Now.Date;
for (int i = 0; i < dates.Count; i++)
{
if (DateTime.TryParse(dates[i], out var d) && d.Date > today)
{
return i;
}
}
return dates.Count > 1 ? 1 : 0;
}After executing the above code example, our output will look like the following image

GitHub references
Also, find a complete code example to build a weather dashboard with .NET MAUI Tab View on GitHub repository.
Frequently Asked Questions
How do I bind the cities collection to SfTabView and template headers/content per city?
You can bind the Cities collection to the ItemsSource property of SfTabView. Then, define the HeaderItemTemplate to display each city’s header, such as the city name or an icon, and ContentItemTemplate to define the UI shown for each city’s content. You can refer to the MainPage.xaml file for a complete implementation example.
How do I load or refresh weather data when the selected tab changes (lazy load, prefetch, cancellation)?
To refresh weather data when the selected tab changes, bind SelectedIndex using SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}“. Then, handle the SelectionChanged event to call each city’s EnsureLoadedAsync() method. You can also prefetch adjacent cities by running background tasks using Task.WhenAll(...). For robustness, make sure to cancel outdated or overlapping fetch requests with a per-city CancellationTokenSource. Refer to the WeatherMainViewModel.cs file for the complete implementation.
Do I need an API key for the weather provider, and can I swap providers easily?
Open-Meteo requires no API key. To swap providers, change the request URL, and adjust models. Keep the API logic in the ViewModel or service layer so views remain unchanged.
How should I model and parse the API response and map weather codes to readable labels/icons?
Mirror JSON with WeatherApiResponse to HourlyData and DailyData, parse via System.Text.Json, and map weather codes to labels or icons with a static dictionary.
How do I handle units and time zones (°C/°F, mph/kph, local dates and day names)?
You can control units directly through Open Meteo query parameters by setting temperature_unit to celsius or fahrenheit and windspeed_unit to kmh or mph, and by using timezone=auto to automatically return timestamps in the device or user’s local zone. In your app, format all numbers and dates using CultureInfo.CurrentCulture, and convert timestamps using DateTimeOffset so that day names such as Mon, Tue, or Thu and local dates display correctly for the user’s locale.
How can tab headers be customized across platforms (state-based styles, icons, spacing, and alignment)?
Use HeaderItemTemplate along with styles to control text appearance, icons, and spacing. Apply state-based visual changes such as selected and unselected states, and adjust header padding and margins to align content consistently across platforms.
How to customize the tab indicator in a .NET MAUI Tab View?
You can customize the tab indicator in .NET MAUI Tab View using built-in properties by changing its placement such as Top, Bottom, or Fill using IndicatorPlacement, adjusting its color or gradient via IndicatorBackground, controlling its width with IndicatorWidthMode using Fit or Stretch, and modifying its corner radius and thickness using IndicatorCornerRadius and IndicatorStrokeThickness.
How do I ensure responsive layouts on Android, iOS, Windows, and macOS?
Use adaptive layouts such as Grid, FlexLayout, or CollectionView with virtualization, avoid fixed sizes, use scalable or vector assets, test window resizing and orientation, and verify touch target sizes and font scaling across devices.

Supercharge your cross-platform apps with Syncfusion's robust .NET MAUI controls.
Build your own weather dashboard app
Thanks for reading! With Syncfusion .NET MAUI Tab View, building a responsive weather dashboard becomes effortless. From initializing the interface and managing tab-based updates to fetching real-time forecasts and transforming raw data into a stunning, scrollable design, you now have everything you need to create an app that feels modern and alive.
This approach not only delivers accurate weather insights but also elevates the overall experience with smooth navigation and visually rich components.
Give it a try, download Essential Studio for .NET MAUI, and see how quickly you can turn this into a real app.
If you have any questions, you can also contact us through our support forum, support portal, or feedback portal. We are always happy to help you!



