Flutter TDD and Clean Architecture Course

repository·master·Indexed 24 days ago

https://github.com/resocoder/flutter-tdd-clean-architecture-course

Code examples and implementations for applying Test-Driven Development (TDD) and Clean Architecture principles to Flutter development. The project demonstrates error handling using ServerException, CacheException, and Failure classes, data sourcing via NumberTriviaRemoteDataSource and NumberTriviaLocalDataSource, and dependency injection setup.

Tokens
1.6K
Snippets
7
Records
12
Agent score
80%

What's inside flutter-tdd-clean-architecture-course

  1. Access the TDD Clean Architecture for Flutter tutorial series

    master

    This repository serves as the code implementation for a course on Test-Driven Development (TDD) and Clean Architecture in Flutter. For a complete step-by-step guide, explanations, and the full tutorial series, visit the official Reso Coder website.

    https://resocoder.com/flutter-clean-architecture-tdd/
  2. Customize the iOS launch screen assets

    master

    To change the launch screen image for the iOS version of your Flutter app, you can use one of two methods:

    1. Direct File Replacement: Replace the existing image files located in the ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory with your own assets.
    2. Xcode Interface:
      • Open the iOS project in Xcode by running open ios/Runner.xcworkspace from your terminal.
      • In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
      • Drag and drop your desired images directly into the asset catalog within the Xcode interface.
    open ios/Runner.xcworkspace
  3. Handle domain-level errors using Failure classes

    master

    In this architecture, errors that occur in the data or domain layers are represented as Failure objects. This allows the application to distinguish between different types of errors (like server or cache issues) in a type-safe way.

    To use these, you can catch or return specific subclasses of Failure. The base Failure class extends Equatable to ensure that failures can be compared by value rather than by instance, which is useful for testing and state management.

    // Example of how failures are structured
    class ServerFailure extends Failure {}
    
    class CacheFailure extends Failure {}
  4. Initialize the application and dependency injection

    master

    The application entry point requires two critical initialization steps before running the app:

    1. Call WidgetsFlutterBinding.ensureInitialized() to ensure Flutter bindings are ready for asynchronous operations.
    2. Call di.init() (from the injection_container.dart module) to initialize the dependency injection container, which sets up all necessary use cases, repositories, and data sources.

    After initialization, use runApp() to launch the MyApp widget.

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await di.init();
      runApp(MyApp());
    }
  5. Use NumberTriviaRemoteDataSource to fetch trivia data

    master

    The NumberTriviaRemoteDataSource interface provides methods to fetch trivia information from the Numbers API via HTTP. Implementations (like NumberTriviaRemoteDataSourceImpl) require an http.Client to be passed into the constructor.

    Available methods:

    • getConcreteNumberTrivia(int number): Fetches trivia for a specific integer.
    • getRandomNumberTrivia(): Fetches trivia for a random integer.

    Both methods return a Future<NumberTriviaModel>. If the server returns any status code other than 200, a ServerException is thrown.

  6. Use NumberTriviaLocalDataSource for local caching

    master

    The NumberTriviaLocalDataSource interface provides methods to manage local persistence of number trivia data using SharedPreferences.

    • getLastNumberTrivia(): Retrieves the previously cached NumberTriviaModel. It returns a Future<NumberTriviaModel>. If no data is found in the cache, it throws a CacheException.
    • cacheNumberTrivia(NumberTriviaModel triviaToCache): Persists a NumberTriviaModel to the local cache. It returns a Future<void>.

    The implementation NumberTriviaLocalDataSourceImpl uses the key CACHED_NUMBER_TRIVIA to store the data as a JSON string.

  7. Convert a String to an Unsigned Integer with InputConverter

    master

    Use the InputConverter class to safely parse a String into an unsigned int. The method stringToUnsignedInteger returns an Either<Failure, int> type (from the dartz package).

    • If the string is a valid non-negative integer, it returns Right(integer).
    • If the string is not a valid integer or is a negative number, it returns Left(InvalidInputFailure()).
  8. Use ServerFailure and CacheFailure for error handling

    master

    The project provides two concrete implementations of the Failure class for common error scenarios:

    • ServerFailure: Represents errors occurring during network requests or server-side operations.
    • CacheFailure: Represents errors occurring during local data persistence or cache operations.
    class ServerFailure extends Failure {}
    
    class CacheFailure extends Failure {}
  9. Handle invalid input with InvalidInputFailure

    master

    When using InputConverter.stringToUnsignedInteger, an invalid input (such as a non-numeric string or a negative number) will result in an InvalidInputFailure. This failure is a subclass of Failure and should be handled in your functional error handling flow (e.g., using .fold() on the Either type).

    class InvalidInputFailure extends Failure {}
  10. Configure the MyApp widget

    master

    The MyApp class is a StatelessWidget that serves as the root of the application. It configures the MaterialApp with the following settings:

    • Title: 'Number Trivia'
    • Theme: Uses a green color scheme (primaryColor: Colors.green.shade800 and accentColor: Colors.green.shade600).
    • Home: The initial route is set to NumberTriviaPage().
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Number Trivia',
          theme: ThemeData(
            primaryColor: Colors.green.shade800,
            accentColor: Colors.green.shade600,
          ),
          home: NumberTriviaPage(),
        );
      }
    }