MVVM Navigation with Xamarin.Forms Shell

One of the topics that I find a bit contentious amongst app developers is how navigation is handled, specifically when it comes to MVVM. Should navigation be done in the code behind of the page? or in the ViewModel? and should navigation be based on a path/URL or based on what ViewModel you want to go to. Regardless of where you stand on this discussion, one thing is very clear – in your application, you need to set a standard mechanism for doing this. Prism and MvvmCross are quite prescriptive when it comes to navigation, making them great starting points for your application; Xamarin.Forms Shell attempts to provide some constructs (eg routes) but fails to deliver an entire strategy for handling navigation between all the pages within your application.

Cross-platform Navigation

Back when I started doing Windows Phone development navigation between pages was simply done by calling Navigate on the Frame of the application. iOS and Android had their own way of doing navigation, which was fine back then because cross-platform wasn’t a thing and each platform was typically built by itself. However, with the introduction of Xamarin (back then monoTouch and monoDroid) it was possible to reuse our C# code and so more thought was applied to how we could reuse not just business logic but also our UI logic. It was also about this time that the MVVM pattern was really catching on and there was time being invested by the community into establishing frameworks for associating viewmodels with views/pages.

I’ve been a big advocate for MVVM frameworks because I see them providing the much-needed pattern for instantiating viewmodels, handling dependency injection, and a consistent pattern for navigation that can be invoked from the viewmodel. I believe that this allows us to build viewmodels that are testable and aren’t interconnected with the UI due to the separation imposed by data binding. If you look back at the origins of MvvmCross you can see that it was developed with this in mind and supported iOS, Android and Windows without any additional UI frameworks (such as Xamarin.Forms). To this day, if you want to develop using Xamarin.iOS and Xamarin.Droid and you want to reuse your business logic, MvvmCross has you covered.

ViewModel to ViewModel Navigation

Coming back to the discussion around navigation, one of the concepts added to MvvmCross was the idea of viewmodel to viewmodel navigation. This meant you could navigate between views/pages by simply calling Navigate and providing the viewmodel you wanted to go to. MvvmCross would determine what view/page this corresponded to and would invoke the platform-specific method for navigation.

The abstraction of view/page navigation was one of the features that Xamarin.Forms brought to the table. In addition to having a common model for defining page layouts (ie XAML), there was now a single model for navigating between pages. Does this negate the need for say MvvmCross? Not at all because whilst Xamarin.Forms deals with the platform navigation idiosyncrasies, it doesn’t provide the abstraction that allows for testing. Whether you’re using Prism or MvvmCross, the INavigationService, gives you the ability to mock out navigation for testing purposes.

ViewModel Dependency Injection

We’re starting to get somewhere now – our navigation pattern needs to be platform agnostic (which we get from Xamarin.Forms) and needs to be testable. The last point I’ll make on MVVM frameworks is that both provide sophisticated dependency injection support. In order for our viewmodels to populate themselves with data, they’ll typically rely on other classes (eg service or data manager classes). Again, in order to make things testable, these services implement interfaces, and our viewmodels only depend (often as part of the constructor) on the interfaces. This requires some form of dependency injection in order to construct each viewmodel.

In summary, our requirements for navigation are:

  • Navigation needs to be platform agnostic
  • Viewmodels need to be testable
  • Viewmodels will have dependencies

This post isn’t here to talk up any particular framework; in fact, quite the reverse. We’re going to go framework-free and see where this leads us. One of the motivations here is the lack of support offered by MVVM frameworks for Xamarin.Forms Shell, which is still hampered by the Xamarin.Forms team (see this open issue). Given the above requirements for navigation, let’s see what we can come up with.

MVVM Basics with Xamarin.Forms Shell

I’m going to back-track to when I first used MVVM – Coming from building apps for PocketPC/Windows Mobile I viewed reflection as a luxury that desktop developers could afford, so a lot of what I built used concrete types with minimal code to wire things up. We’re going to start there and see where it leads us.

Note: This is a thought experiment ONLY – for commercial applications I would currently still be advocating the use of Prism or MvvmCross (rather than Shell).

ViewModelLocator

I’m going to start with a brand new Xamarin.Forms Shell application created in Visual Studio 2019 (16.3 Preview 2) using the Shell solution template. After creating the project the first thing to do is to check it runs; followed by upgrading NuGet packages; and then checking it still runs – there’s nothing like wasting hours wondering why your code doesn’t work, only to find out that a NuGet update broke something.

If we take a look at the AboutPage.xaml that’s created by the template we can see that the BindingContext is set to a new instance of the AboutViewModel. This addresses the basics of MVVM, where you have a viewmodel that is data bound to the page, thus making the viewmodel testable. However, it doesn’t solve the issue of how dependencies can be injected into the viewmodel.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             x:Class="FormsNaviation.Views.AboutPage"
             xmlns:vm="clr-namespace:FormsNaviation.ViewModels"
             Title="{Binding Title}">
    <ContentPage.BindingContext>
        <vm:AboutViewModel />
    </ContentPage.BindingContext>

To give us more control over how the viewmodels are created, we’re going to add in a class that will be responsible for constructing the viewmodels. The ViewModelLocator class will not only be responsible for constructing viewmodels, it will also be responsible for supplying any dependencies the viewmodels may rely on. Our first pass on the ViewModelLocator will be super basic.

public class ViewModelLocator
{
    public AboutViewModel About => new AboutViewModel();
}

To create an instance of the ViewModelLocator that can be referenced throughout the XAML of our application, we can add the instance to the App.xaml file as an Application resource.

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:viewmodels="clr-namespace:FormsNaviation.ViewModels"
             x:Class="FormsNaviation.App">
    <Application.Resources>
        <viewmodels:ViewModelLocator x:Key="ViewModelLocator"/>
    </Application.Resources>
</Application>

And our AboutPage XAML is updated to reference the instance of the ViewModelLocator as a StaticResource.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
       x:Class="FormsNaviation.Views.AboutPage"
       xmlns:vm="clr-namespace:FormsNaviation.ViewModels"
       BindingContext="{Binding About, Source={StaticResource ViewModelLocator}}"
       x:DataType="vm:AboutViewModel" >

