← Back to Blog

Flutter Opacity Localization: Transparency Control for Multilingual Apps

flutteropacitytransparencystateslocalizationaccessibility

Flutter Opacity Localization: Transparency Control for Multilingual Apps

The Opacity widget controls the transparency of its child, enabling fade effects, disabled states, ghost elements, and progressive visibility. When combined with localization, Opacity creates accessible visual hierarchies, context-aware visibility, and localized fade animations that adapt to different languages and cultural expectations. This guide covers comprehensive strategies for localizing Opacity widgets in Flutter multilingual applications.

Understanding Opacity Localization

Opacity widgets require localization for:

  • Disabled states: Visual indicators for inactive elements
  • Loading states: Faded placeholders during data fetching
  • Secondary content: De-emphasized supporting information
  • Ghost elements: Placeholder or preview states
  • Progressive disclosure: Revealing content based on context
  • Accessibility announcements: Describing visibility states

Basic Opacity with Localized Content

Start with a simple disabled state example:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class LocalizedOpacityDemo extends StatefulWidget {
  const LocalizedOpacityDemo({super.key});

  @override
  State<LocalizedOpacityDemo> createState() => _LocalizedOpacityDemoState();
}

class _LocalizedOpacityDemoState extends State<LocalizedOpacityDemo> {
  bool _termsAccepted = false;

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    return Scaffold(
      appBar: AppBar(title: Text(l10n.registrationTitle)),
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(
              l10n.createAccountHeading,
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const SizedBox(height: 24),
            TextField(
              decoration: InputDecoration(
                labelText: l10n.emailLabel,
                hintText: l10n.emailHint,
              ),
            ),
            const SizedBox(height: 16),
            TextField(
              obscureText: true,
              decoration: InputDecoration(
                labelText: l10n.passwordLabel,
                hintText: l10n.passwordHint,
              ),
            ),
            const SizedBox(height: 24),
            CheckboxListTile(
              value: _termsAccepted,
              onChanged: (value) {
                setState(() => _termsAccepted = value ?? false);
              },
              title: Text(l10n.acceptTermsLabel),
              controlAffinity: ListTileControlAffinity.leading,
              contentPadding: EdgeInsets.zero,
            ),
            const Spacer(),
            Semantics(
              label: _termsAccepted
                  ? l10n.submitButtonActiveAccessibility
                  : l10n.submitButtonDisabledAccessibility,
              button: true,
              enabled: _termsAccepted,
              child: Opacity(
                opacity: _termsAccepted ? 1.0 : 0.5,
                child: IgnorePointer(
                  ignoring: !_termsAccepted,
                  child: ElevatedButton(
                    onPressed: () {},
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(vertical: 16),
                    ),
                    child: Text(l10n.createAccountButton),
                  ),
                ),
              ),
            ),
            const SizedBox(height: 16),
            if (!_termsAccepted)
              Text(
                l10n.acceptTermsReminder,
                style: Theme.of(context).textTheme.bodySmall?.copyWith(
                  color: Theme.of(context).colorScheme.error,
                ),
                textAlign: TextAlign.center,
              ),
          ],
        ),
      ),
    );
  }
}

ARB File Structure for Opacity

{
  "registrationTitle": "Register",
  "@registrationTitle": {
    "description": "Title for registration page"
  },
  "createAccountHeading": "Create Your Account",
  "emailLabel": "Email",
  "emailHint": "Enter your email address",
  "passwordLabel": "Password",
  "passwordHint": "Enter a secure password",
  "acceptTermsLabel": "I accept the terms and conditions",
  "createAccountButton": "Create Account",
  "acceptTermsReminder": "Please accept the terms to continue",
  "submitButtonActiveAccessibility": "Create account button, ready to submit",
  "submitButtonDisabledAccessibility": "Create account button, disabled until terms are accepted"
}

Disabled Item List

