← Back to Blog

Flutter SlideTransition Localization: Smooth Sliding Animations for Multilingual Apps

flutterslidetransitionanimationtransitionslocalizationrtl

Flutter SlideTransition Localization: Smooth Sliding Animations for Multilingual Apps

SlideTransition provides explicit control over sliding animations using an Animation controller. Proper localization ensures that sliding elements, navigation transitions, and directional movements work seamlessly across languages, especially handling RTL layouts correctly. This guide covers comprehensive strategies for localizing SlideTransition widgets in Flutter.

Understanding SlideTransition Localization

SlideTransition widgets require localization for:

  • Directional slides: Elements sliding left/right that flip in RTL layouts
  • Navigation transitions: Page slides respecting text direction
  • Panel animations: Side panels and drawers with proper direction
  • List item animations: Staggered entries from the correct side
  • Notification slides: Toast messages appearing from locale-appropriate edges
  • Accessibility feedback: Screen reader announcements for sliding content

Basic SlideTransition with RTL Support

Start with a simple notification panel with RTL-aware sliding:

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

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

  @override
  State<LocalizedNotificationPanel> createState() => _LocalizedNotificationPanelState();
}

class _LocalizedNotificationPanelState extends State<LocalizedNotificationPanel>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<Offset> _slideAnimation;
  bool _isVisible = false;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _setupAnimation();
  }

  void _setupAnimation() {
    final isRtl = Directionality.of(context) == TextDirection.rtl;

    // Slide from right in LTR, from left in RTL
    _slideAnimation = Tween<Offset>(
      begin: Offset(isRtl ? -1.0 : 1.0, 0.0),
      end: Offset.zero,
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeOutCubic,
    ));
  }

  void _togglePanel() {
    setState(() {
      _isVisible = !_isVisible;
      if (_isVisible) {
        _controller.forward();
      } else {
        _controller.reverse();
      }
    });
  }

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

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

    return Scaffold(
      appBar: AppBar(
        title: Text(l10n.notificationPanelTitle),
        actions: [
          Semantics(
            button: true,
            label: _isVisible
                ? l10n.hideNotifications
                : l10n.showNotifications,
            child: IconButton(
              icon: Icon(_isVisible ? Icons.close : Icons.notifications),
              onPressed: _togglePanel,
            ),
          ),
        ],
      ),
      body: Stack(
        children: [
          Center(
            child: Text(l10n.mainContentPlaceholder),
          ),
          Positioned(
            top: 0,
            right: Directionality.of(context) == TextDirection.rtl ? null : 0,
            left: Directionality.of(context) == TextDirection.rtl ? 0 : null,
            bottom: 0,
            width: 300,
            child: SlideTransition(
              position: _slideAnimation,
              child: Material(
                elevation: 8,
                child: Container(
                  color: Theme.of(context).colorScheme.surface,
                  child: Column(
                    children: [
                      Padding(
                        padding: const EdgeInsets.all(16),
                        child: Row(
                          children: [
                            Expanded(
                              child: Text(
                                l10n.notificationsHeader,
                                style: Theme.of(context).textTheme.titleLarge,
                              ),
                            ),
                            TextButton(
                              onPressed: () {},
                              child: Text(l10n.markAllRead),
                            ),
                          ],
                        ),
                      ),
                      const Divider(height: 1),
                      Expanded(
                        child: ListView(
                          padding: const EdgeInsets.all(8),
                          children: [
                            _NotificationItem(
                              title: l10n.newMessageNotification,
                              subtitle: l10n.notificationTime('2'),
                              icon: Icons.message,
                            ),
                            _NotificationItem(
                              title: l10n.updateAvailableNotification,
                              subtitle: l10n.notificationTime('5'),
                              icon: Icons.system_update,
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _NotificationItem extends StatelessWidget {
  final String title;
  final String subtitle;
  final IconData icon;

  const _NotificationItem({
    required this.title,
    required this.subtitle,
    required this.icon,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: ListTile(
        leading: Icon(icon, color: Theme.of(context).colorScheme.primary),
        title: Text(title),
        subtitle: Text(subtitle),
      ),
    );
  }
}

ARB File Structure for SlideTransition

{
  "notificationPanelTitle": "Notifications",
  "@notificationPanelTitle": {
    "description": "Title for notification panel screen"
  },
  "showNotifications": "Show notifications",
  "@showNotifications": {
    "description": "Accessibility label for showing notification panel"
  },
  "hideNotifications": "Hide notifications",
  "@hideNotifications": {
    "description": "Accessibility label for hiding notification panel"
  },
  "mainContentPlaceholder": "Main content area",
  "@mainContentPlaceholder": {
    "description": "Placeholder text for main content"
  },
  "notificationsHeader": "Notifications",
  "@notificationsHeader": {
    "description": "Header text for notification panel"
  },
  "markAllRead": "Mark all as read",
  "@markAllRead": {
    "description": "Button to mark all notifications as read"
  },
  "newMessageNotification": "You have a new message",
  "@newMessageNotification": {
    "description": "New message notification title"
  },
  "updateAvailableNotification": "Update available",
  "@updateAvailableNotification": {
    "description": "Update notification title"
  },
  "notificationTime": "{minutes} min ago",
  "@notificationTime": {
    "description": "Time since notification",
    "placeholders": {
      "minutes": {"type": "String"}
    }
  }
}

RTL-Aware Page Transitions

Implement page transitions that respect text direction:

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

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

  @override
  State<LocalizedPageTransitions> createState() => _LocalizedPageTransitionsState();
}

class _LocalizedPageTransitionsState extends State<LocalizedPageTransitions> {
  int _currentPage = 0;

  void _navigateToPage(int page) {
    final isRtl = Directionality.of(context) == TextDirection.rtl;
    final isForward = page > _currentPage;

    // Adjust direction for RTL
    final slideFromRight = isRtl ? !isForward : isForward;

    Navigator.of(context).push(
      _createSlideRoute(
        page: _PageContent(pageIndex: page),
        slideFromRight: slideFromRight,
      ),
    );

    setState(() => _currentPage = page);
  }

  Route _createSlideRoute({
    required Widget page,
    required bool slideFromRight,
  }) {
    return PageRouteBuilder(
      pageBuilder: (context, animation, secondaryAnimation) => page,
      transitionsBuilder: (context, animation, secondaryAnimation, child) {
        final begin = Offset(slideFromRight ? 1.0 : -1.0, 0.0);
        const end = Offset.zero;

        final tween = Tween(begin: begin, end: end).chain(
          CurveTween(curve: Curves.easeInOutCubic),
        );

        return SlideTransition(
          position: animation.drive(tween),
          child: child,
        );
      },
      transitionDuration: const Duration(milliseconds: 400),
    );
  }

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

    return Scaffold(
      appBar: AppBar(title: Text(l10n.pageTransitionsTitle)),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              l10n.currentPageLabel(_currentPage + 1),
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const SizedBox(height: 32),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: List.generate(4, (index) {
                return Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 8),
                  child: Semantics(
                    button: true,
                    label: l10n.goToPageAccessibility(index + 1),
                    child: ElevatedButton(
                      onPressed: () => _navigateToPage(index),
                      style: ElevatedButton.styleFrom(
                        backgroundColor: _currentPage == index
                            ? Theme.of(context).colorScheme.primary
                            : null,
                        foregroundColor: _currentPage == index
                            ? Theme.of(context).colorScheme.onPrimary
                            : null,
                      ),
                      child: Text('${index + 1}'),
                    ),
                  ),
                );
              }),
            ),
          ],
        ),
      ),
    );
  }
}

class _PageContent extends StatelessWidget {
  final int pageIndex;

  const _PageContent({required this.pageIndex});

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

    return Scaffold(
      appBar: AppBar(
        title: Text(l10n.pageTitle(pageIndex + 1)),
      ),
      body: Center(
        child: Text(
          l10n.pageContentText(pageIndex + 1),
          style: Theme.of(context).textTheme.headlineLarge,
        ),
      ),
    );
  }
}

Staggered List Animation

Create a staggered list with items sliding in from the correct direction:

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

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

  @override
  State<LocalizedStaggeredList> createState() => _LocalizedStaggeredListState();
}

class _LocalizedStaggeredListState extends State<LocalizedStaggeredList>
    with TickerProviderStateMixin {
  final List<AnimationController> _controllers = [];
  final List<Animation<Offset>> _animations = [];
  bool _isLoaded = false;

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

  void _initializeAnimations() {
    for (int i = 0; i < 6; i++) {
      final controller = AnimationController(
        vsync: this,
        duration: const Duration(milliseconds: 400),
      );
      _controllers.add(controller);
    }
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _setupSlideAnimations();
    if (!_isLoaded) {
      _startStaggeredAnimation();
      _isLoaded = true;
    }
  }

  void _setupSlideAnimations() {
    _animations.clear();
    final isRtl = Directionality.of(context) == TextDirection.rtl;

    for (var controller in _controllers) {
      _animations.add(
        Tween<Offset>(
          begin: Offset(isRtl ? -1.0 : 1.0, 0.0),
          end: Offset.zero,
        ).animate(CurvedAnimation(
          parent: controller,
          curve: Curves.easeOutBack,
        )),
      );
    }
  }

  Future<void> _startStaggeredAnimation() async {
    for (int i = 0; i < _controllers.length; i++) {
      await Future.delayed(const Duration(milliseconds: 100));
      if (mounted) {
        _controllers[i].forward();
      }
    }
  }

  Future<void> _resetAndReplay() async {
    for (var controller in _controllers) {
      controller.reset();
    }
    await _startStaggeredAnimation();
  }

  @override
  void dispose() {
    for (var controller in _controllers) {
      controller.dispose();
    }
    super.dispose();
  }

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

    final items = [
      (l10n.inboxItem, Icons.inbox, l10n.inboxCount(12)),
      (l10n.sentItem, Icons.send, l10n.sentCount(45)),
      (l10n.draftsItem, Icons.drafts, l10n.draftsCount(3)),
      (l10n.archiveItem, Icons.archive, l10n.archiveCount(156)),
      (l10n.trashItem, Icons.delete, l10n.trashCount(8)),
      (l10n.spamItem, Icons.warning, l10n.spamCount(24)),
    ];

    return Scaffold(
      appBar: AppBar(
        title: Text(l10n.mailboxTitle),
        actions: [
          Semantics(
            button: true,
            label: l10n.replayAnimationAccessibility,
            child: IconButton(
              icon: const Icon(Icons.replay),
              onPressed: _resetAndReplay,
            ),
          ),
        ],
      ),
      body: ListView.builder(
        padding: const EdgeInsets.all(16),
        itemCount: items.length,
        itemBuilder: (context, index) {
          final item = items[index];

          return SlideTransition(
            position: _animations[index],
            child: Card(
              margin: const EdgeInsets.only(bottom: 12),
              child: Semantics(
                label: l10n.mailboxItemAccessibility(item.$1, item.$3),
                child: ListTile(
                  leading: Container(
                    padding: const EdgeInsets.all(8),
                    decoration: BoxDecoration(
                      color: Theme.of(context).colorScheme.primaryContainer,
                      borderRadius: BorderRadius.circular(8),
                    ),
                    child: Icon(
                      item.$2,
                      color: Theme.of(context).colorScheme.onPrimaryContainer,
                    ),
                  ),
                  title: Text(item.$1),
                  trailing: Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 12,
                      vertical: 4,
                    ),
                    decoration: BoxDecoration(
                      color: Theme.of(context).colorScheme.secondaryContainer,
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: Text(
                      item.$3,
                      style: TextStyle(
                        color: Theme.of(context).colorScheme.onSecondaryContainer,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
                  onTap: () {},
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

Sliding Bottom Sheet

Create a sliding bottom sheet with proper RTL support:

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

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

  @override
  State<LocalizedSlidingBottomSheet> createState() => _LocalizedSlidingBottomSheetState();
}

class _LocalizedSlidingBottomSheetState extends State<LocalizedSlidingBottomSheet>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<Offset> _slideAnimation;

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

    _slideAnimation = Tween<Offset>(
      begin: const Offset(0, 1),
      end: Offset.zero,
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeOutQuart,
    ));
  }

  void _showSheet() {
    _controller.forward();
  }

  void _hideSheet() {
    _controller.reverse();
  }

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

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

    return Scaffold(
      appBar: AppBar(title: Text(l10n.productDetailsTitle)),
      body: Stack(
        children: [
          ListView(
            padding: const EdgeInsets.all(16),
            children: [
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        l10n.sampleProductName,
                        style: Theme.of(context).textTheme.headlineMedium,
                      ),
                      const SizedBox(height: 8),
                      Text(
                        l10n.sampleProductDescription,
                        style: Theme.of(context).textTheme.bodyLarge,
                      ),
                      const SizedBox(height: 16),
                      Text(
                        l10n.productPrice('\$99.99'),
                        style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                          color: Theme.of(context).colorScheme.primary,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),
              ElevatedButton.icon(
                onPressed: _showSheet,
                icon: const Icon(Icons.shopping_cart),
                label: Text(l10n.addToCartButton),
              ),
            ],
          ),
          // Sliding bottom sheet
          Positioned(
            left: 0,
            right: 0,
            bottom: 0,
            child: SlideTransition(
              position: _slideAnimation,
              child: Material(
                elevation: 16,
                borderRadius: const BorderRadius.vertical(
                  top: Radius.circular(20),
                ),
                child: Container(
                  padding: const EdgeInsets.all(24),
                  decoration: BoxDecoration(
                    color: Theme.of(context).colorScheme.surface,
                    borderRadius: const BorderRadius.vertical(
                      top: Radius.circular(20),
                    ),
                  ),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Container(
                        width: 40,
                        height: 4,
                        decoration: BoxDecoration(
                          color: Theme.of(context).colorScheme.outline,
                          borderRadius: BorderRadius.circular(2),
                        ),
                      ),
                      const SizedBox(height: 24),
                      Row(
                        children: [
                          Expanded(
                            child: Text(
                              l10n.selectQuantityLabel,
                              style: Theme.of(context).textTheme.titleLarge,
                            ),
                          ),
                          IconButton(
                            onPressed: _hideSheet,
                            icon: const Icon(Icons.close),
                          ),
                        ],
                      ),
                      const SizedBox(height: 16),
                      Row(
                        children: [
                          _QuantityButton(
                            icon: Icons.remove,
                            label: l10n.decreaseQuantityAccessibility,
                            onPressed: () {},
                          ),
                          Expanded(
                            child: Center(
                              child: Text(
                                '1',
                                style: Theme.of(context).textTheme.headlineMedium,
                              ),
                            ),
                          ),
                          _QuantityButton(
                            icon: Icons.add,
                            label: l10n.increaseQuantityAccessibility,
                            onPressed: () {},
                          ),
                        ],
                      ),
                      const SizedBox(height: 24),
                      SizedBox(
                        width: double.infinity,
                        child: ElevatedButton(
                          onPressed: () {
                            _hideSheet();
                            ScaffoldMessenger.of(context).showSnackBar(
                              SnackBar(content: Text(l10n.addedToCartMessage)),
                            );
                          },
                          child: Padding(
                            padding: const EdgeInsets.all(16),
                            child: Text(l10n.confirmAddToCart),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _QuantityButton extends StatelessWidget {
  final IconData icon;
  final String label;
  final VoidCallback onPressed;

  const _QuantityButton({
    required this.icon,
    required this.label,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    return Semantics(
      button: true,
      label: label,
      child: IconButton.filled(
        onPressed: onPressed,
        icon: Icon(icon),
      ),
    );
  }
}

Sliding Menu with Overlay

Create a sliding side menu with backdrop overlay:

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

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

  @override
  State<LocalizedSlidingMenu> createState() => _LocalizedSlidingMenuState();
}

class _LocalizedSlidingMenuState extends State<LocalizedSlidingMenu>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<Offset> _slideAnimation;
  late Animation<double> _fadeAnimation;

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

    _fadeAnimation = Tween<double>(
      begin: 0.0,
      end: 0.5,
    ).animate(_controller);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _setupSlideAnimation();
  }

  void _setupSlideAnimation() {
    final isRtl = Directionality.of(context) == TextDirection.rtl;

    _slideAnimation = Tween<Offset>(
      begin: Offset(isRtl ? 1.0 : -1.0, 0.0),
      end: Offset.zero,
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeOutCubic,
    ));
  }

  void _openMenu() {
    _controller.forward();
  }

  void _closeMenu() {
    _controller.reverse();
  }

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

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

    final menuItems = [
      (l10n.menuHome, Icons.home),
      (l10n.menuProfile, Icons.person),
      (l10n.menuSettings, Icons.settings),
      (l10n.menuHelp, Icons.help),
      (l10n.menuAbout, Icons.info),
      (l10n.menuLogout, Icons.logout),
    ];

    return Scaffold(
      appBar: AppBar(
        leading: Semantics(
          button: true,
          label: l10n.openMenuAccessibility,
          child: IconButton(
            icon: const Icon(Icons.menu),
            onPressed: _openMenu,
          ),
        ),
        title: Text(l10n.appTitle),
      ),
      body: Stack(
        children: [
          Center(
            child: Text(l10n.mainContentArea),
          ),
          // Backdrop
          AnimatedBuilder(
            animation: _fadeAnimation,
            builder: (context, child) {
              if (_fadeAnimation.value == 0) {
                return const SizedBox.shrink();
              }
              return GestureDetector(
                onTap: _closeMenu,
                child: Container(
                  color: Colors.black.withOpacity(_fadeAnimation.value),
                ),
              );
            },
          ),
          // Sliding menu
          Positioned(
            top: 0,
            bottom: 0,
            left: isRtl ? null : 0,
            right: isRtl ? 0 : null,
            width: 280,
            child: SlideTransition(
              position: _slideAnimation,
              child: Material(
                elevation: 16,
                child: Column(
                  children: [
                    Container(
                      width: double.infinity,
                      padding: const EdgeInsets.all(24),
                      color: Theme.of(context).colorScheme.primaryContainer,
                      child: SafeArea(
                        bottom: false,
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            CircleAvatar(
                              radius: 32,
                              child: Text(
                                l10n.userInitials,
                                style: const TextStyle(fontSize: 24),
                              ),
                            ),
                            const SizedBox(height: 16),
                            Text(
                              l10n.userName,
                              style: Theme.of(context).textTheme.titleLarge,
                            ),
                            Text(
                              l10n.userEmail,
                              style: Theme.of(context).textTheme.bodyMedium,
                            ),
                          ],
                        ),
                      ),
                    ),
                    Expanded(
                      child: ListView.builder(
                        itemCount: menuItems.length,
                        itemBuilder: (context, index) {
                          final item = menuItems[index];
                          return Semantics(
                            button: true,
                            label: l10n.menuItemAccessibility(item.$1),
                            child: ListTile(
                              leading: Icon(item.$2),
                              title: Text(item.$1),
                              onTap: () {
                                _closeMenu();
                              },
                            ),
                          );
                        },
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Carousel with Sliding Items

Create a carousel with sliding transitions:

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

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

  @override
  State<LocalizedSlidingCarousel> createState() => _LocalizedSlidingCarouselState();
}

class _LocalizedSlidingCarouselState extends State<LocalizedSlidingCarousel>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  int _currentIndex = 0;
  bool _isAnimating = false;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 400),
    );
  }

  Future<void> _goToSlide(int index) async {
    if (_isAnimating || index == _currentIndex) return;

    setState(() {
      _isAnimating = true;
      _currentIndex = index;
    });

    _controller.reset();
    await _controller.forward();

    setState(() => _isAnimating = false);
  }

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

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

    final slides = [
      (l10n.carouselSlide1Title, l10n.carouselSlide1Description, Colors.blue),
      (l10n.carouselSlide2Title, l10n.carouselSlide2Description, Colors.green),
      (l10n.carouselSlide3Title, l10n.carouselSlide3Description, Colors.orange),
    ];

    return Scaffold(
      appBar: AppBar(title: Text(l10n.carouselTitle)),
      body: Column(
        children: [
          Expanded(
            child: AnimatedBuilder(
              animation: _controller,
              builder: (context, child) {
                final slide = slides[_currentIndex];
                final slideOffset = Tween<Offset>(
                  begin: Offset(isRtl ? -1.0 : 1.0, 0.0),
                  end: Offset.zero,
                ).animate(CurvedAnimation(
                  parent: _controller,
                  curve: Curves.easeOutCubic,
                ));

                return SlideTransition(
                  position: slideOffset,
                  child: Container(
                    margin: const EdgeInsets.all(24),
                    decoration: BoxDecoration(
                      color: slide.$3.withOpacity(0.2),
                      borderRadius: BorderRadius.circular(24),
                    ),
                    child: Center(
                      child: Padding(
                        padding: const EdgeInsets.all(32),
                        child: Column(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Icon(
                              Icons.star,
                              size: 64,
                              color: slide.$3,
                            ),
                            const SizedBox(height: 24),
                            Text(
                              slide.$1,
                              style: Theme.of(context).textTheme.headlineMedium,
                              textAlign: TextAlign.center,
                            ),
                            const SizedBox(height: 16),
                            Text(
                              slide.$2,
                              style: Theme.of(context).textTheme.bodyLarge,
                              textAlign: TextAlign.center,
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(24),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                IconButton(
                  onPressed: _currentIndex > 0
                      ? () => _goToSlide(_currentIndex - 1)
                      : null,
                  icon: Icon(isRtl ? Icons.arrow_forward : Icons.arrow_back),
                ),
                const SizedBox(width: 16),
                Row(
                  children: List.generate(slides.length, (index) {
                    return Semantics(
                      button: true,
                      label: l10n.carouselIndicatorAccessibility(
                        index + 1,
                        slides.length,
                      ),
                      selected: index == _currentIndex,
                      child: GestureDetector(
                        onTap: () => _goToSlide(index),
                        child: AnimatedContainer(
                          duration: const Duration(milliseconds: 200),
                          margin: const EdgeInsets.symmetric(horizontal: 4),
                          width: index == _currentIndex ? 24 : 8,
                          height: 8,
                          decoration: BoxDecoration(
                            color: index == _currentIndex
                                ? Theme.of(context).colorScheme.primary
                                : Theme.of(context).colorScheme.outline,
                            borderRadius: BorderRadius.circular(4),
                          ),
                        ),
                      ),
                    );
                  }),
                ),
                const SizedBox(width: 16),
                IconButton(
                  onPressed: _currentIndex < slides.length - 1
                      ? () => _goToSlide(_currentIndex + 1)
                      : null,
                  icon: Icon(isRtl ? Icons.arrow_back : Icons.arrow_forward),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Complete ARB File for SlideTransition

{
  "@@locale": "en",

  "notificationPanelTitle": "Notifications",
  "showNotifications": "Show notifications",
  "hideNotifications": "Hide notifications",
  "mainContentPlaceholder": "Main content area",
  "notificationsHeader": "Notifications",
  "markAllRead": "Mark all as read",
  "newMessageNotification": "You have a new message",
  "updateAvailableNotification": "Update available",
  "notificationTime": "{minutes} min ago",
  "@notificationTime": {
    "placeholders": {
      "minutes": {"type": "String"}
    }
  },

  "pageTransitionsTitle": "Page Transitions",
  "currentPageLabel": "Current: Page {page}",
  "@currentPageLabel": {
    "placeholders": {
      "page": {"type": "int"}
    }
  },
  "goToPageAccessibility": "Go to page {page}",
  "@goToPageAccessibility": {
    "placeholders": {
      "page": {"type": "int"}
    }
  },
  "pageTitle": "Page {number}",
  "@pageTitle": {
    "placeholders": {
      "number": {"type": "int"}
    }
  },
  "pageContentText": "Content for page {number}",
  "@pageContentText": {
    "placeholders": {
      "number": {"type": "int"}
    }
  },

  "mailboxTitle": "Mailbox",
  "replayAnimationAccessibility": "Replay animation",
  "inboxItem": "Inbox",
  "sentItem": "Sent",
  "draftsItem": "Drafts",
  "archiveItem": "Archive",
  "trashItem": "Trash",
  "spamItem": "Spam",
  "inboxCount": "{count}",
  "sentCount": "{count}",
  "draftsCount": "{count}",
  "archiveCount": "{count}",
  "trashCount": "{count}",
  "spamCount": "{count}",
  "@inboxCount": {"placeholders": {"count": {"type": "int"}}},
  "@sentCount": {"placeholders": {"count": {"type": "int"}}},
  "@draftsCount": {"placeholders": {"count": {"type": "int"}}},
  "@archiveCount": {"placeholders": {"count": {"type": "int"}}},
  "@trashCount": {"placeholders": {"count": {"type": "int"}}},
  "@spamCount": {"placeholders": {"count": {"type": "int"}}},
  "mailboxItemAccessibility": "{folder} folder, {count} items",
  "@mailboxItemAccessibility": {
    "placeholders": {
      "folder": {"type": "String"},
      "count": {"type": "String"}
    }
  },

  "productDetailsTitle": "Product Details",
  "sampleProductName": "Premium Wireless Headphones",
  "sampleProductDescription": "Experience crystal-clear audio with our premium wireless headphones featuring active noise cancellation and 30-hour battery life.",
  "productPrice": "{price}",
  "@productPrice": {
    "placeholders": {
      "price": {"type": "String"}
    }
  },
  "addToCartButton": "Add to Cart",
  "selectQuantityLabel": "Select Quantity",
  "decreaseQuantityAccessibility": "Decrease quantity",
  "increaseQuantityAccessibility": "Increase quantity",
  "addedToCartMessage": "Added to cart!",
  "confirmAddToCart": "Confirm Add to Cart",

  "openMenuAccessibility": "Open menu",
  "appTitle": "My App",
  "mainContentArea": "Main Content",
  "menuHome": "Home",
  "menuProfile": "Profile",
  "menuSettings": "Settings",
  "menuHelp": "Help",
  "menuAbout": "About",
  "menuLogout": "Logout",
  "userInitials": "JD",
  "userName": "John Doe",
  "userEmail": "john.doe@example.com",
  "menuItemAccessibility": "Navigate to {item}",
  "@menuItemAccessibility": {
    "placeholders": {
      "item": {"type": "String"}
    }
  },

  "carouselTitle": "Features",
  "carouselSlide1Title": "Easy to Use",
  "carouselSlide1Description": "Our intuitive interface makes getting started a breeze.",
  "carouselSlide2Title": "Powerful Features",
  "carouselSlide2Description": "Access advanced tools that help you work smarter.",
  "carouselSlide3Title": "Always Connected",
  "carouselSlide3Description": "Stay in sync across all your devices seamlessly.",
  "carouselIndicatorAccessibility": "Slide {current} of {total}",
  "@carouselIndicatorAccessibility": {
    "placeholders": {
      "current": {"type": "int"},
      "total": {"type": "int"}
    }
  }
}

Best Practices Summary

  1. Handle RTL direction: Flip slide direction for horizontal animations in RTL layouts
  2. Use appropriate offset values: Offset(1, 0) slides from right, Offset(-1, 0) from left
  3. Provide accessibility labels: Announce sliding panel states to screen readers
  4. Combine with opacity: Add fade effects for smoother transitions
  5. Choose meaningful slide directions: Match slide direction to semantic meaning
  6. Test with different locales: Verify directional animations work correctly in RTL
  7. Use staggered animations: Create engaging list entries with delayed starts
  8. Add backdrop overlays: Dim background content for modal sliding panels
  9. Handle disabled states: Disable navigation when at bounds
  10. Maintain consistency: Use the same slide patterns throughout your app

Conclusion

SlideTransition is a powerful widget for creating explicit sliding animations in multilingual Flutter apps. By properly handling RTL layouts, providing meaningful accessibility announcements, and choosing appropriate slide directions, you create intuitive experiences for users worldwide. The patterns shown here—notification panels, page transitions, staggered lists, and sliding menus—can be adapted for any application requiring smooth sliding transitions.

Remember to test your sliding animations with various locales to ensure that directional movements behave correctly regardless of the user's language preference.