The AboutViewModel is returned using the About property exposed on the ViewModelLocator. In order to create viewmodels for other pages, you simply need to create a new property that returns the viewmodel (or it can return they are around the village.

At this stage, we haven’t really added much value over simply creating new instances of the viewmodel in the XAML on each page. However, as we get into dependencies, you’ll understand why it’s important to have at least some helper items that can assist with the development.

Adding Dependencies

As I mentioned, the use of a ViewModelLocator to create the instance of the viewmodel means that there’s a very clear point during the lifecycle to add and remove dependencies. The following locator code illustrates adding a dependency for the ILoginService, which in turn requires an implementation of the IAuthenticationService. I acknowledge that this is a very contrived example, especially since the verification check is just to compare username/password (clearly not a secure approach), but hopefully, it illustrates how dependencies can be provided and that they can be Lazy created at the point they’re first required.

public class ViewModelLocator
{
    private readonly Lazy<IAuthenticationService> authenticationService;
    private readonly Lazy<ILoginService> loginService;

    public ViewModelLocator()
    {
        authenticationService = new Lazy<IAuthenticationService>(() => new AuthenticationService());
        loginService = new Lazy<ILoginService>(() => new LoginService(authenticationService.Value));
    }

    public AboutViewModel About => new AboutViewModel(loginService.Value);

    public NewItemViewModel NewItem => new NewItemViewModel();
}

public interface ILoginService
{
    bool IsLoggedIn { get; }

    bool Login(string username, string password);
}
public class LoginService : ILoginService
{
    private bool isLoggedIn;
    public bool IsLoggedIn => isLoggedIn;

    private readonly IAuthenticationService authenticationService;
    public LoginService(IAuthenticationService authentication)
    {
        authenticationService = authentication;
    }

    public bool Login(string username, string password)
    {
        isLoggedIn = authenticationService.VerifyCredentials(username, password);
        return IsLoggedIn;
    }
}
public interface IAuthenticationService
{
    bool VerifyCredentials(string username, string password);
}
public class AuthenticationService : IAuthenticationService
{
    public bool VerifyCredentials(string username, string password)
    {
        return !string.IsNullOrWhiteSpace(username) && username == password;
    }
}

Going back to our original requirements, we now have the ability to provision viewmodels in a way that they are testable (i.e. they have no dependencies that can’t be mocked as part of testing the viewmodel) and that we can inject dependencies. The one thing we haven’t dealt with is the need to have a navigation pattern that is platform agnostic.

Event-Based Navigation

As we observed earlier Xamarin.Forms already provides the abstraction to for platform-agnostic navigation using the INavigation instance. So the issue we’re really left with is how to structure a navigation pattern that is consistent and easy to follow.

MvvmCross provides this in the form of ViewModel-to-ViewModel navigation but this introduces a dependency between viewmodels (and to the INavigationService), which actually makes it harder to test and introduces an interdependency between viewmodels, even if it is a loose dependency via navigation.

Rather than making individual viewmodels aware of navigation, what if we simply stipulate that the viewmodel should raise an event when it is “done”. The definition of done might be that the user has clicked the Ok or Cancel button (which would trigger a command on the viewmodel), or it could be that a new item has successfully been saved. The point is that the viewmodel raises one or more events when it wants to notify the application that the viewmodel has been completed, or “done”. At this point the application needs to make a decision on where to send the user (i.e. which page to navigate to).

What we want to avoid is the viewmodel raising an event that is captured by the corresponding page, in order to then navigate to the next page (or to go back to previous page in the case of a cancel style event). Whilst this keeps the viewmodel from taking on additional dependencies, this would result in navigation code being littered throughout the application, making it hard to find, update and maintain.

An alternative would be to have a central mechanism where navigation, based on viewmodel events, is declared. Remembering that this is a thought experiment only, and that this code would require significant refinement if it was to be used in production.

ViewModel Events

When a particular viewmodel raises an event, we want to be able to invoke an Action that will define how the application should respond. In concrete terms, we’re going to add a NewItemViewModel to the application generated from the Shell template (for some reason the NewItemPage doesn’t have its own viewmodel in the template). The NewItemViewModel will have a Cancel event which will be triggered if the user wants to abandon the process of creating a new item.

public class NewItemViewModel : BaseViewModel
{
    public event EventHandler<Item> ItemSaved;
    public event EventHandler Cancel;

    private Item item;
    public Item Item { get => item; set => SetProperty(ref item, value); }

    public ICommand SaveItemCommand { get; }
    public ICommand CancelCommand { get; }

    public NewItemViewModel()
    {
        SaveItemCommand = new Command(() =>
            {
                MessagingCenter.Send(this, "AddItem", Item);
                ItemSaved?.Invoke(this, Item);
            });
        CancelCommand = new Command(() =>
            {
                Cancel?.Invoke(this, EventArgs.Empty);
            });
    }
}

The application needs to intercept the Cancel event from the NewItemViewModel and navigate the application back to the previous page in the application. Normally an event handler would be wired up like the following code example, which you could expect to see in the OnAppearing method of a page (and then hopefully unwired in the OnDisappearing method).

vm.Cancel += (s, args) => Navigation.PopModalAsync();

However, we need to be able to abstract this code in a way that we can define what Action is invoked when a particular event is raised.

AppShell Event Maps

Let me jump forward a bit to where this is going to end up, so that you can see how we can declare the mapping between a viewmodel event and the Action that should be invoked.

public partial class AppShell : Xamarin.Forms.Shell
{
    public static IDictionary<Type, IEventMap[]> Maps { get; } = new Dictionary<Type, IEventMap[]>();

    public AppShell()
    {
        InitializeComponent();

        // When the ItemSaved event is raised on the NewItemViewModel
        // pop the current page off the navigation stack
        Maps.For<NewItemViewModel>()
            .Do(new EventHandler<Item>((s, args) => Navigation.PopModalAsync()))
            .When((vm, a) => vm.ItemSaved += a, (vm, a) => vm.ItemSaved -= a);

        // When the Cancel event is raised on the NewItemViewModel
        // pop the current page off the navigation stack
        Maps.For<NewItemViewModel>()
            .Do(new EventHandler((s, args) => Navigation.PopModalAsync()))
            .When((vm, a) => vm.Cancel += a, (vm, a) => vm.Cancel -= a);

        // When the SelectedItemChanged event is raised on the ItemsViewModel
        // navigate to the ItemDetailPage, passing in a new ItemDetailViewModel
        // with the selected item
        Maps.For<ItemsViewModel>()
            .Do(new EventHandler<Item>(async (s, args) =>
            {
                if (args == null)
                {
                    return;
                }
                await Navigation.PushAsync(new ItemDetailPage(new ItemDetailViewModel(args)));
            }))
            .When((vm, a) => vm.SelectedItemChanged += a, (vm, a) => vm.SelectedItemChanged -= a);
    }
}

Here’s what this code does:

  • I’ve define a dictionary of event to action mappings (i.e. Maps). We’ve declared as a static to make it easy to access throughout the app (disclaimer: this is a thought experiment!!!)
  • I’m using a fluent syntax, which I’ll include the code for later in this post, to link the type of viewmodel (For), the Action to be invoked (Do) and the Event (When).
  • The When method requires Actions for both wiring and unwiring the event handler due to limitations on C# events.

Connecting Event Mappings

Now that we have these mappings, we need to use them in the application itself. The mappings are used in the OnAppearing and OnDisappearing methods to wire and unwire the Action to the viewmodel events. To do this the pages in our application need to inherit from a base page that has the appropriate logic.

public class BasePage : ContentPage
{
    protected override void OnAppearing()
    {
        base.OnAppearing();

        var vm = this.BindingContext as BaseViewModel;
        if (AppShell.Maps.TryGetValue(vm.GetType(), out IEventMap[] maps))
        {
            foreach (var map in maps)
            {
                map.Wire(vm);
            }
        }
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        var vm = this.BindingContext as BaseViewModel;
        if (AppShell.Maps.TryGetValue(vm.GetType(), out IEventMap[] maps))
        {
            foreach (var map in maps)
            {
                map.Unwire(vm);
            }
        }
    }
}

Adding a Mapping

So far I’ve shown you what an existing mapping looks like and how it connects viewmodel events with an action, typically a navigation of some description. Let me walk you through adding another, so you can see how it works. We’ll add the ability to delete an item to the ItemDetailViewModel. We’ll add a command, DeleteItemCommand, which will be data bound to a button on the ItemDetailsPage. When invoked the DeleteItemCommand will issue a message, specifying the item to delete. After sending a message, the ItemDetailViewModel raises the ItemDeleted event.

public class ItemDetailViewModel : BaseViewModel
{
    public event EventHandler<Item> ItemDeleted;
    public ICommand DeleteItemCommand { get; }

    public Item Item { get; set; }
    public ItemDetailViewModel(Item item = null)
    {
        Title = item?.Text;
        Item = item;

        DeleteItemCommand = new Command(() =>
        {
            MessagingCenter.Send(this, "DeleteItem", Item);
            ItemDeleted?.Invoke(this, Item);
        });
    }
}

When the ItemDeleted event is raised, that’s the indication that this viewmodel is done and that the application should return to the previous page. For this, we just need to add a mapping to the AppShell class.

Maps.For<ItemDetailViewModel>()
    .Do(new EventHandler<Item>((s, args) => Navigation.PopAsync()))
    .When((vm, a) => vm.ItemDeleted += a, (vm, a) => vm.ItemDeleted -= a);

And that’s all there is to it – we can simply add events to the viewmodels; and maps to the AppShell.

Inner Working

Now that I’ve shown you how it works, let’s drill into what makes it work, starting with the EventMap class, which will be used to map an action to a viewmodel event.

public interface IEventMap
{
    void Wire(object viewModel);
    void Unwire(object viewModel);
}

public class EventMap<TViewModel, TDelegate>: IEventMap
{
    public TDelegate Action { get; set; }
    public Action<TViewModel, TDelegate> Arrive { get; set; }
    public Action<TViewModel, TDelegate> Leave { get; set; }

    public EventMap(
            TDelegate action, 
            Action<TViewModel, TDelegate> arrive, 
            Action<TViewModel, TDelegate> leave)
    {
        Action = action;
        Arrive = arrive;
        Leave = leave;
    }

    public void Wire(object viewModel)
    {
        Arrive((TViewModel)viewModel, Action);
    }

    public void Unwire(object viewModel)
    {
        Leave((TViewModel)viewModel, Action);
    }
}

The EventMap class is relatively simple and you can see that the Wire and Unwire methods invoke the corresponding Arrive and Leave methods. And yes, there is some assumptions made here without error handling around the casting of the videwModel object to the appropriate type.

The last bit of magic is the way that the mappings are created using a fluent style of coding. This is actually just a bunch of extension methods with a couple of builder classes added for good measure

public static class EventMapHelpers
{
    public static EventMapBuilderViewModel<TViewModel> For<TViewModel>(this IDictionary<Type, IEventMap[]> maps)
    {
        return new EventMapBuilderViewModel<TViewModel>() { Maps = maps };
    }

    public static EventMapBuilderViewModelEvent<TViewModel, TDelegate>
        Do<TViewModel, TDelegate>(this EventMapBuilderViewModel<TViewModel> viewModelBuilder, TDelegate viewModelEvent)
    {
        return viewModelBuilder.BuildWithEvent(viewModelEvent);
    }
    public static void When<TViewModel, TDelegate>(this EventMapBuilderViewModelEvent<TViewModel, TDelegate> builder, Action<TViewModel, TDelegate> arrive, Action<TViewModel, TDelegate> leave)
    {
        var key = typeof(TViewModel);
        var map = new EventMap<TViewModel, TDelegate>(builder.ViewModelAction, arrive, leave);
        if (builder.Maps.TryGetValue(key, out IEventMap[] existing))
        {
            builder.Maps[key] = existing.Union(new[] { map }).ToArray();
            return;
        }
        builder.Maps[key] = new[] { map };
    }
    public class EventMapBuilderViewModelEvent<TViewModel, TDelegate> :
        EventMapBuilderViewModel<TViewModel>
    {
        public TDelegate ViewModelAction { get; set; }
    }
    public class EventMapBuilderViewModel<TViewModel>
    {
        public IDictionary<Type, IEventMap[]> Maps { get; set; }

        public EventMapBuilderViewModelEvent<TViewModel, TDelegate> BuildWithEvent<TDelegate>(TDelegate viewModelAction)
        {
            return new EventMapBuilderViewModelEvent<TViewModel, TDelegate> { ViewModelAction = viewModelAction, Maps = Maps };
        }
    }
}

MVVM Navigation Summary

Whilst only a thought experiment, I think I’ve shown in this article the importance of having a structured approach to MVVM. Whether you choose to go with Prism or MvvmCross, or perhaps even choose your own pattern, the reality is that you need some structure to your code.

For anyone interested, you can grab my source code from here

Installing Uno as a PWA with WebAssembly

One of the things that I don’t like about the web is that I have to access everything via the browser. Whilst favourites and bookmarks are all well and good, one of the nice things about apps is that I can do things like pin to start/taskbar (Windows) or add to homescreen (Android). Sure I can pin a website, but this leads me to my next point – it still feels like the web, rather than an app experience. There’s still the browser chrome that sits around the edge and it doesn’t feel the same as being in an app. In this post we’re going to look at one aspect of Progressive Web Applications (PWA) which is the ability to “install” the application in order to address some of my feelings towards web applications.

Twitter PWA

Let me start by walking through one of the better PWA experiences offered by Twitter. Over the last couple of months Twitter has changed the layout of their website significantly but what’s interesting is that it appears they’ve been moving towards a single look and feel across both their web and app experience. For example, the experience you see today on the website is basically the same as what you get if you download the Windows Store app. As you’ll see once we install their PWA that the web interface makes sense because it’s designed to work as an app.

Back in 2017, Twitter announced Twitter Lite which they talked about as being a PWA. They talked a lot about how they built it and their post is definitely worth a read. However, I want to focus on the experience they’ve built for users.

Twitter on Mobile Chrome

Let’s look first at their mobile experience. When you go to mobile.twitter.com in Chrome on a mobile device, assuming you’ve signed in, you’ll see a prompt at the bottom of the screen to “Add Twitter to Home screen”. Alternatively if you’re not signed in, or you’ve dismissed the prompt, you can select Add to Home Screen from the side menu in Chrome.

Tapping on Add Twitter to Home screen displays a confirmation prompt

Tapping Add will “install” the mobile website as an application. This includes an icon that you can move around your home screen. Note that the icon is almost indistinguishable from the actual Twitter application that you can download from the store.

When you launch the newly installed Twitter website, what you see is virtually the same as the Twitter application you’d download from the store. There’s no browser chrome and it looks and behaves just like an app.

And in fact if you press and hold on the Twitter icon on the home screen and go to App info, it even displays information in the same way as it would a regular application, including the ability to Uninstall and Force stop.

Twitter on Desktop Chrome

So that’s the mobile experience. Let’s just check what the desktop experience is. It’s not much different, except for there’s no prompt to install the application. However, if you look to the right of the address bar, there’s a little circle with a plus button; hovering over it indicates it will Install Twitter. Similarly if you go to the right side-menu, there’s an option to Install Twitter.

Again, the install process prompts for user confirmation.

Once installed the Twitter website launches without the browser chrome. In addition to the usual minimize, restore and close buttons on the right side of the title bar of the window, there’s a key icon and a vertical dots icon. The key icon allows you to clear all browsing data.

The vertical dots reveals a menu with the option to Uninstall Twitter.

After installing the Twitter website as an application the Twitter icon does appear in the applications list. However, if you attempt to uninstall the Twitter website by right-clicking the icon and selecting uninstall, it’ll just take you to Add/Remove programs in Settings (I suspect Windows thinks you want to uninstall Chrome). You need to launch the Twitter website application and use the Uninstall Twitter option in the menu.

Making Uno a PWA

Now that we’ve seen how a website can be installed and then behaves just like a regular application, let’s take a look at how easily we can do this for an application that uses Uno and WebAssembly.
The firs thing that we’ll need is a manifest file. In my case I’ve called it manifest.json and I’ve put it into the root of the WASM project in my Uno solution. It’s got the following content that defines things like the icon and the name of application etc.

{
    "background_color": "#ffffff",
    "description": "Freakin awesome Uno web app.",
    "display": "standalone",
    "icons": [
      {
        "src": "Icon192x192.png",
        "sizes": "192x192",
        "type": "image/png"
      }

    ],
    "name": "Uno as a PWA",
    "short_name": "Uno PWA",
    "start_url": "/index.html",
    "theme_color": "#ffffff",
    "scope": "/"
}

Of course, we’ll need to add the icon – I’ve added an 192×192 png with the name Icon192x192.png to the wwwroot folder within the WASM project. I also need to connect the manifest file so that the browser knows to look for it. To do this I need to add it as a link to the web page that hosts the Uno WASM application. In the default WASM project generated by the Uno solution template, there’s no html file, since it’s automatically generated. From the Uno documentation it’s possible to add in a template html file (see the Index.html content override section in the Bootstrap readme). However, it’s also possible to just specify the name of the manifest file using the WasmPWAManifestFile attribute in the project file (see Support for PWA Manifest File section in docs).

<WasmPWAManifestFile>manifest.json</WasmPWAManifestFile>

When we now run the WASM project and bring up the developer tools on the Application tab you can select Manifest and see the information contained in the manifest.json.

Also, the plus button is on the right of the address bar (and the option to Install in the side menu), indicating that this website can be installed.

And that’s it – all you need to do to get your Uno website setup to be installed as as PWA.

Create, Build and Publish a Flutter Web App

Using Flutter for building iOS and Android applications brings with it some advantages over other cross platform solutions such as Xamarin.Forms or React but one of the more interesting developments to keep an eye on is the support for running Flutter apps on the web. In this post we’re going to create a basic Flutter app and show it running locally. We’re then going to publish it out to Azure blob storage (and accessed via the Azure CDN) to demonstrate that a Flutter web app runs purely in the browser and can be hosted on a static endpoint (i.e. no server side code!).

Installation and Setup

Currently support for Flutter on web requires the webdev package but before installing the package it’s important to make sure that you’ve upgraded the Flutter SDK to the latest version. Run the upgrade command either from a command prompt, or from the terminal console within Visual Studio Code.

flutter upgrade

Next, we need to activate the webdev package using the following command

flutter packages pub global activate webdev

Note there are a couple of things that I ran into. I’m running this all on Windows 10, and I was trying to activate the webdev from within Visual Studio Code. I had opened a new window, which meant I didn’t have a Flutter project open. When I ran the above command I saw an error because apparently there was no pubsec.yaml file.

PS C:\> flutter packages pub global activate webdev
 Error: No pubspec.yaml file found.
 This command should be run from the root of your Flutter project.
 Do not run this command from the root of your git clone of Flutter.

After hunting around a bit I found that you can omit “packages” from the command in order for the webdev package to be retrieved and activated (only required to be done once thanks to the global parameter):

flutter pub global activate webdev

This command might take a minute or so to run. On completion, I saw an interesting notice regarding the cache folder for packages:

Installed executable webdev.
 Warning: Pub installs executables into C:\Source\tools\flutter\.pub-cache\bin, which is not on your path.
 You can fix that by adding that directory to your system's "Path" environment variable.
 A web search for "configure windows path" will show you how.
 Activated webdev 2.5.0.

Currently, the only folder that I’ve included in the Path environment variable is the bin folder for where I’d extracted the Flutter SDK (in my case c:\source\tools\flutter\bin). This was the first time I’d seen this warning and it made me wonder what other folders I needed to include. Following the direction of this post, coupled with the above warning I added the following paths to the Path environment variable.

[flutter folder]\bin
[flutter folder]\.pub-cache\bin
[flutter folder]\bin\cache\dart-sdk\bin
%APPDATA%\Pub\Cache\bin

Note: Make sure you restart any command prompt, terminal window and Visual Studio Code after saving the changes to the Path environment variable in order for it to take effect.

At this point you should be good to go ahead and start creating your new Flutter for web project.

Flutter: New Web Project

From the command palette in Visual Studio Code you can select Flutter: New Web Project. This will go ahead and create your new project with a folder structure that will seem familiar if you’ve been building Flutter apps already. What is different is that after creating the project, it opens index.html in the code editor – you can close this as you probably don’t need to mess with this page initially.

You’ll also notice that there are two main.dart files, one in the web folder, and one in the lib folder. If you inspect these individually you’ll notice that the one in the web folder is an entrypoint (you can see main.dart.js being invoked from index.html which is located in the same folder) which initializes the web platform before calling the main() function located in main.dart in the lib folder. Inside the main.dart in the lib folder you’ll see the layout of a very basic “Hello World” Flutter app.

Running Flutter for Web

Of course, the first thing you’ll want to do is to run the Flutter app, which can be done easily in Visual Studio Code by pressing F5. The first time you attempt to run the project you may be prompted to Select Environment. Select Dart & Flutter to proceed.

Unfortunately this then greets me with a dialog saying that there are build errors:

NOTE: If you get build errors on a newly created Flutter web project. Run flutter upgrade from the terminal within Visual Studio Code, or from the command prompt.

After upgrading my Flutter web project, pressing F5 and selecting the environment, builds and runs the project. Your default browser, Chrome in my case, will be launched in order to display your Flutter web project. The first run does take a while, so don’t close Chrome if it just seems to be blocked loading the project the first time.

Publishing a Flutter Web App

It’s all great being able to run the Flutter web project from within Visual Studio Code (or via the command line using webdev serve). However, at some point you’ll want to think about publishing the project to the web. The great thing about a Flutter web app is that the compiler generates JavaScript that runs within the browser to power your app. There’s no server side component required in order to serve your app.

Ok, but where is this JavaScript and how do I know what to copy to my web server? When you get around to publishing your web app, instead of running webdev serve you just need to use the build parameter:

webdev build

This command will generate a build folder, which will include index.html and main.dart.js, along with any other files that your Flutter web app will require in order to run. All you need to do is to copy the contents of the folder to the appropriate folder on your web server.

In my case I didn’t want to spin up a web server, or even a new endpoint in Azure, just for my simple Flutter web app. Instead I decided to copy the necessary files up to a folder in an existing Blob storage account (in fact it’s the very same storage account that serves the images for this blog). In front of this storage account I use an Azure CDN endpoint in order to improve performance and caching of the content. The net result is that any file that’s retrieved from https://blogimages.builttoroam.com is served from the CDN cache, if it exists. If it isn’t already in the CDN cache, it’ll be pulled from https://nicksblog.blob.core.windows.net, which is the underlying blob storage endpoint, added to the CDN cache and then served up. The point I’m making is that the following Flutter web app (it’s not an image – you can click on it to launch it in a separate tab) is being served from a static service, rather than an actual web server.

Summary

Whilst Flutter for web is still in preview, the simplicity of using the Flutter layout engine for building apps for the web will be a game changed for those of us who have hated the prospect of developing using CSS and JS. Hopefully as it gets closer to a final release there will be an ability to have a single project that’s able to build for iOS, Android and Web without having to have separate projects.

Just out of interest, here are a couple of other articles worth reading on Flutter for the web:


NDC – Sydney – October 14-18

Workshop: Building Cross-Platform Apps With Flutter
Presenters: Pooja Bhaumik and myself

Register for NDC Sydney and come learn how to build amazing Flutter apps!!


Using C#, XAML, Uno and MvvmCross to Jump Start Your Cross Platform Application

Rukesh has put together a great post, entitled Using C#, XAML + Uno Platform to Build One Codebase, Cross-Platform Apps, that provides a great walk through of getting started with the Uno Platform. It goes through grabbing the Uno extension for Visual Studio and then creating a new multi-platform application using just C# and XAML. The end result is an app that runs on iOS, Android, Windows and of course WebAssembly. In this post I’m going to step through a similar process of creating an app but this time I’m going to reduce the amount of code I need to write by leveraging the MvvmCross framework.

Note: This post makes use of a pre-release version of MvvmCross that has been built specifically to work with Uno. I’ll add a local copy of the NuGet packages to this post but if you want to roll your own you can simply grab the Uno branch from my fork and build your own NuGet packages (right-click on the MvvmCross.Uno project and select Pack to generate the package).

Setting Up the Basic Application

We’re going to start by creating the basic structure of the application which will have: a head or target project for each platform we’re targeting (iOS, Android, UWP and WebAssembly), a shared project which will house the XAML files for our application, and a core library which will house our business logic (in this case just our ViewModels).

Creating the Uno Application

The head projects and the shared project solution structure we pretty much get for free by using the Cross-Platform App (Uno Platform) template.

We’ll give our project a name, MvxUnoStarter, and click Create to generate the solution structure.

Note: At this point I always make sure that each of the head projects, for iOS, Android, Windows and WebAssembly, are able to be run. Currently there are a few open issues that relate mainly to the template that’s part of the Uno extension. I cover most of these in my post https://nicksnettravels.builttoroam.com/uwp-splitview/. In short you need to remove the scale-200 from the image filenames in the shared project, you need to set the Target Android version and the iOS Deployment Target in order to get the projects to build.

Adding Core Library

Next, let’s add a library based on the Class Library (.NET Standard) project template.

We’ll call it MvxUnoStarter.Core

Make sure that you update all the head projects by adding a reference to the newly created core library. Right-click on each project and select Add, Reference from the context menu; then check the box next to the MvxUnoStarter.Core project.

Referencing Local MvvmCross.Uno NuGet Packages

Update 22/9/2019: Before you proceed with adding a reference to the MvvmCross.Uno packages, please make sure you update all Uno package references (Uno.Wasm.Bootstrap, Uno.UI and Uno.Core) to the latest prerelease version. Be careful that you don’t just update all package references as the Logging packages shouldn’t be updated as there’s a known incompatibility with Uno and the latest versions of those libraries.

Support for MvvmCross is still under development – if you want to get started with MvvmCross for Uno today you can either grab the updated source code from my fork (see note above) or you can grab the pre-built MvvmCross.Uno packages:

In order to make use of these packages you’ll need to setup a local package source (if you already have one setup on your machine you can just drop the packages into the folder for them to be available). Open the Options window, from the Tools menu, and locate the Package Sources node. Click the + button to add a new source; give the source a name (eg Local Mvx Packages) and make sure the Source is set to a folder on your computer where you have saved the packages.

Now that you have a local package source, right-click your solution in the Solution Explorer window and select Manage NuGet Packages for Solution. Change the Package source to the local package source and select the MvvmCross.Uno package (we’re not going to use the Plugins package in this post but if you want to try it out, feel free to add it to you projects too) and make sure you check all projects.

Avoiding Linker Issues

A common gotcha with working with WebAssembly is that the compilation process includes a fairly aggressive linker step. Make sure you update the LinkerConfig.xml file to include your core project (i.e. MvxUnoStarter.Core) and the MvvmCross library (i.e. MvvmCross.Uno and if you’ve added the plugins library, make sure MvvmCross.Uno.Plugins is included too).

Build and Run Your Projects

Before continuing I would highly recommend that you make sure all your head projects can be built and run. Go through each one and set them as the startup project and then run the solution. Note that for the Wasm project, don’t attempt to run with the debugger; instead use the Start Without Debugging (Ctrl+F5).

Application Views, ViewModels and Navigation

Now that we’ve done the basic house keeping and we have four head projects that all run, and have appropriate references to both the core project and MvvmCross, we’re all set to start creating some views and viewmodels.

Adding Views

We’ll start by creating two views for our application. Add a folder called Views to the Shared project and then add two new items to the folder, HomePage and SecondPage, both based on the Blank Page item template.

There are a couple of changes we need to make in order to take advantage of the navigation pattern used by MvvmCross. Both views need to inherit from MvxWindowsPage. We’ll update HomePage to include a TextBlock in the centre of the screen, bound to the property HelloWorld.

<views:MvxWindowsPage 
  x:Class="MvxUnoStarter.Shared.Views.HomePage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:views="using:MvvmCross.Platforms.Uap.Views"
  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
  <StackPanel HorizontalAlignment="Center"
              VerticalAlignment="Center">
    <TextBlock Text="{Binding HelloWorld}" />
  </StackPanel>
</views:MvxWindowsPage>

Next we’ll do the same for SecondPage.

<views:MvxWindowsPage 
  x:Class="MvxUnoStarter.Shared.Views.SecondPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:views="using:MvvmCross.Platforms.Uap.Views"
  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
  <Grid HorizontalAlignment="Center"
        VerticalAlignment="Center">
    <TextBlock Text="{Binding HelloWorld}" />
  </Grid>
</views:MvxWindowsPage>

Adding ViewModels

The next thing to do is to create the corresponding viewmodels for the two pages: HomeViewModel and SecondViewModel. HomeViewModel will expose a property HelloWorld that simply returns a string to be displayed on the HomePage.

public class HomeViewModel : MvxNavigationViewModel
{
    public string HelloWorld => "Hello World!!!";
    public HomeViewModel(
        IMvxLogProvider logProvider,
        IMvxNavigationService navigationService) 
        : base(logProvider, navigationService)
    { }
}

SecondViewModel is essentially the same but in this case the string is slightly different to indicate it’s on the second page.

public class SecondViewModel : MvxNavigationViewModel
{
    public string HelloWorld => "Hello World - Page 2!!!";

    public SecondViewModel(
        IMvxLogProvider logProvider, 
        IMvxNavigationService navigationService) 
        : base(logProvider, navigationService)
    {
    }
}

Registering the AppStart ViewModel

In order for MvvmCross to know which page to start on it’s necessary to register a viewmodel as the first viewmodel of the application. This is done by creating a class in the core project that inherits from MvxApplication and then calling RegisterAppStart in the Initialize method. In our case we’ll call this class App, with the following code:

public class App : MvxApplication
{
    public override void Initialize()
    {
        RegisterAppStart<HomeViewModel>();
    }
}

Updating App.xaml to Use MvvmCross

The last update to be made in order for the application to use MvvmCross, which is essentially the entry point for MvvmCross, is to update App.xaml (and the corresponding codebehind file App.xaml.cs) to inherit from MvxApplication. Note that due to a limitation with the XAML compiler’s ability to handle generics we actually need to create an intermediary class that defines the generic parameters, in this case called StartApp.

public abstract class StarterApp : MvxApplication<MvxWindowsSetup<Core.App>, Core.App>
{
}

Next we need to update App.xaml to inherit from StarterApp

<local:StarterApp
    x:Class="MvxUnoStarter.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MvxUnoStarter"
    RequestedTheme="Light">
</local:StarterApp>

And then lastly, update the constructor of App.xaml.cs. As both MvvmCross support for Uno and the Uno support for Wasm are both prerelease there are a couple of changes, wrapped in the __WASM__ conditional block that are required in order for an MvvmCross based application to run using WebAssembly.

public App()
{
#if __WASM__
    Windows.UI.Core.CoreDispatcher.HasThreadAccessOverride = true;
    MvxSetupSingleton.SupportsMultiThreadedStartup = false;
#endif
    ConfigureFilters(LogExtensionPoint.AmbientLoggerFactory);
    InitializeComponent();
    UnhandledException += App_UnhandledException;
}

Navigation in MvvmCross

Currently, running the application will only show Hello World in the middle of the screen, as shown below. What we really want to do is to be able to navigate to the second view.

To do this, let’s add a button to the HomePage XAML.

<views:MvxWindowsPage
  x:Class="MvxUnoStarter.Shared.Views.HomePage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:views="using:MvvmCross.Platforms.Uap.Views"
  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
  <StackPanel HorizontalAlignment="Center"
              VerticalAlignment="Center">
    <TextBlock Text="{Binding HelloWorld}" />
    <Button Content="Next"
            Command="{Binding ShowSecondPageCommand}" />
  </StackPanel>
</views:MvxWindowsPage>

And then we need to add the ShowSecondPageCommand to the HomeViewModel that the button is data bound to.

public class HomeViewModel : MvxNavigationViewModel
{
    public string HelloWorld => "Hello World!!!";
    public IMvxAsyncCommand ShowSecondPageCommand { get; }
    public HomeViewModel(
        IMvxLogProvider logProvider,
        IMvxNavigationService navigationService) : base(logProvider, navigationService)
    {
        ShowSecondPageCommand =
            new MvxAsyncCommand(async () => await NavigationService.Navigate<SecondViewModel>());
    }
}

The ShowSecondPageCommand is instantiated with an MvxAsyncCommand object with the action to navigate to SecondViewModel. This is where the true power of MvvmCross comes in as the navigation to SecondPage is automatically handled behind the scenes.

MvvmCross for Uno

Whilst it feels like there’s a lot of hoops to jump through, once Uno and MvvmCross are setup, we get all the benefits of ViewModel to ViewModel navigation including dependency injection across iOS, Android, Windows and of course WebAssembly. Now you can rapidly build applications that target any platform.

Content from Former Microsoft WPF and Silverlight Team Member

Someone pointed me in the direction of a series of great blog posts that have recently been migrated to a github repository. The posts are quite old, dating back to a period between 2005 and 2013 when Beatriz Stollnitz worked for Microsoft as part of the WPF and Silverlight teams. Here are the links to each of the posts – happy reading!

Update 11th August: I’ve forked the repository and have started to work my way through the sample code to a) update it to WPF on .NET 4.7.2 and b) provide a similar example for UWP + Uno (iOS, Android, WASM). Feel free to check it out and help out!

