Skip to Content
Mix 2.0 is in development! You can access the Mix 1.0 docs here.
DocsOverviewBest Practices

Best Practices

Follow these practices to keep your Mix code consistent and maintainable as your app grows.

These guidelines target scalability and maintainability in Mix, and will evolve with community feedback.

This walkthrough creates a button based on Shadcn’s Button  component, called CustomButton.

File Structure

A well-organized file structure is crucial for maintaining a clean and scalable codebase, especially when working with design systems in Flutter. By dividing the button component into separate files as shown below, we not only enhance readability but also promote modularity and maintainability:

      • button.dart
      • button.variant.dart
      • button.style.dart
  • button.dart - This file will contain the CustomButton class and define the component’s structure.

  • button.variant.dart - Contains the CustomButtonVariant class and all its Variant instances.

  • button.style.dart - In this file, the CustomButtonStyle class will be created, housing all Style instances related to the button.

Widget Structure

Use Mix’s styled widgets for component creation. These widgets are key in structuring and styling your component. For example, the structure of your Button might look like this:

class CustomButton extends StatelessWidget { const CustomButton({ super.key, required this.title, required this.onPress, }); final String title; final void Function() onPress; @override Widget build(BuildContext context) { return Pressable( onPress: onPress, child: Box( child: StyledText(title), ), ); } }

This example uses Mix’s styled widgets: Pressable, Box, and StyledText. See the full list in the StyleWidgets reference.

Defining Variants

Use enums to define variant options. Each enum value can provide associated styles:

enum CustomButtonType { primary, destructive, link } enum CustomButtonSize { medium, large }

This approach provides:

  • Consistency: Predefined styles applied through a simple enum API
  • Flexibility: Add new variants without changing the core component
  • Ease of Use: Developers pick from autocomplete-friendly enum values

Incorporate these variants into the CustomButton:

class CustomButton extends StatelessWidget { const CustomButton({ super.key, required this.title, required this.onPress, this.type = CustomButtonType.primary, this.size = CustomButtonSize.large, }); final String title; final void Function() onPress; final CustomButtonType type; final CustomButtonSize size; @override Widget build(BuildContext context) { return Pressable( onPress: onPress, child: Box( child: StyledText(title), ), ); } }

Styling the component

The CustomButtonStyle class defines styles for each variant. Each method returns a Styler with the appropriate visual properties based on the active type and size.

class CustomButtonStyle { CustomButtonStyle(this.type, this.size); final CustomButtonType type; final CustomButtonSize size; BoxStyler container() { var style = BoxStyler().borderRadius(.circular(8)); // Apply size style = switch (size) { CustomButtonSize.medium => style.paddingX(16).paddingY(8), CustomButtonSize.large => style.paddingX(24).paddingY(16), }; // Apply type style = switch (type) { CustomButtonType.primary => style.color(Colors.black), CustomButtonType.destructive => style.color(Colors.redAccent), CustomButtonType.link => style.color(Colors.transparent), }; return style; } TextStyler label() { var style = TextStyler() .color(Colors.white) .fontWeight(.bold); // Apply size style = switch (size) { CustomButtonSize.medium => style.fontSize(14), CustomButtonSize.large => style.fontSize(18), }; // Apply type style = switch (type) { CustomButtonType.primary => style.color(Colors.white), CustomButtonType.destructive => style.color(Colors.white), CustomButtonType.link => style .color(Colors.black) .decoration(.underline), }; return style; } }

With these styles in place, update the CustomButton class to apply them:

class CustomButton extends StatelessWidget { const CustomButton({ super.key, required this.title, required this.onPress, this.type = CustomButtonType.primary, this.size = CustomButtonSize.large, }); final String title; final void Function() onPress; final CustomButtonType type; final CustomButtonSize size; @override Widget build(BuildContext context) { final style = CustomButtonStyle(type, size); return Pressable( onPress: onPress, child: Box( style: style.container(), child: StyledText( title, style: style.label(), ), ), ); } }

The CustomButton component is now ready to use, following Mix’s recommended structure and conventions.