XAML Control Templates for Windows (UWP) and Platform.Uno

Recently there has been a lot of discussion about using code to declare the user interface of an app. Such as a recent post I did following the announcement of SwiftUI by Apple. In the Xamarin.Forms world the hashtag CSharpForMarkup has become the latest distraction. CSharpForMarkup encourages developers to move away from XAML to defining their layouts using C#. Whilst I’m not against this, I do feel that we’re all discussing the wrong problem. Whether you use code or XAML to define your layouts, Xamarin.Forms suffers from being tied to the underlying platform controls. Renderers, Effects and Visual are all mechanisms to compensate for not having XAML control templates for every control (more on templates).

Enter Platform.Uno and their mission to take the XAML framework from Windows (UWP) and adapt it to other platforms. As an alternative for building apps for iOS and Android, Platform Uno was moderately interesting. With their push to support WebAssembly, Platform.Uno opens up a world of opportunities for building rich applications for the web. Their recent example of publishing the Windows calculator to the web (https://calculator.platform.uno) is just the the start for this technology.

In this post we’ll walk through how the XAML templating system works and how to go about defining a Control Template to customise the behaviour of controls.

Note: In this post I’ll be using XAML so that I can use Blend to assist with writing XAML. You can choose to use C# if you prefer defining your layouts in code. The point of this post is to highlight the templating capabilities of UWP and Platform.Uno.

Lookless or Templated Controls

One of the foundations of XAML was the notion of a templated, or lookless, control. In his post “What does it mean to be ‘lookless’?” Dave observes that a lookless control is one where the UI is declared in XAML and the behaviour or functionality of the control is defined in code. My view is that a lookless control is one where the functionality of the control isn’t tied to a specific design. By this I mean that each aspect of the control should be defined using a template, thus making it a templated control. A developer using the control should be able to adjust any of the templates without affecting the functionality of the control.

What is a Button?

Let’s look at what this means in practice by examining a Button. If I asked you what you thought a Button in UWP is, you’d probably answer by writing the following element:

<Button Content="Press Me!" />

All you’ve done is declared a Button with content that says “Press Me!” that uses the default Button styles. You haven’t actually answered the question. So let’s start with a simple definition of a button.

A Button is an element that responds to the user tapping or clicking by performing some action.

However, in the context of UWP where a Button is an actual control, this definition is a little too vague. For example, the following Border/TextBlock combination matches the definition

<Border Background="Gray" Tapped="PressMeTapped">
    <TextBlock Text="Press Me!"/>
</Border>

Tapping on the Border does invoke the PressMeTapped event handler but is that all we mean when we say a Button. I would argue that one of the significant aspects to a Button is that it gives some visual cues to the user. By this I mean that when the user hovers their mouse over a button they can the mouse pointer changes, or the Button adjusts how it looks. Similarly, when the user taps or clicks on the Button they should get some visual feedback confirming their action. This feedback is what separates a Button from just simply a tap or click event handler that can be attached to any element.

What we’ve just observed is that the Button has a number of different visual states. In the context of a UWP button these are the Normal, PointerOver and Pressed states. Using the default Button style in the Light theme, these states are shown in the following image.

States in the Control Template for the UWP Button
UWP Button States

As part of extending our definition of a Button I was careful not to define what feedback, or change to layout, should be used for each state of the Button. UWP ships with Dark and Light themes and a set of default control styles that combine to give you the states shown above. However, the important thing to remember is that the visual appearance isn’t what defines a Button. Perhaps an updated definition might be something like:

A Button is an element that responds to the user tapping or clicking by performing some action. It has Normal, PointerOver, Pressed and Disabled visual states that can provide feedback and guide the user on how to interact with the control.

Content v Text Properties

Xamarin.Forms deviates from most other XAML platforms when it comes to defining elements and attributes. For example, instead of using a StackPanel, Xamarin.Forms has a StackLayout. Not only are they named differently, they also exhibit different behaviour. Similarly, the Button control has a Text property instead of a Content property. Whilst this might seem like a decision to simplify the Button control it highlights the limitation of Xamarin.Forms due to the lack of templating.

Let’s back up for a second and take a look at the Content property. In a lot of cases you might simply set the Content attribute to a string, such as the earlier example where I set the Content to “Press Me!”. However, to override the styling of the text, or add other content to the Button, I can set the Content property explicitly.

<Button>
    <StackPanel>
        <TextBlock Style="{StaticResource HeaderTextBlockStyle}"
                   Text="Press Me!" />
        <TextBlock Style="{StaticResource BodyTextBlockStyle}"
                   Text="or.... perhaps not" />
    </StackPanel>
</Button>

Note: As the Content property on the Button is annotated with the ContentPropertyAttribute, we don’t need to wrap the StackPanel in Button.Content tags.

Even though I’ve changed the content of the Button, I haven’t in anyway changed the way the Button functions. In fact, in this case I haven’t even changed how the Button changes appearance for the different states.

Using a Content Template

Before we get into customising the different Button states I just wanted to touch on the use of content templates. In the previous example I showed how the Content of the Button could be set to any arbitrary XAML. What happens if I want to reuse the content layout but just with different text? This is where the ContentTemplate property is useful.

The ContentTemplate property expects a DataTemplate which will be used to define the layout for the Content. At runtime the DataContext of the ContentTemplate will be set to the value of the Content property. Let’s see this in practice by moving the XAML from the Content property into the ContentTemplate.

<Button>
  <Button.Content>
    <local:ButtonData Heading="Press Me!" SubText="or.... perhaps not" />
  </Button.Content>
  <Button.ContentTemplate>
    <DataTemplate>
      <StackPanel>
        <TextBlock Style="{StaticResource HeaderTextBlockStyle}"
                   Text="{Binding Heading}" />
        <TextBlock Style="{StaticResource BodyTextBlockStyle}"
                   Text="{Binding SubText}" />
      </StackPanel>
    </DataTemplate>
  </Button.ContentTemplate>
</Button>

In this code block you can see that the Content property has been set to a new instance of the ButtonData class. The two TextBlock elements are subsequently data bound to the Heading and SubText properties.

public class ButtonData
{
    public string Heading { get; set; }

    public string SubText { get; set; }
}

What this shows is that we’ve separated the data (i.e. the instance of the ButtonData class) from the presentation (i.e. the DataTemplate that is specified for the ContentTemplate property). Now if we wanted to reuse the DataTemplate across multiple elements we can extract it as a StaticResource. The following code illustrates extracting the DataTemplate to a StaticResource, as well as updating the data binding syntax. The x:Bind syntax gives us strongly typed data binding. Meaning we get intellisense and better runtime performance as there are no reflection calls. Button1 and Button2 are properties on the page that return instances of the ButtonData class.

<Page.Resources>
    <DataTemplate x:Key="MyButtonTemplate"
                  x:DataType="local:ButtonData">
        <StackPanel>
            <TextBlock Style="{StaticResource HeaderTextBlockStyle}"
                       Text="{x:Bind Heading}" />
            <TextBlock Style="{StaticResource BodyTextBlockStyle}"
                       Text="{x:Bind SubText}" />
        </StackPanel>
    </DataTemplate>
</Page.Resources>
<Button Content="{x:Bind Button1}"
        ContentTemplate="{StaticResource MyButtonTemplate}" />
<Button Content="{x:Bind Button2}"
        ContentTemplate="{StaticResource MyButtonTemplate}" />

Custom Control Templates

So far we’ve seen how the Button control supports arbitrary Content and the use of ContentTemplates to reuse layouts across multiple controls. Neither of these can be used to adjust the visual appearance of the different Button states. In this section we’re going to start by looking at what properties you can adjust to tweak the existing states. We’ll then bust out Blend and start messing with the way a Button looks. All of this will be done without changing the functionality of the Button.

ThemeResources in the Control Template

In the previous section I added some arbitrary XAML to the Content of the Button. However, when I run the application I still see the default background gray. The background of the Button control is part of the default Template for the control. We’ll delve into this in a little more detail in the next sections. For the time being we’re going to adjust the colour of the background and round the corners on the border.

Adjusting the background of the Button when it’s in the Normal state can be done two ways. You can either specify the Background on the Button element itself, or you can override the ButtonBackground resource with the colour of your choosing.

<Page.Resources>
    <Color x:Key="ButtonBackground">DarkGreen</Color>
</Page.Resources>

<Button Content="{x:Bind Button1}"
        Background="Blue"
        ContentTemplate="{StaticResource MyButtonTemplate}" />
<Button Width="200"
        Height="200"
        HorizontalAlignment="Center"
        Content="{x:Bind Button2}"
        ContentTemplate="{StaticResource MyButtonTemplate}" />

At this point if you run the application up on UWP you’ll see that the initial colours for the two Button elements are blue and dark green respectivey. However, if you move the mouse over each of them you’ll see that they switch back to a grey background. This is because we’ve only adjusted the colour used to set the initial background colour. To adjust the colour for the PointerOver and Pressed states we need to adjust the appropriate resources, which are aptly called ButtonBackgroundPressed and ButtonBackgroundPressed. A full list of the overridable colours is available in the documentation for the Button control. Alternatively you can find the entire list of built in color resources in the ThemeResources.xaml file (more information here).

Button Colors in ThemeResources.xaml

In addition to changing the background colour, we also wanted to round the corners of the border that appears around the control when the user moves the mouse in closer. This can be done by simply setting the CornerRadius property to 10 on the Button element.

When we get to talking about visual states we’ll go through how properties on the Button control are modified with each visual state.

Changing the Control Template Layout

Sometimes it’s necessary to make changes that can’t be achieved by either setting the ContentTemplate or overriding the default colour resources. For example, say you wanted to add the Built to Roam logo as a watermark behind the content of the Button. To do this we need to override the Template for the Button. For this I’m going to use Blend as it’s particularly good at edit templates.

To begin editing the control template, locate the Button in the Objects and Timelines window. Right-click on the [Button] and select Edit Template, followed by Edit a Copy.

Editing a Copy of the Default Button Template

Next, you’ll be prompted to give the new template a name and specify where you want the template to be defined. When the new template is created it will include the default Button Template, thus allowing you to customise it. In this case the WatermarkButtonTemplate will be defined in the current document, limiting its availability to just the Button elements on the page.

Naming the New Control Template

Note: In this example we’re copying the default Button template. However, what gets copied into the XAML is actually the entire default Button style. This includes all the default property setters. The value of the Template property is set to a new instance of a ControlTemplate.

I’m not going to go through the changes to the template in detail but the summary is:

  • Wrap the ContentPresenter in a Grid called ContentFrame
  • Move Background, BackgroundSizing, BorderBrush, BorderThickness and CornerRadius from the ContentPresenter to ContentFrame
  • Adjust Visual States so that they reference ContentFrame for border and background properties. Leave any references to Foreground targetting the ContentPresenter element.
  • Add an Image inside the Grid with Source set to an image added to the Assets folder.
  • Set Opacity to 20% (0.2) and Stretch to Uniform on the Image.

Before:

<ControlTemplate TargetType="Button">
    <ContentPresenter
        x:Name="ContentPresenter"
        Padding="{TemplateBinding Padding}"
        HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
        VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
        AutomationProperties.AccessibilityView="Raw"
        Background="{TemplateBinding Background}"
        BackgroundSizing="{TemplateBinding BackgroundSizing}"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}"
        Content="{TemplateBinding Content}"
        ContentTemplate="{TemplateBinding ContentTemplate}"
        ContentTransitions="{TemplateBinding ContentTransitions}"
        CornerRadius="{TemplateBinding CornerRadius}">
        <VisualStateManager.VisualStateGroups>                
        ...
        </VisualStateManager.VisualStateGroups>
    </ContentPresenter>