01-DataContext

02-EmptyBinding

03-GetListBoxItem

04-BindToComboBox

05-DisplayMemberPath

06-SelectedValue

07-ChangePanelItemsControl

08-BarGraph

09-CollectionViewSourceSample

10-MasterDetail

11-MasterDetailThreeLevels

12-DataBoundDialogBox

13-TemplatingItems

14-SortingGroups

15-GroupingTreeView

16-GroupByType

17-BoundListView

18-ThreeLevelMasterDetailADO

19-ObjectDataProviderSample

20-InsertingSeparators

21-CustomSorting

24-AsynchronousBinding

25-BindToEnum

26-DataTriggerSample

27-ConvertXaml

28-FilterSample

29-MultipleFilters

30-MultiBindingConverter

31-ChangesMultithreading

32-PolygonBinding

33-PolygonBinding2

34-PolygonBinding3

35-CommonQuestions

36-ADOIndependentView

37-PlanetsListBox

38-UpdateExplicit

39-TreeViewPerformancePart1

40-TreeViewPerformancePart2

41-TreeViewPerformancePart3

42-WPFPresenter

43-BindToXLinq

44-XLinqXMLMasterDetail

45-DebuggingDataBinding

46-DragDropListBox

47-ExpandTreeViewPart1

48-ExpandTreeViewPart2

