Flavor.Maui
Material Design UI controls for .NET MAUI, inspired by MudBlazor.
Beautiful, consistent, and easy-to-use components that bring Material Design to your .NET MAUI applications with native rendering on every platform for maximum performance.
Controls
| Control |
Description |
FlavorTextField |
Material Design text field with floating labels, masks, converters, validation, adornments, 3 variants. |
FlavorButton |
Text button with Text, Filled, Outlined variants, icons, ripple, loading spinner, sizes, colors. |
FlavorIconButton |
Circular icon-only button with Default, Filled, Outlined variants, sizes, colors. |
FlavorFab |
Floating Action Button — circular (icon-only) or extended (icon + label), 3 sizes, elevation, ripple. |
FlavorFabMenu |
Speed-dial FAB that expands to show child FlavorFabAction items with animated stagger. |
FlavorFabAnchor |
Page-level wrapper that floats a FlavorFab / FlavorFabMenu at any corner/edge position. |
| Platform |
Status |
| Android (API 21+) |
Full support |
| iOS (15+) |
Full support |
| macOS Catalyst (15+) |
Full support |
| Windows (10.0.19041+) |
Partial — FAB works, Button/IconButton/TextField are stubs. Full support coming soon. |
Installation
dotnet add package Flavor.Maui
Quick Start
1. Register in MauiProgram.cs
using Flavor.Maui.Extensions;
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseFlavor(); // Registers all Flavor.Maui handlers
2. Add XAML namespace
xmlns:flavor="clr-namespace:Flavor.Maui.Controls;assembly=Flavor.Maui"
3. Use controls
<flavor:FlavorTextField Label="Name" Placeholder="Enter your name" />
<flavor:FlavorTextField Label="Email" Variant="Filled" InputType="Email" />
<flavor:FlavorTextField Label="Phone" Variant="Standard" InputType="Telephone" />
FlavorTextField
A full-featured Material Design text field with floating labels, validation, input masks, type converters, adornments, and three visual variants.
Variants
| Variant |
Description |
Outlined |
Border around the field (default). Label floats to the top border on focus. |
Filled |
Filled background with optional bottom underline. Label floats inside near the top. |
Standard |
Minimal style with optional bottom underline only. Label floats inside near the top. |
<flavor:FlavorTextField Label="Outlined" Variant="Outlined" />
<flavor:FlavorTextField Label="Filled" Variant="Filled" />
<flavor:FlavorTextField Label="Standard" Variant="Standard" />
Properties Reference
Core Text Properties
| Property |
Type |
Default |
Description |
Text |
string |
"" |
The input text value. Two-way bindable. |
Label |
string |
"" |
Floating label text that animates above the input on focus. |
Placeholder |
string |
"" |
Placeholder hint shown when the label has floated and the field is empty. |
HelperText |
string |
"" |
Assistive text displayed below the field. |
ErrorText |
string |
"" |
Error message shown below the field when HasError is true. |
<flavor:FlavorTextField Label="Username"
Placeholder="e.g. john_doe"
HelperText="Must be at least 3 characters" />
| Property |
Type |
Default |
Description |
InputType |
FlavorInputType |
Text |
Keyboard and input behavior. See enum values below. |
MaxLength |
int |
int.MaxValue |
Maximum number of characters allowed. |
Lines |
int |
1 |
Number of visible lines. Values > 1 enable multi-line input. |
IsReadOnly |
bool |
false |
Makes the field read-only (visible but not editable). |
IsDisabled |
bool |
false |
Disables the field entirely with muted styling. |
AutoFocus |
bool |
false |
Automatically focuses the field when it appears. |
<flavor:FlavorTextField Label="Password" InputType="Password" />
<flavor:FlavorTextField Label="Bio" Lines="4" MaxLength="500" ShowCounter="True" />
<flavor:FlavorTextField Label="Amount" InputType="Numeric" />
Visual & Layout
| Property |
Type |
Default |
Description |
Variant |
FlavorVariant |
Outlined |
Visual style: Outlined, Filled, or Standard. |
Dense |
bool |
false |
Compact mode. Outlined: 40dp height. Filled/Standard: 48dp height. Normal: 56dp. |
FullWidth |
bool |
false |
Stretches the field to fill the available width. |
AccentColor |
Color |
#594AE2 |
Custom color for focus border, label, and underline. |
Underline |
bool |
true |
Show/hide the bottom underline on Filled and Standard variants. |
ShrinkLabel |
bool |
false |
Keeps the label permanently floated (shrunk) even when the field is empty. |
ShowCounter |
bool |
false |
Displays a character counter (e.g., "5/200") below the field. Requires MaxLength. |
<flavor:FlavorTextField Label="Compact" Dense="True" />
<flavor:FlavorTextField Label="Pink accent" AccentColor="#E91E63" />
<flavor:FlavorTextField Label="Message" FullWidth="True"
MaxLength="280" ShowCounter="True" />
<flavor:FlavorTextField Label="Currency" ShrinkLabel="True"
AdornmentText="$" Adornment="Start" />
Adornments (Icons & Text)
| Property |
Type |
Default |
Description |
Adornment |
FlavorAdornment |
None |
Position of the adornment: None, Start, or End. |
AdornmentIcon |
string |
null |
Resource name for adornment icon (e.g., "icon_search"). |
AdornmentText |
string |
null |
Text to display as an adornment (e.g., "$", "kg", "@gmail.com"). |
AdornmentColor |
Color |
null |
Custom color for the adornment icon/text. |
AdornmentAriaLabel |
string |
null |
Accessibility label for the adornment. |
IconSize |
FlavorIconSize |
Medium |
Size of adornment icons: Small (16dp), Medium (24dp), Large (32dp). |
<flavor:FlavorTextField Label="Search"
Adornment="Start" AdornmentIcon="icon_search" />
<flavor:FlavorTextField Label="Price"
Adornment="Start" AdornmentText="$"
InputType="Numeric" />
<flavor:FlavorTextField Label="Email"
Adornment="End" AdornmentText="@gmail.com" />
<flavor:FlavorTextField Label="Filter" Dense="True"
Adornment="Start" AdornmentIcon="icon_search"
IconSize="Small" />
Clearable
| Property |
Type |
Default |
Description |
IsClearable |
bool |
false |
Shows an X button when the field has text and is focused. |
ClearIcon |
string |
null |
Custom icon resource name for the clear button. Uses a default X if not set. |
<flavor:FlavorTextField Label="Search" IsClearable="True" />
<flavor:FlavorTextField Label="Tags" IsClearable="True" ClearIcon="icon_close" />
Validation
| Property |
Type |
Default |
Description |
HasError |
bool |
false |
Manually set to true to show the error state. Also set automatically by validation. |
IsRequired |
bool |
false |
When true, shows an error if the field is empty on blur. |
RequiredError |
string |
"Required" |
Error message shown when a required field is empty. |
Validation |
Func<string, string> |
null |
Custom validation function. Return an error message string, or null/empty for valid. |
ValidationMode |
FlavorValidationMode |
OnBlur |
When to validate: OnBlur (on focus loss) or OnChange (on every keystroke). |
Pattern |
string |
null |
Regex pattern for validation. Fails if the input doesn't match. |
PatternError |
string |
"Invalid format" |
Error message shown when Pattern validation fails. |
OnlyValidateIfDirty |
bool |
false |
Only run validation after the user has typed at least once. |
Immediate |
bool |
false |
Alias for setting ValidationMode="OnChange". |
DebounceInterval |
double |
0 |
Delay (in milliseconds) before validation runs after typing stops. Useful for expensive checks. |
HelperTextOnFocus |
bool |
false |
Show HelperText only when the field is focused. |
<flavor:FlavorTextField Label="Name" IsRequired="True" RequiredError="Name is required" />
<flavor:FlavorTextField Label="Phone"
Pattern="^\d{10}$"
PatternError="Must be 10 digits" />
<flavor:FlavorTextField Label="Username"
ValidationMode="OnChange"
DebounceInterval="500" />
Custom validation in code-behind:
myField.Validation = (text) =>
{
if (text.Length < 8) return "Must be at least 8 characters";
if (!text.Any(char.IsUpper)) return "Must contain an uppercase letter";
if (!text.Any(char.IsDigit)) return "Must contain a number";
return null; // Valid
};
| Property |
Type |
Default |
Description |
Mask |
IFlavorMask |
null |
Input mask that formats text as the user types. |
PatternMask is the built-in mask implementation:
| Pattern Char |
Matches |
# |
Digit (0-9) |
a |
Letter (a-z, A-Z) |
* |
Any character |
| Other |
Literal separator (inserted automatically) |
// In code-behind or ViewModel
phoneField.Mask = new PatternMask("(###) ###-####");
creditCardField.Mask = new PatternMask("####-####-####-####");
dateField.Mask = new PatternMask("##/##/####");
timeField.Mask = new PatternMask("##:##");
zipField.Mask = new PatternMask("#####-####");
Mask behavior:
- Literal separators (parentheses, dashes, slashes) are inserted automatically as the user types.
MaxLength is automatically set to the pattern length.
GetUnmaskedValue() strips literals to return raw input.
| User types |
Pattern |
Displayed |
1234567890 |
(###) ###-#### |
(123) 456-7890 |
4111111111111111 |
####-####-####-#### |
4111-1111-1111-1111 |
12252026 |
##/##/#### |
12/25/2026 |
Value Converters
| Property |
Type |
Default |
Description |
Value |
object |
null |
Typed value bound via the converter. Two-way bindable. |
Converter |
IFlavorConverter |
null |
Converts between string text and typed values. |
Format |
string |
null |
Display format string for the converter. |
Built-in converters via FlavorConverters:
| Converter |
Type |
Example |
FlavorConverters.Int |
int |
"42" → 42 |
FlavorConverters.Double |
double |
"3.14" → 3.14 |
FlavorConverters.Decimal |
decimal |
"99.99" → 99.99m |
FlavorConverters.DateTime |
DateTime |
"2026-04-06" → DateTime |
priceField.Converter = FlavorConverters.Double;
priceField.ValueChanged += (s, value) =>
{
double price = (double)value; // Typed value
};
<flavor:FlavorTextField x:Name="PriceField"
Label="Price"
InputType="Numeric"
Adornment="Start" AdornmentText="$" />
Events
| Event |
Type |
Description |
TextChanged |
EventHandler<TextChangedEventArgs> |
Fired when text changes. Respects DebounceInterval. |
ClearButtonClicked |
EventHandler |
Fired when the clear button is tapped. |
AdornmentClicked |
EventHandler |
Fired when the adornment icon/text is tapped. |
TextFieldFocused |
EventHandler |
Fired when the field receives focus. |
TextFieldBlurred |
EventHandler |
Fired when the field loses focus. |
ValueChanged |
EventHandler<object> |
Fired when Value changes (requires a Converter). |
ErrorChanged |
EventHandler<bool> |
Fired when HasError changes. |
myField.TextChanged += (sender, e) =>
{
Console.WriteLine($"New text: {e.NewTextValue}");
};
myField.AdornmentClicked += (sender, e) =>
{
// Toggle password visibility, open picker, etc.
};
Enums
FlavorVariant
public enum FlavorVariant
{
Standard, // Underline only, minimal style
Filled, // Filled background with optional underline
Outlined // Border around the field (default)
}
public enum FlavorInputType
{
Text, // Default text keyboard
Password, // Obscured input with secure keyboard
Email, // Email keyboard layout
Numeric, // Number pad
Telephone, // Phone dialer keyboard
Url // URL keyboard layout
}
FlavorAdornment
public enum FlavorAdornment
{
None, // No adornment (default)
Start, // Icon or text at the leading edge
End // Icon or text at the trailing edge
}
FlavorIconSize
public enum FlavorIconSize
{
Small, // 16dp
Medium, // 24dp (default)
Large // 32dp
}
FlavorValidationMode
public enum FlavorValidationMode
{
OnBlur, // Validate when field loses focus (default)
OnChange // Validate on every keystroke
}
Theming
Flavor.Maui automatically adapts to light and dark themes via the FlavorColors static class. All colors respond to Application.Current.RequestedTheme changes.
Brand Colors (Theme-independent)
| Color |
Value |
Usage |
Primary |
#594AE2 |
Focus borders, labels, default accent |
PrimaryDark |
#3F2DB8 |
Pressed states |
PrimaryLight |
#7E6FE8 |
Hover states |
Secondary |
#FF4081 |
Secondary actions |
Error |
#F44336 |
Error states |
Warning |
#FF9800 |
Warning states |
Info |
#2196F3 |
Info states |
Success |
#4CAF50 |
Success states |
Theme-Aware Colors
| Color |
Light |
Dark |
Surface |
#FFFFFF |
#1E1E2E |
Background |
#F5F5F5 |
#121212 |
TextPrimary |
#424242 |
#E0E0E0 |
TextSecondary |
#757575 |
#AAAAAA |
TextDisabled |
#BDBDBD |
#666666 |
BorderDefault |
#BDBDBD |
#555555 |
FilledBackground |
#F0F0F0 |
#2A2A3A |
Complete Examples
<StackLayout Spacing="8" Padding="16">
<flavor:FlavorTextField Label="Email"
InputType="Email"
IsRequired="True"
RequiredError="Email is required"
Adornment="Start" AdornmentIcon="icon_email" />
<flavor:FlavorTextField Label="Password"
InputType="Password"
IsRequired="True"
Adornment="End" AdornmentIcon="icon_eye_off" />
<Button Text="Sign In" />
</StackLayout>
<StackLayout Spacing="8" Padding="16">
<flavor:FlavorTextField x:Name="CardNumber"
Label="Card Number"
InputType="Numeric"
Adornment="Start" AdornmentIcon="icon_credit_card" />
<Grid ColumnDefinitions="*,*" ColumnSpacing="8">
<flavor:FlavorTextField x:Name="Expiry"
Label="MM/YY"
InputType="Numeric" />
<flavor:FlavorTextField Label="CVV"
InputType="Numeric"
MaxLength="4"
Grid.Column="1" />
</Grid>
<flavor:FlavorTextField Label="Amount"
InputType="Numeric"
Adornment="Start" AdornmentText="$"
x:Name="AmountField" />
</StackLayout>
// Code-behind
CardNumber.Mask = new PatternMask("####-####-####-####");
Expiry.Mask = new PatternMask("##/##");
AmountField.Converter = FlavorConverters.Double;
Search with Debounce
<flavor:FlavorTextField Label="Search"
IsClearable="True"
Adornment="Start" AdornmentIcon="icon_search"
DebounceInterval="300"
Dense="True"
TextChanged="OnSearchTextChanged" />
<StackLayout Spacing="8" Padding="16">
<flavor:FlavorTextField x:Name="PasswordField"
Label="Password"
InputType="Password"
IsRequired="True" />
<flavor:FlavorTextField x:Name="ConfirmField"
Label="Confirm Password"
InputType="Password" />
</StackLayout>
PasswordField.Validation = (text) =>
{
if (text.Length < 8) return "At least 8 characters";
if (!text.Any(char.IsUpper)) return "Needs an uppercase letter";
if (!text.Any(char.IsDigit)) return "Needs a number";
return null;
};
ConfirmField.Validation = (text) =>
text != PasswordField.Text ? "Passwords don't match" : null;
Architecture
Flavor.Maui uses the .NET MAUI handler pattern with fully native rendering on each platform:
FlavorTextField (MAUI Control)
|
v
FlavorTextFieldHandler (Property Mapper)
|
+-- Android: Custom FrameLayout + EditText + Canvas drawing
+-- iOS: Custom UIView + UITextField + CoreAnimation
+-- MacCatalyst: Same as iOS (UIKit via Catalyst)
+-- Windows: WinUI TextBox wrapper (basic)
Each platform renders using native APIs (no MAUI wrappers), ensuring:
- 60fps floating label animations
- Native keyboard behaviors
- Platform-consistent look and feel
- Minimal overhead
A Material Design text button with three variants, icon slots, ripple, loading state, and built-in color/size presets.
<flavor:FlavorButton Text="Save" Variant="Filled" Color="Primary" />
<flavor:FlavorButton Text="Cancel" Variant="Outlined" Color="Secondary" />
<flavor:FlavorButton Text="Delete" Variant="Text" Color="Error" />
Properties
| Property |
Type |
Default |
Description |
Text |
string |
"" |
Button text. |
Variant |
FlavorVariant |
Filled |
Text, Filled, or Outlined. |
Color |
FlavorColor |
Primary |
Preset: Default, Primary, Secondary, Tertiary, Info, Success, Warning, Error, Dark, Transparent, Inherit, Surface. |
AccentColor |
Color |
null |
Override preset color with a custom color. |
Size |
FlavorSize |
Medium |
Small, Medium, Large. |
CornerRadius |
double |
platform default |
Button corner radius. |
FullWidth |
bool |
false |
Stretches to fill available width. |
Dense |
bool |
false |
Compact padding. |
StartIcon / EndIcon |
string |
null |
Icon resource name at the leading / trailing edge. |
IconSize |
FlavorIconSize |
Medium |
Small / Medium / Large. |
IconColor |
Color |
null |
Override icon tint. |
IsDisabled |
bool |
false |
Disables the button. |
IsLoading |
bool |
false |
Shows a loading spinner in place of the text. |
Ripple |
bool |
true |
Native ripple/press feedback. |
DropShadow |
bool |
true |
Drop shadow under the button. |
Elevation |
int |
platform default |
Shadow elevation. |
Command / CommandParameter |
ICommand / object |
null |
MVVM binding. |
Events
| Event |
Description |
Clicked |
Fired on tap/release. |
Pressed |
Fired when the user starts pressing. |
Released |
Fired when the press is released. |
<flavor:FlavorButton Text="Upload"
Variant="Filled"
Color="Primary"
StartIcon="icon_upload"
IsLoading="{Binding IsUploading}"
Command="{Binding UploadCommand}" />
A circular icon-only button with optional variants, ripple, and presets.
<flavor:FlavorIconButton Icon="icon_edit" Color="Primary" />
<flavor:FlavorIconButton Icon="icon_delete" Variant="Outlined" Color="Error" />
<flavor:FlavorIconButton Icon="icon_close" Variant="Filled" Color="Secondary" Size="Large" />
Properties
| Property |
Type |
Default |
Description |
Icon |
string |
null |
Icon resource name. |
Variant |
FlavorVariant |
Text |
Text (transparent), Filled, or Outlined. |
Color |
FlavorColor |
Default |
Color preset. |
AccentColor |
Color |
null |
Custom override color. |
Size |
FlavorSize |
Medium |
Small / Medium / Large. |
IsDisabled |
bool |
false |
Disabled state. |
Ripple |
bool |
true |
Native ripple feedback. |
DropShadow |
bool |
false |
Shadow under the button. |
Elevation |
int |
0 |
Shadow elevation. |
Command / CommandParameter |
ICommand / object |
null |
MVVM binding. |
Events: Clicked.
FlavorFab
A Material Design Floating Action Button — circular (icon-only) or extended (icon + label).
<flavor:FlavorFab Icon="icon_add" Color="Primary" />
<flavor:FlavorFab Icon="icon_edit" Label="Compose" Color="Secondary" />
<flavor:FlavorFab Icon="icon_add" Size="Small" />
<flavor:FlavorFab Icon="icon_add" Size="Medium" />
<flavor:FlavorFab Icon="icon_add" Size="Large" />
Properties
| Property |
Type |
Default |
Description |
Icon |
string |
null |
Main icon (circular FAB). |
StartIcon / EndIcon |
string |
null |
Leading / trailing icon for extended FAB. |
IconSize |
FlavorIconSize |
Medium |
Icon size. |
IconColor |
Color |
null |
Custom icon tint. |
Label |
string |
null |
Text label — when set, FAB becomes extended. |
Color |
FlavorColor |
Primary |
Color preset. |
AccentColor |
Color |
null |
Custom background color override. |
Size |
FlavorSize |
Medium |
Small, Medium, Large. |
IsDisabled |
bool |
false |
Disabled state. |
Ripple |
bool |
true |
Native ripple. |
Elevation |
int |
6 |
Shadow elevation. |
Command / CommandParameter |
ICommand / object |
null |
MVVM binding. |
Floating over a page
Wrap your page content in FlavorFabAnchor to float the FAB at a corner:
<flavor:FlavorFabAnchor Position="BottomEnd">
<flavor:FlavorFabAnchor.Fab>
<flavor:FlavorFab Icon="icon_add" Color="Primary"
Command="{Binding AddCommand}" />
</flavor:FlavorFabAnchor.Fab>
<ScrollView>
</ScrollView>
</flavor:FlavorFabAnchor>
Position values: BottomEnd (default), BottomCenter, BottomStart, TopEnd, TopCenter, TopStart.
Material Design Speed Dial — a main FAB that expands to reveal child FlavorFabAction items with a staggered animation.
<flavor:FlavorFabAnchor Position="BottomEnd">
<flavor:FlavorFabAnchor.Fab>
<flavor:FlavorFabMenu Icon="icon_add" CloseIcon="icon_close"
Color="Primary"
Direction="Top">
<flavor:FlavorFabAction Icon="icon_edit"
Label="Edit"
Color="Secondary"
Command="{Binding EditCommand}" />
<flavor:FlavorFabAction Icon="icon_send"
Label="Send"
Color="Success"
Command="{Binding SendCommand}" />
<flavor:FlavorFabAction Icon="icon_delete"
Label="Delete"
Color="Error"
Command="{Binding DeleteCommand}" />
</flavor:FlavorFabMenu>
</flavor:FlavorFabAnchor.Fab>
</flavor:FlavorFabAnchor>
| Property |
Type |
Default |
Description |
Icon |
string |
null |
Main FAB icon (closed state). |
CloseIcon |
string |
null |
Icon shown when menu is open. If null, the Icon rotates 45°. |
Color |
FlavorColor |
Primary |
Main FAB color preset. |
AccentColor |
Color |
null |
Custom color override. |
Size |
FlavorSize |
Large |
Main FAB size. |
Direction |
FlavorFabDirection |
Top |
Expansion direction: Top, Bottom, Left, Right. |
IsOpen |
bool |
false |
Two-way bindable open state. |
ActionSpacing |
double |
16 |
Pixel gap between action items. |
Actions |
ObservableCollection<FlavorFabAction> |
— |
Child actions (default content property). |
FlavorFabAction Properties
| Property |
Type |
Description |
Icon |
string |
Action icon resource. |
Label |
string |
Optional label shown next to the action. |
Color |
FlavorColor |
Preset color. |
AccentColor |
Color |
Custom color override. |
IconColor |
Color |
Custom icon tint. |
IsVisible |
bool |
Show/hide this action. |
Command / CommandParameter |
ICommand / object |
MVVM binding. |
Clicked |
event |
Raised on tap. |
FlavorFabAnchor automatically shows a dim backdrop while the menu is open, tap-to-dismiss enabled.
Sample App
Browse the sample app source code — clone the repo and run it to see every feature in action.
| Page |
Demonstrates |
| TextField — Basic |
All three variants, input types, read-only/disabled states, multi-line |
| TextField — Validation |
Required fields, pattern regex, custom callbacks, debounce |
| TextField — Icons |
Start/end icons, clearable, text adornments, icon sizes |
| TextField — Masks |
Phone, credit card, date, time, zip masks + type converters |
| TextField — Styling |
Dense mode, accent colors, underline toggle, combinations |
| Button |
Text/Filled/Outlined variants, sizes, colors, icons, loading state |
| IconButton |
Variants, sizes, colors, with and without background |
| FAB |
Circular and extended FABs, sizes, custom colors |
| FAB Menu |
Speed-dial with stagger animation, directions, backdrop |
Run the sample
git clone https://github.com/mistrypragnesh40/Flavor.Maui.Demo.git
cd Flavor.Maui.Demo
# iOS
dotnet build samples/Flavor.Maui.Sample -f net10.0-ios -t:Run
# Android
dotnet build samples/Flavor.Maui.Sample -f net10.0-android -t:Run
# macOS
dotnet build samples/Flavor.Maui.Sample -f net10.0-maccatalyst -t:Run
License
MIT License - see LICENSE for details.
Author
Pragnesh Mistry