Tips for Building Adaptive Apps with Xamarin.Forms - Part 1
An adaptive UI adapts its layout based on the needs of the user. You may adapt your UI for several reasons, such as the user’s role or experience. Most of the time, you will change an app layout or features when it will run on devices with different sizes. Adaptive apps are similair to responsive apps. They both change appearance based on the browser environment they are being viewed on. The main difference between the two is that adaptive app design has multiple layout sizes -- that are fixed. When the site detects the available space, it selects the layout most appropriate for that specific device and snaps into place.
Responsive websites respond to the size of the browser. If you open a responsive site on the desktop and then change the size of the browser window, the content will smoothly move to arrange for the browser window.
Building adaptive applications
One of the goals when developing an adaptive UI or app is to avoid duplication. For example, you typically don't want to develop your app twice, once for tablets and again for phones. You want to have one single app with one single instance of each UI for all devices. There is a lot of information on how to create great mobile apps that run on all platforms with Xamarin.Forms, but one area that is lacking is the adaptive conversation and optimizing apps for a range of device sizes. For instance, while it requires no additional work for your app to work on phones and tablets, you may not be taking advantage of different screen sizes or your app may not look as good as it could at different resolutions.
With Xamarin.Forms apps not only are we developing once for Android, iOS and Windows, but also all device sizes like phones and tablets (and desktop). This means that our UI needs to be adaptive in order to maximize sharing and reduce repetitive code while still delivering the best experience for all users. In this blog post I will show you all of the basic tips available today to help you create the most adaptive Xamarin.Forms UI possible.
Tip 1: Use Basic Layout Controls
XAML is a beautiful application layout. When Microsoft wrote it they took the best from HTML and combined it with new ideas focused on building application UI’s to really make a great markup language. Xamarin.Forms uses a variation of Microsoft's original XAML with a few key layout controls: Grid, StackPanel, and ScrollView. The first tip toward creating an adaptive UI is to take advantage and use the primitive layout controls as they're intended. With these controls you can avoid using absolute positioning and sizes of inner controls to get a very fluid layout that stretches and squeezes onto almost every screen size. I'll give a brief overview of each primitive layout control for newcomers.
Grid - with a Grid layout, you specifically position all of its children. You set which row they go in, and how many columns they span. Then, based on the size you set for each row or column, the children will fill the space. That means we don’t normally set Height and Width explicitly on the children.
StackLayout – with a StackLayout, you create a single line of child elements either horizontally or vertically. With a StackLayout it’s more common to set the Width or Height (typically not both) on child elements since the children don’t stretch to fill the entire space like in a Grid.
ScrollView – When content may overflow on a smaller device you can put your Grid or StackLayout inside a ScrollView to get automatic scrolling. By automatic, I mean that scrolling is only enabled when it’s necessary. If you’re using a ListView or TableView you get scrolling already because these controls have a ScrollViewer in their templates. Using ScrollViewers separately is often overlooked by developers because it’s not even listed as a type of control or view in the documentation, yet it’s very useful in many situations.
By using these primitive XAML layout controls you can guarantee that your content is at least viewable on all sized devices. The next step you may want to take is to offer a different experience on different devices.
Tip 2: Use Device.Idiom
You may have heard of the Xamarin.Forms.Device class that gives you information that helps you customize layout and functionality on a per-platform basis. It gives you info about whether the app is being run on Windows, iOS, or Android, and you’ve probably seen this used in many samples (the most common usage is to add extra padding to the top of pages Only in iOS to avoid the status bar). In this class there is also the Idiom enumeration which tells information about the type of device such as phone, tablet or in the case of UWP, sometimes a desktop app. You can use this class to provide device type specific logic. This is what the code looks like:
if (Xamarin.Forms.Device.Idiom == TargetIdiom.Phone)
{
// apply phone only code
mainGrid.Padding = new Thickness(10, 10, 10, 10);
}
else if (Xamarin.Forms.Device.Idiom == TargetIdiom.Tablet)
{
// apply tablet only code
mainGrid.Padding = new Thickness(100, 10, 100, 10);
}
This code adds more left and right padding to a Grid named "mainGrid" for tablet devices.
Use Device.Idiom in XAML
Now if you’re familiar with XAML a question you’ll probably seek out is how can we set this same logic in XAML? There actually is a way we can set this logic completely in XAML.
<Grid x:Name="mainGrid">
<Grid.Padding>
<OnIdiom x:TypeArguments="Thickness" Tablet="100, 10, 100, 10" Phone="10, 10, 10, 10" />
</Grid.Padding>
...
</Grid>
To set a property in XAML you simply open the tags of that property, and create an element of the expected value type, in this case it’s OnIdiom. The TypeArguments property here is required and probably the trickiest part especially if you’re setting some specialized property type. The easiest way to determine this value is to jump over to your code and see what type the property is. In this case a grid’s padding property is of type Thickness. Then we can set the Tablet and Phone values just as if we were setting it in code.
Use Device.Idiom with Styles
Using Device.Idiom in XAML or code can sometimes get repetitive and inefficient once you start applying similar changes throughout your app. Styles let you reuse any part of XAML including Device.Idiom. For example, imagine you want to create a "phone style" and a "tablet style" for a page that just sets the padding appropriately for each device size. You can simply define one Style for each and set it on every page. Then when you want to change some aspect of either style you can change it in one place. Styles are a feature of XAML that allow you to encapsulate property settings in one location and reuse them throughout your app. You can create implicit styles, that apply to all controls of a type, or explicit styles that have a unique key and get assigned to specific controls. Styles are scoped to a single page or globally depending on where you define them. I’m not going to go any deeper into explaining how styles work, so check out the Xamarin.Forms documentation for more. Let's see how you can use Styles with Device.Idiom. Following the example I mentioned above with a phone style and a tablet style for all pages, here I've defined my two styles in my app.xaml file with keys (this creates an explicit, global style).
<Application.Resources>
<ResourceDictionary>
<Style x:Key="PhoneGridStyle" TargetType="Grid">
<Setter Property="Padding" Value="10, 10, 10, 10" />
</$tyle>
<Style x:Key="TabletGridStyle" TargetType="Grid">
<Setter Property="Padding" Value="100, 10, 100, 10" />
</Style>
</ResourceDictionary>
</Application.Resources>
Notice that my styles actually target a Grid and not a Page, but I think you could target a Page object too assuming it has all the properties you need to modify. Then on every page that has a Grid I want to set its Style to my global styles and I can use Device.Idiom to set them accordingly.
<Grid>
<Grid.Style>
<OnIdiom x:TypeArguments="Style" Tablet="{StaticResource TabletGridStyle}" Phone="{StaticResource PhoneGridStyle}" />
</Grid.Style>
...
</Grid>
This really helps minimize my XAML so maintenance is a breeze. Remember that the styles are not the important part of making your app adaptive. The key takeaway is using the Device.Idiom class but just know that you can set it in code or XAML, with or without a Style – whatever you prefer.
Tip 3: Handle Device Orientation
Making your app adaptive isn’t just about device type or size, it can also be about device orientation. The code examples I’ve shown so far do not take orientation of the device into account. Currently there’s no special namespace or class that gives you this information in Xamarin.Forms. There are some samples provided by Xamarin that show how to use a DependencyService to get it, however, I’ve found that the easiest solution is to just compare the app’s width to the height using App.Current.MainPage. Most if not all devices are rectangular so this provides all the information you need. Here’s an example:
private void MyPage_SizeChanged(object sender, EventArgs e)
{
if(App.Current.MainPage.Width > App.Current.MainPage.Height)
{
// device is landscape
}
else
{
// device is portrait (or square)
}
}
Of course this doesn’t tell you if the device is upside down like the Dependency Service might. So why might you want this? Well, it may be a good opportunity to adjust the layout direction of your UI to create a horizontal display for Landscape and a vertical display for Portrait, regardless of device type. Sometimes the direction of content flow is more important than device size. Knowing how to handle this is useful and can provide different results from just relying on the Xamarin.Forms.Device class alone for adaptive layouts.
Additional adaptive app techniques
In this post I covered the most basic techniques for creating adaptive Xamarin.Forms UIs. Using a combination of Device.Idiom alongside XAML best practices while also detecting device orientation are the fundamental tips for creating adaptive Xamarin.Forms apps. In a second part of this three-part series, we discuss some specialized features of Xamarin.Forms that lend themselves to adaptive apps: ContentViews and the MasterDetailPage template.
For additional reading on building adaptive apps with Xamarin.Forms, be sure to check out Tips for Building Adaptive Apps with Xamarin.Forms - Part 3 and Adaptive Techniques with Content Views and Master Detail.