Flutter DecoratedBox Localization: Pure Decoration for Multilingual Interfaces
DecoratedBox is a Flutter widget that paints a decoration behind or in front of its child. Unlike Container, DecoratedBox focuses solely on decoration without affecting sizing or layout, making it ideal for applying visual styling to multilingual content without altering its natural dimensions.
Understanding DecoratedBox in Localization Context
DecoratedBox applies BoxDecoration to its child without adding constraints, padding, or margins. For multilingual apps, this provides:
- Pure visual styling without layout interference
- Decoration that adapts to content size naturally
- Direction-aware gradient and decoration rendering
- Separation of decoration from layout concerns
Why DecoratedBox Matters for Multilingual Apps
Focused decoration provides:
- Natural sizing: Content determines its own size
- Clean separation: Decoration logic separate from layout
- Performance: Lighter weight than Container for pure decoration
- RTL support: Decorations render correctly in all directions
Basic DecoratedBox Implementation
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class LocalizedDecoratedBoxExample extends StatelessWidget {
const LocalizedDecoratedBoxExample({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return DecoratedBox(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
l10n.decoratedContent,
style: Theme.of(context).textTheme.bodyLarge,
),
),
);
}
}
Background and Foreground Decoration
Background Decoration
class LocalizedBackgroundCard extends StatelessWidget {
final String title;
final String content;
const LocalizedBackgroundCard({
super.key,
required this.title,
required this.content,
});
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: AlignmentDirectional.topStart,
end: AlignmentDirectional.bottomEnd,
colors: [
Theme.of(context).colorScheme.primaryContainer,
Theme.of(context).colorScheme.secondaryContainer,
],
),
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Text(
content,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
);
}
}
Foreground Decoration Overlay
class LocalizedOverlayCard extends StatelessWidget {
final String imageUrl;
final String title;
final String subtitle;
const LocalizedOverlayCard({
super.key,
required this.imageUrl,
required this.title,
required this.subtitle,
});
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Stack(
children: [
Image.network(
imageUrl,
height: 200,
width: double.infinity,
fit: BoxFit.cover,
),
Positioned.fill(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black.withOpacity(0.8),
],
),
),
position: DecorationPosition.foreground,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.white70,
),
),
],
),
),
),
),
],
),
);
}
}
Direction-Aware Gradients
RTL-Safe Gradient Decoration
class DirectionalGradientBox extends StatelessWidget {
final Widget child;
final List<Color> colors;
const DirectionalGradientBox({
super.key,
required this.child,
required this.colors,
});
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: AlignmentDirectional.centerStart,
end: AlignmentDirectional.centerEnd,
colors: colors,
),
borderRadius: BorderRadius.circular(12),
),
child: child,
);
}
}
// Usage
class GradientCardExample extends StatelessWidget {
const GradientCardExample({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return DirectionalGradientBox(
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.tertiary,
],
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Text(
l10n.gradientCardText,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Colors.white,
),
),
),
);
}
}
Directional Border Decoration
class DirectionalBorderBox extends StatelessWidget {
final Widget child;
final Color borderColor;
final double borderWidth;
const DirectionalBorderBox({
super.key,
required this.child,
required this.borderColor,
this.borderWidth = 2,
});
@override
Widget build(BuildContext context) {
final isRTL = Directionality.of(context) == TextDirection.rtl;
return DecoratedBox(
decoration: BoxDecoration(
border: Border(
left: isRTL
? BorderSide.none
: BorderSide(color: borderColor, width: borderWidth),
right: isRTL
? BorderSide(color: borderColor, width: borderWidth)
: BorderSide.none,
),
),
child: child,
);
}
}
// Or use BorderDirectional
class DirectionalBorderBoxImproved extends StatelessWidget {
final Widget child;
final Color borderColor;
final double borderWidth;
const DirectionalBorderBoxImproved({
super.key,
required this.child,
required this.borderColor,
this.borderWidth = 4,
});
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: ShapeDecoration(
shape: BorderDirectional(
start: BorderSide(color: borderColor, width: borderWidth),
),
),
child: child,
);
}
}
// Usage
class QuoteBox extends StatelessWidget {
const QuoteBox({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return DirectionalBorderBoxImproved(
borderColor: Theme.of(context).colorScheme.primary,
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 16),
child: Text(
l10n.quoteText,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontStyle: FontStyle.italic,
),
),
),
);
}
}
Themed Decoration Patterns
Theme-Aware Decoration
class ThemedDecoratedBox extends StatelessWidget {
final Widget child;
final ThemedDecorationStyle style;
const ThemedDecoratedBox({
super.key,
required this.child,
this.style = ThemedDecorationStyle.primary,
});
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: _getDecoration(context),
child: child,
);
}
BoxDecoration _getDecoration(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
switch (style) {
case ThemedDecorationStyle.primary:
return BoxDecoration(
color: scheme.primaryContainer,
borderRadius: BorderRadius.circular(12),
);
case ThemedDecorationStyle.secondary:
return BoxDecoration(
color: scheme.secondaryContainer,
borderRadius: BorderRadius.circular(12),
);
case ThemedDecorationStyle.outlined:
return BoxDecoration(
border: Border.all(
color: scheme.outline,
width: 1,
),
borderRadius: BorderRadius.circular(12),
);
case ThemedDecorationStyle.elevated:
return BoxDecoration(
color: scheme.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
);
}
}
}
enum ThemedDecorationStyle {
primary,
secondary,
outlined,
elevated,
}
// Usage
class ThemedCardsExample extends StatelessWidget {
const ThemedCardsExample({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Column(
children: [
ThemedDecoratedBox(
style: ThemedDecorationStyle.primary,
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(l10n.primaryStyleCard),
),
),
const SizedBox(height: 16),
ThemedDecoratedBox(
style: ThemedDecorationStyle.elevated,
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(l10n.elevatedStyleCard),
),
),
],
);
}
}
Status Indicators
Status Decoration Badge
class LocalizedStatusDecoration extends StatelessWidget {
final String label;
final StatusLevel level;
const LocalizedStatusDecoration({
super.key,
required this.label,
required this.level,
});
@override
Widget build(BuildContext context) {
final colors = _getStatusColors(context);
return DecoratedBox(
decoration: BoxDecoration(
color: colors.background,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: colors.border, width: 1),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
DecoratedBox(
decoration: BoxDecoration(
color: colors.dot,
shape: BoxShape.circle,
),
child: const SizedBox(width: 8, height: 8),
),
const SizedBox(width: 8),
Text(
label,
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: colors.text,
fontWeight: FontWeight.w500,
),
),
],
),
),
);
}
StatusColorSet _getStatusColors(BuildContext context) {
switch (level) {
case StatusLevel.success:
return StatusColorSet(
background: Colors.green.shade50,
border: Colors.green.shade200,
dot: Colors.green,
text: Colors.green.shade700,
);
case StatusLevel.warning:
return StatusColorSet(
background: Colors.orange.shade50,
border: Colors.orange.shade200,
dot: Colors.orange,
text: Colors.orange.shade700,
);
case StatusLevel.error:
return StatusColorSet(
background: Colors.red.shade50,
border: Colors.red.shade200,
dot: Colors.red,
text: Colors.red.shade700,
);
case StatusLevel.neutral:
return StatusColorSet(
background: Colors.grey.shade100,
border: Colors.grey.shade300,
dot: Colors.grey,
text: Colors.grey.shade700,
);
}
}
}
enum StatusLevel { success, warning, error, neutral }
class StatusColorSet {
final Color background;
final Color border;
final Color dot;
final Color text;
StatusColorSet({
required this.background,
required this.border,
required this.dot,
required this.text,
});
}
// Usage
class StatusBadgesExample extends StatelessWidget {
const StatusBadgesExample({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Wrap(
spacing: 12,
runSpacing: 12,
children: [
LocalizedStatusDecoration(
label: l10n.statusOnline,
level: StatusLevel.success,
),
LocalizedStatusDecoration(
label: l10n.statusAway,
level: StatusLevel.warning,
),
LocalizedStatusDecoration(
label: l10n.statusOffline,
level: StatusLevel.neutral,
),
],
);
}
}
Image Decorations
Decorated Image Container
class LocalizedDecoratedImage extends StatelessWidget {
final String imageUrl;
final String caption;
final double borderRadius;
const LocalizedDecoratedImage({
super.key,
required this.imageUrl,
required this.caption,
this.borderRadius = 16,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 12,
offset: const Offset(0, 6),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(borderRadius),
child: Image.network(
imageUrl,
fit: BoxFit.cover,
),
),
),
const SizedBox(height: 12),
Text(
caption,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.outline,
),
),
],
);
}
}
Avatar with Decoration
class LocalizedDecoratedAvatar extends StatelessWidget {
final String? imageUrl;
final String name;
final double size;
final bool showOnlineIndicator;
const LocalizedDecoratedAvatar({
super.key,
this.imageUrl,
required this.name,
this.size = 48,
this.showOnlineIndicator = false,
});
@override
Widget build(BuildContext context) {
return Stack(
children: [
DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Theme.of(context).colorScheme.primary,
width: 2,
),
boxShadow: [
BoxShadow(
color: Theme.of(context).colorScheme.primary.withOpacity(0.3),
blurRadius: 8,
spreadRadius: 1,
),
],
),
child: Padding(
padding: const EdgeInsets.all(2),
child: CircleAvatar(
radius: size / 2,
backgroundImage: imageUrl != null
? NetworkImage(imageUrl!)
: null,
child: imageUrl == null
? Text(
name.isNotEmpty ? name[0].toUpperCase() : '?',
style: TextStyle(fontSize: size * 0.4),
)
: null,
),
),
),
if (showOnlineIndicator)
Positioned(
right: 0,
bottom: 0,
child: DecoratedBox(
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
border: Border.all(
color: Theme.of(context).colorScheme.surface,
width: 2,
),
),
child: const SizedBox(width: 14, height: 14),
),
),
],
);
}
}
Interactive Decorations
Hover and Press Decoration
class LocalizedInteractiveDecoratedBox extends StatefulWidget {
final Widget child;
final VoidCallback? onTap;
const LocalizedInteractiveDecoratedBox({
super.key,
required this.child,
this.onTap,
});
@override
State<LocalizedInteractiveDecoratedBox> createState() =>
_LocalizedInteractiveDecoratedBoxState();
}
class _LocalizedInteractiveDecoratedBoxState
extends State<LocalizedInteractiveDecoratedBox> {
bool _isHovered = false;
bool _isPressed = false;
@override
Widget build(BuildContext context) {
final scale = _isPressed ? 0.98 : (_isHovered ? 1.02 : 1.0);
final elevation = _isHovered ? 12.0 : 4.0;
return MouseRegion(
onEnter: (_) => setState(() => _isHovered = true),
onExit: (_) => setState(() => _isHovered = false),
child: GestureDetector(
onTapDown: (_) => setState(() => _isPressed = true),
onTapUp: (_) => setState(() => _isPressed = false),
onTapCancel: () => setState(() => _isPressed = false),
onTap: widget.onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 150),
transform: Matrix4.identity()..scale(scale),
transformAlignment: Alignment.center,
child: DecoratedBox(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: elevation,
offset: Offset(0, elevation / 2),
),
],
),
child: widget.child,
),
),
),
);
}
}
ARB File Structure
English (app_en.arb)
{
"@@locale": "en",
"decoratedContent": "This content is styled with a DecoratedBox, keeping decoration separate from layout.",
"gradientCardText": "Beautiful gradient that respects text direction",
"quoteText": "The best way to predict the future is to create it.",
"primaryStyleCard": "Primary themed decoration",
"elevatedStyleCard": "Elevated with shadow decoration",
"statusOnline": "Online",
"statusAway": "Away",
"statusOffline": "Offline",
"featureHighlight": "New Feature",
"featureDescription": "Check out this amazing new capability that's now available.",
"imageCaption": "A beautiful landscape showcasing nature's finest.",
"cardTitle": "Decorated Card",
"cardSubtitle": "Pure decoration without layout constraints"
}
German (app_de.arb)
{
"@@locale": "de",
"decoratedContent": "Dieser Inhalt ist mit einer DecoratedBox gestaltet und hält die Dekoration von dem Layout getrennt.",
"gradientCardText": "Schöner Farbverlauf, der die Textrichtung respektiert",
"quoteText": "Der beste Weg, die Zukunft vorherzusagen, ist, sie zu gestalten.",
"primaryStyleCard": "Primär-thematisierte Dekoration",
"elevatedStyleCard": "Erhöht mit Schattendekoration",
"statusOnline": "Online",
"statusAway": "Abwesend",
"statusOffline": "Offline",
"featureHighlight": "Neue Funktion",
"featureDescription": "Entdecken Sie diese erstaunliche neue Fähigkeit, die jetzt verfügbar ist.",
"imageCaption": "Eine wunderschöne Landschaft, die das Beste der Natur zeigt.",
"cardTitle": "Dekorierte Karte",
"cardSubtitle": "Reine Dekoration ohne Layout-Einschränkungen"
}
Arabic (app_ar.arb)
{
"@@locale": "ar",
"decoratedContent": "تم تنسيق هذا المحتوى باستخدام DecoratedBox، مع الحفاظ على فصل الزخرفة عن التخطيط.",
"gradientCardText": "تدرج لوني جميل يحترم اتجاه النص",
"quoteText": "أفضل طريقة للتنبؤ بالمستقبل هي صنعه.",
"primaryStyleCard": "زخرفة بالنمط الأساسي",
"elevatedStyleCard": "مرتفع مع زخرفة الظل",
"statusOnline": "متصل",
"statusAway": "بعيد",
"statusOffline": "غير متصل",
"featureHighlight": "ميزة جديدة",
"featureDescription": "اكتشف هذه الإمكانية الجديدة المذهلة المتاحة الآن.",
"imageCaption": "منظر طبيعي جميل يعرض أفضل ما في الطبيعة.",
"cardTitle": "بطاقة مزخرفة",
"cardSubtitle": "زخرفة نقية بدون قيود التخطيط"
}
Best Practices Summary
Do's
- Use DecoratedBox for pure decoration without layout needs
- Use AlignmentDirectional for gradients to support RTL
- Combine with Padding widget for spacing needs
- Apply directional borders using BorderDirectional
- Test decorations with both LTR and RTL content
Don'ts
- Don't use Container when only decoration is needed
- Don't forget gradient direction affects RTL layouts
- Don't nest DecoratedBox when one suffices
- Don't apply decoration that clips text in longer languages
Conclusion
DecoratedBox provides focused, efficient decoration for multilingual Flutter applications. By separating decoration from layout concerns, it offers cleaner code and better performance for styling needs. Use directional alignments and borders to ensure your decorations work correctly in all text directions, creating polished interfaces for global audiences.