Create a list with disabled items:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class LocalizedDisabledList extends StatelessWidget {
  const LocalizedDisabledList({super.key});

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    final menuItems = [
      (l10n.menuProfile, Icons.person, true),
      (l10n.menuSettings, Icons.settings, true),
      (l10n.menuPremium, Icons.star, false),
      (l10n.menuAnalytics, Icons.analytics, false),
      (l10n.menuSupport, Icons.help, true),
      (l10n.menuLogout, Icons.logout, true),
    ];

    return Scaffold(
      appBar: AppBar(title: Text(l10n.menuTitle)),
      body: ListView.builder(
        padding: const EdgeInsets.all(16),
        itemCount: menuItems.length,
        itemBuilder: (context, index) {
          final (title, icon, isEnabled) = menuItems[index];
          return _MenuItemTile(
            title: title,
            icon: icon,
            isEnabled: isEnabled,
            disabledReason: isEnabled ? null : l10n.premiumFeatureRequired,
            onTap: isEnabled ? () {} : null,
          );
        },
      ),
    );
  }
}

class _MenuItemTile extends StatelessWidget {
  final String title;
  final IconData icon;
  final bool isEnabled;
  final String? disabledReason;
  final VoidCallback? onTap;

  const _MenuItemTile({
    required this.title,
    required this.icon,
    required this.isEnabled,
    this.disabledReason,
    this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    return Semantics(
      label: isEnabled
          ? title
          : l10n.disabledMenuItemAccessibility(title, disabledReason ?? ''),
      button: true,
      enabled: isEnabled,
      child: Opacity(
        opacity: isEnabled ? 1.0 : 0.4,
        child: Card(
          margin: const EdgeInsets.only(bottom: 8),
          child: ListTile(
            leading: Icon(icon),
            title: Text(title),
            subtitle: isEnabled
                ? null
                : Text(
                    disabledReason ?? '',
                    style: TextStyle(
                      color: Theme.of(context).colorScheme.error,
                      fontSize: 12,
                    ),
                  ),
            trailing: isEnabled
                ? const Icon(Icons.chevron_right)
                : const Icon(Icons.lock_outline, size: 20),
            onTap: onTap,
          ),
        ),
      ),
    );
  }
}

Loading State Placeholders

Create faded loading states:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class LocalizedLoadingPlaceholder extends StatefulWidget {
  final bool isLoading;
  final Widget child;
  final Widget? placeholder;

  const LocalizedLoadingPlaceholder({
    super.key,
    required this.isLoading,
    required this.child,
    this.placeholder,
  });

  @override
  State<LocalizedLoadingPlaceholder> createState() =>
      _LocalizedLoadingPlaceholderState();
}

class _LocalizedLoadingPlaceholderState
    extends State<LocalizedLoadingPlaceholder>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _pulseAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1000),
    )..repeat(reverse: true);

    _pulseAnimation = Tween<double>(begin: 0.3, end: 0.7).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    if (!widget.isLoading) {
      return widget.child;
    }

    return Semantics(
      label: l10n.loadingContentAccessibility,
      child: AnimatedBuilder(
        animation: _pulseAnimation,
        builder: (context, child) {
          return Opacity(
            opacity: _pulseAnimation.value,
            child: child,
          );
        },
        child: widget.placeholder ?? _buildDefaultPlaceholder(context),
      ),
    );
  }

  Widget _buildDefaultPlaceholder(BuildContext context) {
    return Container(
      height: 100,
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surfaceVariant,
        borderRadius: BorderRadius.circular(12),
      ),
    );
  }
}

class ContentLoadingDemo extends StatefulWidget {
  const ContentLoadingDemo({super.key});

  @override
  State<ContentLoadingDemo> createState() => _ContentLoadingDemoState();
}

class _ContentLoadingDemoState extends State<ContentLoadingDemo> {
  bool _isLoading = true;
  List<Map<String, String>> _items = [];

  @override
  void initState() {
    super.initState();
    _loadContent();
  }

