Unofficial implementation of Fluent UI for Flutter. It's written based on the official documentation
You can check the web version of it here
Content
- Motivation
- Style
- Navigation
- Widgets
- Button
- Split Button
- Toggle Button
- Checkbox
- Toggle Switches
- Radio Buttons
- Slider
- TextBox
- Auto Suggest Box
- Combo Box
- Tooltip
- Content Dialog
- Flyout
- TODO Teaching tip
- Acrylic
- InfoBar
- TODO Calendar View
- TODO Calendar Date Picker
- Date Picker
- Time Picker
- Progress Bar and Progress Ring
- Scrollbar
- List Tile
- Info Header
- Equivalents with the material library
- Contribution
Motivation
Since flutter has Windows support (currently in stable under an early release flag as of 30/03/2021), it's necessary to have support to its UI guidelines to build apps with fidelity, since it has support for Material and Cupertino. See this for more info on the offical fluent ui support
See also:
Style
You can use the FluentTheme
widget to, well... theme your widgets. You can style your widgets in two ways:
- Using the
FluentApp
widget
FluentApp(
title: 'MyApp',
theme: ThemeData(
...
),
)
- Using the
FluentTheme
widget
FluentTheme(
theme: ThemeData(
...
),
child: ...,
),
Icons
Inside your app, you use icons to represent an action, such as copying text or navigating to the settings page. This library includes an icon library with it, so you can just call Icons.[icon_name]
in any Icon
widget:
Icon(Icons.add)
To style icons, you can use IconTheme
in the app ThemeData
or use the property style
in the Icon
widget. You can see the list of icons here
Colors
This library also includes the Fluent UI colors with it, so you can just call Colors.[color_name]
:
TextStyle(color: Colors.black)
Avaiable colors:
Colors.transparent
Colors.white
Colors.black
Colors.grey
Colors.yellow
Colors.orange
Colors.red
Colors.magenta
Colors.purple
Colors.blue
Colors.teal
Colors.green
Accent color
Common controls use an accent color to convey state information. Learn more.
By default, the accent color is Colors.blue
. However, you can also customize your app's accent color to reflect your brand:
ThemeData(
accentColor: Colors.blue,
)
To use the system's accent color, you can use the plugin system_theme made by me :). It has support for (04/01/2021) Android, Web and Windows.
import 'package:system_theme/system_theme.dart';
ThemeData(
accentColor: SystemTheme.accentInstance.accent,
)
Brightness
You can change the theme brightness to change the color of your app.
-
Brightness.light
-
Brightness.dark
It defaults to the brightness of the device. (MediaQuery.of(context).brightness
)
Typography
To set a typography, you can use the ThemeData
class combined with the Typography
class:
ThemeData(
typography: Typography(
caption: TextStyle(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.normal,
),
),
)
Font
You should use one font throughout your app's UI, and we recommend sticking with the default font for Windows apps, Segoe UI. It's designed to maintain optimal legibility across sizes and pixel densities and offers a clean, light, and open aesthetic that complements the content of the system.
Type ramp
The Windows type ramp establishes crucial relationships between the type styles on a page, helping users read content easily. Learn more
Reveal Focus
Reveal Focus is a lighting effect for 10-foot experiences, such as Xbox One and television screens. It animates the border of focusable elements, such as buttons, when the user moves gamepad or keyboard focus to them. It's turned off by default, but it's simple to enable. Learn more
Reveal Focus calls attention to focused elements by adding an animated glow around the element's border:
This is especially helpful in 10-foot scenarios where the user might not be paying full attention to the entire TV screen.
Enabling it
Reveal Focus is off by default. To enable it, change the focusStyle
in your app ThemeData
:
theme: ThemeData(
focusTheme: FocusStyle(
glowFactor: 4.0,
),
),
To enable it in a 10 foot screen, use the method is10footScreen
:
import 'dart:ui' as ui;
theme: ThemeData(
focusStyle: FocusStyle(
glowFactor: is10footScreen(ui.window.physicalSize.width) ? 2.0 : 0.0,
),
),
Go to the example project to a full example
Why isn't Reveal Focus on by default?
As you can see, it's fairly easy to turn on Reveal Focus when the app detects it's running on 10 foot screen. So, why doesn't the system just turn it on for you? Because Reveal Focus increases the size of the focus visual, which might cause issues with your UI layout. In some cases, you'll want to customize the Reveal Focus effect to optimize it for your app.
Customizing Reveal Focus
You can customize the focus border, border radius and glow color:
focusTheme: FocusStyle(
borderRadius: BorderRadius.zero,
glowColor: theme.accentColor?.withOpacity(0.2),
glowFactor: 0.0,
border: BorderSide(
width: 2.0,
color: theme.inactiveColor ?? Colors.transparent,
),
),
To customize it to a single widget, wrap the widget in a FluentTheme
widget, and change the options you want:
FluentTheme(
theme: context.theme?.copyWith(ThemeData(
focusStyle: ..., // set your FocusStyle here
)),
child: Button(
text: Text('Custom Focus Button'),
onPressed: () {},
)
),
Navigation
The default flutter navigation is available when using the FluentApp
widget, that means you can simply call Navigator.push
and Navigator.pop
to navigate between routes. See navigate to a new screen and back
Navigation panel
Navigation Panel will be rewritten in a near future. See #3 for more info
int _currentIndex = 0;
Scaffold(
left: NavigationPanel(
currentIndex: _currentIndex,
menu: NavigationPanelMenuItem(...),
items: [
NavigationPanelSectionHeader(
header: Text('Cool Navigation Panel Header'),
),
NavigationPanelItem(
icon: Icon(Icons.input),
label: Text('Page 1'),
onTapped: () => setState(() => _currentIndex = 0),
),
NavigationPanelTileSeparator(),
NavigationPanelItem(
icon: Icon(Icons.format_align_center),
label: Text('Page 2'),
onTapped: () => setState(() => _currentIndex = 1),
),
],
),
body: ...,
)
Navigation body
Usually used in Scaffold's body
property. Example:
int _currentIndex = 0;
Scaffold(
left: NavigationPanel(...),
body: NavigationPanelBody(
index: _currentIndex,
transitionBuilder: (child, animation) {
// Refer to page transitions to see more page transitions
return DrillInPageTransition(
child: child,
animation: animation,
);
}
children: [
Page1(),
Page2(),
],
),
),
Page transitions
Page transitions navigate users between pages in an app, providing feedback as the relationship between pages. Page transitions help users understand if they are at the top of a navigation hierarchy, moving between sibling pages, or navigating deeper into the page hierarchy.
This library gives you the following implementations to navigate between your pages:
Entrance
Entrance is a combination of a slide up animation and a fade in animation for the incoming content. Use entrance when the user is taken to the top of a navigational stack, such as navigating between tabs or left-nav items.
The desired feeling is that the user has started over.
Avaiable with the widget EntrancePageTransition
, it produces the following effect:
Drill In
Use drill when users navigate deeper into an app, such as displaying more information after selecting an item.
The desired feeling is that the user has gone deeper into the app.
Avaiable with the widget DrillInPageTransition
, it produces the following effect:
Horizontal
It's avaiable with the widget HorizontalSlidePageTransition
.
It's recommended to widely use these transitions when using the navigation panel.
Tab View
The TabView control is a way to display a set of tabs and their respective content. TabViews are useful for displaying several pages (or documents) of content while giving a user the capability to rearrange, open, or close new tabs. Learn more
Example
SizedBox(
height: 600,
child: TabView(
currentIndex: currentIndex,
onChanged: (index) => setState(() => currentIndex = index),
onNewPressed: () {
setState(() => tabs++);
},
tabs: List.generate(tabs, (index) {
return Tab(
text: Text('Tab $index'),
closeIcon: Tooltip(
message: 'Close tab',
child: IconButton(
icon: Icon(Icons.close),
onPressed: () {
setState(() => tabs--);
if (currentIndex > tabs - 1) currentIndex--;
},
),
),
);
}),
bodies: List.generate(
tabs,
(index) => Container(
color: index.isEven ? Colors.red : Colors.yellow,
),
),
),
),
The code above produces the following:
Widgets:
Button
A button gives the user a way to trigger an immediate action. Learn more
Creating
Button(
text: Text('Cool button'),
onPressed: () {
print('button pressed'),
}
)
To disable the button, set onPressed
to null
Screenshots
Split Button
A Split Button has two parts that can be invoked separately. One part behaves like a standard button and invokes an immediate action. The other part invokes a flyout that contains additional options that the user can choose from. Learn more
You can use a SplitButtonBar
to create a Split Button. It usually takes Button
s in the buttons
property. You can also customize the button spacing by changing the property interval
in its theme.
Example
const double splitButtonHeight = 50.0;
SplitButtonBar(
theme: SplitButtonThemeData(
interval: 1, // the default value is one
),
// There need to be at least 2 items in the buttons, and they must be non-null
buttons: [
SizedBox(
height: splitButtonHeight,
child: Button(
text: Container(
height: 24,
width: 24,
color: context.theme.accentColor,
),
onPressed: () {},
),
),
SizedBox(
height: splitButtonHeight,
child: Button(
text: Icon(Icons.keyboard_arrow_down),
onPressed: () {
// TODO: open the color list here
},
theme: ButtonThemeData(padding: EdgeInsets.all(6)),
)
)
],
)
The code above produces the following button:
Toggle Button
A button that can be on or off.
Example
bool _value = false;
ToggleButton(
child: Text('Toggle Button'),
checked: _value,
onChanged: (value) => setState(() => _value = value),
)
Checkbox
A check box is used to select or deselect action items. It can be used for a single item or for a list of multiple items that a user can choose from. The control has three selection states: unselected, selected, and indeterminate. Use the indeterminate state when a collection of sub-choices have both unselected and selected states. Learn more
Creating
To create a checkbox, use the widget Checkbox
:
bool _checked = true;
Checkbox(
checked: _checked,
onChanged: (value) => setState(() => _checked = value),
)
Handling its states
State | Property | Value |
---|---|---|
checked | checked | true |
unchecked | checked | false |
indeterminate | checked | null |
enabled | onChanged | non-null |
disabled | onChanged | null |
Screenshots
Toggle Switches
The toggle switch represents a physical switch that allows users to turn things on or off, like a light switch. Use toggle switch controls to present users with two mutually exclusive options (such as on/off), where choosing an option provides immediate results. Learn more
Example
bool _checked = false;
ToggleSwitch(
checked: _checked,
onChanged: (v) => setState(() => _checked = v),
)
Toggle Switch | Checkbox |
---|---|
Use a toggle switch for binary settings when changes become effective immediately after the user changes them. | Use check boxes for optional ("nice to have") items. |
Use a checkbox when the user has to perform extra steps for changes to be effective. For example, if the user must click a "submit" or "next" button to apply changes, use a check box. | |
Use check boxes when the user can select multiple items that are related to a single setting or feature. |
Radio Buttons
Radio buttons, also called option buttons, let users select one option from a collection of two or more mutually exclusive, but related, options. Radio buttons are always used in groups, and each option is represented by one radio button in the group.
In the default state, no radio button in a RadioButtons group is selected. That is, all radio buttons are cleared. However, once a user has selected a radio button, the user can't deselect the button to restore the group to its initial cleared state.
The singular behavior of a RadioButtons group distinguishes it from check boxes, which support multi-selection and deselection, or clearing.
Example
int _currentIndex = -1;
final radioButtons = [
'RadioButton 1',
'RadioButton 2',
'RadioButton 3',
];
Column(
children: List.generate(radioButtons.length, (index) {
return Row(children: [
RadioButton(
selected: _currentIndex == index,
// set onChanged to null to disable the button
onChanged: () => setState(() => _currentIndex = index),
),
SizedBox(width: 4),
Text(radioButtons[index])
]);
}),
),
The code above produces the following:
Screenshots
Slider
A slider is a control that lets the user select from a range of values by moving a thumb control along a track. Learn more
A slider is a good choice when you know that users think of the value as a relative quantity, not a numeric value. For example, users think about setting their audio volume to low or medium—not about setting the value to 2 or 5.
Don't use a slider for binary settings. Use a toggle switch instead.
Creating
double _value = 0;
Container(
// The default width is 200.
// The slider does not have its own widget, so you have to add it yourself.
// The slider always try to be as big as possible
width: 200,
child: Slider(
max: 100,
value: _value,
onChanged: (v) => setState(() => value = v),
// Label is the text displayed above the slider when the user is interacting with it.
label: '${sliderValue.toInt()}',
),
)
The code above produces the following:
You can set vertical
to true
to create a vertical slider
Choosing between vertical and horizontal sliders
Horizontal | Vertical |
---|---|
If the control is used to seek within media, like in a video app. | if the slider represents a real-world value that is normally shown vertically (such as temperature). |
Rating Bar
The property
starSpacing
was not implemented yet
The rating control allows users to view and set ratings that reflect degrees of satisfaction with content and services. Learn more
Example
double rating = 0.0;
RatingBar(
rating: rating,
onChanged: (v) => setState(() => rating = v),
)
You can set amount
to change the amount of stars. The rating
must be less than the stars and more than 0. You can also change the icon
, its size and color. You can make the bar read only by setting onChanged
to null
.
TextBox
The TextBox control lets a user type text into an app. It's typically used to capture a single line of text, but can be configured to capture multiple lines of text. The text displays on the screen in a simple, uniform, plaintext format. Learn more
You can use the forms screen in the example app for reference.
Creating
You can use the widget TextBox
to create text boxes:
TextBox(
controller: ...,
header: 'Notes',
placeholder: 'Type your notes here',
),
Screenshots
Auto Suggest Box
Use an AutoSuggestBox to provide a list of suggestions for a user to select from as they type. Learn more
Example
final autoSuggestBox = TextEditingController();
AutoSuggestBox<String>(
controller: autoSuggestBox,
items: [
'Blue',
'Green',
'Red',
'Yellow',
'Grey',
],
onSelected: (text) {
print(text);
},
textBoxBuilder: (context, controller, focusNode, key) {
const BorderSide _kDefaultRoundedBorderSide = BorderSide(
style: BorderStyle.solid,
width: 0.8,
);
return TextBox(
key: key,
controller: controller,
focusNode: focusNode,
suffixMode: OverlayVisibilityMode.editing,
suffix: IconButton(
icon: Icon(Icons.close),
onPressed: () {
controller.clear();
focusNode.unfocus();
},
),
placeholder: 'Type a color',
decoration: BoxDecoration(
border: Border(
top: _kDefaultRoundedBorderSide,
bottom: _kDefaultRoundedBorderSide,
left: _kDefaultRoundedBorderSide,
right: _kDefaultRoundedBorderSide,
),
borderRadius: focusNode.hasFocus
? BorderRadius.vertical(top: Radius.circular(3.0))
: BorderRadius.all(Radius.circular(3.0)),
),
);
},
)
The code above produces the following:
Screenshots
Combo Box
Use a combo box to present a list of items that a user can select from. A combo box starts in a compact state and expands to show a list of selectable items. When the combo box is closed, it either displays the current selection or is empty if there is no selected item. When the user expands the combo box, it displays the list of selectable items. Learn more
Example
final values = ['Blue', 'Green', 'Yellow', 'Red'];
String? comboBoxValue;
SizedBox(
width: 200,
child: ComboBox<String>(
header: 'Colors',
placeholder: 'Selected list item',
isExpanded: true,
items: values
.map((e) => ComboboxMenuItem<String>(
value: e,
child: Text(e),
))
.toList(),
value: comboBoxValue,
onChanged: (value) {
print(value);
if (value != null) setState(() => comboBoxValue = value);
},
),
),
The code above produces the following:
Screenshots
Tooltip
A tooltip is a short description that is linked to another control or object. Tooltips help users understand unfamiliar objects that aren't described directly in the UI. They display automatically when the user moves focus to, presses and holds, or hovers the mouse pointer over a control. The tooltip disappears after a few seconds, or when the user moves the finger, pointer or keyboard/gamepad focus. Learn more
Creating
To add a tooltip to a widget, wrap it in a Tooltip
widget:
Tooltip(
message: 'Tooltip message hehe',
child: Button(
text: Text('Button with tooltip'),
onPressed: () {
print('pressed button with tooltip');
}
),
)
It's located above or below the child
widget. You can specify the preffered location when both locations are available using the preferBelow
property.
Screenshots
Content Dialog
Dialogs are modal UI overlays that provide contextual app information. They block interactions with the app window until being explicitly dismissed. They often request some kind of action from the user. Learn more
Creating
You can create a Dialog with the widget ContentDialog
:
ContentDialog(
title: Text('No WiFi connection'),
content: Text('Check your connection and try again'),
actions: [
Button(
text: Text('Ok'),
onPressed: () {
Navigator.pop(context);
}
)
],
),
Display
You can display the dialog as an overlay by calling the function showDialog
:
showDialog(
context: context,
builder: (context) {
return ContentDialog(...);
},
);
Screenshots
Flyout
A flyout is a light dismiss container that can show arbitrary UI as its content. Flyouts can contain other flyouts or context menus to create a nested experience.
Example
final flyoutController = FlyoutController();
Flyout(
controller: flyoutController,
contentWidth: 450,
content: FlyoutContent(
child: Text(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'),
),
child: Button(
text: Text('Open flyout'),
onPressed: () {
flyoutController.open = true;
},
),
);
@override
void dispose() {
flyoutController.dispose();
super.dispose();
}
The code above produces the following:
Screenshots
Acrylic
Acrylic is a type of Brush that creates a translucent texture. You can apply acrylic to app surfaces to add depth and help establish a visual hierarchy.
Usage
You can easily use the Acrylic
widget as a substitute for the Container
, because it has all the necessary properties it has, but don't use it everywhere everytime:
Do | Don't |
---|---|
Do use acrylic as the background material of non-primary app surfaces like navigation panes. | Don't put desktop acrylic on large background surfaces of your app - this breaks the mental model of acrylic being used primarily for transient surfaces. |
Do extend acrylic to at least one edge of your app to provide a seamless experience by subtly blending with the app’s surroundings. | Don’t place in-app and background acrylics directly adjacent to avoid visual tension at the seams. |
Don't place multiple acrylic panes with the same tint and opacity next to each other because this results in an undesirable visible seam. | |
Don’t place accent-colored text over acrylic surfaces. |
Acrylic(
padding: EdgeInsets.all(12), // Defaults to EdgeInsets.zero
child: Button(
text: Text('Mom it\'s me hehe <3'),
onPressed: () {
print('button inside acrylic pressed');
}
),
color: ...,
width: ...,
height: ...,
),
Widgets using Acrylic
Currently, the following widgets use acrylic in its implementation:
This widget MAY BE unperformatic on VERY OLD devices because it uses expensive widgets in its composition:
ClipRect
andBackdropFilter
. It should not affect the most part of the devices
Screenshots
InfoBar
The InfoBar
widget is for displaying app-wide status messages to users that are highly visible yet non-intrusive. There are built-in Severity levels to easily indicate the type of message shown as well as the option to include your own call to action or hyperlink button. Since the InfoBar is inline with other UI content the option is there for the control to always be visible or dismissed by the user.
Usage
You can easility create it using the InfoBar
widget and theme it using InfoBarThemeData
. It has built-in support for both light and dark theme:
InfoBar(
title: Text('Update available'),
content: Text('Restart the app to apply the latest update.'), // optional
severity: InfoBarSeverity.info, // optional. Default to InfoBarSeverity.info
),
Some screenshots
Calendar View
A calendar view lets a user view and interact with a calendar that they can navigate by month, year, or decade. A user can select a single date or a range of dates. It doesn't have a picker surface and the calendar is always visible. Learn more
Use a calendar view to let a user pick a single date or a range of dates from an always visible calendar. If you need to let a user select multiple dates at one time, you must use a calendar view. If you need to let a user pick only a single date and don't need a calendar to be always visible, consider using date picker control.
The calendar view is made up of 3 separate views: the month view, year view, and decade view. By default, it starts with the month view open. You can specify a startup view by setting the displayMode
property.
Users click the header in the month view to open the year view, and click the header in the year view to open the decade view. Users pick a year in the decade view to return to the year view, and pick a month in the year view to return to the month view. The two arrows to the side of the header navigate forward or backward by month, by year, or by decade.
Example
CalendarView()
The code above produces the following:
Date Picker
The date picker gives you a standardized way to let users pick a localized date value using touch, mouse, or keyboard input. Learn more
The entry point displays the chosen date, and when the user selects the entry point, a picker surface expands vertically from the middle for the user to make a selection. The date picker overlays other UI; it doesn't push other UI out of the way.
We use intl to format the dates. You can change the current locale to change formatting
Example
DateTime date = DateTime.now();
SizedBox(
width: 295,
child: DatePicker(
header: 'Date of birth',
selected: date,
onChanged: (v) => setState(() => date = v),
),
);
The code above produces the following:
Time Picker
The time picker gives you a standardized way to let users pick a time value using touch, mouse, or keyboard input. Learn more
Use a time picker to let a user pick a single time value.
Example
DateTime date = DateTime.now();
SizedBox(
width: 240,
child: TimePicker(
header: 'Arrival time',
selected: date,
onChanged: (v) => setState(() => date = v),
),
),
The code above produces the following:
Progress Bar and Progress Ring
A progress control provides feedback to the user that a long-running operation is underway. It can mean that the user cannot interact with the app when the progress indicator is visible, and can also indicate how long the wait time might be, depending on the indicator used.
Example
Creating a ProgressBar:
ProgressBar(value: 35)
You can omit the value
property to create an indeterminate progress bar:
Indeterminate Progress Bar is a courtesy of @raitonubero. Show him some love
Creating a ProgressRing:
ProgressRing(value: 35)
You can omit the value
property to create an indeterminate progress ring:
Both Indeterminate ProgressBar and Indeterminate ProgressRing is a courtesy of @raitonubero. Show him some love ❤
Screenshots
Scrollbar
A scrollbar thumb indicates which portion of a ScrollView is actually visible. Learn more
Depending on the situation, the scrollbar uses two different visualizations, shown in the following illustration: the panning indicator (left) and the traditional scrollbar (right).
When the scrollbar is visible it is overlaid as 16px on top of the content inside your ScrollView. In order to ensure good UX design you will want to ensure that no interactive content is obscured by this overlay. Additionally if you would prefer not to have UX overlap, leave 16px of padding on the edge of the viewport to allow for the scrollbar.
Example
final _controller = ScrollController();
Scrollbar(
controller: controller,
child: ListView.builder(
/// You can add a padding to the view to avoid having the scrollbar over the UI elements
padding: EdgeInsets.only(right: 16.0),
itemCount: 100,
builder: (context, index) {
return ListTile(title: Text('$index'));
}
),
)
Which produces the following:
You can change the isAlwaysVisible
property to either enable or disable the fade effect. It's disabled by default.
Note: Currently, the leading and trailing arrows are not implemented. See this issue to learn more
List Tile
You can use a ListTile
in a ListView
.
Example
final people = {
'Mass in B minor': 'Johann Sebastian Bach',
'Third Symphony': 'Ludwig van Beethoven',
'Serse': 'George Frideric Hendel',
};
ListView.builder(
itemCount: people.length,
itemBuilder: (context, index) {
final title = people.keys[index];
final subtitle = people[title];
return ListTile(
leading: CircleAvatar(),
title: Text(title),
subtitle: Text(subtitle),
);
}
),
The code above produces the following:
If you want to create a tappable tile, use TappableListTile
instead.
You can create the following variations of a ListTile
:
- CheckboxListTile: a
TappableListTile
with aCheckbox
in the leading. - SwitchListTile: a
TappableListTile
with aToggleSwitch
in the leading. - RadioListTile: a
TappableListTile
with aRadioButton
in the leading.
They share a single pattern. Here's how to implement the following image:
CheckboxListTile(
value: true,
onChanged: (v) => print(v),
title: Text(
'I agree to the terms of service for this site',
style: TextStyle(fontWeight: FontWeight.normal),
),
)
Screenshots
Info Header
You can use an InfoHeader
to tell the user the purpose of something
Example
InfoHeader(
header: 'Control header',
child: ComboBox(...),
),
The code above produces the following:
Some widgets, such as ComboBox
and TextBox
, already come with a header
property, so you can use them easily with them:
ComboBox(
header: 'Control header',
...
)
This will produce the same as the image above.
Equivalents with the material library
The list of equivalents between this library and flutter/material.dart
Material | Fluent |
---|---|
TextButton | Button |
IconButton | IconButton |
Checkbox | Checkbox |
RadioButton | RadioButton |
- | RatingBar |
- | SplitButton |
- | ToggleButton |
Switch | ToggleSwitch |
TextField | TextBox |
DropdownButton | ComboBox |
- | AutoSuggestBox |
AlertDialog | ContentDialog |
MaterialBanner | InfoBar |
Tooltip | Tooltip |
- | Flyout |
Drawer | NavigationPanel |
Divider | Divider |
VerticalDivider | Divider |
Card | Acrylic |
ListTile | ListTile |
RadioListTile | RadioListTile |
CheckboxListTile | CheckboxListTile |
SwitchListTile | SwitchListTile |
LinearProgressIndicator | ProgressBar |
CircularProgressIndicator | ProgressRing |
_DatePickerDialog | DatePicker |
_TimePickerDialog | TimePicker |
Contribution
Feel free to open an issue if you find an error or make pull requests.
All the widgets above with the mark of (3) will not be implemented soon, so you can create a pull request with the implementation for them :). You can also help to improve the documentation for all the widgets, and in this readme.
All type of contributions are welcome :)