</ControlTemplate>

After:

<ControlTemplate TargetType="Button">
    <Grid
        x:Name="ContentFrame"
        Padding="{TemplateBinding Padding}"
        Background="{TemplateBinding Background}"
        BackgroundSizing="{TemplateBinding BackgroundSizing}"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}"
        CornerRadius="{TemplateBinding CornerRadius}">
        <Image
            Opacity="0.2"
            Source="Assets/BuiltToRoamLogo.png"
            Stretch="Uniform" />
        <ContentPresenter
            x:Name="ContentPresenter"
            HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
            VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
            AutomationProperties.AccessibilityView="Raw"
            Content="{TemplateBinding Content}"
            ContentTemplate="{TemplateBinding ContentTemplate}"
            ContentTransitions="{TemplateBinding ContentTransitions}"
            FontFamily="Segoe UI" />
        <VisualStateManager.VisualStateGroups>
        ...
        </VisualStateManager.VisualStateGroups>
    </Grid>
</ControlTemplate>

The net result is that the image is displayed on top of the background colour (which is set on the parent Grid) but beneath the content.

The important thing about defining this ControlTemplate is that it can be reused on any Button, irrespective of the Content that is being displayed. Again, these changes have affected the design of the Button but we still haven’t modified what happens when the user interacts with the Button.

