### Setup Dependency Injection with GetIt in Flutter Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Configures the GetIt service locator for dependency injection. It registers various features, use cases, repositories, data sources, and external dependencies. This setup is crucial for managing object creation and dependencies in a clean architecture. ```dart import 'package:data_connection_checker/data_connection_checker.dart'; import 'package:get_it/get_it.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; final sl = GetIt.instance; Future init() async { //! Features - Number Trivia // Bloc - registered as factory (new instance each time) sl.registerFactory( () => NumberTriviaBloc( concrete: sl(), inputConverter: sl(), random: sl(), ), ); // Use cases - lazy singletons sl.registerLazySingleton(() => GetConcreteNumberTrivia(sl())); sl.registerLazySingleton(() => GetRandomNumberTrivia(sl())); // Repository sl.registerLazySingleton( () => NumberTriviaRepositoryImpl( localDataSource: sl(), networkInfo: sl(), remoteDataSource: sl(), ), ); // Data sources sl.registerLazySingleton( () => NumberTriviaRemoteDataSourceImpl(client: sl()), ); sl.registerLazySingleton( () => NumberTriviaLocalDataSourceImpl(sharedPreferences: sl()), ); //! Core sl.registerLazySingleton(() => InputConverter()); sl.registerLazySingleton(() => NetworkInfoImpl(sl())); //! External final sharedPreferences = await SharedPreferences.getInstance(); sl.registerLazySingleton(() => sharedPreferences); sl.registerLazySingleton(() => http.Client()); sl.registerLazySingleton(() => DataConnectionChecker()); } // In main.dart: void main() async { WidgetsFlutterBinding.ensureInitialized(); await init(); runApp(MyApp()); } // Accessing dependencies: final bloc = sl(); ``` -------------------------------- ### Test NumberTriviaBloc State Transitions and Event Handling (Dart) Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt This Dart code snippet demonstrates how to test the NumberTriviaBloc for state transitions and event handling. It uses mock objects for dependencies like GetConcreteNumberTrivia, GetRandomNumberTrivia, and InputConverter. The tests cover initial state, successful trivia loading, and error handling for invalid input, utilizing `setUp`, `test`, `group`, `expectLater`, and `emitsInOrder` from the `flutter_test` package. ```dart import 'package:dartz/dartz.dart'; import 'package:mockito/mockito.dart'; import 'package:flutter_test/flutter_test.dart'; // Assuming these are defined elsewhere in the project // class GetConcreteNumberTrivia {} // class GetRandomNumberTrivia {} // class InputConverter {} // class NumberTriviaBloc {} // class Empty {} // class Loading {} // class Loaded {} // class Error {} // class NumberTrivia {} // class InvalidInputFailure {} // const String INVALID_INPUT_FAILURE_MESSAGE = 'Invalid Input'; class MockGetConcreteNumberTrivia extends Mock implements GetConcreteNumberTrivia {} class MockGetRandomNumberTrivia extends Mock implements GetRandomNumberTrivia {} class MockInputConverter extends Mock implements InputConverter {} void main() { NumberTriviaBloc bloc; MockGetConcreteNumberTrivia mockGetConcreteNumberTrivia; MockGetRandomNumberTrivia mockGetRandomNumberTrivia; MockInputConverter mockInputConverter; setUp(() { mockGetConcreteNumberTrivia = MockGetConcreteNumberTrivia(); mockGetRandomNumberTrivia = MockGetRandomNumberTrivia(); mockInputConverter = MockInputConverter(); bloc = NumberTriviaBloc( concrete: mockGetConcreteNumberTrivia, random: mockGetRandomNumberTrivia, inputConverter: mockInputConverter, ); }); test('initialState should be Empty', () { expect(bloc.initialState, equals(Empty())); }); group('GetTriviaForConcreteNumber', () { final tNumberString = '1'; final tNumberParsed = 1; final tNumberTrivia = NumberTrivia(number: 1, text: 'test trivia'); test('should emit [Loading, Loaded] when data is gotten successfully', () async { // arrange when(mockInputConverter.stringToUnsignedInteger(any)) .thenReturn(Right(tNumberParsed)); when(mockGetConcreteNumberTrivia(any)) .thenAnswer((_) async => Right(tNumberTrivia)); // assert later final expected = [Empty(), Loading(), Loaded(trivia: tNumberTrivia)]; expectLater(bloc, emitsInOrder(expected)); // act bloc.add(GetTriviaForConcreteNumber(tNumberString)); }); test('should emit [Error] when input is invalid', () async { // arrange when(mockInputConverter.stringToUnsignedInteger(any)) .thenReturn(Left(InvalidInputFailure())); // assert later final expected = [Empty(), Error(message: INVALID_INPUT_FAILURE_MESSAGE)]; expectLater(bloc, emitsInOrder(expected)); // act bloc.add(GetTriviaForConcreteNumber(tNumberString)); }); }); } // Run tests: // flutter test test/features/number_trivia/presentation/bloc/ ``` -------------------------------- ### Unit Test Use Cases with Mockito in Flutter Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Demonstrates unit testing a use case ('GetConcreteNumberTrivia') using Mockito for mocking repository dependencies. It sets up the test environment, mocks the repository, and asserts that the use case correctly interacts with the repository and returns the expected result. ```dart import 'package:dartz/dartz.dart'; import 'package:mockito/mockito.dart'; import 'package:flutter_test/flutter_test.dart'; class MockNumberTriviaRepository extends Mock implements NumberTriviaRepository {} void main() { GetConcreteNumberTrivia usecase; MockNumberTriviaRepository mockRepository; setUp(() { mockRepository = MockNumberTriviaRepository(); usecase = GetConcreteNumberTrivia(mockRepository); }); final tNumber = 1; final tNumberTrivia = NumberTrivia(number: 1, text: 'test'); test('should get trivia for the number from the repository', () async { // arrange when(mockRepository.getConcreteNumberTrivia(any)) .thenAnswer((_) async => Right(tNumberTrivia)); // act final result = await usecase(Params(number: tNumber)); // assert expect(result, Right(tNumberTrivia)); verify(mockRepository.getConcreteNumberTrivia(tNumber)); verifyNoMoreInteractions(mockRepository); }); } // Run tests: // flutter test test/features/number_trivia/domain/usecases/ ``` -------------------------------- ### Implement UseCase Interface and Params in Dart Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Defines the abstract 'UseCase' interface for all use cases, promoting the Single Responsibility Principle and using Dartz's 'Either' type for functional error handling. It also includes 'NoParams' for use cases without parameters and 'Params' for those requiring an integer input. ```dart import 'package:dartz/dartz.dart'; import 'package:equatable/equatable.dart'; abstract class UseCase { Future> call(Params params); } class NoParams extends Equatable { @override List get props => []; } // Implementing a use case: class GetConcreteNumberTrivia implements UseCase { final NumberTriviaRepository repository; GetConcreteNumberTrivia(this.repository); @override Future> call(Params params) async { return await repository.getConcreteNumberTrivia(params.number); } } class Params extends Equatable { final int number; Params({@required this.number}); @override List get props => [number]; } // Usage with error handling: final useCase = GetConcreteNumberTrivia(repository); final result = await useCase(Params(number: 42)); result.fold( (failure) => print('Error: ${failure.runtimeType}'), (trivia) => print('Success: ${trivia.text}'), ); ``` -------------------------------- ### Core Error Handling: Exceptions and Failures (Dart) Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Defines a typed error system separating data layer exceptions (ServerException, CacheException) from domain layer failures (ServerFailure, CacheFailure, InvalidInputFailure). Includes a utility function to map Failures to user-friendly messages. ```dart // Exceptions - thrown in data layer class ServerException implements Exception {} class CacheException implements Exception {} // Failures - returned to presentation layer import 'package:equatable/equatable.dart'; abstract class Failure extends Equatable { @override List get props => []; } class ServerFailure extends Failure {} class CacheFailure extends Failure {} class InvalidInputFailure extends Failure {} // Usage in BLoC: String _mapFailureToMessage(Failure failure) { switch (failure.runtimeType) { case ServerFailure: return 'Server Failure'; case CacheFailure: return 'Cache Failure'; case InvalidInputFailure: return 'Invalid Input - The number must be a positive integer or zero.'; default: return 'Unexpected error'; } } ``` -------------------------------- ### Implement Number Trivia Repository with Network-First Strategy (Dart) Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Implements the NumberTriviaRepository with a network-first strategy, automatically caching results and falling back to local storage if the network is unavailable. It handles server and cache exceptions, returning typed Failures to the domain layer. ```dart import 'package:dartz/dartz.dart'; class NumberTriviaRepositoryImpl implements NumberTriviaRepository { final NumberTriviaRemoteDataSource remoteDataSource; final NumberTriviaLocalDataSource localDataSource; final NetworkInfo networkInfo; NumberTriviaRepositoryImpl({ @required this.remoteDataSource, @required this.localDataSource, @required this.networkInfo, }); @override Future> getConcreteNumberTrivia(int number) async { return await _getTrivia(() => remoteDataSource.getConcreteNumberTrivia(number)); } @override Future> getRandomNumberTrivia() async { return await _getTrivia(() => remoteDataSource.getRandomNumberTrivia()); } Future> _getTrivia( Future Function() getConcreteOrRandom, ) async { if (await networkInfo.isConnected) { try { final remoteTrivia = await getConcreteOrRandom(); localDataSource.cacheNumberTrivia(remoteTrivia); return Right(remoteTrivia); } on ServerException { return Left(ServerFailure()); } } else { try { final localTrivia = await localDataSource.getLastNumberTrivia(); return Right(localTrivia); } on CacheException { return Left(CacheFailure()); } } } } ``` -------------------------------- ### InputConverter for String to Unsigned Integer Conversion (Dart) Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt A utility class that validates user input strings and converts them to unsigned integers. It returns either a Failure (InvalidInputFailure) for invalid formats or negative numbers, or the parsed integer wrapped in a Right. ```dart import 'package:dartz/dartz.dart'; class InputConverter { Either stringToUnsignedInteger(String str) { try { final integer = int.parse(str); if (integer < 0) throw FormatException(); return Right(integer); } on FormatException { return Left(InvalidInputFailure()); } } } // Usage: final converter = InputConverter(); // Valid input: final result1 = converter.stringToUnsignedInteger('42'); result1.fold( (failure) => print('Invalid'), (number) => print('Parsed: $number'), // Parsed: 42 ); // Invalid input: final result2 = converter.stringToUnsignedInteger('-5'); result2.fold( (failure) => print('Invalid: negative number'), // Invalid: negative number (number) => print('Parsed: $number'), ); final result3 = converter.stringToUnsignedInteger('abc'); result3.fold( (failure) => print('Invalid: not a number'), // Invalid: not a number (number) => print('Parsed: $number'), ); ``` -------------------------------- ### NetworkInfo Abstraction for Connectivity Check (Dart) Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt An abstract class defining an interface for checking network connectivity. The implementation uses the 'data_connection_checker' package to determine if the device is currently online. ```dart import 'package:data_connection_checker/data_connection_checker.dart'; abstract class NetworkInfo { Future get isConnected; } class NetworkInfoImpl implements NetworkInfo { final DataConnectionChecker connectionChecker; NetworkInfoImpl(this.connectionChecker); @override Future get isConnected => connectionChecker.hasConnection; } // Usage: final networkInfo = NetworkInfoImpl(DataConnectionChecker()); if (await networkInfo.isConnected) { print('Online - fetch from API'); } else { print('Offline - use cached data'); } ``` -------------------------------- ### Create GetRandomNumberTrivia UseCase in Dart Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Implements the 'GetRandomNumberTrivia' use case, which inherits from the 'UseCase' interface and takes 'NoParams'. This use case is responsible for fetching random number trivia data by calling the corresponding method on the 'NumberTriviaRepository'. ```dart import 'package:dartz/dartz.dart'; class GetRandomNumberTrivia implements UseCase { final NumberTriviaRepository repository; GetRandomNumberTrivia(this.repository); @override Future> call(NoParams params) async { return await repository.getRandomNumberTrivia(); } } // Usage: final getRandomTrivia = GetRandomNumberTrivia(repository); final result = await getRandomTrivia(NoParams()); result.fold( (failure) { if (failure is ServerFailure) { print('Server error occurred'); } else if (failure is CacheFailure) { print('Cache error occurred'); } }, (trivia) => print('Random trivia: ${trivia.number} - ${trivia.text}'), ); ``` -------------------------------- ### Define NumberTriviaRepository Interface in Dart Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Defines the abstract 'NumberTriviaRepository' contract, outlining the methods for fetching concrete and random number trivia. This interface abstracts the data source implementation details from the domain layer, adhering to the principles of Clean Architecture. ```dart import 'package:dartz/dartz.dart'; abstract class NumberTriviaRepository { Future> getConcreteNumberTrivia(int number); Future> getRandomNumberTrivia(); } // Repository is injected into use cases: final repository = sl(); // From dependency injection final useCase = GetConcreteNumberTrivia(repository); ``` -------------------------------- ### NumberTriviaRemoteDataSource: Fetching Trivia from API in Dart Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Implements the NumberTriviaRemoteDataSource interface to handle HTTP communication with the Numbers API. It fetches concrete or random number trivia using the http package and throws a ServerException on API failures. Requires the 'http' and 'dart:convert' packages. ```dart import 'dart:convert'; import 'package:http/http.dart' as http; abstract class NumberTriviaRemoteDataSource { /// Calls the http://numbersapi.com/{number} endpoint. /// Throws a [ServerException] for all error codes. Future getConcreteNumberTrivia(int number); /// Calls the http://numbersapi.com/random endpoint. /// Throws a [ServerException] for all error codes. Future getRandomNumberTrivia(); } class NumberTriviaRemoteDataSourceImpl implements NumberTriviaRemoteDataSource { final http.Client client; NumberTriviaRemoteDataSourceImpl({@required this.client}); @override Future getConcreteNumberTrivia(int number) = _getTriviaFromUrl('http://numbersapi.com/$number'); @override Future getRandomNumberTrivia() = _getTriviaFromUrl('http://numbersapi.com/random'); Future _getTriviaFromUrl(String url) async { final response = await client.get( url, headers: {'Content-Type': 'application/json'}, ); if (response.statusCode == 200) { return NumberTriviaModel.fromJson(json.decode(response.body)); } else { throw ServerException(); } } } // Usage: final dataSource = NumberTriviaRemoteDataSourceImpl(client: http.Client()); try { final trivia = await dataSource.getConcreteNumberTrivia(42); print(trivia.text); } on ServerException { print('Server error'); } ``` -------------------------------- ### NumberTriviaLocalDataSource: Local Caching with SharedPreferences in Dart Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Implements the NumberTriviaLocalDataSource interface for handling local caching of number trivia using SharedPreferences. It provides methods to retrieve the last cached trivia and to cache new trivia. Throws a CacheException if no data is found. Requires the 'shared_preferences' and 'dart:convert' packages. ```dart import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; const CACHED_NUMBER_TRIVIA = 'CACHED_NUMBER_TRIVIA'; abstract class NumberTriviaLocalDataSource { /// Gets the cached [NumberTriviaModel] which was gotten the last time /// the user had an internet connection. /// Throws [CacheException] if no cached data is present. Future getLastNumberTrivia(); Future cacheNumberTrivia(NumberTriviaModel triviaToCache); } class NumberTriviaLocalDataSourceImpl implements NumberTriviaLocalDataSource { final SharedPreferences sharedPreferences; NumberTriviaLocalDataSourceImpl({@required this.sharedPreferences}); @override Future getLastNumberTrivia() { final jsonString = sharedPreferences.getString(CACHED_NUMBER_TRIVIA); if (jsonString != null) { return Future.value(NumberTriviaModel.fromJson(json.decode(jsonString))); } else { throw CacheException(); } } @override Future cacheNumberTrivia(NumberTriviaModel triviaToCache) { return sharedPreferences.setString( CACHED_NUMBER_TRIVIA, json.encode(triviaToCache.toJson()), ); } } // Usage: final prefs = await SharedPreferences.getInstance(); final localDataSource = NumberTriviaLocalDataSourceImpl(sharedPreferences: prefs); // Cache trivia: await localDataSource.cacheNumberTrivia(triviaModel); // Retrieve cached trivia: try { final cached = await localDataSource.getLastNumberTrivia(); print('Cached: ${cached.text}'); } on CacheException { print('No cached data'); } ``` -------------------------------- ### Implement NumberTriviaBloc for State Management (Dart) Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Manages the state for the Number Trivia feature, orchestrating use cases and input validation. It handles incoming events, processes them using injected use cases (GetConcreteNumberTrivia, GetRandomNumberTrivia), and emits corresponding states (Empty, Loading, Loaded, Error). ```dart import 'package:bloc/bloc.dart'; import 'package:dartz/dartz.dart'; const String SERVER_FAILURE_MESSAGE = 'Server Failure'; const String CACHE_FAILURE_MESSAGE = 'Cache Failure'; const String INVALID_INPUT_FAILURE_MESSAGE = 'Invalid Input - The number must be a positive integer or zero.'; class NumberTriviaBloc extends Bloc { final GetConcreteNumberTrivia getConcreteNumberTrivia; final GetRandomNumberTrivia getRandomNumberTrivia; final InputConverter inputConverter; NumberTriviaBloc({ @required GetConcreteNumberTrivia concrete, @required GetRandomNumberTrivia random, @required this.inputConverter, }) : getConcreteNumberTrivia = concrete, getRandomNumberTrivia = random; @override NumberTriviaState get initialState => Empty(); @override Stream mapEventToState(NumberTriviaEvent event) async* { if (event is GetTriviaForConcreteNumber) { final inputEither = inputConverter.stringToUnsignedInteger(event.numberString); yield* inputEither.fold( (failure) async* { yield Error(message: INVALID_INPUT_FAILURE_MESSAGE); }, (integer) async* { yield Loading(); final failureOrTrivia = await getConcreteNumberTrivia(Params(number: integer)); yield* _eitherLoadedOrErrorState(failureOrTrivia); }, ); } else if (event is GetTriviaForRandomNumber) { yield Loading(); final failureOrTrivia = await getRandomNumberTrivia(NoParams()); yield* _eitherLoadedOrErrorState(failureOrTrivia); } } Stream _eitherLoadedOrErrorState( Either failureOrTrivia, ) async* { yield failureOrTrivia.fold( (failure) => Error(message: _mapFailureToMessage(failure)), (trivia) => Loaded(trivia: trivia), ); } String _mapFailureToMessage(Failure failure) { switch (failure.runtimeType) { case ServerFailure: return SERVER_FAILURE_MESSAGE; case CacheFailure: return CACHE_FAILURE_MESSAGE; default: return 'Unexpected error'; } } } ``` -------------------------------- ### NumberTriviaModel: JSON Serialization and Deserialization in Dart Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Defines the NumberTriviaModel, extending a domain entity with JSON serialization and deserialization capabilities. It supports converting between Dart objects and JSON maps for API and cache operations. Requires the 'meta' package for annotations. ```dart import 'package:meta/meta.dart'; class NumberTriviaModel extends NumberTrivia { NumberTriviaModel({ @required String text, @required int number, }) : super(text: text, number: number); factory NumberTriviaModel.fromJson(Map json) { return NumberTriviaModel( text: json['text'], number: (json['number'] as num).toInt(), ); } Map toJson() { return { 'text': text, 'number': number, }; } } // Parsing API response: final jsonResponse = {'text': '42 is interesting', 'number': 42}; final model = NumberTriviaModel.fromJson(jsonResponse); print(model.number); // 42 // Serializing for cache: final jsonString = json.encode(model.toJson()); // {"text":"42 is interesting","number":42} ``` -------------------------------- ### Define Number Trivia BLoC States (Dart) Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Defines immutable states representing different UI conditions for the Number Trivia feature. These states include Empty, Loading, Loaded (with trivia data), and Error (with a message), allowing for clear UI updates based on the BLoC's output. ```dart import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; @immutable abstract class NumberTriviaState extends Equatable { @override List get props => []; } class Empty extends NumberTriviaState {} class Loading extends NumberTriviaState {} class Loaded extends NumberTripleState { final NumberTrivia trivia; Loaded({@required this.trivia}); @override List get props => [trivia]; } class Error extends NumberTriviaState { final String message; Error({@required this.message}); @override List get props => [message]; } // Handling states in UI: // BlocBuilder( // builder: (context, state) { // if (state is Empty) { // return MessageDisplay(message: 'Start searching!'); // } else if (state is Loading) { // return LoadingWidget(); // } else if (state is Loaded) { // return TriviaDisplay(numberTrivia: state.trivia); // } else if (state is Error) { // return MessageDisplay(message: state.message); // } // }, // ) ``` -------------------------------- ### Define Number Trivia BLoC Events (Dart) Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Defines immutable events that trigger state changes in the NumberTriviaBloc. These events, such as fetching trivia for a concrete number or a random number, are designed to be easily dispatched from the UI and processed by the BLoC. ```dart import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; @immutable abstract class NumberTriviaEvent extends Equatable { @override List get props => []; } class GetTriviaForConcreteNumber extends NumberTriviaEvent { final String numberString; GetTriviaForConcreteNumber(this.numberString); @override List get props => [numberString]; } class GetTriviaForRandomNumber extends NumberTriviaEvent {} // Dispatching events from UI: // BlocProvider.of(context) // .add(GetTriviaForConcreteNumber('42')); // BlocProvider.of(context) // .add(GetTriviaForRandomNumber()); ``` -------------------------------- ### Define NumberTrivia Entity in Dart Source: https://context7.com/resocoder/flutter-tdd-clean-architecture-course/llms.txt Defines the core domain entity 'NumberTrivia' with properties for text and number. It uses Equatable for value equality comparison, ensuring that two NumberTrivia objects with the same text and number are considered equal. This entity is a fundamental part of the domain layer. ```dart import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; class NumberTrivia extends Equatable { final String text; final int number; NumberTrivia({ @required this.text, @required this.number, }); @override List get props => [text, number]; } // Usage example: final trivia = NumberTrivia( number: 42, text: '42 is the answer to life, the universe, and everything.', ); print(trivia.number); // 42 print(trivia.text); // '42 is the answer to life...' ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.