49-ExpandTreeViewPart3

51-UIVirtualization

52-DataVirtualization

54-PieChartWithLabels

55-PieChartWithLabelsSilverlight

56-PieChartWithLabelsSilverlight

57-DataVirtualization

58-MultipleStyles

59-WPFCollectionViewSource

60-SLCollectionViewSource

61-OredevComputerWeekly

62-DataVirtualizationFiltering

64-DataVirtualizationFilteringSorting

66-SortingHierarchy

67-PieChartWithLabelsUpdates

69-BindRadioButtonsToEnumsPart1

70-BindRadioButtonsToEnumsPart2

71-BindRadioButtonsToEnumsPart3

72-BindRadioButtonsToEnumsPart4

73-BindRadioButtonsToEnumsPart5

74-PositioningDataBoundItems

75-SimultaneousEnableDisable

76-FocusWatcher

77-CaptureWatcher

78-BetterBindableBase

79-BooleanConverters

How to Create a Flutter Widget Using a RenderObject

There is plenty of documentation on how to build a Flutter application and more importantly the significance of widgets. Most articles you’ll read about widgets talk about what a StatelessWidget is and what a StatefulWidget is and how you can inherit from these to create your own widgets. However, what happens when you want to create a widget that perhaps does some custom painting, or can’t easily be represented by a combination of existing widgets?