  Future<void> _loadContent() async {
    setState(() => _isLoading = true);
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      _isLoading = false;
      _items = List.generate(5, (i) => {
        'title': 'Item ${i + 1}',
        'subtitle': 'Description for item ${i + 1}',
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    return Scaffold(
      appBar: AppBar(
        title: Text(l10n.contentListTitle),
        actions: [
          IconButton(
            onPressed: _loadContent,
            icon: const Icon(Icons.refresh),
            tooltip: l10n.refreshTooltip,
          ),
        ],
      ),
      body: ListView.builder(
        padding: const EdgeInsets.all(16),
        itemCount: _isLoading ? 5 : _items.length,
        itemBuilder: (context, index) {
          return LocalizedLoadingPlaceholder(
            isLoading: _isLoading,
            placeholder: _buildPlaceholderCard(context),
            child: _isLoading
                ? const SizedBox()
                : _buildContentCard(context, _items[index]),
          );
        },
      ),
    );
  }

  Widget _buildPlaceholderCard(BuildContext context) {
    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: [
            Container(
              width: 60,
              height: 60,
              decoration: BoxDecoration(
                color: Theme.of(context).colorScheme.surfaceVariant,
                borderRadius: BorderRadius.circular(8),
              ),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Container(
                    height: 16,
                    width: double.infinity,
                    decoration: BoxDecoration(
                      color: Theme.of(context).colorScheme.surfaceVariant,
                      borderRadius: BorderRadius.circular(4),
                    ),
                  ),
                  const SizedBox(height: 8),
                  Container(
                    height: 12,
                    width: 150,
                    decoration: BoxDecoration(
                      color: Theme.of(context).colorScheme.surfaceVariant,
                      borderRadius: BorderRadius.circular(4),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildContentCard(BuildContext context, Map<String, String> item) {
    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      child: ListTile(
        leading: const CircleAvatar(
          backgroundImage: NetworkImage('https://picsum.photos/100'),
        ),
        title: Text(item['title']!),
        subtitle: Text(item['subtitle']!),
        trailing: const Icon(Icons.chevron_right),
        onTap: () {},
      ),
    );
  }
}

Secondary Content Hierarchy

Create visual hierarchy with opacity:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class LocalizedContentHierarchy extends StatelessWidget {
  const LocalizedContentHierarchy({super.key});

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    return Scaffold(
      appBar: AppBar(title: Text(l10n.articleTitle)),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(24),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Primary content - full opacity
            Text(
              l10n.articleHeadline,
              style: Theme.of(context).textTheme.headlineLarge?.copyWith(
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 8),
            // Secondary content - reduced opacity
            Opacity(
              opacity: 0.7,
              child: Semantics(
                label: l10n.articleMetaAccessibility,
                child: Row(
                  children: [
                    Text(
                      l10n.articleAuthor,
                      style: Theme.of(context).textTheme.bodyMedium,
                    ),
                    const Text(' · '),
                    Text(
                      l10n.articleDate,
                      style: Theme.of(context).textTheme.bodyMedium,
                    ),
                    const Text(' · '),
                    Text(
                      l10n.articleReadTime,
                      style: Theme.of(context).textTheme.bodyMedium,
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 24),
            ClipRRect(
              borderRadius: BorderRadius.circular(12),
              child: Image.network(
                'https://picsum.photos/600/300',
                width: double.infinity,
                height: 200,
                fit: BoxFit.cover,
              ),
            ),
            const SizedBox(height: 8),
            // Image caption - tertiary importance
            Opacity(
              opacity: 0.5,
              child: Text(
                l10n.imageCaption,
                style: Theme.of(context).textTheme.bodySmall,
                textAlign: TextAlign.center,
              ),
            ),
            const SizedBox(height: 24),
            // Main body - primary
            Text(
              l10n.articleBody,
              style: Theme.of(context).textTheme.bodyLarge?.copyWith(
                height: 1.8,
              ),
            ),
            const SizedBox(height: 32),
            // Related section - secondary
            Opacity(
              opacity: 0.8,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    l10n.relatedArticlesLabel,
                    style: Theme.of(context).textTheme.titleMedium,
                  ),
                  const SizedBox(height: 12),
                  _buildRelatedItem(context, l10n.relatedArticle1),
                  _buildRelatedItem(context, l10n.relatedArticle2),
                  _buildRelatedItem(context, l10n.relatedArticle3),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildRelatedItem(BuildContext context, String title) {
    return ListTile(
      contentPadding: EdgeInsets.zero,
      leading: ClipRRect(
        borderRadius: BorderRadius.circular(8),
        child: Image.network(
          'https://picsum.photos/80/60',
          width: 80,
          height: 60,
          fit: BoxFit.cover,
        ),
      ),
      title: Text(
        title,
        maxLines: 2,
        overflow: TextOverflow.ellipsis,
      ),
      onTap: () {},
    );
  }
}

Ghost Elements and Previews

Create preview states with opacity:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class LocalizedGhostPreview extends StatefulWidget {
  const LocalizedGhostPreview({super.key});

  @override
  State<LocalizedGhostPreview> createState() => _LocalizedGhostPreviewState();
}

class _LocalizedGhostPreviewState extends State<LocalizedGhostPreview> {
  String _selectedTemplate = '';
  final List<String> _templates = ['minimal', 'modern', 'classic', 'bold'];

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    return Scaffold(
      appBar: AppBar(title: Text(l10n.templateSelectorTitle)),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              l10n.selectTemplateDescription,
              style: Theme.of(context).textTheme.bodyLarge,
            ),
          ),
          Expanded(
            child: GridView.builder(
              padding: const EdgeInsets.all(16),
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                childAspectRatio: 0.75,
                crossAxisSpacing: 16,
                mainAxisSpacing: 16,
              ),
              itemCount: _templates.length,
              itemBuilder: (context, index) {
                final template = _templates[index];
                final isSelected = _selectedTemplate == template;

                return GestureDetector(
                  onTap: () {
                    setState(() => _selectedTemplate = template);
                  },
                  child: Semantics(
                    label: l10n.templateAccessibility(
                      _getTemplateName(l10n, template),
                      isSelected,
                    ),
                    selected: isSelected,
                    child: AnimatedOpacity(
                      duration: const Duration(milliseconds: 200),
                      opacity: isSelected ? 1.0 : 0.5,
                      child: Container(
                        decoration: BoxDecoration(
                          border: Border.all(
                            color: isSelected
                                ? Theme.of(context).colorScheme.primary
                                : Theme.of(context).colorScheme.outline,
                            width: isSelected ? 3 : 1,
                          ),
                          borderRadius: BorderRadius.circular(12),
                        ),
                        child: Column(
                          children: [
                            Expanded(
                              child: ClipRRect(
                                borderRadius: const BorderRadius.vertical(
                                  top: Radius.circular(11),
                                ),
                                child: Image.network(
                                  'https://picsum.photos/200/300?random=$index',
                                  fit: BoxFit.cover,
                                  width: double.infinity,
                                ),
                              ),
                            ),
                            Padding(
                              padding: const EdgeInsets.all(12),
                              child: Text(
                                _getTemplateName(l10n, template),
                                style: Theme.of(context).textTheme.titleSmall,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
          if (_selectedTemplate.isNotEmpty)
            Container(
              padding: const EdgeInsets.all(16),
              child: ElevatedButton(
                onPressed: () {},
                style: ElevatedButton.styleFrom(
                  minimumSize: const Size(double.infinity, 48),
                ),
                child: Text(l10n.applyTemplateButton),
              ),
            ),
        ],
      ),
    );
  }

  String _getTemplateName(AppLocalizations l10n, String template) {
    switch (template) {
      case 'minimal':
        return l10n.templateMinimal;
      case 'modern':
        return l10n.templateModern;
      case 'classic':
        return l10n.templateClassic;
      case 'bold':
        return l10n.templateBold;
      default:
        return template;
    }
  }
}

Progressive Reveal

Create step-based visibility:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class LocalizedProgressiveReveal extends StatefulWidget {
  const LocalizedProgressiveReveal({super.key});

  @override
  State<LocalizedProgressiveReveal> createState() =>
      _LocalizedProgressiveRevealState();
}

class _LocalizedProgressiveRevealState
    extends State<LocalizedProgressiveReveal> {
  int _currentStep = 0;
  final int _totalSteps = 4;

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    final steps = [
      (l10n.step1Title, l10n.step1Description, Icons.person),
      (l10n.step2Title, l10n.step2Description, Icons.email),
      (l10n.step3Title, l10n.step3Description, Icons.lock),
      (l10n.step4Title, l10n.step4Description, Icons.check_circle),
    ];

    return Scaffold(
      appBar: AppBar(
        title: Text(l10n.setupWizardTitle),
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(4),
          child: LinearProgressIndicator(
            value: (_currentStep + 1) / _totalSteps,
          ),
        ),
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              padding: const EdgeInsets.all(16),
              itemCount: steps.length,
              itemBuilder: (context, index) {
                final (title, description, icon) = steps[index];
                final isCompleted = index < _currentStep;
                final isCurrent = index == _currentStep;
                final isLocked = index > _currentStep;

                double opacity;
                if (isCompleted || isCurrent) {
                  opacity = 1.0;
                } else {
                  opacity = 0.3;
                }

                return Semantics(
                  label: _getStepAccessibility(
                    l10n,
                    title,
                    isCompleted,
                    isCurrent,
                    isLocked,
                  ),
                  child: AnimatedOpacity(
                    duration: const Duration(milliseconds: 300),
                    opacity: opacity,
                    child: Card(
                      margin: const EdgeInsets.only(bottom: 12),
                      child: ListTile(
                        leading: CircleAvatar(
                          backgroundColor: isCompleted
                              ? Colors.green
                              : isCurrent
                                  ? Theme.of(context).colorScheme.primary
                                  : Theme.of(context).colorScheme.surfaceVariant,
                          child: isCompleted
                              ? const Icon(Icons.check, color: Colors.white)
                              : Icon(
                                  icon,
                                  color: isCurrent ? Colors.white : null,
                                ),
                        ),
                        title: Text(title),
                        subtitle: Text(description),
                        trailing: isLocked
                            ? const Icon(Icons.lock_outline, size: 20)
                            : isCurrent
                                ? const Icon(Icons.arrow_forward)
                                : null,
                        onTap: isCurrent ? _handleStepTap : null,
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              children: [
                if (_currentStep > 0)
                  Expanded(
                    child: OutlinedButton(
                      onPressed: () {
                        setState(() => _currentStep--);
                      },
                      child: Text(l10n.previousButton),
                    ),
                  ),
                if (_currentStep > 0) const SizedBox(width: 16),
                Expanded(
                  child: ElevatedButton(
                    onPressed: _currentStep < _totalSteps - 1
                        ? () {
                            setState(() => _currentStep++);
                          }
                        : () {},
                    child: Text(
                      _currentStep < _totalSteps - 1
                          ? l10n.nextButton
                          : l10n.finishButton,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  void _handleStepTap() {
    // Handle current step interaction
  }

  String _getStepAccessibility(
    AppLocalizations l10n,
    String title,
    bool isCompleted,
    bool isCurrent,
    bool isLocked,
  ) {
    if (isCompleted) {
      return l10n.stepCompletedAccessibility(title);
    } else if (isCurrent) {
      return l10n.stepCurrentAccessibility(title);
    } else {
      return l10n.stepLockedAccessibility(title);
    }
  }
}

Animated Opacity Transitions

Create smooth fade transitions:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class LocalizedFadeTransition extends StatefulWidget {
  final Widget child;
  final bool isVisible;
  final Duration duration;

  const LocalizedFadeTransition({
    super.key,
    required this.child,
    required this.isVisible,
    this.duration = const Duration(milliseconds: 300),
  });

  @override
  State<LocalizedFadeTransition> createState() => _LocalizedFadeTransitionState();
}

class _LocalizedFadeTransitionState extends State<LocalizedFadeTransition>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: widget.duration,
      value: widget.isVisible ? 1.0 : 0.0,
    );
    _animation = CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInOut,
    );
  }

  @override
  void didUpdateWidget(LocalizedFadeTransition oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.isVisible != oldWidget.isVisible) {
      if (widget.isVisible) {
        _controller.forward();
      } else {
        _controller.reverse();
      }
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _animation,
      child: widget.child,
    );
  }
}

class FadeTransitionDemo extends StatefulWidget {
  const FadeTransitionDemo({super.key});

  @override
  State<FadeTransitionDemo> createState() => _FadeTransitionDemoState();
}

class _FadeTransitionDemoState extends State<FadeTransitionDemo> {
  bool _showDetails = false;
  bool _showBanner = true;

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;

    return Scaffold(
      appBar: AppBar(title: Text(l10n.fadeTransitionTitle)),
      body: Column(
        children: [
          LocalizedFadeTransition(
            isVisible: _showBanner,
            child: Container(
              width: double.infinity,
              padding: const EdgeInsets.all(16),
              color: Theme.of(context).colorScheme.primaryContainer,
              child: Row(
                children: [
                  Expanded(
                    child: Text(
                      l10n.bannerMessage,
                      style: TextStyle(
                        color: Theme.of(context).colorScheme.onPrimaryContainer,
                      ),
                    ),
                  ),
                  IconButton(
                    onPressed: () {
                      setState(() => _showBanner = false);
                    },
                    icon: const Icon(Icons.close),
                    tooltip: l10n.dismissBannerTooltip,
                  ),
                ],
              ),
            ),
          ),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Card(
                    child: Padding(
                      padding: const EdgeInsets.all(16),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            l10n.productTitle,
                            style: Theme.of(context).textTheme.headlineSmall,
                          ),
                          const SizedBox(height: 8),
                          Text(l10n.productShortDescription),
                          const SizedBox(height: 12),
                          TextButton(
                            onPressed: () {
                              setState(() => _showDetails = !_showDetails);
                            },
                            child: Row(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Text(
                                  _showDetails
                                      ? l10n.hideDetailsButton
                                      : l10n.showDetailsButton,
                                ),
                                const SizedBox(width: 4),
                                Icon(
                                  _showDetails
                                      ? Icons.expand_less
                                      : Icons.expand_more,
                                  size: 20,
                                ),
                              ],
                            ),
                          ),
                          LocalizedFadeTransition(
                            isVisible: _showDetails,
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                const Divider(),
                                const SizedBox(height: 8),
                                Text(
                                  l10n.productDetailedDescription,
                                  style: Theme.of(context).textTheme.bodyMedium,
                                ),
                                const SizedBox(height: 16),
                                Row(
                                  children: [
                                    Expanded(
                                      child: ElevatedButton(
                                        onPressed: () {},
                                        child: Text(l10n.addToCartButton),
                                      ),
                                    ),
                                    const SizedBox(width: 8),
                                    IconButton(
                                      onPressed: () {},
                                      icon: const Icon(Icons.favorite_border),
                                      tooltip: l10n.addToFavoritesTooltip,
                                    ),
                                  ],
                                ),
                              ],
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
          if (!_showBanner)
            TextButton(
              onPressed: () {
                setState(() => _showBanner = true);
              },
              child: Text(l10n.showBannerButton),
            ),
        ],
      ),
    );
  }
}

Complete ARB File for Opacity

{
  "@@locale": "en",

  "registrationTitle": "Register",
  "createAccountHeading": "Create Your Account",
  "emailLabel": "Email",
  "emailHint": "Enter your email address",
  "passwordLabel": "Password",
  "passwordHint": "Enter a secure password",
  "acceptTermsLabel": "I accept the terms and conditions",
  "createAccountButton": "Create Account",
  "acceptTermsReminder": "Please accept the terms to continue",
  "submitButtonActiveAccessibility": "Create account button, ready to submit",
  "submitButtonDisabledAccessibility": "Create account button, disabled until terms are accepted",

  "menuTitle": "Menu",
  "menuProfile": "Profile",
  "menuSettings": "Settings",
  "menuPremium": "Premium Features",
  "menuAnalytics": "Analytics",
  "menuSupport": "Support",
  "menuLogout": "Log Out",
  "premiumFeatureRequired": "Premium subscription required",
  "disabledMenuItemAccessibility": "{item}, disabled: {reason}",
  "@disabledMenuItemAccessibility": {
    "placeholders": {
      "item": {"type": "String"},
      "reason": {"type": "String"}
    }
  },

  "contentListTitle": "Content List",
  "loadingContentAccessibility": "Loading content, please wait",
  "refreshTooltip": "Refresh",

  "articleTitle": "Article",
  "articleHeadline": "The Future of Mobile Development",
  "articleAuthor": "John Smith",
  "articleDate": "January 26, 2026",
  "articleReadTime": "5 min read",
  "articleMetaAccessibility": "Article metadata: author, date, and reading time",
  "imageCaption": "Photo credit: Example Photography",
  "articleBody": "Mobile development continues to evolve at a rapid pace. New frameworks and tools emerge regularly, offering developers more powerful ways to build cross-platform applications. Flutter has become a leading choice for many teams, providing a single codebase that works across iOS, Android, web, and desktop platforms.",
  "relatedArticlesLabel": "Related Articles",
  "relatedArticle1": "Getting Started with Flutter",
  "relatedArticle2": "Best Practices for Mobile UI",
  "relatedArticle3": "Cross-Platform Development Guide",

  "templateSelectorTitle": "Choose Template",
  "selectTemplateDescription": "Select a template for your project",
  "templateMinimal": "Minimal",
  "templateModern": "Modern",
  "templateClassic": "Classic",
  "templateBold": "Bold",
  "templateAccessibility": "{name} template, {selected, select, true{selected} other{not selected}}",
  "@templateAccessibility": {
    "placeholders": {
      "name": {"type": "String"},
      "selected": {"type": "bool"}
    }
  },
  "applyTemplateButton": "Apply Template",

  "setupWizardTitle": "Setup Wizard",
  "step1Title": "Personal Info",
  "step1Description": "Enter your name and details",
  "step2Title": "Contact Info",
  "step2Description": "Provide your email address",
  "step3Title": "Security",
  "step3Description": "Set up your password",
  "step4Title": "Complete",
  "step4Description": "Review and finish setup",
  "previousButton": "Previous",
  "nextButton": "Next",
  "finishButton": "Finish",
  "stepCompletedAccessibility": "Step {title} completed",
  "stepCurrentAccessibility": "Current step: {title}",
  "stepLockedAccessibility": "Step {title} locked",
  "@stepCompletedAccessibility": {
    "placeholders": {"title": {"type": "String"}}
  },
  "@stepCurrentAccessibility": {
    "placeholders": {"title": {"type": "String"}}
  },
  "@stepLockedAccessibility": {
    "placeholders": {"title": {"type": "String"}}
  },

  "fadeTransitionTitle": "Fade Effects",
  "bannerMessage": "Welcome! Check out our new features.",
  "dismissBannerTooltip": "Dismiss",
  "productTitle": "Premium Widget Pack",
  "productShortDescription": "A collection of beautiful Flutter widgets for your app.",
  "showDetailsButton": "Show Details",
  "hideDetailsButton": "Hide Details",
  "productDetailedDescription": "This premium pack includes over 50 professionally designed widgets, ready to use in your Flutter projects. Each widget is fully customizable and supports dark mode, RTL layouts, and accessibility features.",
  "addToCartButton": "Add to Cart",
  "addToFavoritesTooltip": "Add to favorites",
  "showBannerButton": "Show Banner"
}

Best Practices Summary

  1. Provide accessibility labels: Always describe what reduced opacity means to screen readers
  2. Use appropriate opacity levels: 0.5 for disabled, 0.7 for secondary, 1.0 for primary
  3. Animate transitions: Smooth opacity changes are less jarring
  4. Combine with IgnorePointer: Prevent interaction with faded disabled elements
  5. Maintain minimum contrast: Ensure text remains readable at reduced opacity
  6. Consider context: Opacity meanings may vary across cultures
  7. Use semantic grouping: Group related elements by opacity level
  8. Test with accessibility tools: Verify visibility for users with low vision
  9. Provide alternative indicators: Don't rely solely on opacity for state
  10. Document visual hierarchy: Establish consistent opacity rules across the app

Conclusion

Opacity enables subtle visual hierarchies through disabled states, loading placeholders, secondary content, and progressive reveals that enhance multilingual applications. By providing proper accessibility labels and maintaining sufficient contrast, you can build applications that communicate importance effectively while remaining accessible to all users. The key is using opacity thoughtfully to guide attention without making content unreadable.

Remember to test your opacity effects with various accessibility settings and ensure that important information remains visible and understandable regardless of visual treatment.