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!