Tweaking Control Template Visual States

The last set of changes we’re going to look at is how we modify the behaviour of the Button when the user does interact with it. In this case we’re going to look at the Pressed state. We’re going to change the default behaviour to slightly enlarge the Button when it’s in the Pressed state. To do this we need to modify the Pressed VisualState that is defined inside the ControlTemplate.

Using Blend we’ll step through adjusting the Pressed Visual State to expand the Button to 150% of it’s original size (i.e. a scale of 1.5 in both X and Y directions). To get started in the Objects and Timeline window, right click the Button element and select Edit Template, then Edit Current. This is similar to what we did previously but this time we already have a ControlTemplate in the XAML for the page.

Edit an Existing Control Template

Next, from the States window, select the Pressed state. If you click where the word Pressed is written you should see both the focus border around the Pressed state and a red dot appear alongside, indicating that Blend is in recording mode. You should also see a red border appear around the main design surface. If you look at the Button on the design surface it should appear like it’s been Pressed to illustrate what the Pressed state looks like.

Switching to the Pressed state

In the Properties window, locate the Transform section. Select the third icon in, which is for adjusting the Scale of the element. Set both X and Y values to 1.5. You should see a white square alongside each of the values indicating that you’ve explicitly set the value.

Adjusting the Scale Transform for X and Y

If you go back to the Objects and Timeline window you should see that under the ContentFrame there are entries for RenderTransform/ScaleX and RenderTransform/ScaleY to indicate that these properties have been modified for the currently selected visual state.

Visual State Properties

If you run the application now and click on the Button you should see that whilst the mouse button is depressed the Button moves to the Pressed state which shows the Button enlarged.

Enlarged Pressed State for Button

If you examine the change to the XAML, what you’ll see is that two Setters were added to the Pressed Visual State.

<VisualState x:Name="Pressed">
    <VisualState.Setters>
        <Setter Target="ContentFrame.(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Value="1.5" />
        <Setter Target="ContentFrame.(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Value="1.5" />
    </VisualState.Setters>
    <Storyboard>
        ... other existing animations ...
    </Storyboard>
