Skip to main content

Theming & Customization

Learn how to customize Appikorn Madix Widgets to match your app's design system.

Overview​

Appikorn Madix Widgets are designed to be highly customizable. Each widget accepts style parameters that allow you to create a consistent design system across your application.

Theme Integration​

Using Flutter's ThemeData​

Appikorn widgets automatically inherit from Flutter's ThemeData:

MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Roboto',
textTheme: TextTheme(
headlineLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
bodyLarge: TextStyle(fontSize: 16),
),
),
home: MyHomePage(),
)

Custom Theme Extension​

Create a custom theme extension for Appikorn widgets:

class AppiTheme extends ThemeExtension<AppiTheme> {
final Color primaryColor;
final Color secondaryColor;
final Color accentColor;
final BorderRadius borderRadius;
final double spacing;

const AppiTheme({
required this.primaryColor,
required this.secondaryColor,
required this.accentColor,
required this.borderRadius,
required this.spacing,
});


AppiTheme copyWith({
Color? primaryColor,
Color? secondaryColor,
Color? accentColor,
BorderRadius? borderRadius,
double? spacing,
}) {
return AppiTheme(
primaryColor: primaryColor ?? this.primaryColor,
secondaryColor: secondaryColor ?? this.secondaryColor,
accentColor: accentColor ?? this.accentColor,
borderRadius: borderRadius ?? this.borderRadius,
spacing: spacing ?? this.spacing,
);
}


AppiTheme lerp(ThemeExtension<AppiTheme>? other, double t) {
if (other is! AppiTheme) return this;
return AppiTheme(
primaryColor: Color.lerp(primaryColor, other.primaryColor, t)!,
secondaryColor: Color.lerp(secondaryColor, other.secondaryColor, t)!,
accentColor: Color.lerp(accentColor, other.accentColor, t)!,
borderRadius: BorderRadius.lerp(borderRadius, other.borderRadius, t)!,
spacing: lerpDouble(spacing, other.spacing, t)!,
);
}
}

Color Schemes​

Light Theme​

final lightTheme = ThemeData(
brightness: Brightness.light,
primarySwatch: Colors.blue,
extensions: [
AppiTheme(
primaryColor: Color(0xFF2196F3),
secondaryColor: Color(0xFF03DAC6),
accentColor: Color(0xFFFF5722),
borderRadius: BorderRadius.circular(12),
spacing: 16.0,
),
],
);

Dark Theme​

final darkTheme = ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.blue,
extensions: [
AppiTheme(
primaryColor: Color(0xFF1976D2),
secondaryColor: Color(0xFF00BCD4),
accentColor: Color(0xFFFF5722),
borderRadius: BorderRadius.circular(12),
spacing: 16.0,
),
],
);

Widget-Specific Styling​

TextAppi Styling​

TextAppi(
text: 'Styled Text',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryColor,
letterSpacing: 0.5,
),
textAlign: TextAlign.center,
)

BoxAppi Styling​

BoxAppi(
width: 200,
height: 100,
color: Theme.of(context).primaryColor,
borderRadius: 16,
border: Border.all(
color: Theme.of(context).dividerColor,
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: Offset(0, 2),
),
],
child: Center(
child: TextAppi(
text: 'Styled Box',
style: TextStyle(color: Colors.white),
),
),
)

Form Field Styling​

TextFieldAppi(
label: 'Email',
hintText: 'Enter your email',
borderRadius: 12,
borderColor: Theme.of(context).primaryColor,
focusedBorderColor: Theme.of(context).accentColor,
fillColor: Theme.of(context).cardColor,
textStyle: Theme.of(context).textTheme.bodyLarge,
)

Design Tokens​

Create a design system with consistent tokens:

class DesignTokens {
// Colors
static const Color primary = Color(0xFF2196F3);
static const Color secondary = Color(0xFF03DAC6);
static const Color accent = Color(0xFFFF5722);
static const Color surface = Color(0xFFFFFFFF);
static const Color background = Color(0xFFF5F5F5);
static const Color error = Color(0xFFB00020);

// Typography
static const TextStyle heading1 = TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
letterSpacing: -0.5,
);