One option is that you add your custom painting to the widget hierarchy. For example in this StackOverflow posting the author, Collin Jackson, creates a class called ProgressPainter that inherits from CustomPainter. An instance of the ProgressPainter is added to the widget hierarchy in order to paint directly to the canvas.

An alternative is to create a class that inherits from RenderObject in order to encapsulate the painting as part of the rendering of a widget. This type of encapsulation is used by the built in Flutter widgets. The example we’ll walk through in a second actually mirrors the code for the Opacity widget, a built in Flutter widget that allows you to control the opacity of child widgets.

What’s a Widget, Element and RenderObject

Before we get into the example, it’s worth understanding the relationship between a widget and its corresponding RenderObject. There are a couple of other posts that are worth a quick read that cover the difference between a Widget, an Element and a RenderObject:

As you start to get familiar with Flutter, you’ll find yourself defining what you want to appear on the screen using a mixture of widgets. Each time you use a widget you’re setting properties on it or nesting other widgets within it. You use widgets to describe what the visual hierarchy should look like at a point in time. You can think of widgets holding configuration information about the visual hierarchy, or being a template for the visual hierarchy.

When your application runs and an instance of a widget is created it’s associated with an Element. Where widgets are immutable and may be recreated based on changing state of the application, Elements mutate based on the widget that they’re associated with. Elements combine in a tree structure to define the current layout of the application.

