arch-atlas

Adding animation

Presentation (Building the UI)

21:33

Summary

In this lesson, we add a sliding text animation to the splash screen of our Flutter application. We learn how to initialize an AnimationController and a Tween<Offset> to manage specific positional movement along the Y-axis. Crucially, the lesson demonstrates how to optimize widget rebuilds using AnimatedBuilder combined with SlideTransition, and emphasizes clean UI architecture by extracting the animation logic into its own dedicated stateless widget to keep the main view body readable.

Key Ideas

  • Using SingleTickerProviderStateMixin to provide a vsync ticker, which synchronizes the AnimationController with the device's screen refresh rate.
  • Defining a Tween<Offset> to mathematically calculate the intermediate values between a starting coordinate and an ending coordinate for the animation.
  • Leveraging AnimatedBuilder alongside SlideTransition to efficiently rebuild only the specific parts of the UI that depend on the animation, avoiding full-page rebuilds.
  • Extracting animation-specific UI components into their own files/widgets (e.g., SlidingText) to maintain a clean presentation layer and adhere to single responsibility principles.
  • Preventing memory leaks by rigorously overriding the dispose() method to dispose of the AnimationController when the widget is removed from the tree.

Code Snippets

Animation Initialization & Disposaldart
late AnimationController animationController;
late Animation<Offset> slidingAnimation;

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

void initSlidingAnimation() {
  animationController = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 1),
  );

  slidingAnimation = Tween<Offset>(
    begin: const Offset(0, 2),
    end: Offset.zero,
  ).animate(animationController);

  animationController.forward();
}

@override
void dispose() {
  animationController.dispose();
  super.dispose();
}
Extracted SlidingText Widgetdart
class SlidingText extends StatelessWidget {
  const SlidingText({
    Key? key,
    required this.slidingAnimation,
  }) : super(key: key);

  final Animation<Offset> slidingAnimation;

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: slidingAnimation,
      builder: (context, _) {
        return SlideTransition(
          position: slidingAnimation,
          child: const Text(
            'Read Free Books',
            textAlign: TextAlign.center,
          ),
        );
      },
    );
  }
}

Architecture Insight

Architecture insight

AnimationController is a purely presentation concern — it has no business meaning to the rest of the app. CA places this firmly in the View; it never touches a use case or repository.

The AnimatedBuilder optimization (rebuilding only the animated subtree) is also a presentation-layer detail. The dispose() discipline shown here generalizes: any presentation-owned resource — controllers, streams, subscriptions — gets disposed in the View. Domain and Data resources have their own lifecycle owners (typically GetIt singletons that live for the app's lifetime).

My Notes

  • Performance over `setState`: The instructor explicitly demonstrates that adding a listener to the animation and calling setState() forces the entire SplashViewBody to rebuild every frame. The optimized approach is to wrap the specific moving widget in an AnimatedBuilder to isolate those granular rebuilds.
  • Preventing Memory Leaks: A common gotcha is forgetting the dispose() method. The instructor emphasizes that an AnimationController allocates resources that must be freed manually when the view is destroyed.
  • Pragmatic Refactoring: Extracting the SlidingText into its own file (sliding_text.dart) is a practical step toward keeping the presentation layer modular. While we aren't deep into Domain/Data layers of Clean Architecture yet, keeping widgets small and focused builds the right habits early on.