Flutter: Building Beautiful Windows apps — Fluent Design Structure and Navigation

Xuan Han Tan
ITNEXT
Published in
6 min readJun 2, 2021

--

The Fluent Design is Microsoft’s solution for designing beautiful Windows programs. Flutter has finally expanded support to Windows UWP in Google I/O 2021, which calls for well-designed Windows apps. In this article, I will show you how to create a basic Fluent Design app with Flutter.

This guide will work best for Win32 and UWP Flutter apps. If you have not set up your UWP Flutter app yet, follow my other guide to do so.

Add the required packages

The first step is to install the fluent_ui package by bdlukaa.

In a command line inside your app’s folder, type the following command:

flutter pub add fluent_ui

Now, it is time to start creating our Fluent Design app!

FluentApp

In main.dart, import the fluent_ui package:

import 'package:fluent_ui/fluent_ui.dart';

Then, create a FluentApp widget in the build function (you may want to replace the default Flutter template code). This is the base of your Fluent app.

return FluentApp();

Your code should now look like this:

import 'package:fluent_ui/fluent_ui.dart';void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return FluentApp();
}
}

Similar to a MaterialApp, a FluentApp also has a theme property which accepts a ThemeData() and lets you customise the appearance of your app. You can also use the darkTheme property to set a separate dark theme.

Some key properties for ThemeData() are the accentColor, which is the color of highlighted elements, and the scaffoldBackgroundColor, which is the background color of your app. Of course, there are also many other properties such as iconTheme, buttonTheme and contentDialogTheme, allowing you to customise the appearance of icons, buttons and dialogs respectively.

Here is an example of the use of themes in a FluentApp:

return FluentApp(
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
accentColor: Colors.blue,
iconTheme: const IconThemeData(size: 24)),
darkTheme: ThemeData(
scaffoldBackgroundColor: Colors.black,
accentColor: Colors.blue,
iconTheme: const IconThemeData(size: 24)),

);

NavigationView

A NavigationView controls the movement between Fluent Design pages. Add a NavigationView to the home property of the Fluent App like this:

return FluentApp(
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
accentColor: Colors.blue,
iconTheme: const IconThemeData(size: 24)),
darkTheme: ThemeData(
scaffoldBackgroundColor: Colors.black,
accentColor: Colors.blue,
iconTheme: const IconThemeData(size: 24)),
home: NavigationView()
);

An app bar is commonly found in many Windows apps and can be implemented to the NavigationView with a NavigationAppBar in the appBar property.

home: NavigationView(
appBar: NavigationAppBar(
title: Text("Fluent Design App Bar")
),
)
An app bar is seen at the top of the Microsoft Store app

NavigationPane

Next, a NavigationPane can be added to the NavigationView to navigate between pages. There are five different displayMode (styles) available:

  1. Open: The pane is expanded and placed on the left of the content. Each category or page must have an icon.
Source: Microsoft Design — Controls and Patterns

2. Compact: The pane is placed on the left of the content and only shows the icons until it is expanded.

Source: Microsoft Design — Controls and Patterns

3. Minimal: Only the menu button is shown until the pane is expanded. When expanded, it is placed on the left of the content.

Source: Microsoft Design — Controls and Patterns

4. Auto: This mode dynamically chooses between Minimal, Compact and Open based on the width of the window.

Source: Microsoft Design — Controls and Patterns

5. Top: The pane is placed above the content. It is useful for categories or pages which cannot be represented by an icon.

Source: Microsoft Design — Controls and Patterns

To create the NavigationPane, we can use the pane property of the NavigationView. Then, we can set the displayMode to PaneDisplayMode.auto, PaneDisplayMode.open, PaneDisplayMode.compact, PaneDisplayMode.minimal or PaneDisplayMode.top.

home: NavigationView(
appBar: NavigationAppBar(
title: Text("Fluent Design App Bar")),
pane: NavigationPane(
displayMode: PaneDisplayMode.auto,
),

)

Next, we will need to specify the items in our NavigationPane. We can set the items property to a list of PaneItems. Each PaneItem accepts an icon and a title. Here is my example:

pane: NavigationPane(
displayMode: PaneDisplayMode.auto,
items: [
PaneItem(
icon: Icon(Icons.code),
title: Text("Sample Page 1")
),
PaneItem(
icon: Icon(Icons.desktop_windows_outlined),
title: Text("Sample Page 2")
)
]

),
The NavigatorPane that I made. It has two PaneItem widgets.

Now, create a variable of type int called index in your MyAppState class. This will be responsible for managing the selected page from the NavigationPane.

class MyAppState extends State<MyApp> {
int index = 0;

Now, we will link the index as the selected index of the NavigationPane. Set the selected property of the NavigationPane to index.

pane: NavigationPane(
selected: index,
...

To update the index variable when the selected PaneItem is changed, we will need to specify the onChanged property.

pane: NavigationPane(
selected: index,
onChanged: (newIndex){
setState(() {
index = newIndex;
});
},

...

Optional: To add Acrylic transparency effects in the NavigationPane, we can set the useAcrylic property to true in the NavigationView.

home: NavigationView(
appBar: NavigationAppBar(
title: Text("Fluent Design App Bar")),
useAcrylic: true,
...

NavigationBody

A NavigationBody is used to implement page transitions into a navigation view, and will do the relevant transitions when switching between pages.

We can set the NavigationBody as the content property of our NavigationView.

home: NavigationView(
content: NavigationBody(),
...

Next, we will need to specify the index property as the selected index of the NavigationPane. We can set it to our index variable.

home: NavigationView(
content: NavigationBody(
index: index
),
...

Following that, we will need to specify the children property as a List containing widgets to show for each PaneItem. Note: the order of the widgets in the children property have to be the same as the order of the PaneItem widgets.

Usually, these widgets are ScaffoldPage widgets. Here is an example of my children property:

content: NavigationBody(
index: index,
children: [
ScaffoldPage(),
ScaffoldPage(),
],

),

ScaffoldPage

The ScaffoldPage is the Fluent Design equivalent of the Material Scaffold.

The header property specifies the top bar.

ScaffoldPage(
header: Text(
"Sample Page 1",
style: TextStyle(fontSize: 60),
),

),
The header text “My music” is seen in the Grove Music app

The content property specifies the other widgets in the ScaffoldPage, similar to the body property in a Material Scaffold.

ScaffoldPage(
header: Text(
"Sample Page 1",
style: TextStyle(fontSize: 60),
),
content: Center(
child: Text("Welcome to Page 1!"),
),

);

Here’s how my app looks so far:

Navigator.push & Navigator.pop

The FluentApp supports the same navigation functions as the MaterialApp, as we have all come to love. However, instead of a MaterialPageRoute, we use a FluentPageRoute when navigating between pages in a FluentApp.

Navigator.push(context, FluentPageRoute(builder: (context) => Page2()));

Conclusion

That’s all for this article and I hope you are now able to create basic Fluent Design apps with Flutter. Do give some claps if you found this article useful!

In the next article, I will show you how we can make Fluent UI Buttons, Checkboxes and Text Boxes. See you then!

--

--