You’d think that if an Element defines the current layout of the application it would be what draws, or renders, each widget. This role actually resides with the RenderObject which is attached to a RenderObjectElement, a sub-class of Element (in contrast to a ComponentElement which is predominantly responsible for composition of other Elements).

Element and RenderObject By Example

I’m sure at this point this is all sounding very theoretical, so let’s walk through a basic example. After creating a new Flutter project, simple_widgets, I’ve stripped back all the default source code to a minimum app that simply displays Hello World! in the Center of the screen. In the following code we can see that the structure of the app uses a Center widget with a Text widget nested via the child property.

void main() => runApp(
      Center(
        child: Text(
          'Hello World!',
          textDirection: TextDirection.ltr,
        ),
      ),
    );

In VS Code if you press Ctrl+Alt+D the Dart DevTools will be displayed attached to the currently running Flutter app. As you can see from the left tree on the following image, our App is indeed made up a Center with Text nested within it. However, if we look on the right side we can see that there’s actually an additional node on the tree, RichText. The RichText node is actually a nested widget that is returned by the build method of the Text widget and actually does the heavy lifting for the Text widget.

What we’re really seeing in the Dart DevTools are actually the elements that have been created and correspond to what’s being displayed on the screen. To see this clearly you can click the “Debug Paint” button (to the right of the clock button in the toolbar of the Dart DevTools) and then select the RichText node in the right tree. In the running app you’ll see markers similar to the following image identifying the element that’s being displayed on the screen.

If a Widget is the template, or cookie-cutter, and an Element is the instantiation of a widget, the question is where does all the work get done to display content on the screen. This work happens within a RenderObject, or in most cases a derivative of the RenderObject class. In the case of the RichText widget, it creates a RenderParagraph which is solely responsible for rendering out the text associated with the RichText widget.

As you can see from the earlier image taken from the Dev DartTools, the RenderParagraph includes properties such as constraints and size, that are common to all RenderObjects, as well as textStart, textDirection etc. The implementation of the RenderParagraph is too complex to go into here but needless to say it’s responsible for rendering the text associated with the RichText widget to the screen.

Creating a Tint Widget

The purpose of the widget is to overlay a tint across the content specified via the child property of the widget. The code for this widget isn’t too dissimilar to the built in Opacity widget. There going to be two classes involved:

  • Tint – this is a widget that a developer can use in their widget hierarchy in order to provide a coloured overlay, specified by the color property.
  • RenderTint – this is the descendent of RenderObject and will be responsible for painting the tint overlay.

Tint Widget

The Tint widget itself is relatively straightforward, mainly because we inherit from SingleChildRenderObjectWidget that does most of the heavy lifting for us. The important aspects of the class are that it accepts a Color as a required parameter for its constructor and that it overrides both the createRenderObject and the updateRenderObject. In the createRenderObject it returns an new instance of the RenderTint class.

class Tint extends SingleChildRenderObjectWidget {
  const Tint({
    Key key,
    @required this.color,
    Widget child,
  })  : assert(color != null),
        super(key: key, child: child);

  final Color color;

  @override
  RenderTint createRenderObject(BuildContext context) {
    return RenderTint(
      color: color,
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderTint renderObject) {
    renderObject
      ..color = color;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(ColorProperty('color', color));
  }
}

One thing you’ll notice in the code for the Tint widget is that there’s no code relating to the creation, mounting or unmounting of any element. This is because this is all handled by the SingleChildRenderObjectWidget which overrides the createElement method to return a new instance of the SingleChildRenderObjectElement class.

As a side note, one of the amazing things about Flutter is that as you’re developing your app or creating a widget, you can always drill into the various classes that make up the built-in widgets. In VS Code, pressing F12, when you have a class name selected or the cursor positioned within the class name, will take you to the definition of that class. The Flutter code is extremely well documented and has been split out into various smaller classes to make it easier to identify the purpose of each class.

RenderTint class

Where the purpose of the Tint widget is to allow developers to apply a Tint in their widget hierarchy, the responsibility of the RenderTint class it to draw the tint. The RenderTint is a descendent of the RenderObject. However, rather than implementing all the requirements of a RenderObject, such as calculating size etc, the RenderTint inherits from the RenderProxyBox which handles nearly everything related to layout and positioning.

At the beginning of the class there is some boilerplate code that includes the constructor, that accepts a Color, and the property color that allows the tint colour to be updated. It’s important to note that the setter of the color property does call markNeedPaint, which is important, otherwise the paint method won’t get invoked again.

class RenderTint extends RenderProxyBox {
  RenderTint({
    Color color = Colors.transparent,
    RenderBox child,
  })  : assert(color != null),
        _color = color,
        super(child);

