https://github.com/graundtech/fluent2_kit
Fluent2 Design System Material based
https://github.com/graundtech/fluent2_kit
components design-system fluent microsoft theming
Last synced: 7 days ago
JSON representation
Fluent2 Design System Material based
- Host: GitHub
- URL: https://github.com/graundtech/fluent2_kit
- Owner: graundtech
- License: mit
- Created: 2023-12-21T18:16:33.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2026-05-22T15:52:45.000Z (17 days ago)
- Last Synced: 2026-05-22T17:57:06.853Z (17 days ago)
- Topics: components, design-system, fluent, microsoft, theming
- Language: Dart
- Homepage: https://pub.dev/packages/fluent2_kit
- Size: 6.17 MB
- Stars: 8
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
Create beautiful, cohesive Microsoft experiences using Fluent 2.
A Material-based Fluent 2 design system for Flutter.
## Installing
Add this to your `pubspec.yaml` file and run `dart pub get` to download the package.
```dart
dependencies:
fluent2_kit:
```
Import it in the files where it will be used:
```dart
import 'package:fluent2_kit/fluent2_kit.dart';
```
### Coexisting with `fluent_ui`
`fluent2_kit` targets the **Fluent 2 mobile/iOS spec** and uses class names like `FluentButton`, `FluentCard`, `FluentTextField`. The popular [`fluent_ui`](https://pub.dev/packages/fluent_ui) package targets the **Fluent UI desktop/Windows spec** and uses mostly unprefixed names (`Button`, `Card`, `TextBox`), so day-to-day collisions are rare.
If you do use both packages in the same project — for example, a cross-platform app with Windows desktop and iOS/Android targets — import one of them with an alias to avoid ambiguity on framework-level types (`FluentIcons`, `Fluent2ThemeData` vs `FluentThemeData`):
```dart
import 'package:fluent2_kit/fluent2_kit.dart';
import 'package:fluent_ui/fluent_ui.dart' as fluent_desktop;
```
Then reference desktop-only types via the prefix:
```dart
fluent_desktop.NavigationView(...) // Windows desktop nav
FluentNavBar(...) // mobile Fluent 2 nav from fluent2_kit
```
## ✨ Features
- Default theme based on Fluent 2
- Useful components, including behavior
- Almost no dependencies beyond Flutter
## Local Android example builds
The example app can be packaged locally on demand:
```sh
scripts/build_android_example.sh --apk
```
Useful options:
```sh
scripts/build_android_example.sh --aab
scripts/build_android_example.sh --both --clean
scripts/build_android_example.sh --apk --build-number 49 --build-name 1.0.6
scripts/build_android_example.sh --apk --skip-tests
```
Signed release builds use `example/android/key.properties` when present.
Without it, the Android project falls back to debug signing so the APK remains
installable for local testing.
Example `key.properties`:
```properties
storeFile=upload-keystore.jks
storePassword=...
keyAlias=...
keyPassword=...
```
## Getting started

#### First, wrap your MaterialApp with FluentProvider
```dart
FluentProvider(
child: MaterialApp(...),
);
```
### The FluentScaffold
Use `FluentScaffold` instead of `Scaffold` when you need Fluent banners, toasts, or bottom-sheet behavior.
```dart
FluentScaffold(
appBar: FluentNavBar(...),
body: Placeholder(),
);
```
### Theme
Import light and dark theme to your project:
```dart
import 'package:fluent2_kit/theme_data.dart' as theme_data;
final theme = theme_data.theme;
final darkTheme = theme_data.darkTheme;
```
Or you can pass your own brandColor:
> Tip: use [Smart Swatch Generator](https://smart-swatch.netlify.app/#7f22e2) to generate your color palette.
```dart
import 'package:fluent2_kit/theme_data.dart';
const _brandColor = MaterialColor(
0xFF7f22e2,
{
50: Color(0xFFf5e6ff),
100: Color(0xFFd9bafa),
200: Color(0xFFbf8df2),
300: Color(0xFFa461eb),
400: Color(0xFF8934e4),
500: Color(0xFF701bcb),
600: Color(0xFF57149f),
700: Color(0xFF3e0e73),
800: Color(0xFF250747),
},
);
ThemeData get theme =>
getTheme(brandColor: _brandColor, brightness: Brightness.light,);
ThemeData get darkTheme =>
getTheme(brandColor: _brandColor, brightness: Brightness.dark);
```
And add the theme to your MaterialApp:
```dart
FluentProvider(
child: MaterialApp(
themeAnimationDuration: Duration.zero,
darkTheme: darkTheme,
themeMode: themeMode,
),
);
```
## Fluent Icons
Import `FluentIcons`:
```dart
import 'package:fluent2_kit/fluent_icons.dart';
```
## Design Tokens
Design tokens are stored values used to assign Fluent styles like color, typography, spacing, or elevation, without hardcoding pixels and hex codes.
### CornerRadius
`FluentThemeData` includes predefined corner radius values.
Use the `FluentCornerRadius` radius tokens to change the corner radius on elements.
You can use the `FluentContainer` component, which is basically a Material Container with properties that are compatible with Fluent 2 UI design rules.
```dart
FluentContainer(
cornerRadius: FluentCornerRadius.large,
)
```
### Spacing Ramp
It’s used in every component and layout to create a familiar and cohesive product experience, regardless of device or environment.
Use `FluentSize` values:
```dart
FluentContainer(
padding: EdgeInsets.symmetric(horizontal: FluentSize.size160.value),
)
```
### Typography
The typography tokens are sets of global tokens that include font size, line height, weight and family.
Use `FluentText`, a component created to accept Fluent typography tokens. So you get this value directly from the theme.
```dart
// ✅
FluentText(
"Text 1",
style: FluentThemeDataModel.of(context)
.fluentTextTheme
?.body2,
),
// ✅
FluentText(
"Text 2",
style: FluentThemeDataModel.of(context)
.fluentTextTheme
?.caption1,
),
// ❌ use the typography tokens from the theme
FluentText(
"Wrong use",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w800,
),
)
```
If you need to change a text style value such as color, use `fluentCopyWith()`:
```dart
FluentText(
"Text",
style: FluentThemeDataModel.of(context)
.fluentTextTheme
?.body2?.fluentCopyWith(
fluentColor: Colors.pink
),
)
```
### Shadows
Fluent offers six sets of shadows, each consisting of two layers.
We have 2 elevation ramps:
- low elevation ramp (shadow2, shadow4, shadow8, shadow16)
- high elevation ramp (shadow28, shadow64)
If you choose brand shadow tokens, the *luminosity equation* will be applied to the shadow color.
You can use them choosing the value from the theme:
```dart
FluentContainer(
shadow: FluentThemeDataModel.of(context).fluentShadowTheme?.shadow2,
width: 100,
height: 100,
),
FluentContainer(
color: FluentColors.of(context)?.brandBackground1Rest,
shadow: FluentThemeDataModel.of(context).fluentShadowTheme?.brandShadow64,
width: 100,
height: 100,
)
```
### Stroke
`FluentContainer` has the strokeStyle property where you can pass stroke styles, and to create lines you can use `FluentStrokeDivider`:
```dart
FluentStrokeDivider(
style: FluentStrokeStyle(color: Colors.blue,thickness: FluentStrokeThickness.strokeWidth20),
startIndent: FluentStrokeBorderIndent.strokeIndent16,
)
```
You can choose the neutral strokes of the theme, the neutral strokes are: Accessible, Stroke1, Stroke2, StrokeDisabled.
```dart
FluentContainer(
strokeStyle: FluentThemeDataModel.of(context).fluentStrokeTheme?.strokeAccessible,
width: 100,
height: 100,
)
```
Or you can pass the weight of the stroke, color, distribution, and endpoint properties.
```dart
FluentContainer(
strokeStyle: FluentStrokeStyle(
thickness: FluentStrokeThickness.strokeWidth15,
color: Colors.red,
padding: 2,
dashArray: [
2,1,2,1
]
),
width: 100,
height: 100,
)
```
### Color Tokens
Use the `FluentColors` and `FluentDarkColors` classes.
**Interaction States:**
According to the documentation, the Fluent palettes are often used to indicate interaction states on components.
```dart
color: FluentColors.of(context)?.brandForeground1Selected
```
```dart
color: FluentColors.of(context)?.brandBackground1Pressed
```
#### Neutral Colors
These colors are used on surfaces, text, and layout elements.
In Light Theme:
```dart
color: FluentColors.neutralBackground1Rest
```
In Dark Theme:
```dart
color: FluentDarkColors.neutralBackground1Rest
```
#### Brand colors
You pass the context, and the color dynamically adjusts for dark mode.
```dart
color: FluentColors.of(context)?.brandBackground1Rest
```
```dart
color: FluentColors.of(context).brandBackgroundTintRest
```
# 🧩 Components
## Fluent Avatar
### Types:
- Standard: circular containers that generally represent an individual.
```dart
FluentAvatar(
child: Image.asset(
"assets/images/avatars/avatar1.jpeg",
fit: BoxFit.cover,
width: double.maxFinite,
height: double.maxFinite,
),
),
```
- Group: square containers that represent many people, like teams, organizations, or companies.
```dart
FluentAvatar(
isGroup: true,
...
)
```
### Behavior
### Presence Badges
There are 8 avatar badge variants: `away`, `available`, `dnd`, `offline`, `unknown`, `busy`, `blocked`, `oof`.
```dart
FluentAvatar(
statusPresenceBadge: StatusPresenceBadge.available,
...
)
```
### Activity Rings
```dart
FluentAvatar(
strokeStyle: FluentStrokeStyle(
padding: 4,
thickness: FluentStrokeThickness.strokeWidth30,
color: Colors.purple
),
child: Image.asset(
"assets/images/avatars/avatar1.jpeg",
fit: BoxFit.cover,
width: double.maxFinite,
height: double.maxFinite,
),
)
```
### Cutout
Avatars at 40 and 56 pixels can display a cutout that communicates other dynamic information, like reactions, mentions, and recent file activity.
```dart
FluentAvatar(
size: FluentAvatarSize.size56,
cutoutSize: CutoutSize.size28,
cutout: Icon(
FluentIcons.heart_12_filled
),
child: Image.asset(...),
)
```
```dart
FluentAvatar(
size: FluentAvatarSize.size56,
cutoutSize: CutoutSize.size20,
cutout: Icon(
FluentIcons.heart_12_filled,
size: 16,
),
child: Image.asset(...),
)
```
## Fluent Button
Standard action button. `title` and `onPressed` are required; everything else is optional.
### Sizes
The `FluentButtonSize` class has the following variations:
- `Large` (52pt)
- `Medium` (40pt, default)
- `Small` (28pt)
```dart
FluentButton(
title: "Click Me",
size: FluentButtonSize.medium,
onPressed: () {},
)
```
### Variants
The `FluentButtonVariant` class contains the following variants:
- `Accent` (default)
- `Outline Accent`
- `Outline`
- `Subtle`
```dart
FluentButton(
title: "Click Me",
variant: FluentButtonVariant.accent,
onPressed: () {},
)
```
### Leading icon
Pass an `Icon` to render before the label.
```dart
FluentButton(
title: "Like",
icon: Icon(FluentIcons.heart_12_filled),
onPressed: () {},
)
```
### Disabled
Pass `onPressed: null` to render the disabled state.
```dart
FluentButton(
title: "Click Me",
onPressed: null,
)
```
### Full width
Set `isFullWidget: true` to stretch the button to the available width.
```dart
FluentButton(
title: "Continue",
isFullWidget: true,
onPressed: () {},
)
```
## Fluent FAB
Floating action button (FAB) for a screen's primary action. Renders as a circular icon-only button by default, or as an extended pill when a `label` is provided.
### Sizes
The `FluentFabSize` class has the following variations:
- `Large` (56pt, default)
- `Small` (48pt)
### Variants
The `FluentFabVariant` class contains the following variants:
- `Accent` (brand background, default)
- `Subtle` (neutral background)
```dart
// Icon-only FAB
FluentFab(
icon: FluentIcons.add_24_filled,
onPressed: () {},
)
// Extended FAB with label
FluentFab(
icon: FluentIcons.add_24_filled,
label: "Create",
onPressed: () {},
)
```
## Fluent Navigation Bar
You can choose the brand or neutral variant in the Fluent NavBar.
```dart
FluentNavBar(
themeColorVariation: FluentThemeColorVariation.brand,
)
```
### Leading
```dart
FluentNavBar(
leading: Icon(FluentIcons.leaf_two_32_filled),
)
```
### Gradient
Supports the `gradient` property:
```dart
FluentNavBar(
title: NavCenterTitle(title: "Title"),
themeColorVariation: FluentThemeColorVariation.brand,
gradient: LinearGradient(
colors: [
Colors.purple,
Colors.deepPurple,
],
),
)
```
### Nav title
There are 4 variants for nav title: `Left title`, `Left subtitle`, `Center title`, `Center`
```dart
FluentNavBar(
themeColorVariation: FluentThemeColorVariation.brand,
title: NavLeftSubtitle(title: "Title", subtitle: "My subtitle",),
)
```
### Nav actions
A list of widgets, the actions that will appear in the right side of nav bar:
```dart
FluentNavBar(
themeColorVariation: FluentThemeColorVariation.brand,
title: NavLeftSubtitle(title: "Title", subtitle: "My subtitle",),
actions: [
Icon(FluentIcons.airplane_24_regular),
Icon(FluentIcons.access_time_20_regular),
Icon(FluentIcons.sparkle_20_filled),
],
)
```
## Fluent List
There are two variants and you can choose them using the named constructor:
- OneLine
```dart
FluentList.oneLine(
// Here you can only pass a list of FluentListItemOneLine
listItems: [...],
)
```

- MultiLine
```dart
FluentList.multiLine(
// Here you can only pass a list of FluentListItemMultiLine
listItems: [...],
)
```

FluentList has the following props:
- `sectionHeaderTitle`
- `sectionHeaderTitleIcon`: The leading icon of section header
- `sectionHeaderBackgroundColor`
- `sectionHeaderTitleVariant`: You can choose the bold or subtle value.
- `sectionHeaderActions`: Receives a FluentSectionHeaderActions where you can pass two action widgets.
- `sectionDescriptionText`
- `sectionDescriptionBackgroundColor`
- `sectionDescriptionIcon`: The icon that will appear in your section description.
- `separator`: A separator to your list.
```dart
FluentList.multiLine(
sectionHeaderTitle: "Notifications",
sectionHeaderTitleVariant: SectionHeaderTitleVariant.bold,
sectionHeaderActions: FluentSectionHeaderActions(
action1: Icon(FluentIcons.circle_20_regular),
action2: Icon(FluentIcons.circle_20_regular),
),
sectionDescriptionText: "Choose how alerts appear on your device.",
separator: FluentStrokeDivider(),
sectionDescriptionIcon: FluentIcons.leaf_three_16_filled,
listItems: [
FluentListItemMultiLine(text: "Item 1"),
FluentListItemMultiLine(text: "Item 2"),
],
)
```
## Fluent Card
```dart
FluentCard(
text: "Text",
subText: "Subtext",
leading: Image.asset(
"assets/images/cards/card2.jpeg",
fit: BoxFit.cover,
width: double.maxFinite,
height: double.maxFinite,
),
coverImage: Image.asset(
"assets/images/cards/card2.jpeg",
fit: BoxFit.cover,
width: double.maxFinite,
height: double.maxFinite,
),
onPressed: () {},
)
```
If you just want to use a flexible container with card styles, you can use `FluentCardContainer`:
```dart
FluentCardContainer(
padding: EdgeInsets.all(FluentSize.size160.value),
child: Text("Card content"),
)
```
## Fluent Radio Button
```dart
FluentRadioButton(
value: Option.option1,
groupValue: _option,
onChanged: (value) {},
)
```
Disabled:
```dart
FluentRadioButton(
value: Option.option1,
groupValue: _option,
onChanged: null,
)
```
## Fluent Checkbox
```dart
FluentCheckbox(
value: isCheckbox1,
onChanged: (value) {},
)
```
Disabled:
```dart
FluentCheckbox(
value: isCheckbox1,
onChanged: null,
)
```
## Fluent Switch Toggle
```dart
FluentSwitchToggle(
value: showIcons,
onChanged: (value) => setState(() {
showIcons = value;
}),
)
```
Disabled:
```dart
FluentSwitchToggle(
value: showIcons,
onChanged: null,
)
```
## Fluent Segmented Control
Lets people pick one option from a small set of mutually exclusive choices. Use the named constructors `.textItems` or `.iconItems`.
### Types
`FluentSegmentedControlType` values:
- `tabs` (default): a sliding thumb inside a single pill track.
- `pillButton`: separate, spaced pills.
### Variants
`FluentSegmentedControlVariant` values:
- `neutral` (default): neutral track with brand-colored selected segment.
- `brand`: brand track with neutral-colored selected segment.
### Text items
```dart
final controller = FluentSegmentedController(value: Sky.midnight);
FluentSegmentedControl.textItems(
fluentController: controller,
textItems: const {
Sky.midnight: "Midnight",
Sky.viridian: "Viridian",
Sky.cerulean: "Cerulean",
},
onValueChanged: (value) {
// handle selection
},
)
```
### Icon items (pill button, brand)
```dart
FluentSegmentedControl.iconItems(
segmentType: FluentSegmentedControlType.pillButton,
variant: FluentSegmentedControlVariant.brand,
iconItems: const {
ViewMode.grid: FluentIcons.grid_20_regular,
ViewMode.list: FluentIcons.list_20_regular,
},
onValueChanged: (value) {
// handle selection
},
)
```
## Fluent Banner
```dart
final myBanner = FluentBanner(
bannerColor: FluentBannerColor.accent,
text: "Your changes were saved.",
);
```
Adding Fluent Banner
```dart
FluentButton(
title: "Open Banner",
onPressed: () async {
FluentScaffoldMessenger.of(context).addBanner(myBanner);
},
)
```
Removing Fluent Banner
```dart
FluentButton(
title: "Close Banner",
onPressed: () async {
FluentScaffoldMessenger.of(context).removeBanner(myBanner);
},
)
```
## Fluent Toast
`FluentToast` has 4 variants of FluentToastColor:
- `Accent`
- `Neutral`
- `Danger`
- `Warning`
```dart
FluentButton(
title: "Accent Toast",
onPressed: (){
FluentToast(
toastColor: FluentToastColor.accent,
text: "Fluent 2 is here",
subText: "See what’s changed.",
icon: Icon(FluentIcons.sparkle_20_filled),
action: Builder(
builder: (context) => IconButton(
onPressed: () {
FluentToastOverlayEntry.of(context).remove();
},
icon: Icon(Icons.cancel),
),
)).show(
context: context,
duration: null,
onDismissed: () {
print("Closed!");
},
);
},
)
```
## Fluent Text Field
Underlined text input mirroring the Microsoft Fluent 2 iOS spec. Supports a floating label, hint (placeholder), assistive/error text, prefix and suffix icons, and an automatic trailing dismiss icon when the field is focused and not empty.
```dart
FluentTextField(
label: "Last Name",
hintText: "Ballinger",
onChanged: (value) {},
obscureText: false,
readOnly: false,
suffixIcon: Icon(FluentIcons.leaf_three_16_filled),
hasError: error != null,
assistiveText: error ?? "assistive",
)
```
### Visual states
The component renders the six states defined by the Fluent 2 iOS spec:
```dart
// Filled — has value, not focused
FluentTextField(
label: "Label",
controller: FluentTextFieldController()..textEditingController.text = "Input text",
assistiveText: "Assistive text",
prefixIcon: Icon(FluentIcons.search_24_regular),
)
// Placeholder — empty, not focused
FluentTextField(
label: "Label",
hintText: "Hint text",
assistiveText: "Assistive text",
prefixIcon: Icon(FluentIcons.search_24_regular),
)
// Focused — empty, focused (label and underline turn brand blue)
FluentTextField(
label: "Label",
hintText: "Hint text",
assistiveText: "Assistive text",
prefixIcon: Icon(FluentIcons.search_24_regular),
autofocus: true,
)
// Typing — has value, focused (auto-shows trailing dismiss icon)
FluentTextField(
label: "Label",
controller: FluentTextFieldController()..textEditingController.text = "Input text",
assistiveText: "Assistive text",
prefixIcon: Icon(FluentIcons.search_24_regular),
)
// Error — label, underline, and assistive text turn red
FluentTextField(
label: "Label",
controller: FluentTextFieldController()..textEditingController.text = "Input text",
hasError: true,
assistiveText: "Password must contain 8 characters and include letters, numbers and symbols",
prefixIcon: Icon(FluentIcons.search_24_regular),
)
// Disabled — hint text uses the disabled neutral foreground (#BDBDBD)
FluentTextField(
label: "Label",
hintText: "Hint text",
assistiveText: "Assistive text",
prefixIcon: Icon(FluentIcons.lock_closed_24_regular),
enabled: false,
)
```
### Additional capabilities
`FluentTextField` also exposes the underlying Flutter `TextField` surface for advanced use cases:
- **Multi-line input**: `maxLines`, `expands`, `scrollController`.
- **Character counter**: `maxLength`, `buildCounter`.
- **Password / obscured input**: `obscureText`, `obscuringCharacter`.
- **Prefix variants**: `prefixIcon` (icon column on the left) and `prefix` (inline widget inside the input).
- **Cursor customization**: `cursorErrorColor`, `cursorHeight`, custom magnifier via `magnifierConfiguration`.
- **Input behavior**: `keyboardType`, `inputFormatters`, `autocorrect`, `enableSuggestions`, `autofocus`, `canRequestFocus`, `clipBehavior`, `textAlign`.
- **Callbacks**: `onChanged`, `onTap`, `onEditingComplete`, `onSubmitted`.
## Fluent Progress Bar
```dart
if (isUpdating)
FluentProgressBar(
value: null,
)
```
## Fluent Heads-up Display
```dart
FluentButton(
title: "Open HUD",
onPressed: () {
FluentHeadsUpDisplayDialog(
future: Future.delayed(Duration(seconds: 1)),
confirmStopMessage: "Are you sure you want to close this?",
hud: FluentHeadsUpDisplay(
text: "Refreshing Data...",
),
).show(context);
},
)
```
## 📚 Additional information
This is mainly inspired by the Fluent 2 iOS Figma UI Kit. It will be gradually adapted to Android as Microsoft releases its Figma UI Kit.
### References
- [Fluent 2 — iOS components](https://fluent2.microsoft.design/components/ios/)
- [Fluent 2 iOS UI Kit (Figma Community)](https://www.figma.com/community/file/836833645402438850)
## 💛 Acknowledgments
Special thanks to the two main developers behind this project, whose work shaped most of the components and patterns in `fluent2_kit`:
- **Leticya Sheyla** — [@LeticyaSheyla](https://github.com/LeticyaSheyla)
- **Railson Ferreira** — [@railson-ferreira](https://github.com/railson-ferreira)