static const TextStyle heading2 = TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
letterSpacing: -0.25,
);

static const TextStyle body1 = TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
letterSpacing: 0.15,
);

// Spacing
static const double spaceXS = 4.0;
static const double spaceSM = 8.0;
static const double spaceMD = 16.0;
static const double spaceLG = 24.0;
static const double spaceXL = 32.0;

// Border Radius
static const double radiusXS = 4.0;
static const double radiusSM = 8.0;
static const double radiusMD = 12.0;
static const double radiusLG = 16.0;
static const double radiusXL = 24.0;

// Shadows
static const List<BoxShadow> shadowSM = [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 4,
offset: Offset(0, 1),
),
];

static const List<BoxShadow> shadowMD = [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 8,
offset: Offset(0, 2),
),
];

static const List<BoxShadow> shadowLG = [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 16,
offset: Offset(0, 4),
),
];
}

Responsive Design​

Breakpoints​

class Breakpoints {
static const double mobile = 480;
static const double tablet = 768;
static const double desktop = 1024;
static const double largeDesktop = 1440;

static bool isMobile(BuildContext context) {
return MediaQuery.of(context).size.width < mobile;
}

static bool isTablet(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return width >= mobile && width < desktop;
}

static bool isDesktop(BuildContext context) {
return MediaQuery.of(context).size.width >= desktop;
}
}

Responsive Widget Usage​

BoxAppi(
width: Breakpoints.isMobile(context) ? 150 : 200,
height: Breakpoints.isMobile(context) ? 80 : 100,
color: Theme.of(context).primaryColor,
borderRadius: Breakpoints.isMobile(context) ? 8 : 12,
)

Animation Themes​

Custom Animation Durations​

class AnimationTokens {
static const Duration fast = Duration(milliseconds: 150);
static const Duration normal = Duration(milliseconds: 300);
static const Duration slow = Duration(milliseconds: 500);

static const Curve easeIn = Curves.easeIn;
static const Curve easeOut = Curves.easeOut;
static const Curve easeInOut = Curves.easeInOut;
static const Curve bounce = Curves.bounceOut;
}

Animated Widget Example​

AnimatedBoxAppi(
width: isExpanded ? 300 : 200,
height: isExpanded ? 200 : 100,
color: Theme.of(context).primaryColor,
duration: AnimationTokens.normal,
curve: AnimationTokens.easeInOut,
)

Best Practices​

1. Consistency​

  • Use design tokens for consistent spacing, colors, and typography
  • Create reusable style components
  • Follow your design system guidelines

2. Accessibility​

  • Ensure sufficient color contrast (WCAG AA: 4.5:1, AAA: 7:1)
  • Use semantic colors for different states
  • Test with screen readers

3. Performance​

  • Use const constructors when possible
  • Avoid rebuilding styled widgets unnecessarily
  • Cache complex style calculations

4. Maintainability​

  • Centralize theme definitions
  • Use meaningful variable names
  • Document custom theme extensions

Example: Complete Theme Setup​

class MyApp extends StatelessWidget {

Widget build(BuildContext context) {
return MaterialApp(
title: 'Appikorn Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Inter',
extensions: [
AppiTheme(
primaryColor: DesignTokens.primary,
secondaryColor: DesignTokens.secondary,
accentColor: DesignTokens.accent,
borderRadius: BorderRadius.circular(DesignTokens.radiusMD),
spacing: DesignTokens.spaceMD,
),
],
),
darkTheme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.blue,
fontFamily: 'Inter',
extensions: [
AppiTheme(
primaryColor: DesignTokens.primary,
secondaryColor: DesignTokens.secondary,
accentColor: DesignTokens.accent,
borderRadius: BorderRadius.circular(DesignTokens.radiusMD),
spacing: DesignTokens.spaceMD,
),
],
),
home: MyHomePage(),
);
}
}

Next, explore our widget gallery to see how theming applies to each component!