</VisualState>

Animating Content Template State Transitions

The change we just made to the Control Template was to change the Scale X and Scale Y properties for the Pressed state. However, this is rather a jarring experience as there’s no transition or animation defined. The final step in this process is to add a transition both too and from the Pressed state.

In the States window, click the arrow with a plus sign and select the first item (i.e. *=>Pressed) to add a transition that will be invoked whenever the Button goes to the Pressed state, regardless of the initial state.

Adding Transition into Pressed State

If you look in the Objects and Timeline window you can now see a timeline that will define what animations are run during the transition.

Start of the Transition Timeline

Over in the Properties window, locate the Transform section and click on the empty square alongside the X property. Select Convert to Local Value, which will set the value to 1. The square will now appear as a sold white square. Repeat for Scale Y

Setting the Start of the Transition

Back in the Objects and Timeline window you should now see a keyframe marker appear at time 0. Drag the vertical orange line to 0.5 seconds.

End of Transition in Timeline

Now in the Properties window set the Scale X and Scale Y to 1.5, which is the end values for the transition. Thisshould match the values previously set for the Pressed state.

End Transition Values

Repeat this process for a transition for leaving the Pressed state. The transition should be the reverse with the Scale X and Y starting at 1.5 and ending at 1.

Transitions In and Out of Pressed State

And there you have it, an animated Pressed state.

Animated Pressed State in Control Template

The final XAML for the Transitions includes two Storyboards that defines the transition to and from the Pressed state.

<VisualStateGroup.Transitions>
    <VisualTransition GeneratedDuration="00:00:00"
                      To="Pressed">
        <Storyboard>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentFrame"
                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)">
                <EasingDoubleKeyFrame KeyTime="00:00:00"
                                      Value="1" />
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
                                      Value="1.5" />
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentFrame"
                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)">
                <EasingDoubleKeyFrame KeyTime="00:00:00"
                                      Value="1" />
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
                                      Value="1.5" />
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </VisualTransition>
    <VisualTransition GeneratedDuration="00:00:00"
                      From="Pressed">
        <Storyboard>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentFrame"
                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)">
                <EasingDoubleKeyFrame KeyTime="00:00:00"
                                      Value="1.5" />
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
                                      Value="1" />
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentFrame"
                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)">
                <EasingDoubleKeyFrame KeyTime="00:00:00"
                                      Value="1.5" />
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
                                      Value="1" />
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </VisualTransition>
</VisualStateGroup.Transitions>

As you can see from this walkthrough, the XAML templating system is relatively sophisticated. It allows for multiple levels of configuration or tweaking in order to get it looking just right.

In coming posts I’ll be talking more about what differentiates XAML and why it’s still being used across Windows, Xamarin.Forms and of course Platform Uno. If you’re looking for cross platform solutions, please don’t hesitate to contact me or anyone on the team at Built to Roam.

Xamarin Developer Summit Schedule Breakdown

In just under a month some of the biggest names in the Xamarin community will be presenting alongside a prominent members of the Microsoft Xamarin and Xamarin.Forms teams at the Xamarin Developer Summit.

Xamarin Developer Summit – 11/12 July – Houston, Texas

Whilst I’m not going to be able to make it across to the summit I wanted to do a bit call out to all the great sessions that are in the schedule. The summit is a result of the massive effort that Dan Siegel has put in and the support from all the fantastic presenters.

If you scan the schedule you’ll no doubt be familiar with some of the names but with so many great sessions, how do you know where to start. I thought I’d take the opportunity to break the sessions into some groups to help you decide what’s of interest. These are of course just my groups based on what I can gather from the session description.

Getting Started / Overview

Workshops

Optimisation / Quality

Testing

Mvvm / Architecture

  • Xamarin.Forms made better with Prism – Hussain Abbasi
  • Streamline & Simplify Events with Reactive Extensions – Shane Neuville
  • Rapid Enterprise Architecture Delivery – Paul Schroeder
  • Reactive UI and Reactive Extensions for Xamarin.Forms – Michael Stonis
  • Mobile architecture with MvvmCross, are you doing it right? – Martijn van Dijk
  • Using Xamarin.Forms Shell to easily create a consistent, dynamic, customized, and feature filled UI – Shane Neuville

Security

  • Authentication and Authorization for Xamarin apps using ADB2C and MSAL – David Allen

Device

AI / ML

  • Cognitive Services in Xamarin Applications – Veronika Kolesnikova
  • Build Smarter Cross-Platform Applications Using Xamarin, Azure Cognitive Services, and ML.NET – Richard Taylor
  • How to build your modern ai app with Xamarin – Lo Kinfey

Azure

  • Crafting Real-Time Mobile Apps with SignalR – James Montemagno
  • Light up Xamarin Apps with ChatBots – Sam Basu
  • Build a mobile chatbot with Xamarin & Bot Framework – Luis Beltran
  • Build a Media Streaming App With Azure and Xamarin – Matt Soucoup
  • Create Mixed Reality Experiences with Azure Spatial Anchors and Xamarin – Sweekriti Satpathy

