Table of Contents

.NET MAUI

Package Installation

Install the following packages into your MAUI application:

  • MyApp.Maui: ReactiveUI.Maui
  • Shared core library: ReactiveUI, ReactiveUI.SourceGenerators
  • Test project: ReactiveUI.Testing
<PackageReference Include="ReactiveUI.Maui" Version="*" />
<PackageReference Include="ReactiveUI.SourceGenerators" Version="*" PrivateAssets="all" />

The modern way to initialize ReactiveUI in MAUI uses RxAppBuilder for dependency injection and platform setup:

1. Configure MauiProgram with RxAppBuilder

using ReactiveUI;
using ReactiveUI.Maui;
using Splat;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureReactiveUI();

        // Initialize ReactiveUI with RxAppBuilder
        var app = RxAppBuilder.CreateReactiveUIBuilder()
            .WithMaui()
            .WithViewsFromAssembly(typeof(App).Assembly)
            .WithRegistration(locator =>
            {
                // Register your services
                locator.RegisterLazySingleton<INavigationService>(() => new NavigationService());
                locator.RegisterLazySingleton<IDataService>(() => new DataService());
            })
            .BuildApp();

        // Optional: Register additional MAUI-specific services
        builder.Services.AddSingleton(app.MainThreadScheduler);
        builder.Services.AddSingleton(app.TaskpoolScheduler);

        return builder.Build();
    }
}

2. Create ViewModels with SourceGenerators

Use ReactiveUI.SourceGenerators for cleaner, compile-time generated reactive properties:

using ReactiveUI;
using ReactiveUI.SourceGenerators;

public partial class MainViewModel : ReactiveObject
{
    [Reactive]
    private string _name = string.Empty;

    [ReactiveCommand]
    private void SayHello() => Console.WriteLine($"Hello {Name}");
    
    [ReactiveCommand]
    private async Task LoadData()
    {
        // Async operations are supported
        await Task.Delay(1000);
    }
}

3. Create Views that Implement IViewFor

<rxui:ReactiveContentPage 
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:rxui="clr-namespace:ReactiveUI.Maui;assembly=ReactiveUI.Maui"
    xmlns:vm="clr-namespace:MyApp.ViewModels"
    x:Class="MyApp.Views.MainPage"
    x:TypeArguments="vm:MainViewModel">
    <VerticalStackLayout Padding="20" Spacing="10">
        <Label Text="Enter your name:" />
        <Entry x:Name="NameEntry" Placeholder="Your name" />
        <Button x:Name="HelloButton" Text="Say Hello" />
        <Button x:Name="LoadButton" Text="Load Data" />
    </VerticalStackLayout>
</rxui:ReactiveContentPage>

4. Wire up Bindings in Code-Behind

using ReactiveUI;
using ReactiveUI.Maui;

namespace MyApp.Views;

public partial class MainPage : ReactiveContentPage<MainViewModel>
{
    public MainPage()
    {
        InitializeComponent();
        
        // ViewModel can be resolved from DI container or created directly
        ViewModel = AppLocator.Current.GetService<MainViewModel>() ?? new MainViewModel();

        this.WhenActivated(disposables =>
        {
            // Two-way binding for Entry
            this.Bind(ViewModel, 
                vm => vm.Name, 
                v => v.NameEntry.Text)
                .DisposeWith(disposables);

            // Command binding for buttons
            this.BindCommand(ViewModel, 
                vm => vm.SayHelloCommand, 
                v => v.HelloButton)
                .DisposeWith(disposables);
                
            this.BindCommand(ViewModel, 
                vm => vm.LoadDataCommand, 
                v => v.LoadButton)
                .DisposeWith(disposables);
        });
    }
}

Alternative: Traditional Setup (Legacy)

If you prefer not to use RxAppBuilder, you can initialize ReactiveUI manually:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>();

        // Register views and view models manually
        AppLocator.CurrentMutable.Register(() => new MainPage(), typeof(IViewFor<MainViewModel>));
        
        // Optional: Register ILogger implementation
        AppLocator.CurrentMutable.RegisterConstant(new SplatLogger(), typeof(ILogger));

        return builder.Build();
    }
}

Routing in MAUI

For navigation, you can use ReactiveUI's routing system or MAUI's built-in Shell navigation. Here's an example using ReactiveUI routing:

public class AppViewModel : ReactiveObject, IScreen
{
    public RoutingState Router { get; }

    public AppViewModel()
    {
        Router = new RoutingState();
        
        // Navigate to the first page
        Router.Navigate.Execute(new MainViewModel()).Subscribe();
    }
}

Key Points

  • Use ReactiveContentPage base class to enable WhenActivated and reactive bindings
  • Use ReactiveUI.SourceGenerators for cleaner property and command declarations
  • Use RxAppBuilder for modern dependency injection and platform setup
  • Always call DisposeWith(disposables) inside WhenActivated to prevent memory leaks
  • Use IScreen and RoutingState for view model-based navigation

Additional Resources