  Color get color => _color;
  Color _color;
  set color(Color color) {
    assert(color != null);
    if (_color == color) return;
    _color = color;
    markNeedsPaint();
    markNeedsSemanticsUpdate();
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    if (child != null) {
      context.paintChild(child, offset);
    }
    context.canvas.drawColor(color, BlendMode.srcOver);
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(ColorProperty('color', color));
  }
}

The paint method is where the actual drawing takes place. In this case it uses the paintChild method on the context to draw any child widgets and then it uses drawColor to apply a fill colour over the top of the canvas.

Tint in Action

That’s pretty much it for defining a simple widget that is responsible for rendering its own content, rather than just composing other widgets. All we need to do now is make use of it.

void main() => runApp(
      Center(
        child: Tint(
          color: Color.fromARGB(40, 255, 0, 0),
          child: Text(
            'Hello World!',
            textDirection: TextDirection.ltr,
          ),
        ),
      ),
    );

And of course, the final output

Hopefully in this post you’ve got an appreciation for the differences between a Widget, Element and RenderObject. I’d highly recommend spending some time walking through some of the Flutter code and understanding how the classes fit together and how you can build better widgets by understanding the relationship these classes have.

Using the UWP SplitView on iOS, Android and WebAssembly with Uno

In this post we’re going to cover one of the basics of app navigation which is the use of the UWP SplitView. If you’re coming from iOS and Android development you might be thinking “huh, I don’t even know what that is.” Well the good news is that it’s actually something you’re already familiar with. Whether you’re used to an app that has a master-details layout, or one that uses a burger menu to display a flyout menu, these can both be implemented using the UWP SplitView.

One thing to be aware of is that the UWP SplitView is one of the basic controls that was added early in the UWP lifecycle. Since then there have been other controls added, such as the NavigationView and the MasterDetailsView (Windows Community Toolkit) that provide extended functionality and are worth exploring depending on the requirements of your project.

New Project – Cross-Platform App (Uno Platform)

Let’s get into it – we’re going to start with creating a new project based on the latest Uno Visual Studio Extension (updated at end of July – if you’re using an older version I would recommend updating). I’m also working with the Visual Studio 2019 (16.3 preview 1.0) as this provides the best support for working with WebAssembly.

Cross-Platform App using the Uno Solution Template

Once you’ve created your solution, I would highly recommend that you a) update NuGet references and b) go through each platform and make sure you can build and run the application. However, as at the time of writing do NOT update the Microsoft.Extensions.Logging.* packages – there’s a note in the csproj files that states “Note that for WebAssembly version 1.1.1 of the console logger required.” My recommendation is to leave these packages at v1.1.1 across all projects. For the Uno libraries I pick the latest prerelease versions but be warned that this occasionally backfires as you may get an unstable version – this is why it’s important to run each platform before proceeding!!

Note: One issue I’m currently seeing in the Uno template is that it generates assets (i.e. images) with names that include the scale factor eg scale-200. This is fine for UWP but fails to build for Android. I’ve gone through and just removed the scale factor from the filenames. You’ll need to rebuild your UWP project to make sure it picks up the filename changes correctly.

Note 2: When running the WebAssembly (aka WASM) project, make sure you select “Start without Debugging” in Visual Studio.

Note 3: When running the iOS build you may need to set the Deployment Target in the Info.plist. If you’re using the latest preview of Visual Studio it will pick up iOS 12.4 on the build agent which will cause you app to fail to deploy if you haven’t set the Deployment Target.

Adding the UWP SplitView

Now that we have our new application up and running, it’s time to add the SplitView control.

Design in Blend

For this we’re going to switch over to Blend – yeh, the product still exists and you’ll see in a minute why we’re going to be using it. To make the switch, right-click on the MainPage.xaml in the shared project and select Design in Blend.

Switching to Design in Blend

Switching to Blend will take a few seconds, particularly if this is the first time you’ve ever opened Blend. You’ll also see errors as the iOS and Android projects won’t open – ignore these and don’t worry about the upgrade log that will popup (these are irritating but can be safely ignored as they won’t impact your project).

Despite selecting Design in Blend from the MainPage.xaml, once in Blend, you’ll actually need to use the Solution Explorer window to open MainPage.xaml in the designer. Once you’ve opened MainPage.xaml, Blend should feel fairly familiar, even if this is the first time you’ve used it. This is because a lot of the windows are shared with Visual Studio – the separation between the products is a mix of legacy (don’t want to disrupt the tiny percentage of developers who still use it) and the optimising that design tasks will be done using Blend.

Blend Designer

Adding the SplitView

There are a number of ways to add the SplitView control into the MainPage. If you’re familiar with writing XAML it’s probably just easiest to add the SplitView element. However, to build familiarity with Blend, let’s add the SplitView using the designer. In the Assets window, enter “splitview” to locate the SplitView control. Use the mouse to drag the SplitView down to the Objects and Timeline window at the position in the hierarchy where you want it to appear. In this case we’re going to drop it inside the existing Grid – I typically have a Grid as the root of most pages to allow setting of background and other properties using a application-wide style, independently of what content is nested within the Grid.

Adding the UWP SplitView

Adding the SplitView this way will add it as the second child of the Grid, immediately following the existing TextBlock. As we want the SplitView to take up the entire space of the page, we’re going to move the TextBlock in the main content area by dragging the TextBlock into the Grid that is nested within the SplitView. The XAML of the page should now look like the following:

<Page
    x:Class="UwpSplitViewSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:UwpSplitViewSample"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <SplitView>
            <SplitView.Pane>
                <Grid />
            </SplitView.Pane>
            <Grid>
                <TextBlock
                    Margin="20,20,20,20"
                    FontSize="30"
                    Text="Hello, world !" />
            </Grid>
        </SplitView>
    </Grid>
</Page>

The SplitView is made up of two distinct areas: the main content area and the pane that can be shown/hidden (i.e. the flyout). From this code we can see that the SplitView as two nested XAML elements which align to the SplitView areas. The first is the SplitView.Pane which defines the layout of the content that will appear within the pane/flyout of the SplitView. The second defines the layout of content to appear within the main content area of the SplitView, in this case a Grid with a TextBlock contained within it.

Adjusting Layout with Blend

I’ll repeat this: For those of us familiar with writing XAML, you’ll find that manually crafting XAML is by far the quickest way to add and edit elements. However, Blend provides a number of shortcuts for positioning elements that can come in handy. Some of which we’ll cover here.

Designing the SplitView Pane

For the purpose of this post, all we’re going to do is to add a StackPanel with a TextBlock and Button as children to both the SplitView.Pane and the SplitView. The TextBlock will be used to indicate what part of the SplitView it is (Pane or Content) and the Button will be used to Show and Hide the pane of the SplitView.

Let’s start with the SplitView.Pane which already has a Grid element. In the Objects and Timeline window, right-click on the Grid and select Change Layout Type, followed by StackPanel.

Change Layout Type to StackPanel

You should see the Grid change to StackPanel and remains selected in the Objects and Timeline window. Next, with the StackPanel still in focus, use the Assets window to locate the TextBlock and double-click on TextBlock to add a TextBlock as a child to the StackPanel. Repeat, this time for a Button.

We’re going to position the StackPanel in the centre of the SplitView pane. To do this, again keep the StackPanel selected in the Objects and Timeline window, we’re going to go to the Properties window and scroll down to the Layout section at set the HorizontalAlignment and VerticalAlignment properties to Center.

Centering the StackPanel

Whilst we’re using the Properties window, select the TextBlock and change the Text property to “Pane”, and then select the Button and change the Content property to “Hide Pane” (Note that unlike Xamarin.Forms that has a Text property, to set the text on a Button in UWP XAML you need to set the Content property).