Data

Design

  • Let’s Make Crazy Beautiful UI With Xamarin.Forms – David Ortinau
  • Creating Consistent UI with Xamarin.Forms Visual – David Ortinau

General

DevOps

For more information on any of these topics then you should Register and bring all your learnings back to your company, community through user groups and other forums.

Navigate Flutter Apps with Routes

One of the most important aspects of an app is the flow or journey that the user takes through the app. Apps are often described in terms of pages, or screens, and navigating between them. In this post I’m going to cover dividing your application into routes, and how to work with the Flutter navigation system.

Whilst a Flutter app is constructed out of widgets, there still needs to be a mechanism for the app to respond to user interaction. For example, tapping on an row in a list of items might display the details of that item. Subsequently tapping on the back button should return the user to the list of items. In order to support navigating between different pages, Flutter includes the Navigator class. According to the documentation the Navigator class is:

 A widget that manages a set of child widgets with a stack discipline. 

For someone who’s just got their head around how to layout widgets, this statement makes no sense. In this post we’re going to ignore this definition and cover the basics of how to navigate between pages. I’m also going to ignore the Flutter definition of a Route. For now we’ll assume that a route is roughly equivalent to a page or a screen in your app. As you can imagine, all but very basic apps have multiple pages that the user is able to navigate between. Flutter apps are no different except that you navigate Flutter apps with routes.

Flutter Navigation

Let’s get into navigating between routes. We’ll start with almost the most basic Flutter app you can create. Technically you can create a Flutter app that doesn’t start with the MaterialApp but then you’re left doing a lot of heavy lifting yourself. The following code sets up a basic app that is comprised of two classes that inherit from StatelessWidget. I could have combined these into a single widget by simply setting the value of the home attribute in the MaterialApp to be the new Container widget. However, I’ve created a separate widget, FirstNoRoutePage, to help make it easy to see how the pages of the app are defined.

void main() => runApp(MyNoRouteNavApp());

class MyNoRouteNavApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: FirstNoRoutePage(),
    );
  }
}

class FirstNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: Text('First Page'),
    );
  }
}

Ad-Hoc Routes with Navigator.push()

Now that we have the basic app structure, it’s time to navigate to our first route. We’re going to use the Navigator widget to push a newly created, or ad-hoc, route onto the stack. The following code:

  • Wraps the Text widget in a FlatButton
  • The onPressed calls the push method on the Navigator, passing in a newly constructed MaterialPageRoute.
  • The MaterialPageRoute builder is set to return a new instance of the SecondNoRoutePage widget
class FirstNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'First Page',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.push(
              context,
              MaterialPageRoute(
                builder: (BuildContext context) => SecondNoRoutePage(),
              ));
        },
      ),
    );
  }
}

class SecondNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: Text(
        'Second Page',
        style: TextStyle(color: Colors.white),
      ),
    );
  }
}

We now have an application that has two pages that the user can navigate between. On the first page, clicking on the “First Page” text will push a new route that shows the second page (i.e. the SecondNoRoutePage widget). Pressing the device back button (Android only) will navigate the user back to the first page. It does this by popping the route off the stack maintained by the Navigator.

Go Back with Navigator.pop()

For the purpose of this post I’ve kept the pages simple, showing only elements necessary to show which page the user is on, or to allow the user to perform an action. I’ve chosen not to use a Scaffold, which means no AppBar. This also means no back button shown in the AppBar. On iOS this makes it impossible for the user to navigate to the previous page as there is no dedicated back button, unlike Android. If you use a Scaffold in your widget, you’ll see the AppBar. The AppBar adjusts to include a back button if there’s more than one route on the stack.

Flutter provides built in support for navigating back to the previous route via the AppBar back button or, in the case of Android, the device back button. In addition, the pop method on the Navigator can be used to pop the current route off the stack. This will return the user to the previous route.

class SecondNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'Second Page',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.pop(context);
        },
      ),
    );
  }
}

What the Current Route?

Earlier in the post we created a new route when navigating to the second page of the app. However, what we glossed over is the fact that the first page of the app is also a route. To show this, let’s add some code that adds the name of the current route to both pages. This will highlight where in the Flutter navigation stack the user is currently at.

class FirstNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var route = ModalRoute.of(context).settings.name;
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'First Page - $route',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.push(
              context,
              MaterialPageRoute(
                builder: (BuildContext context) => SecondNoRoutePage(),
              ));
        },
      ),
    );
  }
}

class SecondNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var route = ModalRoute.of(context).settings.name;
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'Second Page - $route',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.pop(context);
        },
      ),
    );
  }
}

What gets displayed on the two pages are ‘First Page – /’ and ‘Second Page – null’. This indicates that the first route is assigned a name of ‘/’, whilst the second route does not have a name (i.e. null value). The first route is created from the home property being set on the MaterialApp. It is assigned a name equal to the defaultRouteName constant from the Navigator class (i.e. ‘/’) to indicate it is the entry point for the application.

