fluent_ui

Implements Fluent UI to flutter. This library can be considered as an alternative to react's fluent ui lib. It's based on the official documentation: https://developer.microsoft.com/en-us/fluentui#/styles/web.

Motivation

Since flutter has Windows support, it's necessary to have support to its ui guidelines (as it has Material and Cupertino support) to build apps with fidelity. See this for more info on the offical fluent ui support

Documentation

Layout

You can use a Scaffold to create your layout.

Scaffold(
  header: AppBar(
    title: Text('Fluent UI App Showcase'),
    bottom: Pivot(
      currentIndex: index,
      onChanged: (i) => setState(() => index = i),
      pivots: [
        PivotItem(text: Text('Buttons')),
        PivotItem(text: Text('Surfaces')),
      ],
    ),
  ),
  body: PivotView(
    currentIndex: index,
    pages: <Widget>[
      Page1(),
      Page2(),
    ]
  ),
)

Icons

To use icons, add fluentui_system_icons to your dependencies in pubspec.yaml file:

dependencies:
    ...
    fluentui_system_icons: ^1.1.89

Simple usage example:

import 'package:fluentui_system_icons/fluent_icons.dart';

class MyFlutterWidget extends StatelessWidget {
  Widget build(BuildContext context) {
    return IconButton(
      // Use the FluentIcons. + name of the icon you want
      icon: Icon(FluentIcons.access_time_24_regular),
      onPressed: () => print("Button pressed"),
    );
  }
}

For more info see this

Colors

To use a Color, just call Colors.(colorName).

final black = Colors.black;
final blue = Colors.blue;

Motion

Flutter already provides a great set of Motion/Animation Widgets. All of them are supported. Learn more

Page transition

Page refresh

Use an AnimatedSwitcher to achieve the same effect:

AnimatedSwitcher(
  child: [widget1(), widget2()][currentPage],
  transitionBuilder: (child, animation) {
    return PageRefreshTransition(child: child, animation: animation);
  }
)

Drill

It's the same as a Hero animation.

Buttons

You can create the buttons using the Button widget. It's the default implementation for DefaultButton, PrimaryButton, CompoundButton, ActionButton and ContextualButton

  • Button
    Button(
      text: Text('I am your button :)'),
      onPressed: () => print('pressed'),
    ),
    Button.action(...), // Creates an ActionButton
    Button.compound(...), // Creates a CompoundButton
    Button.contextual(...), // Creates a ContextualButton
    Button.icon(...) /// Creates an IconButton
    Button.primary(...) /// Creates a PrimaryButton
    
  • IconButton
    IconButton(
      icon: MyIcon(),
      onPressed: () {},
    ),
    IconButton.menu(
      icon: MyIcon(),
      menu: MyMenu(),
      onPressed: openMenu,
    ),
    
  • Checkbox
    bool value = true;
    Checkbox(
      checked: value,
      onChange: (v) => setState(() => value = v),
    ),
    

ListCells

The implementation for ListCell. You can create cells with checkboxes and toggles

ListCell(title: Text('MyList')),
Divider(),
ListCell.checkbox(...),
Divider(),
ListCell.toggle(...),

Pop-ups

  • Dialog and Modal
    showDialog(
      context: context,
      builder: (context) => Dialog(
        title: Text('My title'),
        body: Text('The body'),
        footer: [
          Button.primary(text: Text('Button 1')),
          Button(text: Text('Button 1')),
        ],
      ),
    );
    
  • Snackbar
    showSnackbar(
      context: context,
      snackbar: Snackbar(
        title: Text('My beautiful snackbar'),
          button: Button.primary(
            text: Text('Button'),
            onPressed: () {},
          ),
        ),
      );
    

Theming

Almost every widget has a style in Theme. There isn't a specific class for any widget theme, all of them are in Theme.

Theme(
  cardStyle: CardStyle(...),
),

ButtonState

You'll find ButtonState<T> in some props in Style. That's because you need to handle what will be rendered in different button states. For example:

ButtonStyle(
  color: (state) {
    if (state.isDisabled) return disabledColor;
    else if (state.isHovering) return hoveringColor;
    else if (state.isPressing) return pressingColor;
    else return defaultColor;
  },
),

There are four states:

  • Disabled - When the button is disabled. Usually when onPressed is null
  • Hovering - When the mouse is over the button. This collor is lighter than pressing's
  • Pressing - When the mouse is clicking the button or when the screen is being tapped.
  • None - When nothing is happening to the button.

Material equivalents

MaterialFluent
ScaffoldScaffold
AppBarAppBar
DialogDialog
CardCard
CheckboxCheckbox
DividerDivider
TabBarPivot
TabBarViewPivotView
ListTileListCell
SwitchToggle
IconIcon
IconButtonIconButton
TextButtonButton
SnackbarSnackbar
BottomNavigationBarBottomNavigation

Avaiable widgets

Other

Null safety

Null safety support will be avaiable once it reaches stable

TODO:

  • Fix tooltip fidelity
  • Implement slider
  • Implement dropdown
  • Sound

Libraries

fluent_ui