The last thing we’re going to do with the XAML for the SplitView.Pane is to add an event handler to the Click event of the Button. With the Button selected in the Objects and Timeline window, go to the Properties window, and click on the lightning bold icon to switch from properties to events view. Locate the Click event and type the name of the event handler you want to create, in this case PaneButtonClick. When you press Enter the PaneButtonClick method will be created in the MainPage.xaml.cs code behind file and the XAML will be updated with the Click property.

Adding an Event Handler for the Click Event

For the moment, that’s where we’ll leave the XAML for the SplitView.Pane and we’ll implement the logic for the Click event handler shortly.

Designing the SplitView Content

The layout of the SplitView content is going to be very similar to that of the pane: A StackPanel with nested TextBlock and Button. However, we’re going to go about it slightly differently, considering the fact that we already have a TextBlock nested in a Grid in the content area. We’re going to start by wrapping the TextBlock in a StackPanel. To do this, right-click somewhere in the TextBlock on the designer and select Group Into, followed by StackPanel (this also works if you right-click on the TextBlock in the Objects and Timeline window).

After doing this the hierarchy of elements in the SplitView content will be Grid, StackPanel, TextBlock. However, if you look at the XAML you’ll notice that Blend has added some additional attributes, setting the Margin on the StackPanel and the Height and Width on the TextBlock. To remove these, we can just right-click the StackPanel and select Layout, followed by Reset All. Repeat this on the TextBlock.

From here we can repeat the remaining steps that we did for the SplitView pane:

  • Add a Button to the StackPanel
  • Set HorizontalAlignment and VerticalAlignment on the StackPanel to Center
  • Set Text on the TextBlock to “Content Area”
  • Set Content on the Button to “Show Pane”
  • Add an event handler called ShowPaneButtonClick to the Button Click event

The final XAML in MainPage.xaml should look similar to:

<Page x:Class="UwpSplitViewSample.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:local="using:UwpSplitViewSample"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <SplitView>
      <SplitView.Pane>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center">
          <TextBlock Text="Pane"
                     TextWrapping="Wrap" />
          <Button Click="PaneButtonClick"
                  Content="Hide Pane" />
        </StackPanel>
      </SplitView.Pane>
      <Grid>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Orientation="Vertical">
          <TextBlock Text="Content Area" />
          <Button Click="ShowPaneButtonClick"
                  Content="Show Pane" />
        </StackPanel>
      </Grid>
    </SplitView>
  </Grid>
</Page>

DisplayMode Property on the UWP SplitView

The UWP SplitView has two properties that are worth exploring in a bit of detail and we’ll use these to control the behaviour, or should I say the layout, of the SplitView based on screen width. Let’s start with the easy one, which is the IsPaneOpen property. As you can imagine from the name, this property indicates whether the pane of the SplitView is currently open (i.e. displayed in full, rather than hidden or in compact mode), or not.

The second property worth looking at is the DisplayMode of the SplitView, and has the following four possible values:

  • Overlay – When the pane is visible it appears as an overlay across the content area. It doesn’t affect the size of the content area when it is shown/hidden.
  • Inline – When the pane is displayed it reduces the size of the content area by the width of the pane. As the pane is shown/hidden the width of the content area shrinks and grows accordingly.
  • CompactOverlay – Same as Overlay, except when the pane isn’t open, it still takes up space on the page equal to the CompactPaneLength property. Typically the compact view would show a list of icon buttons as a summary of the options available in the pane when it’s open.
  • CompactInline – Same as Inline, except when the pane isn’t open it still takes up space on the page equal to the CompactPaneLength property.

We’ll leave the use of the CompactOverlay and CompactInline options for the moment and focus on the other two options. The Overlay option is great for when the size of the application is narrow, for example on a mobile phone. The Inline option is better suited for when the application has sufficient width to allow the pane to be shown and there still to remain sufficient content area to be useful.

Using VisualState to Set DisplayMode

With this in mind, we’re going to use visual states to set the DisplayMode property based on the width of the application. For the purpose of this post we’re going to use a split point of 900, meaning that if the width of the application is greater than 900 the DisplayMode will be set to Inline. If it’s below 900 it’ll be set to Overlay.

To begin with, we’re going to set the default value of the DisplayMode to Compact by adding DisplayMode=”Compact” to the SplitView element. Next in Blend we’re going to add two visual states via the States window. In the States window, click the Add state group button and give the state group a name, SizeStateGroup.

Adding a Visual State Group

Next, click the Add state button twice to add two visual states and name them NarrowState and WideState. The actual names of the visual state group and the states are only for your benefit at this point, so you can name them according to what makes sense for you.

Adding a Visual State

Click on the WideState and you should see a red dot appear along side the state name. This indicates that Blend is in visual state editing mode. You should also see a red border appear around the main design area. Note that when you’re in visual state editing mode in Blend, any change you make via the tool windows (eg changing a property) will be tracked against the selected visual state.

Visual State Editing Mode

In visual state editing mode for the WideState, use the Properties window to change the DisplayMode to Inline. Next click the lightning icon button, Edit Adaptive Triggers, alongside the WideState visual state.

Adding an AdaptiveTrigger

In the Collection Editor: StateTriggers, set the MinWindowHeight to 0 and the MinWindowWidth to 900. This is adding a trigger which will return true when the width of the application exceeds 900. At this point the VisualStateManager will automatically switch to the WideState, without you having to explicitly run any code. Repeat the process of setting an adaptive trigger but this time for the NarrowState and set both MinWindowHeight and MinWindowWidth to 0.

The final XAML:

<Page x:Class="UwpSplitViewSample.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:local="using:UwpSplitViewSample"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <SplitView x:Name="splitView"
               DisplayMode="Overlay">
      <SplitView.Pane>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center">
          <TextBlock Text="Pane"
                     TextWrapping="Wrap" />
          <Button Click="PaneButtonClick"
                  Content="Hide Pane" />
        </StackPanel>
      </SplitView.Pane>
      <Grid>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Orientation="Vertical">
          <TextBlock Text="Content Area" />
          <Button Click="ShowPaneButtonClick"
                  Content="Show Pane" />
        </StackPanel>
      </Grid>
    </SplitView>
    <VisualStateManager.VisualStateGroups>
      <VisualStateGroup x:Name="SizeStateGroup">
       
        <VisualState x:Name="WideState">
          <VisualState.StateTriggers>
            <AdaptiveTrigger MinWindowHeight="0"
                             MinWindowWidth="900" />
          </VisualState.StateTriggers>
          <VisualState.Setters>
            <Setter Target="splitView.(SplitView.DisplayMode)" Value="Inline" />
          </VisualState.Setters>
        </VisualState>
        <VisualState x:Name="NarrowState">
          <VisualState.StateTriggers>
            <AdaptiveTrigger MinWindowHeight="0"
                             MinWindowWidth="0" />
          </VisualState.StateTriggers>
        </VisualState>
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
  </Grid>
</Page>

Note: In UWP the order of the VisualState elements doesn’t matter as all the AdaptiveTriggers are evaluated and the best match is determined. For example if the application width is 1000, the conditions for the triggers for both NarrowState and WideState are met. However, the WideState is a better match since the width is > 900. For Uno (i.e. iOS, Android and WebAssembly), the order matters – the first trigger to match, wins. In the above XAML you’ll see the WideState before the NarrowState to make sure that if the application width is > 900, the WideState visual state is active.

Opening and Closing the UWP SplitView Pane

If you run the application at this point and adjust the width of the application the DisplayMode property will switch between Inline and Overlay. However, you won’t notice any change because we currently don’t have a way to open the pane. To do this we’ll add some code to the Click event handlers we created earlier, as follows:

private void PaneButtonClick(object sender, RoutedEventArgs e)
{
    splitView.IsPaneOpen = false;
}
private void ShowPaneButtonClick(object sender, RoutedEventArgs e)
{
    splitView.IsPaneOpen = true;
}

UWP SplitView In Action

With all the XAML and code in place, let’s see it in action on all various platforms.

UWP with Resizing Application
Opening the Pane on WASM
Auto-dismiss Flyout on Android

In this post we’ve covered off how easily you can build a responsive interface using Blend and the UWP SplitView. The fact that it then just works on iOS, Android and WebAssembly is a big bonus thanks to the Uno platform.