As we created the route for the second page, it’s our responsibility to name the route. In the above scenario we’re creating the route at the point where it is required (i.e. when navigating to the second page), and as such we don’t need to give it a name. That is, unless we want to see that the current route is by inspecting it’s name. Let’s update the call to Navigator.push to include a name for the second route.

onPressed: () {
  Navigator.push(
      context,
      MaterialPageRoute(
        builder: (BuildContext context) => SecondNoRoutePage(),
        settings: RouteSettings(name: '/second')
      ));
},

There’s no requirement for the route name to include the ‘/’. However, it is a convention to do so, and there are some scenarios where the ‘/’ has some implied behaviour.

Named Routes in Flutter Navigation

In the previous section we added a name to the ad-hoc route that we created. An alternative to creating routes as they’re required, is to define the routes for the app up front. For example the following code shows three routes that have been defined for an app and can be used for Flutter navigation.

class Routes {
  static const String firstPage = '/';
  static const String secondPage = '/second';
  static const String thirdPage = '/third';
}

class MyNavApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        Routes.firstPage: (BuildContext context) => FirstPage(),
        Routes.secondPage: (BuildContext context) => SecondPage(),
        Routes.thirdPage: (BuildContext context) => ThirdPage(),
      },
    );
  }
}

The Routes class includes the names of each of the three routes. Inside the constructor of the MaterialApp widget the three routes have been declared. Each route is an association between the route name and the builder method that’s used to construct the widget.

Navigate.pushNamed()

When the user clicks the button, the Navigate.pushNamed method is used to navigate to the corresponding route:

onPressed: () {
  Navigator.pushNamed(context, Routes.secondPage);
},

Skipping Over Routes with Navigator.popUntil

Using named routes not only makes it easier to manage the pages in your app, it also means that you can create conditional logic that is dependent on the route name. For example, the Navigator.popUntil allows you to keep popping routes off the stack until the predicate is true. In the following code the predicate looks at the name property of the route to determine whether it is the first page of the app.

class ThirdPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'Third Page',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.popUntil(context, (r) => r.settings.name == Routes.firstPage);
        },
      ),
    );
  }
}

Setting the Initial Route

Initially the first page of the app was defined by setting the home property. This was used to define the initial route for the app. When the app was updated to use a set of predefined routes, the initial route was inferred by looking for the route where the name matches the defaultRouteName (i.e. ‘/’). If there wasn’t a route with that name isn’t found, an error will be raised and the app will fail to start.

It is also possible to set the initialRoute property on the MaterialApp. However, this doesn’t negate the need to set either the home property, or have a route declared with a name of ‘/’. What’s really interesting about the initialRoute property is that it can be used to launch the app on a route that has other routes in the navigation stack. For example in the following code the initialRoute is set to the thirdPage, which is defined as ‘/second/third’. When this app launches, it will launch showing ThirdPage but if the user taps the back button, they will go to SecondPage and then FirstPage if they tap again.

class Routes {
  static const String firstPage = '/';
  static const String secondPage = '/second';
  static const String thirdPage = '/second/third';
}

class MyNavApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: Routes.thirdPage,
      routes: {
        Routes.firstPage: (BuildContext context) => FirstPage(),
        Routes.secondPage: (BuildContext context) => SecondPage(),
        Routes.thirdPage: (BuildContext context) => ThirdPage(),
      },
    );
  }
}

On app startup the initalRoute is split on ‘/’ and then any routes that match the segments are navigated to. In this case the app navigates to FirstPage, then SecondPage and finally ThirdPage.

Intercepting Back Button

The last topic for this post looks at how to intercept the back button. This can be done by including the WillPopScope widget and returning an appropriate value in the onWillPop callback.

class ThirdPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        return new Future.value(true);
      },
      child: Container(
        alignment: Alignment.center,
        child: Text('Third Page'),
      ),
    );
  }
}

Note that the onWillPop expects a Future to be returned, allowing you to return an asynchronous result. In this case the code is simply returning the value of true to allow the current route to be popped.

Summary of Flutter Navigation

This post on Flutter navigation has covered a number of the navigation related methods on the Navigator class. As part of defining how your app looks, you should define all the routes and how the user will navigate between them. If you do this early in the development process you’ll be able to validate the flow of your app as you continue development.

Apple Introduces SwiftUI; So What?

Shortly after Apple announced SwiftUI a twitter thread erupted discussing a hypothetical Sharp UI. It was positioned an alternative for declarative ui development, across Xamarin applications in C# or F#.

What’s interesting is that both Google, with Jetpack Compose, and now Apple, with SwiftUI, have joined the modern evolution of app development by introducing a declarative way to define user interfaces. Declarative UI development has been around for a long time. For example, take any of a number of XAML based frameworks that Microsoft has produced (something completely missed by Martin’s post on What SwiftUI Means for Flutter who incorrectly claims declarative ui development was invented in React by Facebook).

So why now? Why is it that Apple, Google and Microsoft have all recognised that declarative UI is the way forward?

XAML as a Declarative UI

The back history of XAML goes way back to the WinForms days. It was common for developers to fight the IDE in order to wrestle control of the window layout. XAML was supposed to fix everything. It is not designed for humans (much the same way storyboards weren’t designed to be manually coded). XAML id designed for developer tooling such as Blend.

