abdicate object assigning responsibility
The ImagesService itself handles the conversion of Json to the object itself, instead of the ImagesApi
This commit is contained in:
		
							parent
							
								
									a0ed894016
								
							
						
					
					
						commit
						6a84a9bef0
					
				
					 9 changed files with 255 additions and 25 deletions
				
			
		
							
								
								
									
										17
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,16 +1,13 @@ | |||
| # mc_gallery | ||||
| 
 | ||||
| A new Flutter project. | ||||
| ## Dart docs explanation | ||||
| 
 | ||||
| ## Getting Started | ||||
| ## Emulation | ||||
| 
 | ||||
| This project is a starting point for a Flutter application. | ||||
| ## Maintaining scope | ||||
| It's an 'assignment' | ||||
| 
 | ||||
| A few resources to get you started if this is your first Flutter project: | ||||
| ## Model vs. DTO | ||||
| 
 | ||||
| - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) | ||||
| - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) | ||||
| 
 | ||||
| For help getting started with Flutter development, view the | ||||
| [online documentation](https://docs.flutter.dev/), which offers tutorials, | ||||
| samples, guidance on mobile development, and a full API reference. | ||||
| ## Extra quirks | ||||
| Just because I had those assets lying around | ||||
|  | @ -1,15 +1,13 @@ | |||
| import 'dart:async'; | ||||
| 
 | ||||
| import '../data/models/image_model.dart'; | ||||
| 
 | ||||
| /// Interface for implementing image-fetching strategies, specific to a resource location on the internet. | ||||
| /// | ||||
| /// Since I used a site that was more obscure than the ones in the examples, this (otherwise pointless | ||||
| /// and convoluting) interface is for adding a bit of flexibility to change strategy to some other site. | ||||
| abstract class ImagesApi { | ||||
|   FutureOr<Iterable<ImageModel>> fetchImageUri({required String token}); | ||||
|   FutureOr<Iterable<Map<String, dynamic>>> fetchImageUri({required String token}); | ||||
| 
 | ||||
|   FutureOr<List<ImageModel>> searchImages({ | ||||
|   FutureOr<Iterable<Map<String, dynamic>>> searchImages({ | ||||
|     required String searchStr, | ||||
|     required String token, | ||||
|   }); | ||||
|  |  | |||
|  | @ -14,14 +14,15 @@ class UnsplashImagesApi implements ImagesApi { | |||
|   final random = Random(); | ||||
| 
 | ||||
|   @override | ||||
|   FutureOr<Iterable<ImageModel>> fetchImageUri({required String token}) async { | ||||
|   FutureOr<Iterable<Map<String, dynamic>>> fetchImageUri({required String token}) async { | ||||
|     // Dummy fetching delay emulation | ||||
|     await Future.delayed(const Duration( | ||||
|         milliseconds: ConstValues.defaultEmulatedLatencyMillis * ConstValues.numberOfImages)); | ||||
| 
 | ||||
|     try { | ||||
|       // Create fixed number of images | ||||
|       return Iterable<int>.generate(ConstValues.numberOfImages).map((final imageIndex) { | ||||
|       final dummyImageModels = | ||||
|           Iterable<int>.generate(ConstValues.numberOfImages).map((final imageIndex) { | ||||
|         // Drawing from a normal distribution | ||||
|         final imageSide = | ||||
|             random.nextIntInRange(min: ConstValues.minImageSize, max: ConstValues.maxImageSize); | ||||
|  | @ -36,6 +37,9 @@ class UnsplashImagesApi implements ImagesApi { | |||
|           imageName: Strings.current.imageNameFetch(imageIndex + 1, imageSide), | ||||
|         ); | ||||
|       }); | ||||
| 
 | ||||
|       // Emulating serialization | ||||
|       return dummyImageModels.map((final dummyModel) => dummyModel.toJson()); | ||||
|     } on Exception catch (ex, stackTrace) { | ||||
|       _loggingService.handleException(ex, stackTrace); | ||||
|       return const Iterable.empty(); | ||||
|  | @ -43,7 +47,7 @@ class UnsplashImagesApi implements ImagesApi { | |||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   FutureOr<List<ImageModel>> searchImages({ | ||||
|   FutureOr<Iterable<Map<String, dynamic>>> searchImages({ | ||||
|     required String searchStr, | ||||
|     required String token, | ||||
|   }) async { | ||||
|  | @ -55,7 +59,7 @@ class UnsplashImagesApi implements ImagesApi { | |||
| 
 | ||||
|     try { | ||||
|       // Create (randomly-bounded) dummy number of images | ||||
|       return Iterable<int>.generate(numberOfResults).map((final imageIndex) { | ||||
|       final dummyImageModels = Iterable<int>.generate(numberOfResults).map((final imageIndex) { | ||||
|         // Drawing from a normal distribution | ||||
|         final imageSide = | ||||
|             random.nextIntInRange(min: ConstValues.minImageSize, max: ConstValues.maxImageSize); | ||||
|  | @ -68,7 +72,10 @@ class UnsplashImagesApi implements ImagesApi { | |||
|           // Custom dummy name for the image | ||||
|           imageName: Strings.current.imageNameSearch(searchStr, imageIndex + 1), | ||||
|         ); | ||||
|       }).toList(growable: false); | ||||
|       }); | ||||
| 
 | ||||
|       // Emulating serialization | ||||
|       return dummyImageModels.map((final dummyModel) => dummyModel.toJson()); | ||||
|     } on Exception catch (ex, stackTrace) { | ||||
|       _loggingService.handleException(ex, stackTrace); | ||||
|       return List.empty(); | ||||
|  |  | |||
|  | @ -1,3 +1,8 @@ | |||
| import 'package:json_annotation/json_annotation.dart'; | ||||
| 
 | ||||
| part 'image_model.g.dart'; | ||||
| 
 | ||||
| @JsonSerializable() | ||||
| class ImageModel { | ||||
|   const ImageModel({ | ||||
|     required this.uri, | ||||
|  | @ -15,4 +20,9 @@ class ImageModel { | |||
| 
 | ||||
|   /// Given name of the image. | ||||
|   final String imageName; | ||||
| 
 | ||||
|   factory ImageModel.fromJson(Map<String, dynamic> json) => _$ImageModelFromJson(json); | ||||
| 
 | ||||
|   /// Connect the generated [_$PersonToJson] function to the `toJson` method. | ||||
|   Map<String, dynamic> toJson() => _$ImageModelToJson(this); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										20
									
								
								lib/features/home/data/models/image_model.g.dart
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								lib/features/home/data/models/image_model.g.dart
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| // GENERATED CODE - DO NOT MODIFY BY HAND | ||||
| 
 | ||||
| part of 'image_model.dart'; | ||||
| 
 | ||||
| // ************************************************************************** | ||||
| // JsonSerializableGenerator | ||||
| // ************************************************************************** | ||||
| 
 | ||||
| ImageModel _$ImageModelFromJson(Map<String, dynamic> json) => ImageModel( | ||||
|       uri: Uri.parse(json['uri'] as String), | ||||
|       imageIndex: json['imageIndex'] as int, | ||||
|       imageName: json['imageName'] as String, | ||||
|     ); | ||||
| 
 | ||||
| Map<String, dynamic> _$ImageModelToJson(ImageModel instance) => | ||||
|     <String, dynamic>{ | ||||
|       'uri': instance.uri.toString(), | ||||
|       'imageIndex': instance.imageIndex, | ||||
|       'imageName': instance.imageName, | ||||
|     }; | ||||
|  | @ -42,7 +42,8 @@ class ImagesService { | |||
|   Future<void> _init() async { | ||||
|     _loggingService.info('Fetching and creating image models...'); | ||||
|     _imageModels = { | ||||
|       for (final imageModel in await _imagesApi.fetchImageUri(token: '')) | ||||
|       for (final imageModel in (await _imagesApi.fetchImageUri(token: '')) | ||||
|           .map((final emulatedModelSerialized) => ImageModel.fromJson(emulatedModelSerialized))) | ||||
|         imageModel.imageName: imageModel | ||||
|     }; | ||||
| 
 | ||||
|  | @ -90,10 +91,13 @@ class ImagesService { | |||
|               ..reversed; | ||||
|             return _imageModels.valuesByKeys(keys: rankedKeys).toList(growable: false); | ||||
|           case SearchOption.web: | ||||
|             return await _imagesApi.searchImages( | ||||
|             return (await _imagesApi.searchImages( | ||||
|               searchStr: imageNamePart, | ||||
|               token: '', | ||||
|             ); | ||||
|             )) | ||||
|                 .map( | ||||
|                     (final emulatedModelSerialized) => ImageModel.fromJson(emulatedModelSerialized)) | ||||
|                 .toList(growable: false); | ||||
|         } | ||||
|       } finally { | ||||
|         unlock(); | ||||
|  |  | |||
|  | @ -46,9 +46,9 @@ class GalleryView extends StatelessWidget { | |||
|                 ], | ||||
|                 builder: (context, final values, child) => !model.isDisplayingPressingPrompt.value | ||||
|                     ? IconButton( | ||||
|                         icon: !model.isSearchingListenable.value | ||||
|                             ? const Icon(Icons.search) | ||||
|                             : const Icon(Icons.close), | ||||
|                         isSelected: model.isSearchingListenable.value, | ||||
|                         icon: const Icon(Icons.search), | ||||
|                         selectedIcon: const Icon(Icons.close), | ||||
|                         onPressed: model.searchPressed, | ||||
|                       ) | ||||
|                     : const SizedBox.shrink(), | ||||
|  |  | |||
							
								
								
									
										189
									
								
								pubspec.lock
									
										
									
									
									
								
							
							
						
						
									
										189
									
								
								pubspec.lock
									
										
									
									
									
								
							|  | @ -57,6 +57,62 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.1.0" | ||||
|   build: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: build | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.3.1" | ||||
|   build_config: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: build_config | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.1.1" | ||||
|   build_daemon: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: build_daemon | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.1.0" | ||||
|   build_resolvers: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: build_resolvers | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.1.0" | ||||
|   build_runner: | ||||
|     dependency: "direct dev" | ||||
|     description: | ||||
|       name: build_runner | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.3.3" | ||||
|   build_runner_core: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: build_runner_core | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "7.2.7" | ||||
|   built_collection: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: built_collection | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "5.1.1" | ||||
|   built_value: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: built_value | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "8.4.2" | ||||
|   cached_network_image: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|  | @ -92,6 +148,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.2.1" | ||||
|   checked_yaml: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: checked_yaml | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.0.1" | ||||
|   clock: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -99,6 +162,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.1.1" | ||||
|   code_builder: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: code_builder | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "4.4.0" | ||||
|   collection: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -183,6 +253,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "6.1.4" | ||||
|   fixnum: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: fixnum | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.0.1" | ||||
|   flutter: | ||||
|     dependency: "direct main" | ||||
|     description: flutter | ||||
|  | @ -233,6 +310,13 @@ packages: | |||
|     description: flutter | ||||
|     source: sdk | ||||
|     version: "0.0.0" | ||||
|   frontend_server_client: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: frontend_server_client | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.2.0" | ||||
|   get_it: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|  | @ -254,6 +338,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "6.0.0" | ||||
|   graphs: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: graphs | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.2.0" | ||||
|   http: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -261,6 +352,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.13.5" | ||||
|   http_multi_server: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: http_multi_server | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.2.1" | ||||
|   http_parser: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -289,6 +387,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.8.1" | ||||
|   io: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: io | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.0.3" | ||||
|   js: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -296,6 +401,20 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.6.4" | ||||
|   json_annotation: | ||||
|     dependency: "direct dev" | ||||
|     description: | ||||
|       name: json_annotation | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "4.7.0" | ||||
|   json_serializable: | ||||
|     dependency: "direct dev" | ||||
|     description: | ||||
|       name: json_serializable | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "6.5.4" | ||||
|   lints: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -338,6 +457,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.8.0" | ||||
|   mime: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: mime | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.0.3" | ||||
|   nested: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -471,6 +597,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.6.2" | ||||
|   pool: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: pool | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.5.1" | ||||
|   process: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -492,6 +625,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.1.3" | ||||
|   pubspec_parse: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: pubspec_parse | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.2.1" | ||||
|   rxdart: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -499,11 +639,39 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.27.7" | ||||
|   shelf: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shelf | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.4.0" | ||||
|   shelf_web_socket: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shelf_web_socket | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.0.3" | ||||
|   sky_engine: | ||||
|     dependency: transitive | ||||
|     description: flutter | ||||
|     source: sdk | ||||
|     version: "0.0.99" | ||||
|   source_gen: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: source_gen | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.2.6" | ||||
|   source_helper: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: source_helper | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.3.3" | ||||
|   source_span: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -539,6 +707,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.1.0" | ||||
|   stream_transform: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: stream_transform | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.1.0" | ||||
|   string_scanner: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -595,6 +770,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.4.12" | ||||
|   timing: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: timing | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.0.0" | ||||
|   typed_data: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -623,6 +805,13 @@ packages: | |||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.0.2" | ||||
|   web_socket_channel: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: web_socket_channel | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.2.0" | ||||
|   win32: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  |  | |||
|  | @ -50,6 +50,11 @@ dev_dependencies: | |||
| 
 | ||||
|   flutter_lints: ^2.0.1 | ||||
| 
 | ||||
|   # Builders | ||||
|   build_runner: ^2.3.3 | ||||
|   json_annotation: ^4.7.0 | ||||
|   json_serializable: ^6.5.4 | ||||
| 
 | ||||
| flutter: | ||||
|   uses-material-design: true | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue