arch-atlas

Refactor code

Presentation (Building the UI)

12:46

Summary

This lesson focuses on refactoring the Splash screen's code to improve readability and adhere to the Single Responsibility Principle (SRP). The instructor extracts the animation initialization and the navigation logic into their own dedicated methods, cleaning up the initState function. Additionally, the foundational folder structure for the new "Home" feature is created, demonstrating how to organize files into presentation and data layers while utilizing the Get package to implement a faded, timed route transition between the two screens.

Key Ideas

  • Extracting initialization and routing logic from initState into dedicated methods significantly improves code readability.
  • The Single Responsibility Principle (SRP) dictates that functions and methods should only perform one primary task.
  • Feature-based folder structure isolates UI and data logic, keeping the project modular.
  • Future.delayed can be used to wait for a specific duration before triggering navigation away from a Splash screen.
  • The get package provides a simplified API (Get.to()) for screen routing and includes built-in animations like Transition.fade.
  • Hardcoded durations and magic numbers should be extracted into a centralized constants file (e.g., kTransitionDuration) for app-wide consistency.

Code Snippets

Final state of SplashViewBodyState with extracted methods for SRPdart
class _SplashViewBodyState extends State<SplashViewBody>
    with SingleTickerProviderStateMixin {
  late AnimationController animationController;
  late Animation<Offset> slidingAnimation;

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

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

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Image.asset(AssetsData.logo),
        const SizedBox(
          height: 4,
        ),
        SlidingText(slidingAnimation: slidingAnimation),
      ],
    );
  }

  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();
  }

  void navigateToHome() {
    Future.delayed(
      const Duration(seconds: 2),
      () {
        Get.to(
          () => const HomeView(),
          transition: Transition.fade,
          duration: kTransitionDuration,
        );
      },
    );
  }
}
Extracted transition duration constantdart
import 'package:flutter/material.dart';

const kPrimaryColor = Color(0xff100B20);
const kTransitionDuration = Duration(milliseconds: 250);
Initial HomeView scaffolddart
import 'package:flutter/material.dart';

class HomeView extends StatelessWidget {
  const HomeView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold();
  }
}

Architecture Insight

Architecture insight

Widget extraction is the precursor to layer extraction. The same instinct — *"this thing has its own job, give it a name"* — drives the eventual split into use cases, repositories, and entities. Practicing it on widgets builds the muscle for the harder calls later.

A useful heuristic: if a widget needs more than three positional parameters, you're probably missing an abstraction. The same is true for use cases later.

My Notes

  • The instructor strongly emphasizes the Single Responsibility Principle (SRP) at the method level. This is a crucial habit: initState should orchestrate initializations, not contain the raw logic for them.
  • [Editor's note] The instructor sets up data and presentation folders for the Home feature, explicitly stating it's a 2-layer approach (MVVM) instead of 3. In a pure Clean Architecture setup, you would typically still maintain a domain layer to decouple the UI/Presentation from the Data layer. The instructor is blending standard MVVM folder structuring into a broader Clean Architecture context, which is common in Flutter but structurally distinct from Uncle Bob's strict definition.
  • [Editor's note] The usage of Get.to() for navigation is very quick, but it violates dependency rules by tightly coupling views to a specific third-party routing package. Abstracting this behind an interface (or using a standard Router API) is a safer long-term architectural choice.