A few XAML frameworks later and what we find ourselves in Xamarin.Forms. The XAML is non-standard version. It is similar, yet in ways dramatically different from every flavour of XAML that predates it. The industry has moved on from trying to get previewers, such as Blend, to work. The developer community favours hot reload and the ability to adjust layout within a running app.

I’m sure that Xamarin.Forms will get there with XAML but is it too much of a liability? Should we look for an alternative?

Declarative UI in Code aka #CSharpForMarkup

Following down the discussion on the SharpUI twitter thread we end up discussing an alternative to XAML, which is declaring UI in code. This sounds awfully familiar to what SwiftUI or Flutter is doing, except this is for Xamarin.Forms.

Normally I would be against using declarative ui development in code as I feel that it becomes harder to separate the UI logic from the application logic. However, having spent time reviewing CSharpForMarkup I feel that it is a viable alternative to XAML and perhaps even removes a layer or two of the cognitive load Adam talks about

Cross Platform is the Future

At Built to Roam we spend a lot of time discussing app strategy with our clients. We often talk about the spectrum of app development options ranging from native all the way through to web. Almost the first thing we do is to discount and remove from discussion both native and web. If the client wanted a web experience, they would have gone to a web development agency, instead of come to us. We’re not going to recommend building a native application, even in Xamarin, when we should be considering cross platform options.

If you’re following the announcements about SwiftUI, or Jetpack Compose, sure go ahead and read up on them. Then pack them into their single platform box and put them back on the shelf. Take our your cross platform tool of choice (React Native, Flutter, Xamarin.Forms etc) and get back to building high quality amazing apps for both iOS and Android.


Nick Randolph @thenickrandolph
If you have an app and want to go cross platform, or are just starting you app development journey, contact Built to Roam.


Productivity Musings on App Navigation, Information Density and Work Environment

This morning I set out to explore the most recent ramblings on app navigation. I expected to come across a bunch of designers talking about their sliding, popping, whirling interface. How they are going to radicalize the way we interact with mobile applications. The first thing I was struck with was that I needed to get out of my own mobile-centric thought process. I immediately extended my investigation to include desktop, tablet and of course web. After coming across various sites about different navigation constructs, my thought process wandered off. I soon found myself reflecting on some of the design decisions that we live and work with daily. This lead me to some productivity musings that I think generate more questions than answers.

Mobile is Not Desktop

The launch point for my productivity musings was that a couple of the sites reminded me of the panorama and pivot controls from Windows Phone. I think some credit should go to Microsoft’s Metro Modern Fluent (whatever it’s now called) design system. The Panorama and Pivot controls, when done well provided an immersive experience that let users rapidly dig into information.

Fast forward to Windows 8 and Microsoft decides to expand the Windows Phone design system to big-Windows….. #FAIL. We tried. We really did try. And in some cases we produced some amazing applications. However, fundamentally the design system wasn’t designed to scale. The Panorama didn’t have the right information density for a desktop application. One of the reasons that the Mail and Calendar apps for Windows were never able to take on Outlook, is that the information density is just too low.

Stupid Apps

I feel we would be remiss for not chastising Apple for a generation of “stupid apps”. When the iPhone came out, it set about revolutionizing what the world meant when it said smartphone. It was like all the devices prior to it weren’t smart. This simply isn’t true – there were plenty of devices in market that from a functionality perspective were smart. What Apple did was to bring smartphones to the masses. They did that by dumbing it down. Apps did one thing. You could run one App at a time. You can only get Apps from the App Store. There was only one devices that had Apps, the iPhone.

This lead the way for a new breed of developers, “App Developers”. These were developers who were able to string a couple of screens together and call it an app. These developers were in a league of their own. Web and desktop developers looked over and saw “App Developers” making money from apps that did one thing. Ina lot of cases the apps didn’t even do that one thing well, in other words, stupid apps.

Over time the mobile ecosystem has evolved. App Developers have matured and today most app developers build apps for both iOS and Android. Apps themselves have evolved and increased in complexity and sophistication. AI and Machine Learning is being integrated to allow mobile applications to solve a range of tasks. However, despite all this innovation, the majority of mobile apps still do only a small number of functions.

Information Density

As my productivity musings continue, if we look at apps designed for desktop, we can see that they have more functions and much higher information density. Apps such as Word, Excel, VS Code and Photoshop are heavily optimised for mouse and keyboard. As such there is a much high information density, allowing more data to be presented and manipulated on any given screen.

Of course, there’s a trade off between ease of use and information density. Actually, let me correct that somewhat. It’s not necessarily ease of use that decreases with increase information. Rather, it’s the ability for new users to learn an application. Take Excel for example. There are plenty of spreadsheet alternatives out there. I’m continually amazed by the number of people that use Google Sheets. This is because they’ve only ever learnt the first 10% of spreadsheet capabilities. Those that you pick up when you first start using a spreadsheet. Most users barely get past using a spreadsheet as a glorified list making tool. If you look at how spreadsheets are used in finance or engineering, it’s staggering how much data can be calculated, displayed and interpreted on a single screen.

What struck me about the links that I read regarding navigation, is that very few of them deal with high information density. All the designs seem to be focused on building slick interfaces that work well with a single hand, or respond well when the browser is resized. There’s almost no mention of how to deal with complex data sets, or multiple windows, or the learning process for complex applications.

Multiple Windows

At this point, my productivity musings turned towards a pet annoyance, which is the lack of support for multiple windows. Since around the time of Windows 8, it seems that most developers have forgotten that both PC and Mac support applications with multiple windows. Both Apple and Microsoft realise the significance of the window metaphor. Whilst it was Microsoft that adopted the name Windows for the OS, both companies continue to support and evolve the window metaphor.

It should be noted that in Windows 8, Microsoft made a daft move by trying to push apps full screen. How did it make sense to have an operating system called Windows that doesn’t support applications running in a windows? Unfortunately I think the lasting impact of this, coupled with the birth of app developers who knew how to build “stupid apps,” has meant that we’ve had to put up with a host of single-windowed apps.

Take for example this comment from Derik about wanting to pop out an editor window to drag to another screen:

Desktop apps, for both PC and Mac, need to start by thinking in multiple windows. Don’t just make it an after thought.

Window Management

We’re approaching the end of my productivity musings. At this point I went on a slight tangent along the idea of multiple windows and thinking about the way that they’re managed. Both Windows and MacOS have mechanisms for closing, opening, restoring, minimising, splitting windows. However, the one thing I routinely notice among Mac users is that they struggle with basic window management. Perhaps they’ve never bother to spend the time to work out how to manage windows; perhaps it’s harder to do on a Mac. Personally I stick with Windows as that’s where I’m comfortable. If you can’t manage your application windows effectively, that’s on you to learn how to do it.

Multi-Monitor

Moving on from multiple windows we get to multiple monitors. This one is a criticism of companies that choose to invest in finding the right staff but then fail to equip them to do their job. If your staff do work that’s heavily computational, make sure they have a desktop PC or Mac to work on. Regardless of whether you equip them with a desktop or a laptop, when your staff are working at a desk, they need to have at least two external monitors to work on and ideally external mouse and keyboard. You’ll spend a little more up front setting up their work station but you’ll get that back in productivity within the first month, if not the first week!

That’s Not a Mouse!

If you decide that you’re going to supply MacBook or iMac or some other Apple product to your staff, please make sure you do the right thing and buy them a real mouse and keyboard set.

A mouse has more than just a single function. Go get a mouse that will really get the job done.

A keyboard isn’t a work of art. Go get a keyboard that will really make you productive. It doesn’t have to be split like in the following image but it is worth considering split keyboards as they are supposed to be more ergonomic.

Productivity Musings in Summary

This post is a bit of a ramble about the hits and misses of app development. However, it’s worth consider where we are in the software development industry. Are we so focused on the latest technology; the latest hot trend; the latest design metaphor, that we’ve lost the ability to build productivity software?


App Navigation Links

The following are a selection of links that I browsed when investigating some of the latest trends when it comes to navigation within applications. Whilst they’re not strictly a productivity musing, they did form the genesis for this post and I’d recommend taking a read as they may influence how you design the experience of your next application.

Decompilers for .NET and Windows (UWP) Apps

I think I’ve been living under a rock as I’ve only just come across dnSpy, a decompiler for .net!

I’ve been building apps and services with .NET for a long time, so a Twitter thread talking about decompilers amused me. David Kean’s comment pretty accurately reflects my sentiment regarding Reflector.

Reflector was such a simple tool and it just worked. That was until Red Gate took over and I can’t even remember what happened to it. Does it still exist?

The thread started with Jared commenting that developers should be using ilSpy instead of hacking with ildasm. I’ve rocked ilSpy in my toolkit for a while now and it’s always served me well. In fact I go so far as to set it as the default file handler for .dll files. After all, what other program are you going to want to launch when you double-click on a .dll file.

dnSpy: A Decompiler for .NET

What really knocked my socks off was that someone mentioned dnSpy which I’d never heard of. Thinking it was something similar to ilspy I didn’t think much of it but figured I would download it and take a look.

Next thing I knew it was like I had opened an entire development environment. The layout was familiar, all the way down to the Debug menu that allows me to attach to process.

What? come again? Attach to Process? Yes, that’s right you can attach to a running process, set breakpoints and intercept exceptions (and I’m sure a whole bunch more things. Ironically whilst I was attempting to modify an image of dnspy running, Paint.NET crashed on me. Whilst Paint.NET was hung, I was able to attach to the process using dnspy and see that it was stuck waiting for a print dialog to return.

dnSpy: a Decompiler for .NET

If you’re using ilspy, you should check out dnspy

For anyone still using ildasm, you should check out ilspy and dnspy

Developers not using a decompiler, what have you been doing? Get yourself a decompiler for .NET, and use either ilspy or dnspy.


Nick Randolph @thenickrandolph
If you need help debugging your application, contact Built to Roam