diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/CODEOWNERS b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/CODEOWNERS index 865e9d071..08ce92de6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/CODEOWNERS +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/CODEOWNERS @@ -1,3 +1,3 @@ # Every request must be reviewed and accepted by: -- @felangel @AnnaPS @simpson-peter @marwfair @valentinallavayol +- @marwfair @matiasleyba @SofiaRey \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/dependabot.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/dependabot.yaml index ff1fc23cd..a432dd443 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/dependabot.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/dependabot.yaml @@ -5,6 +5,11 @@ updates: directory: "/" schedule: interval: "daily" + # Updating patch versions for "github-actions" is too chatty. + # See https://github.com/flutter/flutter/issues/158350. + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-patch"] - package-ecosystem: "pub" directory: "/api" schedule: @@ -105,3 +110,8 @@ updates: directory: "/packages/news_blocks_ui" schedule: interval: "daily" + - package-ecosystem: "pub" + directory: "/flutter_news_template/hooks" + schedule: + interval: "daily" + target-branch: "templates" diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/api.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/api.yaml index 35eb38d03..1b9572e41 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/api.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/api.yaml @@ -16,7 +16,7 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - dart_sdk: 3.5.2 + dart_sdk: 3.10.0 working_directory: api analyze_directories: "routes lib test" coverage_excludes: "**/*.g.dart" diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/article_repository.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/article_repository.yaml index c6a88abd1..cc70ace09 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/article_repository.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/article_repository.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - dart_sdk: 3.5.2 + dart_sdk: 3.10.0 working_directory: packages/article_repository diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/authentication_client.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/authentication_client.yaml index 79f21a0da..f4e7d8a8d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/authentication_client.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/authentication_client.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - dart_sdk: 3.5.2 + dart_sdk: 3.10.0 working_directory: packages/authentication_client/authentication_client diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/deep_link_client.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/deep_link_client.yaml index 126e374ed..20d5ace01 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/deep_link_client.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/deep_link_client.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - dart_sdk: 3.4.3 + dart_sdk: 3.10.0 working_directory: packages/deep_link_client/deep_link_client diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/form_inputs.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/form_inputs.yaml index bae4f2f81..45d4ed966 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/form_inputs.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/form_inputs.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - dart_sdk: 3.5.2 + dart_sdk: 3.10.0 working_directory: packages/form_inputs diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/news_blocks.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/news_blocks.yaml index 2aaf7539d..9a326ec6b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/news_blocks.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/news_blocks.yaml @@ -17,5 +17,5 @@ jobs: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: coverage_excludes: "**/*.g.dart" - dart_sdk: 3.5.2 + dart_sdk: 3.10.0 working_directory: api/packages/news_blocks diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/notifications_client.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/notifications_client.yaml index 43401f5e4..e6fa99dec 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/notifications_client.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/notifications_client.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - dart_sdk: 3.5.2 + dart_sdk: 3.10.0 working_directory: packages/notifications_client/notifications_client diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/package_info_client.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/package_info_client.yaml index d1b693b8b..47a12e934 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/package_info_client.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/package_info_client.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - dart_sdk: 3.5.2 + dart_sdk: 3.10.0 working_directory: packages/package_info_client diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/spell_checker.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/spell_checker.yaml index e8f6b3a73..9de20da48 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/spell_checker.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/spell_checker.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: zwaldowski/cspell-action@v1 with: paths: "**/*.{md,dart}" diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/storage.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/storage.yaml index 5fdea1db9..e926bd29a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/storage.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/storage.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - dart_sdk: 3.5.2 + dart_sdk: 3.10.0 working_directory: packages/storage/storage diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/token_storage.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/token_storage.yaml index 8510f4c8a..1652300f6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/token_storage.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/token_storage.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - dart_sdk: 3.5.2 + dart_sdk: 3.10.0 working_directory: packages/authentication_client/token_storage diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/{{#snakeCase}}{{project_name}}{{/snakeCase}}.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/{{#snakeCase}}{{project_name}}{{/snakeCase}}.yaml index 77787affd..f3d3a82d1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/{{#snakeCase}}{{project_name}}{{/snakeCase}}.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.github/workflows/{{#snakeCase}}{{project_name}}{{/snakeCase}}.yaml @@ -28,9 +28,9 @@ jobs: channel: [stable] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: subosito/flutter-action@v2.18.0 + - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 with: channel: ${{#mustacheCase}}matrix.channel{{/mustacheCase}} flutter-version: {{flutter_version}} @@ -70,7 +70,7 @@ jobs: pubviz print -d > pubviz.html - name: Upload Visual Architecture Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: visual-architecture-report path: ./pubviz.html diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.gitignore b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.gitignore index 2b50a5036..5644d0b60 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.gitignore +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.gitignore @@ -93,6 +93,7 @@ unlinked_spec.ds **/ios/Flutter/app.zip **/ios/Flutter/flutter_assets/ **/ios/Flutter/flutter_export_environment.sh +**/ios/Flutter/ephemeral/** **/ios/ServiceDefinitions.json **/ios/Runner/GeneratedPluginRegistrant.* diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.vscode/cspell.json b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.vscode/cspell.json index 078389e34..85462c77c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.vscode/cspell.json +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/.vscode/cspell.json @@ -131,7 +131,9 @@ "xcworkspace", "xxlg", "xxxlg", - "xxxs" + "xxxs", + "ARGB", + "canonicalized" ], "ignorePaths": [ ".github/workflows/**", @@ -139,4 +141,4 @@ "**/api/lib/src/data/static_news_data.dart", "**/lib/terms_of_service/terms_of_service_mock_text.dart" ] -} +} \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/README.md b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/README.md index 580015bda..1edbeea92 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/README.md +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/README.md @@ -1,3 +1,3 @@ ## Getting Started 🚀 -To get started, see the official documentation at https://flutter.github.io/news_toolkit. +To get started, see the official documentation at https://vgventures.github.io/news_toolkit/. diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/analysis_options.yaml index d9b1825fe..cb0a42be0 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/analysis_options.yaml @@ -1,5 +1,7 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml analyzer: + errors: + document_ignores: ignore exclude: - lib/l10n/* - lib/generated/* diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/analysis_options.yaml index 44fe184a5..b5aa63eef 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/analysis_options.yaml @@ -1,5 +1,7 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml analyzer: + errors: + document_ignores: ignore exclude: - build/** - lib/**/*.g.dart diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/api.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/api.dart index 2fb692e43..4eadca868 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/api.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/api.dart @@ -1,6 +1,3 @@ -/// {{app_name}} API Server-Side Library -library api; - export 'src/data/in_memory_news_data_source.dart' show InMemoryNewsDataSource; export 'src/data/models/models.dart' show diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/client.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/client.dart index 607fb2bd4..cfc9b4f6c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/client.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/client.dart @@ -1,6 +1,3 @@ -/// {{app_name}} API Client-Side Library -library client; - export 'package:news_blocks/news_blocks.dart' show BlockAction, @@ -9,7 +6,6 @@ export 'package:news_blocks/news_blocks.dart' ImageBlock, NewsBlock, NewsBlocksConverter, - PostCategory, PostGridGroupBlock, PostGridTileBlock, PostLargeBlock, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/client/{{#snakeCase}}{{project_name}}{{/snakeCase}}_api_client.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/client/{{#snakeCase}}{{project_name}}{{/snakeCase}}_api_client.dart index cc380a25f..cebd67476 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/client/{{#snakeCase}}{{project_name}}{{/snakeCase}}_api_client.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/client/{{#snakeCase}}{{project_name}}{{/snakeCase}}_api_client.dart @@ -47,10 +47,10 @@ class {{project_name.pascalCase()}}ApiClient { required TokenProvider tokenProvider, http.Client? httpClient, }) : this._( - baseUrl: 'https://{{api_url}}', - httpClient: httpClient, - tokenProvider: tokenProvider, - ); + baseUrl: 'https://{{api_url}}', + httpClient: httpClient, + tokenProvider: tokenProvider, + ); /// Create an instance of [{{project_name.pascalCase()}}ApiClient] that integrates /// with a local instance of the API (http://localhost:8080). @@ -60,25 +60,25 @@ class {{project_name.pascalCase()}}ApiClient { required TokenProvider tokenProvider, http.Client? httpClient, }) : this._( - baseUrl: 'http://localhost:8080', - httpClient: httpClient, - tokenProvider: tokenProvider, - ); + baseUrl: 'http://localhost:8080', + httpClient: httpClient, + tokenProvider: tokenProvider, + ); /// {@macro {{project_name.snakeCase()}}_api_client} {{project_name.pascalCase()}}ApiClient._({ required String baseUrl, required TokenProvider tokenProvider, http.Client? httpClient, - }) : _baseUrl = baseUrl, - _httpClient = httpClient ?? http.Client(), - _tokenProvider = tokenProvider; + }) : _baseUrl = baseUrl, + _httpClient = httpClient ?? http.Client(), + _tokenProvider = tokenProvider; final String _baseUrl; final http.Client _httpClient; final TokenProvider _tokenProvider; - /// GET /api/v1/articles/ + /// GET /api/v1/articles/<id> /// Requests article content metadata. /// /// Supported parameters: @@ -116,7 +116,7 @@ class {{project_name.pascalCase()}}ApiClient { return ArticleResponse.fromJson(body); } - /// GET /api/v1/articles//related + /// GET /api/v1/articles/<id>/related /// Requests related articles. /// /// Supported parameters: @@ -155,18 +155,18 @@ class {{project_name.pascalCase()}}ApiClient { /// Requests news feed metadata. /// /// Supported parameters: - /// * [category] - The desired news [Category]. + /// * [categoryId] - The desired id of news category. /// * [limit] - The number of results to return. /// * [offset] - The (zero-based) offset of the first item /// in the collection to return. Future getFeed({ - Category? category, + String? categoryId, int? limit, int? offset, }) async { final uri = Uri.parse('$_baseUrl/api/v1/feed').replace( queryParameters: { - if (category != null) 'category': category.name, + 'category': ?categoryId, if (limit != null) 'limit': '$limit', if (offset != null) 'offset': '$offset', }, @@ -250,9 +250,9 @@ class {{project_name.pascalCase()}}ApiClient { /// GET /api/v1/search/relevant?q=term /// Requests relevant content based on the provided search [term]. Future relevantSearch({required String term}) async { - final uri = Uri.parse('$_baseUrl/api/v1/search/relevant').replace( - queryParameters: {'q': term}, - ); + final uri = Uri.parse( + '$_baseUrl/api/v1/search/relevant', + ).replace(queryParameters: {'q': term}); final response = await _httpClient.get( uri, headers: await _getRequestHeaders(), @@ -289,9 +289,7 @@ class {{project_name.pascalCase()}}ApiClient { /// POST /api/v1/subscriptions /// Creates a new subscription for the associated user. - Future createSubscription({ - required String subscriptionId, - }) async { + Future createSubscription({required String subscriptionId}) async { final uri = Uri.parse('$_baseUrl/api/v1/subscriptions').replace( queryParameters: {'subscriptionId': subscriptionId}, ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/in_memory_news_data_source.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/in_memory_news_data_source.dart index b6ca99610..97272a1ea 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/in_memory_news_data_source.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/in_memory_news_data_source.dart @@ -44,14 +44,18 @@ class InMemoryNewsDataSource implements NewsDataSource { final result = _newsItems.where((item) => item.post.id == id); if (result.isEmpty) return null; final articleNewsItem = result.first; - final article = (preview - ? articleNewsItem.contentPreview - : articleNewsItem.content) - .toArticle(title: articleNewsItem.post.title, url: articleNewsItem.url); + final article = + (preview ? articleNewsItem.contentPreview : articleNewsItem.content) + .toArticle( + title: articleNewsItem.post.title, + url: articleNewsItem.url, + ); final totalBlocks = article.totalBlocks; final normalizedOffset = math.min(offset, totalBlocks); - final blocks = - article.blocks.sublist(normalizedOffset).take(limit).toList(); + final blocks = article.blocks + .sublist(normalizedOffset) + .take(limit) + .toList(); return Article( title: article.title, blocks: blocks, @@ -102,12 +106,12 @@ class InMemoryNewsDataSource implements NewsDataSource { @override Future getFeed({ - Category category = Category.top, + required String categoryId, int limit = 20, int offset = 0, }) async { final feed = - _newsFeedData[category] ?? const Feed(blocks: [], totalBlocks: 0); + _newsFeedData[categoryId] ?? const Feed(blocks: [], totalBlocks: 0); final totalBlocks = feed.totalBlocks; final normalizedOffset = math.min(offset, totalBlocks); final blocks = feed.blocks.sublist(normalizedOffset).take(limit).toList(); @@ -115,7 +119,7 @@ class InMemoryNewsDataSource implements NewsDataSource { } @override - Future> getCategories() async => _newsFeedData.keys.toList(); + Future> getCategories() async => _categories; @override Future getUser({required String userId}) async { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/article.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/article.g.dart index a50ba09da..c3c0af37b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/article.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/article.g.dart @@ -7,15 +7,15 @@ part of 'article.dart'; // ************************************************************************** Article _$ArticleFromJson(Map json) => Article( - title: json['title'] as String, - blocks: const NewsBlocksConverter().fromJson(json['blocks'] as List), - totalBlocks: (json['totalBlocks'] as num).toInt(), - url: Uri.parse(json['url'] as String), - ); + title: json['title'] as String, + blocks: const NewsBlocksConverter().fromJson(json['blocks'] as List), + totalBlocks: (json['totalBlocks'] as num).toInt(), + url: Uri.parse(json['url'] as String), +); Map _$ArticleToJson(Article instance) => { - 'title': instance.title, - 'blocks': const NewsBlocksConverter().toJson(instance.blocks), - 'totalBlocks': instance.totalBlocks, - 'url': instance.url.toString(), - }; + 'title': instance.title, + 'blocks': const NewsBlocksConverter().toJson(instance.blocks), + 'totalBlocks': instance.totalBlocks, + 'url': instance.url.toString(), +}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/feed.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/feed.g.dart index 20ede9cda..826e61149 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/feed.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/feed.g.dart @@ -7,11 +7,11 @@ part of 'feed.dart'; // ************************************************************************** Feed _$FeedFromJson(Map json) => Feed( - blocks: const NewsBlocksConverter().fromJson(json['blocks'] as List), - totalBlocks: (json['totalBlocks'] as num).toInt(), - ); + blocks: const NewsBlocksConverter().fromJson(json['blocks'] as List), + totalBlocks: (json['totalBlocks'] as num).toInt(), +); Map _$FeedToJson(Feed instance) => { - 'blocks': const NewsBlocksConverter().toJson(instance.blocks), - 'totalBlocks': instance.totalBlocks, - }; + 'blocks': const NewsBlocksConverter().toJson(instance.blocks), + 'totalBlocks': instance.totalBlocks, +}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/subscription.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/subscription.g.dart index b2319bee3..1fbc2d12b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/subscription.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/subscription.g.dart @@ -7,12 +7,13 @@ part of 'subscription.dart'; // ************************************************************************** Subscription _$SubscriptionFromJson(Map json) => Subscription( - id: json['id'] as String, - name: $enumDecode(_$SubscriptionPlanEnumMap, json['name']), - cost: SubscriptionCost.fromJson(json['cost'] as Map), - benefits: - (json['benefits'] as List).map((e) => e as String).toList(), - ); + id: json['id'] as String, + name: $enumDecode(_$SubscriptionPlanEnumMap, json['name']), + cost: SubscriptionCost.fromJson(json['cost'] as Map), + benefits: (json['benefits'] as List) + .map((e) => e as String) + .toList(), +); Map _$SubscriptionToJson(Subscription instance) => { @@ -36,7 +37,4 @@ SubscriptionCost _$SubscriptionCostFromJson(Map json) => ); Map _$SubscriptionCostToJson(SubscriptionCost instance) => - { - 'monthly': instance.monthly, - 'annual': instance.annual, - }; + {'monthly': instance.monthly, 'annual': instance.annual}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/user.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/user.g.dart index 10aa83917..2016a73b2 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/user.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/models/user.g.dart @@ -7,15 +7,14 @@ part of 'user.dart'; // ************************************************************************** User _$UserFromJson(Map json) => User( - id: json['id'] as String, - subscription: - $enumDecode(_$SubscriptionPlanEnumMap, json['subscription']), - ); + id: json['id'] as String, + subscription: $enumDecode(_$SubscriptionPlanEnumMap, json['subscription']), +); Map _$UserToJson(User instance) => { - 'id': instance.id, - 'subscription': _$SubscriptionPlanEnumMap[instance.subscription]!, - }; + 'id': instance.id, + 'subscription': _$SubscriptionPlanEnumMap[instance.subscription]!, +}; const _$SubscriptionPlanEnumMap = { SubscriptionPlan.none: 'none', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/news_data_source.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/news_data_source.dart index 56feae61c..044093daa 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/news_data_source.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/news_data_source.dart @@ -59,8 +59,7 @@ abstract class NewsDataSource { int offset = 0, }); - /// Returns a news [Feed] for the provided [category]. - /// By default [Category.top] is used. + /// Returns a news [Feed] for the provided [categoryId]. /// /// In addition, the feed can be paginated by supplying /// [limit] and [offset]. @@ -69,7 +68,7 @@ abstract class NewsDataSource { /// * [offset] - The (zero-based) offset of the first item /// in the collection to return. Future getFeed({ - Category category = Category.top, + required String categoryId, int limit = 20, int offset = 0, }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/static_news_data.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/static_news_data.dart index 4b2db6d2f..681489c28 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/static_news_data.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/data/static_news_data.dart @@ -5,10 +5,7 @@ const subscriptions = [ Subscription( id: 'dd339fda-33e9-49d0-9eb5-0ccb77eb760f', name: SubscriptionPlan.premium, - cost: SubscriptionCost( - annual: 16200, - monthly: 1499, - ), + cost: SubscriptionCost(annual: 16200, monthly: 1499), benefits: [ 'Ut rhoncus dui vel imperdiet ullamcorper.', 'Proin pellentesque erat et metus fringilla tincidunt.', @@ -18,19 +15,13 @@ const subscriptions = [ Subscription( id: '375af719-c9e0-44c4-be05-4527df45a13d', name: SubscriptionPlan.plus, - cost: SubscriptionCost( - annual: 10800, - monthly: 999, - ), + cost: SubscriptionCost(annual: 10800, monthly: 999), benefits: ['Nunc scelerisque nulla quis urna auctor.'], ), Subscription( id: '34809bc1-28e5-4967-b029-2432638b0dc7', name: SubscriptionPlan.basic, - cost: SubscriptionCost( - annual: 5400, - monthly: 499, - ), + cost: SubscriptionCost(annual: 5400, monthly: 499), benefits: ['Nunc scelerisque nulla quis urna auctor.'], ), ]; @@ -57,14 +48,16 @@ final popularArticles = [ NewsItem( post: PostSmallBlock( id: '5c47495a-608b-4e8b-a7f0-642a02594888', - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'CNN', publishedAt: DateTime(2022, 3, 17), imageUrl: 'https://cdn.cnn.com/cnnnext/dam/assets/220518135103-03-boeing-starliner-pre-launch-0518-super-tease.jpg', - title: 'Boeing makes third attempt to launch its ' + title: + 'Boeing makes third attempt to launch its ' 'Starliner capsule to the ISS', - description: 'Boeing will try yet again Thursday to send the capsule it ' + description: + 'Boeing will try yet again Thursday to send the capsule it ' 'designed to ferry astronauts to and from the International ' 'Space Station on a successful, uncrewed test mission. ' 'After two prior attempts to complete such a mission failed, ' @@ -72,10 +65,11 @@ final popularArticles = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Sean Hollister', publishedAt: DateTime(2022, 3, 17), - title: 'Boeing makes third attempt to launch its ' + title: + 'Boeing makes third attempt to launch its ' 'Starliner capsule to the ISS', imageUrl: 'https://cdn.cnn.com/cnnnext/dam/assets/220518135103-03-boeing-starliner-pre-launch-0518-super-tease.jpg', @@ -93,18 +87,19 @@ final relevantArticles = [ NewsItem( post: PostSmallBlock( id: '781b6a65-0357-45c7-8789-3ee890e43e0e', - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'CNN', publishedAt: DateTime(2022, 5, 20), imageUrl: 'https://cdn.cnn.com/cnnnext/dam/assets/220519121645-01-monkeypox-explainer-super-tease.jpg', title: 'What is monkeypox and its signs and symptoms?', - description: 'Where did monkeypox come from, what are the signs and ' + description: + 'Where did monkeypox come from, what are the signs and ' "symptoms and how worried should you be? Here's what we know.", ), content: [ ArticleIntroductionBlock( - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Sandee LaMotte', publishedAt: DateTime(2022, 5, 20), title: 'What is monkeypox and its signs and symptoms?', @@ -131,12 +126,13 @@ final technologyLargeItems = [ NewsItem( post: PostLargeBlock( id: '499305f6-5096-4051-afda-824dcfc7df23', - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Sean Hollister', publishedAt: DateTime(2022, 4, 19), imageUrl: 'https://cdn.vox-cdn.com/thumbor/OTpmptgr7XcTVAJ27UBvIxl0vrg=/0x146:2040x1214/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/22049166/shollister_201117_4303_0003.0.jpg', - title: 'Nvidia and AMD GPUs are returning to shelves ' + title: + 'Nvidia and AMD GPUs are returning to shelves ' 'and prices are finally falling', action: const NavigateToArticleAction( articleId: '499305f6-5096-4051-afda-824dcfc7df23', @@ -144,10 +140,11 @@ final technologyLargeItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Sean Hollister', publishedAt: DateTime(2022, 4, 19), - title: 'Nvidia and AMD GPUs are returning to shelves ' + title: + 'Nvidia and AMD GPUs are returning to shelves ' 'and prices are finally falling', imageUrl: 'https://cdn.vox-cdn.com/thumbor/OTpmptgr7XcTVAJ27UBvIxl0vrg=/0x146:2040x1214/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/22049166/shollister_201117_4303_0003.0.jpg', @@ -175,7 +172,8 @@ final technologyLargeItems = [ ), const SpacerBlock(spacing: Spacing.large), const TextParagraphBlock( - text: 'Apparently, the breakthrough can also pave the way to a new ' + text: + 'Apparently, the breakthrough can also pave the way to a new ' 'world where humans could communicate with animals and that can ' 'lead to an ecosystem where every living being receives equal ' 'respect in this supernatural power-driven world.', @@ -233,7 +231,8 @@ final technologyLargeItems = [ const TextHeadlineBlock(text: 'Now, comes the big surprise!'), const SpacerBlock(spacing: Spacing.medium), const TextParagraphBlock( - text: 'In the next few months, a machine language will be developed in ' + text: + 'In the next few months, a machine language will be developed in ' 'such a way that farmers indulged in pig farming will communicate ' 'with their swine, cutting down unnecessary chaos between animals ' 'and humans in large farms and slaughterhouses.', @@ -245,7 +244,8 @@ final technologyLargeItems = [ ), const SpacerBlock(spacing: Spacing.medium), const TextCaptionBlock( - text: 'Caption. Et has minim elitr intellegat. Mea aeterno eleifend ' + text: + 'Caption. Et has minim elitr intellegat. Mea aeterno eleifend ' 'antiopam ad, nam no suscipit quaerendum. ' 'At nam minimum ponderum.', color: TextCaptionColor.normal, @@ -260,14 +260,16 @@ final technologyLargeItems = [ TrendingStoryBlock( content: PostSmallBlock( id: '5c47497a-608b-4e9b-a7f0-642a02594900', - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Fall Guys', publishedAt: DateTime(2022, 3, 9), imageUrl: 'https://cdn2.unrealengine.com/preregevent-3840x2160-03-pp-3840x2160-74911d8b9813.jpg', - title: 'Fall Guys: Ultimate Knockout' + title: + 'Fall Guys: Ultimate Knockout' ' free for ALL', - description: 'Welcome to Fall Guys: Free for All! You are invited to ' + description: + 'Welcome to Fall Guys: Free for All! You are invited to ' 'dive and dodge your way to victory in the pantheon of clumsy. ' 'Rookie or pro? Solo or partied up? ' 'Fall Guys delivers ever-evolving, ' @@ -278,10 +280,11 @@ final technologyLargeItems = [ ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Sean Hollister', publishedAt: DateTime(2022, 4, 19), - title: 'Nvidia and AMD GPUs are returning to shelves ' + title: + 'Nvidia and AMD GPUs are returning to shelves ' 'and prices are finally falling', imageUrl: 'https://cdn.vox-cdn.com/thumbor/OTpmptgr7XcTVAJ27UBvIxl0vrg=/0x146:2040x1214/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/22049166/shollister_201117_4303_0003.0.jpg', @@ -299,12 +302,13 @@ final technologyLargeItems = [ relatedArticles: [ PostSmallBlock( id: '2224b86e-60d6-461a-a5d8-e8e71589669f', - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'The Drive', publishedAt: DateTime(2022, 3, 17), imageUrl: 'https://www.thedrive.com/uploads/2022/05/17/asdrf.jpg?auto=webp', - title: 'Leaked Pic Hints Ford F-150 Raptor R ' + title: + 'Leaked Pic Hints Ford F-150 Raptor R ' 'Will Get the Mustang GT500 V8', description: 'A build sheet leaked to Instagram indicates that the upcoming ' @@ -319,7 +323,7 @@ final technologyLargeItems = [ NewsItem( post: PostLargeBlock( id: '7dd29642-2bbd-40ea-80af-99e4381bbabb', - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Jasmine Hicks', publishedAt: DateTime(2022, 6, 2), imageUrl: @@ -332,7 +336,7 @@ final technologyLargeItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Jasmine Hicks', publishedAt: DateTime(2022, 6, 2), title: @@ -344,7 +348,7 @@ final technologyLargeItems = [ ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Jasmine Hicks', publishedAt: DateTime(2022, 6, 2), title: @@ -364,13 +368,14 @@ final technologyMediumItems = [ NewsItem( post: PostMediumBlock( id: 'd62a154f-b69a-4e8c-87ee-a1bdfcec3cfa', - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Victoria Song', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://cdn.vox-cdn.com/thumbor/vztwGP7fw5SuqtFUfNC3WpXOS4U=/0x38:1920x1043/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/23449170/Google_Pixel_Watch_1.png', title: "It's okay if the Pixel Watch only manages a day of battery life", - description: 'A new report claims that Google’s forthcoming Pixel Watch ' + description: + 'A new report claims that Google’s forthcoming Pixel Watch ' 'will “only” get 24 hours of battery life. While that’s nowhere ' 'close to fitness trackers, it’s actually pretty standard for a ' 'full-featured flagship smartwatch.', @@ -380,7 +385,7 @@ final technologyMediumItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Victoria Song', publishedAt: DateTime(2022, 6, 2), title: @@ -391,7 +396,7 @@ final technologyMediumItems = [ ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Victoria Song', publishedAt: DateTime(2022, 6, 2), title: @@ -411,14 +416,16 @@ final technologySmallItems = [ NewsItem( post: PostSmallBlock( id: '36f4a017-d099-4fce-8727-1d9ca6a0398c', - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Tom Phillips', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://assets.reedpopcdn.com/stray_XlfRQmc.jpg/BROK/thumbnail/1600x900/format/jpg/quality/80/stray_XlfRQmc.jpg', - title: 'Stray launches next month, included in pricier PlayStation ' + title: + 'Stray launches next month, included in pricier PlayStation ' 'Plus tiers on day one', - description: "Stray, everyone's favourite upcoming cyberpunk cat game, " + description: + "Stray, everyone's favourite upcoming cyberpunk cat game, " 'launches for PC, PlayStation 4 and PS5 on 19th July.', action: const NavigateToArticleAction( articleId: '36f4a017-d099-4fce-8727-1d9ca6a0398c', @@ -426,23 +433,25 @@ final technologySmallItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Tom Phillips', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://assets.reedpopcdn.com/stray_XlfRQmc.jpg/BROK/thumbnail/1600x900/format/jpg/quality/80/stray_XlfRQmc.jpg', - title: 'Stray launches next month, included in pricier PlayStation ' + title: + 'Stray launches next month, included in pricier PlayStation ' 'Plus tiers on day one', ), ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Tom Phillips', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://assets.reedpopcdn.com/stray_XlfRQmc.jpg/BROK/thumbnail/1600x900/format/jpg/quality/80/stray_XlfRQmc.jpg', - title: 'Stray launches next month, included in pricier PlayStation ' + title: + 'Stray launches next month, included in pricier PlayStation ' 'Plus tiers on day one', ), ], @@ -453,12 +462,14 @@ final technologySmallItems = [ NewsItem( post: PostSmallBlock( id: 'f903c34b-e4a7-4db1-8945-94f9fb7c7284', - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Mike Andronico', publishedAt: DateTime(2022, 6, 2), - title: 'Walmart has a big PS5 restock today — ' + title: + 'Walmart has a big PS5 restock today — ' 'here’s how to have the best shot', - description: 'Walmart will have the PS5 in stock today exclusively for ' + description: + 'Walmart will have the PS5 in stock today exclusively for ' "Walmart+ members. Here's how to have the best shot at scoring this " 'still-elusive console.', action: const NavigateToArticleAction( @@ -468,20 +479,22 @@ final technologySmallItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Victoria Song', publishedAt: DateTime(2022, 6, 2), - title: 'Walmart has a big PS5 restock today — ' + title: + 'Walmart has a big PS5 restock today — ' 'here’s how to have the best shot', isPremium: true, ), ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: _technologyCategory.id, author: 'Victoria Song', publishedAt: DateTime(2022, 6, 2), - title: 'Walmart has a big PS5 restock today — ' + title: + 'Walmart has a big PS5 restock today — ' 'here’s how to have the best shot', ), ], @@ -503,12 +516,13 @@ final sportsLargeItems = [ NewsItem( post: PostLargeBlock( id: 'e24e8c44-fcba-4312-92bc-4da4c83e1f4b', - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Peter Brody', publishedAt: DateTime(2022, 6, 3), imageUrl: 'https://cdn.vox-cdn.com/thumbor/a3ES9_uJ0NKxcWTH3xrtM0FulHE=/0x0:3482x1823/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/23605079/usa_today_18419260.jpg', - title: 'Yankees win, 2-1, as Jameson Taillon carries ' + title: + 'Yankees win, 2-1, as Jameson Taillon carries ' 'perfect game into eighth', description: 'Taillon took us tantalizingly close to history, and Rizzo saved the ' @@ -519,23 +533,25 @@ final sportsLargeItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Peter Brody', publishedAt: DateTime(2022, 6, 3), imageUrl: 'https://cdn.vox-cdn.com/thumbor/a3ES9_uJ0NKxcWTH3xrtM0FulHE=/0x0:3482x1823/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/23605079/usa_today_18419260.jpg', - title: 'Yankees win, 2-1, as Jameson Taillon carries ' + title: + 'Yankees win, 2-1, as Jameson Taillon carries ' 'perfect game into eighth', ), ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Peter Brody', publishedAt: DateTime(2022, 6, 3), imageUrl: 'https://cdn.vox-cdn.com/thumbor/a3ES9_uJ0NKxcWTH3xrtM0FulHE=/0x0:3482x1823/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/23605079/usa_today_18419260.jpg', - title: 'Yankees win, 2-1, as Jameson Taillon carries ' + title: + 'Yankees win, 2-1, as Jameson Taillon carries ' 'perfect game into eighth', ), ], @@ -550,12 +566,13 @@ final sportsMediumItems = [ NewsItem( post: PostMediumBlock( id: '82c49bf1-946d-4920-a801-302291f367b5', - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Tom Dierberger', publishedAt: DateTime(2022, 5, 5), imageUrl: 'https://www.nbcsports.com/sites/rsnunited/files/styles/metatags_opengraph/public/article/hero/pat-bev-ja-morant-USA.jpg', - title: 'Patrick Beverley throws shade at Warriors ' + title: + 'Patrick Beverley throws shade at Warriors ' 'for Ja Morant struggles - NBC Sports', description: 'Patrick Beverley is no longer participating in the NBA playoffs, ' @@ -568,35 +585,38 @@ final sportsMediumItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Tom Dierberger', publishedAt: DateTime(2022, 5, 22), imageUrl: 'https://www.nbcsports.com/sites/rsnunited/files/styles/metatags_opengraph/public/article/hero/pat-bev-ja-morant-USA.jpg', - title: 'Patrick Beverley throws shade at Warriors ' + title: + 'Patrick Beverley throws shade at Warriors ' 'for Ja Morant struggles - NBC Sports', ), ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Tom Dierberger', publishedAt: DateTime(2022, 5, 22), imageUrl: 'https://www.nbcsports.com/sites/rsnunited/files/styles/metatags_opengraph/public/article/hero/pat-bev-ja-morant-USA.jpg', - title: 'Patrick Beverley throws shade at Warriors ' + title: + 'Patrick Beverley throws shade at Warriors ' 'for Ja Morant struggles - NBC Sports', ), ], relatedArticles: [ PostSmallBlock( id: 'b77f8854-7483-4353-ac91-5a8a10e576b0', - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Bleacher Report', publishedAt: DateTime(2022, 3, 17), imageUrl: 'https://img.bleacherreport.net/img/images/photos/003/921/731/fb79b6bfb3d129622735c307266f225c_crop_exact.jpg?w=1200&h=1200&q=75', - title: '1 Trade Every Lottery Team Must Consider ' + title: + '1 Trade Every Lottery Team Must Consider ' 'If It Wins No. 1 Draft Pick', description: 'The 2022 NBA draft lottery is Tuesday, and it could change ' @@ -616,14 +636,16 @@ final sportsSmallItems = [ NewsItem( post: PostSmallBlock( id: 'b1e70b22-b7a3-4b07-808d-3735fe7131af', - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Jasmyn Wimbish', publishedAt: DateTime(2022, 6, 3), imageUrl: 'https://sportshub.cbsistatic.com/i/r/2022/06/03/ff016f39-ad02-4dd9-8237-f1c7d3b1b5a6/thumbnail/1200x675/ed10f396f5f3cdf4b6b912e44fdf2597/untitled-design-2022-06-02t212223-267.png', - title: 'NBA commissioner Adam Silver talks about league expansion, ' + title: + 'NBA commissioner Adam Silver talks about league expansion, ' 'potential All-NBA changes ahead of Finals', - description: 'Silver touched on a number of topics during his ' + description: + 'Silver touched on a number of topics during his ' 'annual press conference.', action: const NavigateToArticleAction( articleId: 'b1e70b22-b7a3-4b07-808d-3735fe7131af', @@ -631,23 +653,25 @@ final sportsSmallItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Jasmyn Wimbish', publishedAt: DateTime(2022, 6, 3), imageUrl: 'https://sportshub.cbsistatic.com/i/r/2022/06/03/ff016f39-ad02-4dd9-8237-f1c7d3b1b5a6/thumbnail/1200x675/ed10f396f5f3cdf4b6b912e44fdf2597/untitled-design-2022-06-02t212223-267.png', - title: 'NBA commissioner Adam Silver talks about league expansion, ' + title: + 'NBA commissioner Adam Silver talks about league expansion, ' 'potential All-NBA changes ahead of Finals', ), ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Jasmyn Wimbish', publishedAt: DateTime(2022, 6, 3), imageUrl: 'https://sportshub.cbsistatic.com/i/r/2022/06/03/ff016f39-ad02-4dd9-8237-f1c7d3b1b5a6/thumbnail/1200x675/ed10f396f5f3cdf4b6b912e44fdf2597/untitled-design-2022-06-02t212223-267.png', - title: 'NBA commissioner Adam Silver talks about league expansion, ' + title: + 'NBA commissioner Adam Silver talks about league expansion, ' 'potential All-NBA changes ahead of Finals', ), ], @@ -658,12 +682,14 @@ final sportsSmallItems = [ NewsItem( post: PostSmallBlock( id: '7f03f6bf-011f-49cf-88b8-08c79a21745c', - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Adam Rowe', publishedAt: DateTime(2022, 6, 3), - title: 'Five-Star International Recruit Tyrese Proctor will reclassify ' + title: + 'Five-Star International Recruit Tyrese Proctor will reclassify ' 'up to 2022 and enroll at Duke this summer', - description: 'Duke will be getting one of their top Class of 2023 ' + description: + 'Duke will be getting one of their top Class of 2023 ' 'recruits, Tyrese Proctor, a year earlier than originally expected', action: const NavigateToArticleAction( articleId: '7f03f6bf-011f-49cf-88b8-08c79a21745c', @@ -671,19 +697,21 @@ final sportsSmallItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Adam Rowe', publishedAt: DateTime(2022, 6, 3), - title: 'Five-Star International Recruit Tyrese Proctor will reclassify ' + title: + 'Five-Star International Recruit Tyrese Proctor will reclassify ' 'up to 2022 and enroll at Duke this summer', ), ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.sports, + categoryId: _sportsCategory.id, author: 'Adam Rowe', publishedAt: DateTime(2022, 6, 3), - title: 'Five-Star International Recruit Tyrese Proctor will reclassify ' + title: + 'Five-Star International Recruit Tyrese Proctor will reclassify ' 'up to 2022 and enroll at Duke this summer', ), ], @@ -705,13 +733,14 @@ final healthLargeItems = [ NewsItem( post: PostLargeBlock( id: 'f237463b-f4d8-4b23-a468-d448a446b03b', - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Neuroscience News', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://neurosciencenews.com/files/2022/06/asd-genetics-neurosicnes-public.jpg', title: 'Broad Spectrum of Autism Depends on Spectrum of Genetic Factors', - description: 'Genetic factors influence the severity of symptoms in ' + description: + 'Genetic factors influence the severity of symptoms in ' 'children on the autism spectrum, and different genetic factors were ' 'associated with different symptoms of ASD.', action: const NavigateToArticleAction( @@ -720,7 +749,7 @@ final healthLargeItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Neuroscience News', publishedAt: DateTime(2022, 6, 2), imageUrl: @@ -731,7 +760,7 @@ final healthLargeItems = [ ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Neuroscience News', publishedAt: DateTime(2022, 6, 2), imageUrl: @@ -740,9 +769,7 @@ final healthLargeItems = [ 'Broad Spectrum of Autism Depends on Spectrum of Genetic Factors', ), ], - url: Uri.parse( - 'https://neurosciencenews.com/asd-genetics-symptoms-20731/', - ), + url: Uri.parse('https://neurosciencenews.com/asd-genetics-symptoms-20731/'), ), ]; @@ -751,14 +778,16 @@ final healthMediumItems = [ NewsItem( post: PostMediumBlock( id: '057d4de4-a7c5-4dc3-b231-33052bcea53d', - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Neuroscience News', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://neurosciencenews.com/files/2022/06/amd-eye-supplements-neurosinces-public.jpg', - title: 'Study Confirms Benefit of Supplements for Slowing Age-Related ' + title: + 'Study Confirms Benefit of Supplements for Slowing Age-Related ' 'Macular Degeneration', - description: 'The AREDS2 dietary supplement that substitutes ' + description: + 'The AREDS2 dietary supplement that substitutes ' 'antioxidants lutein and zeaxanthin for beta-carotene reduces the ' 'risk of age-related macular degeneration progression, a new study ' 'reveals.', @@ -768,29 +797,29 @@ final healthMediumItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Neuroscience News', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://neurosciencenews.com/files/2022/06/amd-eye-supplements-neurosinces-public.jpg', - title: 'Study Confirms Benefit of Supplements for Slowing Age-Related ' + title: + 'Study Confirms Benefit of Supplements for Slowing Age-Related ' 'Macular Degeneration', ), ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Neuroscience News', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://neurosciencenews.com/files/2022/06/amd-eye-supplements-neurosinces-public.jpg', - title: 'Study Confirms Benefit of Supplements for Slowing Age-Related ' + title: + 'Study Confirms Benefit of Supplements for Slowing Age-Related ' 'Macular Degeneration', ), ], - url: Uri.parse( - 'https://neurosciencenews.com/asd-genetics-symptoms-20731/', - ), + url: Uri.parse('https://neurosciencenews.com/asd-genetics-symptoms-20731/'), ), ]; @@ -799,13 +828,15 @@ final healthSmallItems = [ NewsItem( post: PostSmallBlock( id: 'b1fc2ffc-eb02-42ce-af65-79702172a987', - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Northwestern University', publishedAt: DateTime(2022, 5, 4), imageUrl: 'https://scitechdaily.com/images/Ear-Hearing-Concept.jpg', - title: 'Restoring Hearing: New Tool To Create Ear Hair Cells ' + title: + 'Restoring Hearing: New Tool To Create Ear Hair Cells ' 'Lost Due to Aging or Noise', - description: '‘We have overcome a major hurdle’ to restore hearing, ' + description: + '‘We have overcome a major hurdle’ to restore hearing, ' 'investigators say. Gene discovery allows the production of inner ' 'or outer ear hair cells, death of outer hair cells due to aging ' 'or noise cause most hearing loss...', @@ -815,33 +846,36 @@ final healthSmallItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Northwestern University', publishedAt: DateTime(2022, 5, 4), imageUrl: 'https://scitechdaily.com/images/Ear-Hearing-Concept.jpg', - title: 'Restoring Hearing: New Tool To Create Ear Hair Cells ' + title: + 'Restoring Hearing: New Tool To Create Ear Hair Cells ' 'Lost Due to Aging or Noise', ), ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Northwestern University', publishedAt: DateTime(2022, 5, 4), imageUrl: 'https://scitechdaily.com/images/Ear-Hearing-Concept.jpg', - title: 'Restoring Hearing: New Tool To Create Ear Hair Cells ' + title: + 'Restoring Hearing: New Tool To Create Ear Hair Cells ' 'Lost Due to Aging or Noise', ), ], relatedArticles: [ PostSmallBlock( id: '7a5661d7-32a3-4b05-9fe2-7bc5ff8ea904', - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'New York Times', publishedAt: DateTime(2022, 3, 17), imageUrl: 'https://static01.nyt.com/images/2022/05/17/science/00kidney1/00kidney1-facebookJumbo.jpg', - title: 'Targeting the Uneven Burden of Kidney Disease ' + title: + 'Targeting the Uneven Burden of Kidney Disease ' 'on Black Americans', description: 'New treatments aim for a gene variant causing the illness in ' @@ -856,12 +890,14 @@ final healthSmallItems = [ NewsItem( post: PostSmallBlock( id: '67c36008-42f3-4ed3-9bcc-2c96acaa27d3', - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Gabby Landsverk', publishedAt: DateTime(2022, 6, 2), - title: 'Keto and Mediterranean diets both help manage blood sugar, ' + title: + 'Keto and Mediterranean diets both help manage blood sugar, ' 'but keto may have more side effects, according to research', - description: 'A high-fat ketogenic diet and a high-fiber Mediterranean ' + description: + 'A high-fat ketogenic diet and a high-fiber Mediterranean ' 'diet may be equally effective for balancing blood sugar levels, ' 'according to a study published May 31 in the American Journal of ' 'Clinical Nutrition.', @@ -871,19 +907,21 @@ final healthSmallItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Gabby Landsverk', publishedAt: DateTime(2022, 6, 2), - title: 'Keto and Mediterranean diets both help manage blood sugar, ' + title: + 'Keto and Mediterranean diets both help manage blood sugar, ' 'but keto may have more side effects, according to research', ), ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.health, + categoryId: _healthCategory.id, author: 'Gabby Landsverk', publishedAt: DateTime(2022, 6, 2), - title: 'Keto and Mediterranean diets both help manage blood sugar, ' + title: + 'Keto and Mediterranean diets both help manage blood sugar, ' 'but keto may have more side effects, according to research', ), ], @@ -906,13 +944,14 @@ final scienceLargeItems = [ NewsItem( post: PostLargeBlock( id: 'f6e66eb4-add9-4181-bf1e-ce09c629287d', - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Megan Marples and Ashley Strickland', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://neurosciencenews.com/files/2022/06/asd-genetics-neurosicnes-public.jpg', title: 'A rare, 5-planet alignment will take over the sky this month', - description: 'Mercury, Venus, Mars, Jupiter and Saturn will align in the ' + description: + 'Mercury, Venus, Mars, Jupiter and Saturn will align in the ' 'month of June, with the waning crescent moon making a special ' 'appearance on June 24.', action: const NavigateToArticleAction( @@ -921,7 +960,7 @@ final scienceLargeItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Megan Marples and Ashley Strickland', publishedAt: DateTime(2022, 6, 2), imageUrl: @@ -931,7 +970,7 @@ final scienceLargeItems = [ ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Megan Marples and Ashley Strickland', publishedAt: DateTime(2022, 6, 2), imageUrl: @@ -950,13 +989,14 @@ final scienceMediumItems = [ NewsItem( post: PostMediumBlock( id: '506271f9-394e-48e4-a6d8-9d438f561532', - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Jeff Foust', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://spacenews.com/wp-content/uploads/2021/11/crew2-depature.jpg', title: 'NASA to buy five additional Crew Dragon flights', - description: 'NASA is planning to purchase five more Crew Dragon ' + description: + 'NASA is planning to purchase five more Crew Dragon ' 'missions from SpaceX, a move the agency says is needed to ensure ' 'long-term access to the station.', action: const NavigateToArticleAction( @@ -965,7 +1005,7 @@ final scienceMediumItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Jeff Foust', publishedAt: DateTime(2022, 6, 2), imageUrl: @@ -975,7 +1015,7 @@ final scienceMediumItems = [ ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Jeff Foust', publishedAt: DateTime(2022, 6, 2), imageUrl: @@ -994,13 +1034,14 @@ final scienceSmallItems = [ NewsItem( post: PostSmallBlock( id: '1273746e-900d-45d9-a4f3-acbb462de797', - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Tomasz Nowakowski', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://scx2.b-cdn.net/gfx/news/2022/super-earth-exoplanet.jpg', title: 'Super-Earth exoplanet orbiting nearby star discovered', - description: 'An international team of astronomers reports the discovery ' + description: + 'An international team of astronomers reports the discovery ' 'of a new super-Earth exoplanet orbiting a nearby M-dwarf star ' 'known as Ross 508.', action: const NavigateToArticleAction( @@ -1009,7 +1050,7 @@ final scienceSmallItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Tomasz Nowakowski', publishedAt: DateTime(2022, 6, 2), imageUrl: @@ -1019,7 +1060,7 @@ final scienceSmallItems = [ ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Tomasz Nowakowski', publishedAt: DateTime(2022, 6, 2), imageUrl: @@ -1034,11 +1075,12 @@ final scienceSmallItems = [ NewsItem( post: PostSmallBlock( id: '52c74e71-36b3-45aa-bc06-f50b1d2631fa', - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Phil Plait', publishedAt: DateTime(2022, 6, 2), title: 'Are supermassive black holes killing their host galaxies?', - description: 'Why are young galaxies in the distant Universe dying? ' + description: + 'Why are young galaxies in the distant Universe dying? ' 'Their supermassive black holes may be killing them.', action: const NavigateToArticleAction( articleId: '52c74e71-36b3-45aa-bc06-f50b1d2631fa', @@ -1046,7 +1088,7 @@ final scienceSmallItems = [ ), content: [ ArticleIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Phil Plait', publishedAt: DateTime(2022, 6, 2), title: 'Are supermassive black holes killing their host galaxies?', @@ -1054,7 +1096,7 @@ final scienceSmallItems = [ ], contentPreview: [ ArticleIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Phil Plait', publishedAt: DateTime(2022, 6, 2), title: 'Are supermassive black holes killing their host galaxies?', @@ -1071,7 +1113,7 @@ final scienceVideoItems = [ NewsItem( post: PostGridTileBlock( id: '384a15ff-a50e-46d5-96a7-8864facdcc48', - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Loren Grush', publishedAt: DateTime(2022, 5, 6), imageUrl: @@ -1083,19 +1125,21 @@ final scienceVideoItems = [ articleId: '384a15ff-a50e-46d5-96a7-8864facdcc48', ), ), - content: const [ + content: [ VideoIntroductionBlock( - category: PostCategory.science, - title: 'SpaceX successfully returns four astronauts from the ' + categoryId: _scienceCategory.id, + title: + 'SpaceX successfully returns four astronauts from the ' 'International Space Station', videoUrl: 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', ), ], - contentPreview: const [ + contentPreview: [ VideoIntroductionBlock( - category: PostCategory.science, - title: 'SpaceX successfully returns four astronauts from the ' + categoryId: _scienceCategory.id, + title: + 'SpaceX successfully returns four astronauts from the ' 'International Space Station', videoUrl: 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', @@ -1104,12 +1148,13 @@ final scienceVideoItems = [ relatedArticles: [ PostSmallBlock( id: 'bfd5aa62-4c50-40c6-9aef-5511535c7b68', - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'SciTechDaily', publishedAt: DateTime(2022, 3, 17), imageUrl: 'https://scitechdaily.com/images/Sample-of-Hypatia-Stone-From-Outside-Solar-System.jpg', - title: 'Extraterrestrial Stone Could Be First Evidence ' + title: + 'Extraterrestrial Stone Could Be First Evidence ' 'on Earth of Supernova Ia Explosion', description: 'New chemistry ‘forensics’ indicates that the stone named Hypatia ' @@ -1126,7 +1171,7 @@ final scienceVideoItems = [ NewsItem( post: PostGridTileBlock( id: '13e448bb-cd26-4ae0-b138-4a67067f7a93', - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'Daniel Strain', publishedAt: DateTime(2022, 5, 6), imageUrl: @@ -1138,19 +1183,21 @@ final scienceVideoItems = [ articleId: '13e448bb-cd26-4ae0-b138-4a67067f7a93', ), ), - content: const [ + content: [ VideoIntroductionBlock( - category: PostCategory.science, - title: 'A surging glow in a distant galaxy could change ' + categoryId: _scienceCategory.id, + title: + 'A surging glow in a distant galaxy could change ' 'the way we look at black holes', videoUrl: 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', ), ], - contentPreview: const [ + contentPreview: [ VideoIntroductionBlock( - category: PostCategory.science, - title: 'A surging glow in a distant galaxy could change ' + categoryId: _scienceCategory.id, + title: + 'A surging glow in a distant galaxy could change ' 'the way we look at black holes', videoUrl: 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', @@ -1163,20 +1210,21 @@ final scienceVideoItems = [ NewsItem( post: PostGridTileBlock( id: '842e3193-86d2-4069-a7e6-f769faa6f970', - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'SciTechDaily', publishedAt: DateTime(2022, 5, 5), imageUrl: 'https://scitechdaily.com/images/Qubit-Platform-Single-Electron-on-Solid-Neon.jpg', - title: 'The Quest for an Ideal Quantum Bit: New Qubit Breakthrough Could ' + title: + 'The Quest for an Ideal Quantum Bit: New Qubit Breakthrough Could ' 'Revolutionize Quantum Computing', action: const NavigateToVideoArticleAction( articleId: '842e3193-86d2-4069-a7e6-f769faa6f970', ), ), - content: const [ + content: [ VideoIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, title: 'The Quest for an Ideal Quantum Bit: New Qubit Breakthrough Could ' 'Revolutionize Quantum Computing', @@ -1184,9 +1232,9 @@ final scienceVideoItems = [ 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', ), ], - contentPreview: const [ + contentPreview: [ VideoIntroductionBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, title: 'The Quest for an Ideal Quantum Bit: New Qubit Breakthrough Could ' 'Revolutionize Quantum Computing', @@ -1201,29 +1249,32 @@ final scienceVideoItems = [ NewsItem( post: PostGridTileBlock( id: '1f79da6f-64cb-430a-b7b2-2318d23b719f', - category: PostCategory.science, + categoryId: _scienceCategory.id, author: 'SciTechDaily', publishedAt: DateTime(2022, 5, 4), imageUrl: 'https://scitechdaily.com/images/Black-Hole-Sonification.gif', - title: 'Hear What a Black Hole Sounds Like – New NASA Black Hole ' + title: + 'Hear What a Black Hole Sounds Like – New NASA Black Hole ' 'Sonifications With a Remix', action: const NavigateToVideoArticleAction( articleId: '1f79da6f-64cb-430a-b7b2-2318d23b719f', ), ), - content: const [ + content: [ VideoIntroductionBlock( - category: PostCategory.science, - title: 'Hear What a Black Hole Sounds Like – New NASA Black Hole ' + categoryId: _scienceCategory.id, + title: + 'Hear What a Black Hole Sounds Like – New NASA Black Hole ' 'Sonifications With a Remix', videoUrl: 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', ), ], - contentPreview: const [ + contentPreview: [ VideoIntroductionBlock( - category: PostCategory.science, - title: 'Hear What a Black Hole Sounds Like – New NASA Black Hole ' + categoryId: _scienceCategory.id, + title: + 'Hear What a Black Hole Sounds Like – New NASA Black Hole ' 'Sonifications With a Remix', videoUrl: 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', @@ -1244,30 +1295,24 @@ final topNewsFeedBlocks = [ const SpacerBlock(spacing: Spacing.small), const SectionHeaderBlock( title: 'Technology', - action: NavigateToFeedCategoryAction( - category: Category.technology, - ), + action: NavigateToFeedCategoryAction(category: _technologyCategory), ), technologyLargeItems.last.post, const BannerAdBlock(size: BannerAdSize.large), const SpacerBlock(spacing: Spacing.small), const SectionHeaderBlock( title: 'Science Videos', - action: NavigateToFeedCategoryAction( - category: Category.science, - ), + action: NavigateToFeedCategoryAction(category: _scienceCategory), ), PostGridGroupBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, tiles: [...scienceVideoItems.map((e) => e.post).cast()], ), const SpacerBlock(spacing: Spacing.large), const DividerHorizontalBlock(), const SectionHeaderBlock( title: 'Sports', - action: NavigateToFeedCategoryAction( - category: Category.sports, - ), + action: NavigateToFeedCategoryAction(category: _sportsCategory), ), sportsMediumItems.first.post, const SpacerBlock(spacing: Spacing.medium), @@ -1275,9 +1320,7 @@ final topNewsFeedBlocks = [ const SpacerBlock(spacing: Spacing.small), const SectionHeaderBlock( title: 'Health', - action: NavigateToFeedCategoryAction( - category: Category.health, - ), + action: NavigateToFeedCategoryAction(category: _healthCategory), ), healthSmallItems.first.post, const SpacerBlock(spacing: Spacing.small), @@ -1368,37 +1411,41 @@ final scienceFeedBlocks = [ const SpacerBlock(spacing: Spacing.small), const SectionHeaderBlock(title: 'Science Videos'), PostGridGroupBlock( - category: PostCategory.science, + categoryId: _scienceCategory.id, tiles: [...scienceVideoItems.map((e) => e.post).cast()], ), const SpacerBlock(spacing: Spacing.medium), ]; List get _newsItems { - return [ - ...technologyItems, - ...sportsItems, - ...healthItems, - ...scienceItems, - ]; + return [...technologyItems, ...sportsItems, ...healthItems, ...scienceItems]; } -final _newsFeedData = { - Category.top: topNewsFeedBlocks.toFeed(), - Category.technology: technologyFeedBlocks.toFeed(), - Category.sports: sportsFeedBlocks.toFeed(), - Category.health: healthFeedBlocks.toFeed(), - Category.science: scienceFeedBlocks.toFeed(), +final _newsFeedData = { + _topCategory.id: topNewsFeedBlocks.toFeed(), + _technologyCategory.id: technologyFeedBlocks.toFeed(), + _sportsCategory.id: sportsFeedBlocks.toFeed(), + _healthCategory.id: healthFeedBlocks.toFeed(), + _scienceCategory.id: scienceFeedBlocks.toFeed(), }; +const _topCategory = Category(id: 'top', name: 'Top'); +const _sportsCategory = Category(id: 'sports', name: 'Sports'); +const _technologyCategory = Category(id: 'technology', name: 'Technology'); +const _healthCategory = Category(id: 'health', name: 'Health'); +const _scienceCategory = Category(id: 'science', name: 'Science'); + +const List _categories = [ + _topCategory, + _sportsCategory, + _technologyCategory, + _healthCategory, + _scienceCategory, +]; + extension on List { Feed toFeed() => Feed(blocks: this, totalBlocks: length); Article toArticle({required String title, required Uri url}) { - return Article( - title: title, - blocks: this, - totalBlocks: length, - url: url, - ); + return Article(title: title, blocks: this, totalBlocks: length, url: url); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/article_response/article_response.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/article_response/article_response.dart index 05aadda02..adf882911 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/article_response/article_response.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/article_response/article_response.dart @@ -47,11 +47,11 @@ class ArticleResponse extends Equatable { @override List get props => [ - title, - content, - totalCount, - url, - isPremium, - isPreview, - ]; + title, + content, + totalCount, + url, + isPremium, + isPreview, + ]; } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/categories_response/categories_response.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/categories_response/categories_response.dart index e1456eecc..95d778739 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/categories_response/categories_response.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/categories_response/categories_response.dart @@ -7,7 +7,7 @@ part 'categories_response.g.dart'; /// {@template categories_response} /// A news categories response object which contains available news categories. /// {@endtemplate} -@JsonSerializable() +@JsonSerializable(explicitToJson: true) class CategoriesResponse extends Equatable { /// {@macro categories_response} const CategoriesResponse({required this.categories}); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/categories_response/categories_response.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/categories_response/categories_response.g.dart index 51719c624..6e8f8c0b0 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/categories_response/categories_response.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/categories_response/categories_response.g.dart @@ -9,22 +9,11 @@ part of 'categories_response.dart'; CategoriesResponse _$CategoriesResponseFromJson(Map json) => CategoriesResponse( categories: (json['categories'] as List) - .map((e) => $enumDecode(_$CategoryEnumMap, e)) + .map((e) => Category.fromJson(e as Map)) .toList(), ); Map _$CategoriesResponseToJson(CategoriesResponse instance) => { - 'categories': - instance.categories.map((e) => _$CategoryEnumMap[e]!).toList(), + 'categories': instance.categories.map((e) => e.toJson()).toList(), }; - -const _$CategoryEnumMap = { - Category.business: 'business', - Category.entertainment: 'entertainment', - Category.top: 'top', - Category.health: 'health', - Category.science: 'science', - Category.sports: 'sports', - Category.technology: 'technology', -}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/current_user_response/current_user_response.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/current_user_response/current_user_response.g.dart index 082947ef2..1f0703791 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/current_user_response/current_user_response.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/current_user_response/current_user_response.g.dart @@ -12,7 +12,5 @@ CurrentUserResponse _$CurrentUserResponseFromJson(Map json) => ); Map _$CurrentUserResponseToJson( - CurrentUserResponse instance) => - { - 'user': instance.user.toJson(), - }; + CurrentUserResponse instance, +) => {'user': instance.user.toJson()}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/feed_response/feed_response.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/feed_response/feed_response.g.dart index e18aff7ef..319529930 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/feed_response/feed_response.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/feed_response/feed_response.g.dart @@ -7,9 +7,9 @@ part of 'feed_response.dart'; // ************************************************************************** FeedResponse _$FeedResponseFromJson(Map json) => FeedResponse( - feed: const NewsBlocksConverter().fromJson(json['feed'] as List), - totalCount: (json['totalCount'] as num).toInt(), - ); + feed: const NewsBlocksConverter().fromJson(json['feed'] as List), + totalCount: (json['totalCount'] as num).toInt(), +); Map _$FeedResponseToJson(FeedResponse instance) => { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/popular_search_response/popular_search_response.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/popular_search_response/popular_search_response.g.dart index 6f48f41c9..9a8366e86 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/popular_search_response/popular_search_response.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/popular_search_response/popular_search_response.g.dart @@ -7,16 +7,15 @@ part of 'popular_search_response.dart'; // ************************************************************************** PopularSearchResponse _$PopularSearchResponseFromJson( - Map json) => - PopularSearchResponse( - articles: const NewsBlocksConverter().fromJson(json['articles'] as List), - topics: - (json['topics'] as List).map((e) => e as String).toList(), - ); + Map json, +) => PopularSearchResponse( + articles: const NewsBlocksConverter().fromJson(json['articles'] as List), + topics: (json['topics'] as List).map((e) => e as String).toList(), +); Map _$PopularSearchResponseToJson( - PopularSearchResponse instance) => - { - 'articles': const NewsBlocksConverter().toJson(instance.articles), - 'topics': instance.topics, - }; + PopularSearchResponse instance, +) => { + 'articles': const NewsBlocksConverter().toJson(instance.articles), + 'topics': instance.topics, +}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/related_articles_response/related_articles_response.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/related_articles_response/related_articles_response.g.dart index ccb810e7f..d4a09c6ab 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/related_articles_response/related_articles_response.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/related_articles_response/related_articles_response.g.dart @@ -7,17 +7,19 @@ part of 'related_articles_response.dart'; // ************************************************************************** RelatedArticlesResponse _$RelatedArticlesResponseFromJson( - Map json) => - RelatedArticlesResponse( - relatedArticles: - const NewsBlocksConverter().fromJson(json['relatedArticles'] as List), - totalCount: (json['totalCount'] as num).toInt(), - ); + Map json, +) => RelatedArticlesResponse( + relatedArticles: const NewsBlocksConverter().fromJson( + json['relatedArticles'] as List, + ), + totalCount: (json['totalCount'] as num).toInt(), +); Map _$RelatedArticlesResponseToJson( - RelatedArticlesResponse instance) => - { - 'relatedArticles': - const NewsBlocksConverter().toJson(instance.relatedArticles), - 'totalCount': instance.totalCount, - }; + RelatedArticlesResponse instance, +) => { + 'relatedArticles': const NewsBlocksConverter().toJson( + instance.relatedArticles, + ), + 'totalCount': instance.totalCount, +}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/relevant_search_response/relevant_search_response.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/relevant_search_response/relevant_search_response.g.dart index 16083d5a1..beb590005 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/relevant_search_response/relevant_search_response.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/relevant_search_response/relevant_search_response.g.dart @@ -7,16 +7,15 @@ part of 'relevant_search_response.dart'; // ************************************************************************** RelevantSearchResponse _$RelevantSearchResponseFromJson( - Map json) => - RelevantSearchResponse( - articles: const NewsBlocksConverter().fromJson(json['articles'] as List), - topics: - (json['topics'] as List).map((e) => e as String).toList(), - ); + Map json, +) => RelevantSearchResponse( + articles: const NewsBlocksConverter().fromJson(json['articles'] as List), + topics: (json['topics'] as List).map((e) => e as String).toList(), +); Map _$RelevantSearchResponseToJson( - RelevantSearchResponse instance) => - { - 'articles': const NewsBlocksConverter().toJson(instance.articles), - 'topics': instance.topics, - }; + RelevantSearchResponse instance, +) => { + 'articles': const NewsBlocksConverter().toJson(instance.articles), + 'topics': instance.topics, +}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/subscriptions_response/subscriptions_response.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/subscriptions_response/subscriptions_response.g.dart index 1999a6da4..1f734e205 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/subscriptions_response/subscriptions_response.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/lib/src/models/subscriptions_response/subscriptions_response.g.dart @@ -7,15 +7,15 @@ part of 'subscriptions_response.dart'; // ************************************************************************** SubscriptionsResponse _$SubscriptionsResponseFromJson( - Map json) => - SubscriptionsResponse( - subscriptions: (json['subscriptions'] as List) - .map((e) => Subscription.fromJson(e as Map)) - .toList(), - ); + Map json, +) => SubscriptionsResponse( + subscriptions: (json['subscriptions'] as List) + .map((e) => Subscription.fromJson(e as Map)) + .toList(), +); Map _$SubscriptionsResponseToJson( - SubscriptionsResponse instance) => - { - 'subscriptions': instance.subscriptions.map((e) => e.toJson()).toList(), - }; + SubscriptionsResponse instance, +) => { + 'subscriptions': instance.subscriptions.map((e) => e.toJson()).toList(), +}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/analysis_options.yaml index 0d852c865..f47899084 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/analysis_options.yaml @@ -1,4 +1,6 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml analyzer: + errors: + document_ignores: ignore exclude: - lib/**/*.g.dart diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/news_blocks.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/news_blocks.dart index 81b3b8bfd..1ad20f897 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/news_blocks.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/news_blocks.dart @@ -18,7 +18,6 @@ export 'src/news_block.dart' show NewsBlock; export 'src/news_blocks_converter.dart' show NewsBlocksConverter; export 'src/newsletter_block.dart' show NewsletterBlock; export 'src/post_block.dart' show PostBlock, PostBlockActions; -export 'src/post_category.dart' show PostCategory; export 'src/post_grid_group_block.dart' show PostGridGroupBlock; export 'src/post_grid_tile_block.dart' show PostGridTileBlock, PostGridTileBlockExt; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/article_introduction_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/article_introduction_block.dart index 924b8df17..8a0a81d7c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/article_introduction_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/article_introduction_block.dart @@ -12,7 +12,7 @@ part 'article_introduction_block.g.dart'; class ArticleIntroductionBlock with EquatableMixin implements NewsBlock { /// {@macro article_introduction_block} const ArticleIntroductionBlock({ - required this.category, + required this.categoryId, required this.author, required this.publishedAt, required this.title, @@ -29,8 +29,8 @@ class ArticleIntroductionBlock with EquatableMixin implements NewsBlock { /// The article introduction block type identifier. static const identifier = '__article_introduction__'; - /// The category of the associated article. - final PostCategory category; + /// The category id of the associated article. + final String categoryId; /// The author of the associated article. final String author; @@ -57,12 +57,12 @@ class ArticleIntroductionBlock with EquatableMixin implements NewsBlock { @override List get props => [ - type, - category, - author, - publishedAt, - imageUrl, - title, - isPremium, - ]; + type, + categoryId, + author, + publishedAt, + imageUrl, + title, + isPremium, + ]; } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/article_introduction_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/article_introduction_block.g.dart index 1bef118ba..d987145f6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/article_introduction_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/article_introduction_block.g.dart @@ -9,36 +9,41 @@ part of 'article_introduction_block.dart'; // ************************************************************************** ArticleIntroductionBlock _$ArticleIntroductionBlockFromJson( - Map json) => - $checkedCreate( - 'ArticleIntroductionBlock', - json, - ($checkedConvert) { - final val = ArticleIntroductionBlock( - category: $checkedConvert( - 'category', (v) => $enumDecode(_$PostCategoryEnumMap, v)), - author: $checkedConvert('author', (v) => v as String), - publishedAt: $checkedConvert( - 'published_at', (v) => DateTime.parse(v as String)), - title: $checkedConvert('title', (v) => v as String), - type: $checkedConvert('type', - (v) => v as String? ?? ArticleIntroductionBlock.identifier), - imageUrl: $checkedConvert('image_url', (v) => v as String?), - isPremium: $checkedConvert('is_premium', (v) => v as bool? ?? false), - ); - return val; - }, - fieldKeyMap: const { - 'publishedAt': 'published_at', - 'imageUrl': 'image_url', - 'isPremium': 'is_premium' - }, + Map json, +) => $checkedCreate( + 'ArticleIntroductionBlock', + json, + ($checkedConvert) { + final val = ArticleIntroductionBlock( + categoryId: $checkedConvert('category_id', (v) => v as String), + author: $checkedConvert('author', (v) => v as String), + publishedAt: $checkedConvert( + 'published_at', + (v) => DateTime.parse(v as String), + ), + title: $checkedConvert('title', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? ArticleIntroductionBlock.identifier, + ), + imageUrl: $checkedConvert('image_url', (v) => v as String?), + isPremium: $checkedConvert('is_premium', (v) => v as bool? ?? false), ); + return val; + }, + fieldKeyMap: const { + 'categoryId': 'category_id', + 'publishedAt': 'published_at', + 'imageUrl': 'image_url', + 'isPremium': 'is_premium', + }, +); Map _$ArticleIntroductionBlockToJson( - ArticleIntroductionBlock instance) { + ArticleIntroductionBlock instance, +) { final val = { - 'category': _$PostCategoryEnumMap[instance.category]!, + 'category_id': instance.categoryId, 'author': instance.author, 'published_at': instance.publishedAt.toIso8601String(), }; @@ -55,12 +60,3 @@ Map _$ArticleIntroductionBlockToJson( val['type'] = instance.type; return val; } - -const _$PostCategoryEnumMap = { - PostCategory.business: 'business', - PostCategory.entertainment: 'entertainment', - PostCategory.health: 'health', - PostCategory.science: 'science', - PostCategory.sports: 'sports', - PostCategory.technology: 'technology', -}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/banner_ad_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/banner_ad_block.dart index cb2636fb0..b67b1f1ea 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/banner_ad_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/banner_ad_block.dart @@ -16,7 +16,7 @@ enum BannerAdSize { extraLarge, /// The anchored adaptive size of a banner ad. - anchoredAdaptive + anchoredAdaptive, } /// {@template banner_ad_block} diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/banner_ad_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/banner_ad_block.g.dart index 00d11d41a..e1454e128 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/banner_ad_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/banner_ad_block.g.dart @@ -9,19 +9,19 @@ part of 'banner_ad_block.dart'; // ************************************************************************** BannerAdBlock _$BannerAdBlockFromJson(Map json) => - $checkedCreate( - 'BannerAdBlock', - json, - ($checkedConvert) { - final val = BannerAdBlock( - size: $checkedConvert( - 'size', (v) => $enumDecode(_$BannerAdSizeEnumMap, v)), - type: $checkedConvert( - 'type', (v) => v as String? ?? BannerAdBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('BannerAdBlock', json, ($checkedConvert) { + final val = BannerAdBlock( + size: $checkedConvert( + 'size', + (v) => $enumDecode(_$BannerAdSizeEnumMap, v), + ), + type: $checkedConvert( + 'type', + (v) => v as String? ?? BannerAdBlock.identifier, + ), + ); + return val; + }); Map _$BannerAdBlockToJson(BannerAdBlock instance) => { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/block_action.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/block_action.dart index e7f73dc8f..172e5ef34 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/block_action.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/block_action.dart @@ -10,7 +10,7 @@ enum BlockActionType { navigation, /// An unknown action type. - unknown + unknown, } /// {@template block_action} @@ -19,10 +19,7 @@ enum BlockActionType { /// {@endtemplate} abstract class BlockAction { /// {@macro block_action} - const BlockAction({ - required this.type, - required this.actionType, - }); + const BlockAction({required this.type, required this.actionType}); /// The type key used to identify the type of this action. final String type; @@ -202,9 +199,7 @@ class NavigateToSlideshowAction with EquatableMixin implements BlockAction { @JsonSerializable() class UnknownBlockAction with EquatableMixin implements BlockAction { /// {@macro unknown_block_action} - const UnknownBlockAction({ - this.type = UnknownBlockAction.identifier, - }); + const UnknownBlockAction({this.type = UnknownBlockAction.identifier}); /// Converts a `Map` into a [UnknownBlock] instance. factory UnknownBlockAction.fromJson(Map json) => diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/block_action.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/block_action.g.dart index 8aa42f1b2..abdb113b6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/block_action.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/block_action.g.dart @@ -9,124 +9,102 @@ part of 'block_action.dart'; // ************************************************************************** NavigateToArticleAction _$NavigateToArticleActionFromJson( - Map json) => - $checkedCreate( - 'NavigateToArticleAction', - json, - ($checkedConvert) { - final val = NavigateToArticleAction( - articleId: $checkedConvert('article_id', (v) => v as String), - type: $checkedConvert('type', - (v) => v as String? ?? NavigateToArticleAction.identifier), - ); - return val; - }, - fieldKeyMap: const {'articleId': 'article_id'}, - ); + Map json, +) => $checkedCreate('NavigateToArticleAction', json, ($checkedConvert) { + final val = NavigateToArticleAction( + articleId: $checkedConvert('article_id', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? NavigateToArticleAction.identifier, + ), + ); + return val; +}, fieldKeyMap: const {'articleId': 'article_id'}); Map _$NavigateToArticleActionToJson( - NavigateToArticleAction instance) => - { - 'article_id': instance.articleId, - 'type': instance.type, - }; + NavigateToArticleAction instance, +) => {'article_id': instance.articleId, 'type': instance.type}; NavigateToVideoArticleAction _$NavigateToVideoArticleActionFromJson( - Map json) => - $checkedCreate( - 'NavigateToVideoArticleAction', - json, - ($checkedConvert) { - final val = NavigateToVideoArticleAction( - articleId: $checkedConvert('article_id', (v) => v as String), - type: $checkedConvert('type', - (v) => v as String? ?? NavigateToVideoArticleAction.identifier), - ); - return val; - }, - fieldKeyMap: const {'articleId': 'article_id'}, + Map json, +) => $checkedCreate( + 'NavigateToVideoArticleAction', + json, + ($checkedConvert) { + final val = NavigateToVideoArticleAction( + articleId: $checkedConvert('article_id', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? NavigateToVideoArticleAction.identifier, + ), ); + return val; + }, + fieldKeyMap: const {'articleId': 'article_id'}, +); Map _$NavigateToVideoArticleActionToJson( - NavigateToVideoArticleAction instance) => - { - 'article_id': instance.articleId, - 'type': instance.type, - }; + NavigateToVideoArticleAction instance, +) => {'article_id': instance.articleId, 'type': instance.type}; NavigateToFeedCategoryAction _$NavigateToFeedCategoryActionFromJson( - Map json) => - $checkedCreate( - 'NavigateToFeedCategoryAction', - json, - ($checkedConvert) { - final val = NavigateToFeedCategoryAction( - category: $checkedConvert( - 'category', (v) => $enumDecode(_$CategoryEnumMap, v)), - type: $checkedConvert('type', - (v) => v as String? ?? NavigateToFeedCategoryAction.identifier), - ); - return val; - }, - ); + Map json, +) => $checkedCreate('NavigateToFeedCategoryAction', json, ($checkedConvert) { + final val = NavigateToFeedCategoryAction( + category: $checkedConvert( + 'category', + (v) => Category.fromJson(v as Map), + ), + type: $checkedConvert( + 'type', + (v) => v as String? ?? NavigateToFeedCategoryAction.identifier, + ), + ); + return val; +}); Map _$NavigateToFeedCategoryActionToJson( - NavigateToFeedCategoryAction instance) => - { - 'category': _$CategoryEnumMap[instance.category]!, - 'type': instance.type, - }; - -const _$CategoryEnumMap = { - Category.business: 'business', - Category.entertainment: 'entertainment', - Category.top: 'top', - Category.health: 'health', - Category.science: 'science', - Category.sports: 'sports', - Category.technology: 'technology', + NavigateToFeedCategoryAction instance, +) => { + 'category': instance.category.toJson(), + 'type': instance.type, }; NavigateToSlideshowAction _$NavigateToSlideshowActionFromJson( - Map json) => - $checkedCreate( - 'NavigateToSlideshowAction', - json, - ($checkedConvert) { - final val = NavigateToSlideshowAction( - articleId: $checkedConvert('article_id', (v) => v as String), - slideshow: $checkedConvert('slideshow', - (v) => SlideshowBlock.fromJson(v as Map)), - type: $checkedConvert('type', - (v) => v as String? ?? NavigateToSlideshowAction.identifier), - ); - return val; - }, - fieldKeyMap: const {'articleId': 'article_id'}, - ); + Map json, +) => $checkedCreate('NavigateToSlideshowAction', json, ($checkedConvert) { + final val = NavigateToSlideshowAction( + articleId: $checkedConvert('article_id', (v) => v as String), + slideshow: $checkedConvert( + 'slideshow', + (v) => SlideshowBlock.fromJson(v as Map), + ), + type: $checkedConvert( + 'type', + (v) => v as String? ?? NavigateToSlideshowAction.identifier, + ), + ); + return val; +}, fieldKeyMap: const {'articleId': 'article_id'}); Map _$NavigateToSlideshowActionToJson( - NavigateToSlideshowAction instance) => - { - 'article_id': instance.articleId, - 'slideshow': instance.slideshow.toJson(), - 'type': instance.type, - }; + NavigateToSlideshowAction instance, +) => { + 'article_id': instance.articleId, + 'slideshow': instance.slideshow.toJson(), + 'type': instance.type, +}; UnknownBlockAction _$UnknownBlockActionFromJson(Map json) => - $checkedCreate( - 'UnknownBlockAction', - json, - ($checkedConvert) { - final val = UnknownBlockAction( - type: $checkedConvert( - 'type', (v) => v as String? ?? UnknownBlockAction.identifier), - ); - return val; - }, - ); + $checkedCreate('UnknownBlockAction', json, ($checkedConvert) { + final val = UnknownBlockAction( + type: $checkedConvert( + 'type', + (v) => v as String? ?? UnknownBlockAction.identifier, + ), + ); + return val; + }); Map _$UnknownBlockActionToJson(UnknownBlockAction instance) => - { - 'type': instance.type, - }; + {'type': instance.type}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/category.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/category.dart index f17b2b18f..7344c0e65 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/category.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/category.dart @@ -1,27 +1,30 @@ -/// The supported news category types. -enum Category { - /// News relating to business. - business, +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; - /// News relating to entertainment. - entertainment, +part 'category.g.dart'; - /// Breaking news. - top, +/// {@template category} +/// Represents a news category. +/// {@endtemplate} +@JsonSerializable() +class Category extends Equatable { + /// {@macro category} + const Category({required this.id, required this.name}); - /// News relating to health. - health, + /// Converts a `Map` into + /// a [Category] instance. + factory Category.fromJson(Map json) => + _$CategoryFromJson(json); - /// News relating to science. - science, + /// Category id. + final String id; - /// News relating to sports. - sports, + /// Category name. + final String name; - /// News relating to technology. - technology; + /// Converts the current instance to a `Map`. + Map toJson() => _$CategoryToJson(this); - /// Returns a [Category] for the [categoryName]. - static Category fromString(String categoryName) => - Category.values.firstWhere((category) => category.name == categoryName); + @override + List get props => [id, name]; } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/category.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/category.g.dart new file mode 100644 index 000000000..367b676c5 --- /dev/null +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/category.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: cast_nullable_to_non_nullable, implicit_dynamic_parameter, lines_longer_than_80_chars, prefer_const_constructors, require_trailing_commas + +part of 'category.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Category _$CategoryFromJson(Map json) => + $checkedCreate('Category', json, ($checkedConvert) { + final val = Category( + id: $checkedConvert('id', (v) => v as String), + name: $checkedConvert('name', (v) => v as String), + ); + return val; + }); + +Map _$CategoryToJson(Category instance) => { + 'id': instance.id, + 'name': instance.name, +}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/divider_horizontal_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/divider_horizontal_block.dart index 462b2ed81..2d733c329 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/divider_horizontal_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/divider_horizontal_block.dart @@ -11,9 +11,7 @@ part 'divider_horizontal_block.g.dart'; @JsonSerializable() class DividerHorizontalBlock with EquatableMixin implements NewsBlock { /// {@macro divider_horizontal_block} - const DividerHorizontalBlock({ - this.type = DividerHorizontalBlock.identifier, - }); + const DividerHorizontalBlock({this.type = DividerHorizontalBlock.identifier}); /// Converts a `Map` into /// a [DividerHorizontalBlock] instance. diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/divider_horizontal_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/divider_horizontal_block.g.dart index f041f3e8a..94f871f26 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/divider_horizontal_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/divider_horizontal_block.g.dart @@ -9,21 +9,17 @@ part of 'divider_horizontal_block.dart'; // ************************************************************************** DividerHorizontalBlock _$DividerHorizontalBlockFromJson( - Map json) => - $checkedCreate( - 'DividerHorizontalBlock', - json, - ($checkedConvert) { - final val = DividerHorizontalBlock( - type: $checkedConvert( - 'type', (v) => v as String? ?? DividerHorizontalBlock.identifier), - ); - return val; - }, - ); + Map json, +) => $checkedCreate('DividerHorizontalBlock', json, ($checkedConvert) { + final val = DividerHorizontalBlock( + type: $checkedConvert( + 'type', + (v) => v as String? ?? DividerHorizontalBlock.identifier, + ), + ); + return val; +}); Map _$DividerHorizontalBlockToJson( - DividerHorizontalBlock instance) => - { - 'type': instance.type, - }; + DividerHorizontalBlock instance, +) => {'type': instance.type}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/html_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/html_block.dart index 15e893f23..daf4ccadc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/html_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/html_block.dart @@ -10,10 +10,7 @@ part 'html_block.g.dart'; @JsonSerializable() class HtmlBlock with EquatableMixin implements NewsBlock { /// {@macro html_block} - const HtmlBlock({ - required this.content, - this.type = HtmlBlock.identifier, - }); + const HtmlBlock({required this.content, this.type = HtmlBlock.identifier}); /// Converts a `Map` into /// an [HtmlBlock] instance. diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/html_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/html_block.g.dart index a99148d0c..739387bfb 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/html_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/html_block.g.dart @@ -8,20 +8,19 @@ part of 'html_block.dart'; // JsonSerializableGenerator // ************************************************************************** -HtmlBlock _$HtmlBlockFromJson(Map json) => $checkedCreate( - 'HtmlBlock', - json, - ($checkedConvert) { - final val = HtmlBlock( - content: $checkedConvert('content', (v) => v as String), - type: $checkedConvert( - 'type', (v) => v as String? ?? HtmlBlock.identifier), - ); - return val; - }, - ); +HtmlBlock _$HtmlBlockFromJson(Map json) => + $checkedCreate('HtmlBlock', json, ($checkedConvert) { + final val = HtmlBlock( + content: $checkedConvert('content', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? HtmlBlock.identifier, + ), + ); + return val; + }); Map _$HtmlBlockToJson(HtmlBlock instance) => { - 'content': instance.content, - 'type': instance.type, - }; + 'content': instance.content, + 'type': instance.type, +}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/image_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/image_block.dart index 7bd24f2bc..1a185d37d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/image_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/image_block.dart @@ -11,10 +11,7 @@ part 'image_block.g.dart'; @JsonSerializable() class ImageBlock with EquatableMixin implements NewsBlock { /// {@macro image_block} - const ImageBlock({ - required this.imageUrl, - this.type = ImageBlock.identifier, - }); + const ImageBlock({required this.imageUrl, this.type = ImageBlock.identifier}); /// Converts a `Map` into a [ImageBlock] instance. factory ImageBlock.fromJson(Map json) => diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/image_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/image_block.g.dart index 53ad0b0c4..f411a9827 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/image_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/image_block.g.dart @@ -8,22 +8,17 @@ part of 'image_block.dart'; // JsonSerializableGenerator // ************************************************************************** -ImageBlock _$ImageBlockFromJson(Map json) => $checkedCreate( - 'ImageBlock', - json, - ($checkedConvert) { - final val = ImageBlock( - imageUrl: $checkedConvert('image_url', (v) => v as String), - type: $checkedConvert( - 'type', (v) => v as String? ?? ImageBlock.identifier), - ); - return val; - }, - fieldKeyMap: const {'imageUrl': 'image_url'}, - ); +ImageBlock _$ImageBlockFromJson(Map json) => + $checkedCreate('ImageBlock', json, ($checkedConvert) { + final val = ImageBlock( + imageUrl: $checkedConvert('image_url', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? ImageBlock.identifier, + ), + ); + return val; + }, fieldKeyMap: const {'imageUrl': 'image_url'}); Map _$ImageBlockToJson(ImageBlock instance) => - { - 'image_url': instance.imageUrl, - 'type': instance.type, - }; + {'image_url': instance.imageUrl, 'type': instance.type}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/newsletter_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/newsletter_block.dart index 184fd99db..a37e5fa60 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/newsletter_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/newsletter_block.dart @@ -11,9 +11,7 @@ part 'newsletter_block.g.dart'; @JsonSerializable() class NewsletterBlock with EquatableMixin implements NewsBlock { /// {@macro newsletter_block} - const NewsletterBlock({ - this.type = NewsletterBlock.identifier, - }); + const NewsletterBlock({this.type = NewsletterBlock.identifier}); /// Converts a `Map` into /// a [NewsletterBlock] instance. diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/newsletter_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/newsletter_block.g.dart index 62834c640..71279235c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/newsletter_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/newsletter_block.g.dart @@ -9,19 +9,15 @@ part of 'newsletter_block.dart'; // ************************************************************************** NewsletterBlock _$NewsletterBlockFromJson(Map json) => - $checkedCreate( - 'NewsletterBlock', - json, - ($checkedConvert) { - final val = NewsletterBlock( - type: $checkedConvert( - 'type', (v) => v as String? ?? NewsletterBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('NewsletterBlock', json, ($checkedConvert) { + final val = NewsletterBlock( + type: $checkedConvert( + 'type', + (v) => v as String? ?? NewsletterBlock.identifier, + ), + ); + return val; + }); Map _$NewsletterBlockToJson(NewsletterBlock instance) => - { - 'type': instance.type, - }; + {'type': instance.type}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_block.dart index ec8853b91..098abdf24 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_block.dart @@ -8,7 +8,7 @@ abstract class PostBlock with EquatableMixin implements NewsBlock { /// {@macro post_block} const PostBlock({ required this.id, - required this.category, + required this.categoryId, required this.author, required this.publishedAt, required this.title, @@ -26,8 +26,8 @@ abstract class PostBlock with EquatableMixin implements NewsBlock { /// The identifier of this post. final String id; - /// The category of this post. - final PostCategory category; + /// The category id of this post. + final String categoryId; /// The author of this post. final String author; @@ -63,18 +63,18 @@ abstract class PostBlock with EquatableMixin implements NewsBlock { @override List get props => [ - id, - category, - author, - publishedAt, - imageUrl, - title, - description, - action, - isPremium, - isContentOverlaid, - type, - ]; + id, + categoryId, + author, + publishedAt, + imageUrl, + title, + description, + action, + isPremium, + isContentOverlaid, + type, + ]; } /// The extension on [PostBlock] that provides information about actions. diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_category.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_category.dart deleted file mode 100644 index 3a0622d3e..000000000 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_category.dart +++ /dev/null @@ -1,20 +0,0 @@ -/// The supported news post category types. -enum PostCategory { - /// News post relating to business. - business, - - /// News post relating to entertainment. - entertainment, - - /// News post relating to health. - health, - - /// News post relating to science. - science, - - /// News post relating to sports. - sports, - - /// News post relating to technology. - technology, -} diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_group_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_group_block.dart index c1b2e2aa4..90438fd96 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_group_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_group_block.dart @@ -12,7 +12,7 @@ part 'post_grid_group_block.g.dart'; class PostGridGroupBlock with EquatableMixin implements NewsBlock { /// {@macro post_grid_group_block} const PostGridGroupBlock({ - required this.category, + required this.categoryId, required this.tiles, this.type = PostGridGroupBlock.identifier, }); @@ -24,8 +24,8 @@ class PostGridGroupBlock with EquatableMixin implements NewsBlock { /// The post grid block type identifier. static const identifier = '__post_grid_group__'; - /// The category of this post grid group. - final PostCategory category; + /// The category id of this post grid group. + final String categoryId; /// The associated list of [PostGridTileBlock] tiles. @NewsBlocksConverter() @@ -38,5 +38,5 @@ class PostGridGroupBlock with EquatableMixin implements NewsBlock { Map toJson() => _$PostGridGroupBlockToJson(this); @override - List get props => [category, tiles, type]; + List get props => [categoryId, tiles, type]; } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_group_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_group_block.g.dart index 341f8a018..8a942da13 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_group_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_group_block.g.dart @@ -9,38 +9,26 @@ part of 'post_grid_group_block.dart'; // ************************************************************************** PostGridGroupBlock _$PostGridGroupBlockFromJson(Map json) => - $checkedCreate( - 'PostGridGroupBlock', - json, - ($checkedConvert) { - final val = PostGridGroupBlock( - category: $checkedConvert( - 'category', (v) => $enumDecode(_$PostCategoryEnumMap, v)), - tiles: $checkedConvert( - 'tiles', - (v) => (v as List) - .map((e) => - PostGridTileBlock.fromJson(e as Map)) - .toList()), - type: $checkedConvert( - 'type', (v) => v as String? ?? PostGridGroupBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('PostGridGroupBlock', json, ($checkedConvert) { + final val = PostGridGroupBlock( + categoryId: $checkedConvert('category_id', (v) => v as String), + tiles: $checkedConvert( + 'tiles', + (v) => (v as List) + .map((e) => PostGridTileBlock.fromJson(e as Map)) + .toList(), + ), + type: $checkedConvert( + 'type', + (v) => v as String? ?? PostGridGroupBlock.identifier, + ), + ); + return val; + }, fieldKeyMap: const {'categoryId': 'category_id'}); Map _$PostGridGroupBlockToJson(PostGridGroupBlock instance) => { - 'category': _$PostCategoryEnumMap[instance.category]!, + 'category_id': instance.categoryId, 'tiles': instance.tiles.map((e) => e.toJson()).toList(), 'type': instance.type, }; - -const _$PostCategoryEnumMap = { - PostCategory.business: 'business', - PostCategory.entertainment: 'entertainment', - PostCategory.health: 'health', - PostCategory.science: 'science', - PostCategory.sports: 'sports', - PostCategory.technology: 'technology', -}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_tile_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_tile_block.dart index 374dcb081..6b8cf8a9a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_tile_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_tile_block.dart @@ -14,7 +14,7 @@ class PostGridTileBlock extends PostBlock { /// {@macro post_grid_tile_block} const PostGridTileBlock({ required super.id, - required super.category, + required super.categoryId, required super.author, required super.publishedAt, required String super.imageUrl, @@ -42,27 +42,27 @@ class PostGridTileBlock extends PostBlock { extension PostGridTileBlockExt on PostGridTileBlock { /// Converts [PostGridTileBlock] into a [PostLargeBlock] instance. PostLargeBlock toPostLargeBlock() => PostLargeBlock( - id: id, - category: category, - author: author, - publishedAt: publishedAt, - imageUrl: imageUrl!, - title: title, - isContentOverlaid: true, - description: description, - action: action, - ); + id: id, + categoryId: categoryId, + author: author, + publishedAt: publishedAt, + imageUrl: imageUrl!, + title: title, + isContentOverlaid: true, + description: description, + action: action, + ); /// Converts [PostGridTileBlock] into a [PostMediumBlock] instance. PostMediumBlock toPostMediumBlock() => PostMediumBlock( - id: id, - category: category, - author: author, - publishedAt: publishedAt, - imageUrl: imageUrl!, - title: title, - isContentOverlaid: true, - description: description, - action: action, - ); + id: id, + categoryId: categoryId, + author: author, + publishedAt: publishedAt, + imageUrl: imageUrl!, + title: title, + isContentOverlaid: true, + description: description, + action: action, + ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_tile_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_tile_block.g.dart index e276806ad..765ecd81a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_tile_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_grid_tile_block.g.dart @@ -15,35 +15,41 @@ PostGridTileBlock _$PostGridTileBlockFromJson(Map json) => ($checkedConvert) { final val = PostGridTileBlock( id: $checkedConvert('id', (v) => v as String), - category: $checkedConvert( - 'category', (v) => $enumDecode(_$PostCategoryEnumMap, v)), + categoryId: $checkedConvert('category_id', (v) => v as String), author: $checkedConvert('author', (v) => v as String), publishedAt: $checkedConvert( - 'published_at', (v) => DateTime.parse(v as String)), + 'published_at', + (v) => DateTime.parse(v as String), + ), imageUrl: $checkedConvert('image_url', (v) => v as String), title: $checkedConvert('title', (v) => v as String), description: $checkedConvert('description', (v) => v as String?), action: $checkedConvert( - 'action', - (v) => const BlockActionConverter() - .fromJson(v as Map?)), + 'action', + (v) => const BlockActionConverter().fromJson( + v as Map?, + ), + ), type: $checkedConvert( - 'type', (v) => v as String? ?? PostGridTileBlock.identifier), + 'type', + (v) => v as String? ?? PostGridTileBlock.identifier, + ), isPremium: $checkedConvert('is_premium', (v) => v as bool? ?? false), ); return val; }, fieldKeyMap: const { + 'categoryId': 'category_id', 'publishedAt': 'published_at', 'imageUrl': 'image_url', - 'isPremium': 'is_premium' + 'isPremium': 'is_premium', }, ); Map _$PostGridTileBlockToJson(PostGridTileBlock instance) { final val = { 'id': instance.id, - 'category': _$PostCategoryEnumMap[instance.category]!, + 'category_id': instance.categoryId, 'author': instance.author, 'published_at': instance.publishedAt.toIso8601String(), }; @@ -62,12 +68,3 @@ Map _$PostGridTileBlockToJson(PostGridTileBlock instance) { val['type'] = instance.type; return val; } - -const _$PostCategoryEnumMap = { - PostCategory.business: 'business', - PostCategory.entertainment: 'entertainment', - PostCategory.health: 'health', - PostCategory.science: 'science', - PostCategory.sports: 'sports', - PostCategory.technology: 'technology', -}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_large_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_large_block.dart index 18723bfdc..46fda8434 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_large_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_large_block.dart @@ -12,7 +12,7 @@ class PostLargeBlock extends PostBlock { /// {@macro post_large_block} const PostLargeBlock({ required super.id, - required super.category, + required super.categoryId, required super.author, required super.publishedAt, required String super.imageUrl, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_large_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_large_block.g.dart index 2c1338316..028ce4779 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_large_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_large_block.g.dart @@ -15,38 +15,46 @@ PostLargeBlock _$PostLargeBlockFromJson(Map json) => ($checkedConvert) { final val = PostLargeBlock( id: $checkedConvert('id', (v) => v as String), - category: $checkedConvert( - 'category', (v) => $enumDecode(_$PostCategoryEnumMap, v)), + categoryId: $checkedConvert('category_id', (v) => v as String), author: $checkedConvert('author', (v) => v as String), publishedAt: $checkedConvert( - 'published_at', (v) => DateTime.parse(v as String)), + 'published_at', + (v) => DateTime.parse(v as String), + ), imageUrl: $checkedConvert('image_url', (v) => v as String), title: $checkedConvert('title', (v) => v as String), description: $checkedConvert('description', (v) => v as String?), action: $checkedConvert( - 'action', - (v) => const BlockActionConverter() - .fromJson(v as Map?)), + 'action', + (v) => const BlockActionConverter().fromJson( + v as Map?, + ), + ), type: $checkedConvert( - 'type', (v) => v as String? ?? PostLargeBlock.identifier), + 'type', + (v) => v as String? ?? PostLargeBlock.identifier, + ), isPremium: $checkedConvert('is_premium', (v) => v as bool? ?? false), isContentOverlaid: $checkedConvert( - 'is_content_overlaid', (v) => v as bool? ?? false), + 'is_content_overlaid', + (v) => v as bool? ?? false, + ), ); return val; }, fieldKeyMap: const { + 'categoryId': 'category_id', 'publishedAt': 'published_at', 'imageUrl': 'image_url', 'isPremium': 'is_premium', - 'isContentOverlaid': 'is_content_overlaid' + 'isContentOverlaid': 'is_content_overlaid', }, ); Map _$PostLargeBlockToJson(PostLargeBlock instance) { final val = { 'id': instance.id, - 'category': _$PostCategoryEnumMap[instance.category]!, + 'category_id': instance.categoryId, 'author': instance.author, 'published_at': instance.publishedAt.toIso8601String(), }; @@ -66,12 +74,3 @@ Map _$PostLargeBlockToJson(PostLargeBlock instance) { val['type'] = instance.type; return val; } - -const _$PostCategoryEnumMap = { - PostCategory.business: 'business', - PostCategory.entertainment: 'entertainment', - PostCategory.health: 'health', - PostCategory.science: 'science', - PostCategory.sports: 'sports', - PostCategory.technology: 'technology', -}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_medium_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_medium_block.dart index 797b244eb..c968cc93d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_medium_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_medium_block.dart @@ -12,7 +12,7 @@ class PostMediumBlock extends PostBlock { /// {@macro post_medium_block} const PostMediumBlock({ required super.id, - required super.category, + required super.categoryId, required super.author, required super.publishedAt, required String super.imageUrl, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_medium_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_medium_block.g.dart index 5020ee2e7..737c7cd55 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_medium_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_medium_block.g.dart @@ -15,38 +15,46 @@ PostMediumBlock _$PostMediumBlockFromJson(Map json) => ($checkedConvert) { final val = PostMediumBlock( id: $checkedConvert('id', (v) => v as String), - category: $checkedConvert( - 'category', (v) => $enumDecode(_$PostCategoryEnumMap, v)), + categoryId: $checkedConvert('category_id', (v) => v as String), author: $checkedConvert('author', (v) => v as String), publishedAt: $checkedConvert( - 'published_at', (v) => DateTime.parse(v as String)), + 'published_at', + (v) => DateTime.parse(v as String), + ), imageUrl: $checkedConvert('image_url', (v) => v as String), title: $checkedConvert('title', (v) => v as String), description: $checkedConvert('description', (v) => v as String?), action: $checkedConvert( - 'action', - (v) => const BlockActionConverter() - .fromJson(v as Map?)), + 'action', + (v) => const BlockActionConverter().fromJson( + v as Map?, + ), + ), type: $checkedConvert( - 'type', (v) => v as String? ?? PostMediumBlock.identifier), + 'type', + (v) => v as String? ?? PostMediumBlock.identifier, + ), isPremium: $checkedConvert('is_premium', (v) => v as bool? ?? false), isContentOverlaid: $checkedConvert( - 'is_content_overlaid', (v) => v as bool? ?? false), + 'is_content_overlaid', + (v) => v as bool? ?? false, + ), ); return val; }, fieldKeyMap: const { + 'categoryId': 'category_id', 'publishedAt': 'published_at', 'imageUrl': 'image_url', 'isPremium': 'is_premium', - 'isContentOverlaid': 'is_content_overlaid' + 'isContentOverlaid': 'is_content_overlaid', }, ); Map _$PostMediumBlockToJson(PostMediumBlock instance) { final val = { 'id': instance.id, - 'category': _$PostCategoryEnumMap[instance.category]!, + 'category_id': instance.categoryId, 'author': instance.author, 'published_at': instance.publishedAt.toIso8601String(), }; @@ -66,12 +74,3 @@ Map _$PostMediumBlockToJson(PostMediumBlock instance) { val['type'] = instance.type; return val; } - -const _$PostCategoryEnumMap = { - PostCategory.business: 'business', - PostCategory.entertainment: 'entertainment', - PostCategory.health: 'health', - PostCategory.science: 'science', - PostCategory.sports: 'sports', - PostCategory.technology: 'technology', -}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_small_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_small_block.dart index 0bb7ddb09..003c3d525 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_small_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_small_block.dart @@ -12,7 +12,7 @@ class PostSmallBlock extends PostBlock { /// {@macro post_small_block} const PostSmallBlock({ required super.id, - required super.category, + required super.categoryId, required super.author, required super.publishedAt, required super.title, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_small_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_small_block.g.dart index 778287260..3759f0f66 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_small_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/post_small_block.g.dart @@ -15,35 +15,41 @@ PostSmallBlock _$PostSmallBlockFromJson(Map json) => ($checkedConvert) { final val = PostSmallBlock( id: $checkedConvert('id', (v) => v as String), - category: $checkedConvert( - 'category', (v) => $enumDecode(_$PostCategoryEnumMap, v)), + categoryId: $checkedConvert('category_id', (v) => v as String), author: $checkedConvert('author', (v) => v as String), publishedAt: $checkedConvert( - 'published_at', (v) => DateTime.parse(v as String)), + 'published_at', + (v) => DateTime.parse(v as String), + ), title: $checkedConvert('title', (v) => v as String), imageUrl: $checkedConvert('image_url', (v) => v as String?), description: $checkedConvert('description', (v) => v as String?), action: $checkedConvert( - 'action', - (v) => const BlockActionConverter() - .fromJson(v as Map?)), + 'action', + (v) => const BlockActionConverter().fromJson( + v as Map?, + ), + ), type: $checkedConvert( - 'type', (v) => v as String? ?? PostSmallBlock.identifier), + 'type', + (v) => v as String? ?? PostSmallBlock.identifier, + ), isPremium: $checkedConvert('is_premium', (v) => v as bool? ?? false), ); return val; }, fieldKeyMap: const { + 'categoryId': 'category_id', 'publishedAt': 'published_at', 'imageUrl': 'image_url', - 'isPremium': 'is_premium' + 'isPremium': 'is_premium', }, ); Map _$PostSmallBlockToJson(PostSmallBlock instance) { final val = { 'id': instance.id, - 'category': _$PostCategoryEnumMap[instance.category]!, + 'category_id': instance.categoryId, 'author': instance.author, 'published_at': instance.publishedAt.toIso8601String(), }; @@ -62,12 +68,3 @@ Map _$PostSmallBlockToJson(PostSmallBlock instance) { val['type'] = instance.type; return val; } - -const _$PostCategoryEnumMap = { - PostCategory.business: 'business', - PostCategory.entertainment: 'entertainment', - PostCategory.health: 'health', - PostCategory.science: 'science', - PostCategory.sports: 'sports', - PostCategory.technology: 'technology', -}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/section_header_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/section_header_block.g.dart index 33cd5b129..0093afb49 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/section_header_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/section_header_block.g.dart @@ -9,27 +9,24 @@ part of 'section_header_block.dart'; // ************************************************************************** SectionHeaderBlock _$SectionHeaderBlockFromJson(Map json) => - $checkedCreate( - 'SectionHeaderBlock', - json, - ($checkedConvert) { - final val = SectionHeaderBlock( - title: $checkedConvert('title', (v) => v as String), - action: $checkedConvert( - 'action', - (v) => const BlockActionConverter() - .fromJson(v as Map?)), - type: $checkedConvert( - 'type', (v) => v as String? ?? SectionHeaderBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('SectionHeaderBlock', json, ($checkedConvert) { + final val = SectionHeaderBlock( + title: $checkedConvert('title', (v) => v as String), + action: $checkedConvert( + 'action', + (v) => + const BlockActionConverter().fromJson(v as Map?), + ), + type: $checkedConvert( + 'type', + (v) => v as String? ?? SectionHeaderBlock.identifier, + ), + ); + return val; + }); Map _$SectionHeaderBlockToJson(SectionHeaderBlock instance) { - final val = { - 'title': instance.title, - }; + final val = {'title': instance.title}; void writeNotNull(String key, dynamic value) { if (value != null) { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slide_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slide_block.dart index 8a8706209..d02e1167f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slide_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slide_block.dart @@ -44,11 +44,5 @@ class SlideBlock with EquatableMixin implements NewsBlock { final String type; @override - List get props => [ - type, - caption, - description, - photoCredit, - imageUrl, - ]; + List get props => [type, caption, description, photoCredit, imageUrl]; } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slide_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slide_block.g.dart index a1b64e6c0..2535dca4a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slide_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slide_block.g.dart @@ -9,24 +9,23 @@ part of 'slide_block.dart'; // ************************************************************************** SlideBlock _$SlideBlockFromJson(Map json) => $checkedCreate( - 'SlideBlock', - json, - ($checkedConvert) { - final val = SlideBlock( - caption: $checkedConvert('caption', (v) => v as String), - description: $checkedConvert('description', (v) => v as String), - photoCredit: $checkedConvert('photo_credit', (v) => v as String), - imageUrl: $checkedConvert('image_url', (v) => v as String), - type: $checkedConvert( - 'type', (v) => v as String? ?? SlideBlock.identifier), - ); - return val; - }, - fieldKeyMap: const { - 'photoCredit': 'photo_credit', - 'imageUrl': 'image_url' - }, + 'SlideBlock', + json, + ($checkedConvert) { + final val = SlideBlock( + caption: $checkedConvert('caption', (v) => v as String), + description: $checkedConvert('description', (v) => v as String), + photoCredit: $checkedConvert('photo_credit', (v) => v as String), + imageUrl: $checkedConvert('image_url', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? SlideBlock.identifier, + ), ); + return val; + }, + fieldKeyMap: const {'photoCredit': 'photo_credit', 'imageUrl': 'image_url'}, +); Map _$SlideBlockToJson(SlideBlock instance) => { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_block.g.dart index decba4fd4..7881dde58 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_block.g.dart @@ -9,23 +9,22 @@ part of 'slideshow_block.dart'; // ************************************************************************** SlideshowBlock _$SlideshowBlockFromJson(Map json) => - $checkedCreate( - 'SlideshowBlock', - json, - ($checkedConvert) { - final val = SlideshowBlock( - title: $checkedConvert('title', (v) => v as String), - slides: $checkedConvert( - 'slides', - (v) => (v as List) - .map((e) => SlideBlock.fromJson(e as Map)) - .toList()), - type: $checkedConvert( - 'type', (v) => v as String? ?? SlideshowBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('SlideshowBlock', json, ($checkedConvert) { + final val = SlideshowBlock( + title: $checkedConvert('title', (v) => v as String), + slides: $checkedConvert( + 'slides', + (v) => (v as List) + .map((e) => SlideBlock.fromJson(e as Map)) + .toList(), + ), + type: $checkedConvert( + 'type', + (v) => v as String? ?? SlideshowBlock.identifier, + ), + ); + return val; + }); Map _$SlideshowBlockToJson(SlideshowBlock instance) => { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_introduction_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_introduction_block.dart index 8285f0bc5..fb64f3f7e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_introduction_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_introduction_block.dart @@ -39,11 +39,7 @@ class SlideshowIntroductionBlock with EquatableMixin implements NewsBlock { Map toJson() => _$SlideshowIntroductionBlockToJson(this); @override - List get props => [ - title, - coverImageUrl, - type, - ]; + List get props => [title, coverImageUrl, type]; @override final String type; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_introduction_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_introduction_block.g.dart index f18cb829b..ad311d211 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_introduction_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/slideshow_introduction_block.g.dart @@ -9,28 +9,32 @@ part of 'slideshow_introduction_block.dart'; // ************************************************************************** SlideshowIntroductionBlock _$SlideshowIntroductionBlockFromJson( - Map json) => - $checkedCreate( - 'SlideshowIntroductionBlock', - json, - ($checkedConvert) { - final val = SlideshowIntroductionBlock( - title: $checkedConvert('title', (v) => v as String), - coverImageUrl: $checkedConvert('cover_image_url', (v) => v as String), - type: $checkedConvert('type', - (v) => v as String? ?? SlideshowIntroductionBlock.identifier), - action: $checkedConvert( - 'action', - (v) => const BlockActionConverter() - .fromJson(v as Map?)), - ); - return val; - }, - fieldKeyMap: const {'coverImageUrl': 'cover_image_url'}, + Map json, +) => $checkedCreate( + 'SlideshowIntroductionBlock', + json, + ($checkedConvert) { + final val = SlideshowIntroductionBlock( + title: $checkedConvert('title', (v) => v as String), + coverImageUrl: $checkedConvert('cover_image_url', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? SlideshowIntroductionBlock.identifier, + ), + action: $checkedConvert( + 'action', + (v) => + const BlockActionConverter().fromJson(v as Map?), + ), ); + return val; + }, + fieldKeyMap: const {'coverImageUrl': 'cover_image_url'}, +); Map _$SlideshowIntroductionBlockToJson( - SlideshowIntroductionBlock instance) { + SlideshowIntroductionBlock instance, +) { final val = { 'title': instance.title, 'cover_image_url': instance.coverImageUrl, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/spacer_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/spacer_block.g.dart index 498d89eab..9f5389981 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/spacer_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/spacer_block.g.dart @@ -8,19 +8,20 @@ part of 'spacer_block.dart'; // JsonSerializableGenerator // ************************************************************************** -SpacerBlock _$SpacerBlockFromJson(Map json) => $checkedCreate( - 'SpacerBlock', - json, - ($checkedConvert) { - final val = SpacerBlock( - spacing: $checkedConvert( - 'spacing', (v) => $enumDecode(_$SpacingEnumMap, v)), - type: $checkedConvert( - 'type', (v) => v as String? ?? SpacerBlock.identifier), - ); - return val; - }, - ); +SpacerBlock _$SpacerBlockFromJson(Map json) => + $checkedCreate('SpacerBlock', json, ($checkedConvert) { + final val = SpacerBlock( + spacing: $checkedConvert( + 'spacing', + (v) => $enumDecode(_$SpacingEnumMap, v), + ), + type: $checkedConvert( + 'type', + (v) => v as String? ?? SpacerBlock.identifier, + ), + ); + return val; + }); Map _$SpacerBlockToJson(SpacerBlock instance) => { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_caption_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_caption_block.g.dart index 461d450b8..af0212038 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_caption_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_caption_block.g.dart @@ -9,20 +9,20 @@ part of 'text_caption_block.dart'; // ************************************************************************** TextCaptionBlock _$TextCaptionBlockFromJson(Map json) => - $checkedCreate( - 'TextCaptionBlock', - json, - ($checkedConvert) { - final val = TextCaptionBlock( - text: $checkedConvert('text', (v) => v as String), - color: $checkedConvert( - 'color', (v) => $enumDecode(_$TextCaptionColorEnumMap, v)), - type: $checkedConvert( - 'type', (v) => v as String? ?? TextCaptionBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('TextCaptionBlock', json, ($checkedConvert) { + final val = TextCaptionBlock( + text: $checkedConvert('text', (v) => v as String), + color: $checkedConvert( + 'color', + (v) => $enumDecode(_$TextCaptionColorEnumMap, v), + ), + type: $checkedConvert( + 'type', + (v) => v as String? ?? TextCaptionBlock.identifier, + ), + ); + return val; + }); Map _$TextCaptionBlockToJson(TextCaptionBlock instance) => { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_headline_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_headline_block.g.dart index e6a187ff5..2ce9da36d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_headline_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_headline_block.g.dart @@ -9,21 +9,16 @@ part of 'text_headline_block.dart'; // ************************************************************************** TextHeadlineBlock _$TextHeadlineBlockFromJson(Map json) => - $checkedCreate( - 'TextHeadlineBlock', - json, - ($checkedConvert) { - final val = TextHeadlineBlock( - text: $checkedConvert('text', (v) => v as String), - type: $checkedConvert( - 'type', (v) => v as String? ?? TextHeadlineBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('TextHeadlineBlock', json, ($checkedConvert) { + final val = TextHeadlineBlock( + text: $checkedConvert('text', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? TextHeadlineBlock.identifier, + ), + ); + return val; + }); Map _$TextHeadlineBlockToJson(TextHeadlineBlock instance) => - { - 'text': instance.text, - 'type': instance.type, - }; + {'text': instance.text, 'type': instance.type}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_lead_paragraph_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_lead_paragraph_block.g.dart index 0161f785f..0dfb55cf7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_lead_paragraph_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_lead_paragraph_block.g.dart @@ -9,23 +9,18 @@ part of 'text_lead_paragraph_block.dart'; // ************************************************************************** TextLeadParagraphBlock _$TextLeadParagraphBlockFromJson( - Map json) => - $checkedCreate( - 'TextLeadParagraphBlock', - json, - ($checkedConvert) { - final val = TextLeadParagraphBlock( - text: $checkedConvert('text', (v) => v as String), - type: $checkedConvert( - 'type', (v) => v as String? ?? TextLeadParagraphBlock.identifier), - ); - return val; - }, - ); + Map json, +) => $checkedCreate('TextLeadParagraphBlock', json, ($checkedConvert) { + final val = TextLeadParagraphBlock( + text: $checkedConvert('text', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? TextLeadParagraphBlock.identifier, + ), + ); + return val; +}); Map _$TextLeadParagraphBlockToJson( - TextLeadParagraphBlock instance) => - { - 'text': instance.text, - 'type': instance.type, - }; + TextLeadParagraphBlock instance, +) => {'text': instance.text, 'type': instance.type}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_paragraph_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_paragraph_block.g.dart index f0d6aeeae..ea6ac4f49 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_paragraph_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/text_paragraph_block.g.dart @@ -9,21 +9,16 @@ part of 'text_paragraph_block.dart'; // ************************************************************************** TextParagraphBlock _$TextParagraphBlockFromJson(Map json) => - $checkedCreate( - 'TextParagraphBlock', - json, - ($checkedConvert) { - final val = TextParagraphBlock( - text: $checkedConvert('text', (v) => v as String), - type: $checkedConvert( - 'type', (v) => v as String? ?? TextParagraphBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('TextParagraphBlock', json, ($checkedConvert) { + final val = TextParagraphBlock( + text: $checkedConvert('text', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? TextParagraphBlock.identifier, + ), + ); + return val; + }); Map _$TextParagraphBlockToJson(TextParagraphBlock instance) => - { - 'text': instance.text, - 'type': instance.type, - }; + {'text': instance.text, 'type': instance.type}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/trending_story_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/trending_story_block.g.dart index 280c83ae0..bac1e67d1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/trending_story_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/trending_story_block.g.dart @@ -9,19 +9,19 @@ part of 'trending_story_block.dart'; // ************************************************************************** TrendingStoryBlock _$TrendingStoryBlockFromJson(Map json) => - $checkedCreate( - 'TrendingStoryBlock', - json, - ($checkedConvert) { - final val = TrendingStoryBlock( - content: $checkedConvert('content', - (v) => PostSmallBlock.fromJson(v as Map)), - type: $checkedConvert( - 'type', (v) => v as String? ?? TrendingStoryBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('TrendingStoryBlock', json, ($checkedConvert) { + final val = TrendingStoryBlock( + content: $checkedConvert( + 'content', + (v) => PostSmallBlock.fromJson(v as Map), + ), + type: $checkedConvert( + 'type', + (v) => v as String? ?? TrendingStoryBlock.identifier, + ), + ); + return val; + }); Map _$TrendingStoryBlockToJson(TrendingStoryBlock instance) => { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/unknown_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/unknown_block.g.dart index caf4afa98..06d0a7359 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/unknown_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/unknown_block.g.dart @@ -9,19 +9,15 @@ part of 'unknown_block.dart'; // ************************************************************************** UnknownBlock _$UnknownBlockFromJson(Map json) => - $checkedCreate( - 'UnknownBlock', - json, - ($checkedConvert) { - final val = UnknownBlock( - type: $checkedConvert( - 'type', (v) => v as String? ?? UnknownBlock.identifier), - ); - return val; - }, - ); + $checkedCreate('UnknownBlock', json, ($checkedConvert) { + final val = UnknownBlock( + type: $checkedConvert( + 'type', + (v) => v as String? ?? UnknownBlock.identifier, + ), + ); + return val; + }); Map _$UnknownBlockToJson(UnknownBlock instance) => - { - 'type': instance.type, - }; + {'type': instance.type}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_block.dart index b000c6750..b8906cf26 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_block.dart @@ -11,10 +11,7 @@ part 'video_block.g.dart'; @JsonSerializable() class VideoBlock with EquatableMixin implements NewsBlock { /// {@macro video_block} - const VideoBlock({ - required this.videoUrl, - this.type = VideoBlock.identifier, - }); + const VideoBlock({required this.videoUrl, this.type = VideoBlock.identifier}); /// Converts a `Map` into a [VideoBlock] instance. factory VideoBlock.fromJson(Map json) => diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_block.g.dart index 6fb7edfc1..bf8c3e4d1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_block.g.dart @@ -8,22 +8,17 @@ part of 'video_block.dart'; // JsonSerializableGenerator // ************************************************************************** -VideoBlock _$VideoBlockFromJson(Map json) => $checkedCreate( - 'VideoBlock', - json, - ($checkedConvert) { - final val = VideoBlock( - videoUrl: $checkedConvert('video_url', (v) => v as String), - type: $checkedConvert( - 'type', (v) => v as String? ?? VideoBlock.identifier), - ); - return val; - }, - fieldKeyMap: const {'videoUrl': 'video_url'}, - ); +VideoBlock _$VideoBlockFromJson(Map json) => + $checkedCreate('VideoBlock', json, ($checkedConvert) { + final val = VideoBlock( + videoUrl: $checkedConvert('video_url', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? VideoBlock.identifier, + ), + ); + return val; + }, fieldKeyMap: const {'videoUrl': 'video_url'}); Map _$VideoBlockToJson(VideoBlock instance) => - { - 'video_url': instance.videoUrl, - 'type': instance.type, - }; + {'video_url': instance.videoUrl, 'type': instance.type}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_introduction_block.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_introduction_block.dart index 8c95a1dff..cb3ca0afb 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_introduction_block.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_introduction_block.dart @@ -12,7 +12,7 @@ part 'video_introduction_block.g.dart'; class VideoIntroductionBlock with EquatableMixin implements NewsBlock { /// {@macro video_introduction_block} const VideoIntroductionBlock({ - required this.category, + required this.categoryId, required this.title, required this.videoUrl, this.type = VideoIntroductionBlock.identifier, @@ -26,8 +26,8 @@ class VideoIntroductionBlock with EquatableMixin implements NewsBlock { /// The video introduction block type identifier. static const identifier = '__video_introduction__'; - /// The category of the associated article. - final PostCategory category; + /// The category id of the associated article. + final String categoryId; /// The title of the associated article. final String title; @@ -42,5 +42,5 @@ class VideoIntroductionBlock with EquatableMixin implements NewsBlock { Map toJson() => _$VideoIntroductionBlockToJson(this); @override - List get props => [category, title, videoUrl, type]; + List get props => [categoryId, title, videoUrl, type]; } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_introduction_block.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_introduction_block.g.dart index a69ec2122..cb06ca958 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_introduction_block.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/lib/src/video_introduction_block.g.dart @@ -9,38 +9,30 @@ part of 'video_introduction_block.dart'; // ************************************************************************** VideoIntroductionBlock _$VideoIntroductionBlockFromJson( - Map json) => - $checkedCreate( - 'VideoIntroductionBlock', - json, - ($checkedConvert) { - final val = VideoIntroductionBlock( - category: $checkedConvert( - 'category', (v) => $enumDecode(_$PostCategoryEnumMap, v)), - title: $checkedConvert('title', (v) => v as String), - videoUrl: $checkedConvert('video_url', (v) => v as String), - type: $checkedConvert( - 'type', (v) => v as String? ?? VideoIntroductionBlock.identifier), - ); - return val; - }, - fieldKeyMap: const {'videoUrl': 'video_url'}, + Map json, +) => $checkedCreate( + 'VideoIntroductionBlock', + json, + ($checkedConvert) { + final val = VideoIntroductionBlock( + categoryId: $checkedConvert('category_id', (v) => v as String), + title: $checkedConvert('title', (v) => v as String), + videoUrl: $checkedConvert('video_url', (v) => v as String), + type: $checkedConvert( + 'type', + (v) => v as String? ?? VideoIntroductionBlock.identifier, + ), ); + return val; + }, + fieldKeyMap: const {'categoryId': 'category_id', 'videoUrl': 'video_url'}, +); Map _$VideoIntroductionBlockToJson( - VideoIntroductionBlock instance) => - { - 'category': _$PostCategoryEnumMap[instance.category]!, - 'title': instance.title, - 'video_url': instance.videoUrl, - 'type': instance.type, - }; - -const _$PostCategoryEnumMap = { - PostCategory.business: 'business', - PostCategory.entertainment: 'entertainment', - PostCategory.health: 'health', - PostCategory.science: 'science', - PostCategory.sports: 'sports', - PostCategory.technology: 'technology', + VideoIntroductionBlock instance, +) => { + 'category_id': instance.categoryId, + 'title': instance.title, + 'video_url': instance.videoUrl, + 'type': instance.type, }; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/pubspec.yaml index cca58449a..fe9ade188 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: equatable: ^2.0.3 @@ -12,12 +12,12 @@ dependencies: meta: ^1.7.0 dev_dependencies: - build_runner: ^2.0.0 + build_runner: ^2.4.15 coverage: ^1.1.0 - json_serializable: ^6.8.0 + json_serializable: ^6.9.5 mocktail: ^1.0.2 test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 dependency_overrides: frontend_server_client: ^4.0.0 \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/article_introduction_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/article_introduction_block_test.dart index 3ac1d6e47..9933f8932 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/article_introduction_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/article_introduction_block_test.dart @@ -4,8 +4,9 @@ import 'package:test/test.dart'; void main() { group('ArticleIntroductionBlock', () { test('can be (de)serialized', () { + const category = Category(id: 'technology', name: 'Technology'); final block = ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 9), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/block_action_converter_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/block_action_converter_test.dart index 51217822b..58606e9e7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/block_action_converter_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/block_action_converter_test.dart @@ -7,17 +7,15 @@ void main() { group('BlockActionConverter', () { test('can (de)serialize BlockAction', () { final converter = BlockActionConverter(); + const category = Category(id: 'sports', name: 'Sports'); const actions = [ NavigateToArticleAction(articleId: 'articleId'), - NavigateToFeedCategoryAction(category: Category.top), + NavigateToFeedCategoryAction(category: category), ]; for (final action in actions) { - expect( - converter.fromJson(converter.toJson(action)), - equals(action), - ); + expect(converter.fromJson(converter.toJson(action)), equals(action)); } }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/block_action_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/block_action_test.dart index e41e71491..13a87513d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/block_action_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/block_action_test.dart @@ -1,14 +1,11 @@ -// ignore_for_file: prefer_const_constructors_in_immutables, lines_longer_than_80_chars, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:news_blocks/news_blocks.dart'; import 'package:test/test.dart'; class CustomBlockAction extends BlockAction { CustomBlockAction() - : super( - type: '__custom_block__', - actionType: BlockActionType.navigation, - ); + : super(type: '__custom_block__', actionType: BlockActionType.navigation); @override Map toJson() => {'type': type}; @@ -46,7 +43,9 @@ void main() { }); test('returns NavigateToFeedCategoryAction', () { - final action = NavigateToFeedCategoryAction(category: Category.top); + const category = Category(id: 'sports', name: 'Sports'); + + final action = NavigateToFeedCategoryAction(category: category); expect(BlockAction.fromJson(action.toJson()), equals(action)); }); @@ -62,10 +61,7 @@ void main() { group('UnknownBlockAction', () { test('can be (de)serialized', () { final action = UnknownBlockAction(); - expect( - UnknownBlockAction.fromJson(action.toJson()), - equals(action), - ); + expect(UnknownBlockAction.fromJson(action.toJson()), equals(action)); }); }); @@ -91,7 +87,9 @@ void main() { group('NavigateToFeedCategoryAction', () { test('can be (de)serialized', () { - final action = NavigateToFeedCategoryAction(category: Category.top); + const category = Category(id: 'sports', name: 'Sports'); + + final action = NavigateToFeedCategoryAction(category: category); expect( NavigateToFeedCategoryAction.fromJson(action.toJson()), equals(action), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/category_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/category_test.dart index 2f05437a8..9c98f8203 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/category_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/category_test.dart @@ -2,56 +2,21 @@ import 'package:news_blocks/news_blocks.dart'; import 'package:test/test.dart'; void main() { - group('Category', () { - group('fromString', () { - test('returns business', () { - expect( - Category.fromString('business'), - equals(Category.business), - ); - }); - - test('returns business', () { - expect( - Category.fromString('entertainment'), - equals(Category.entertainment), - ); - }); - - test('returns top', () { - expect( - Category.fromString('top'), - equals(Category.top), - ); - }); - - test('returns health', () { - expect( - Category.fromString('health'), - equals(Category.health), - ); - }); - - test('returns science', () { - expect( - Category.fromString('science'), - equals(Category.science), - ); - }); - - test('returns sports', () { - expect( - Category.fromString('sports'), - equals(Category.sports), - ); - }); + group(Category, () { + test('can be (de)serialized', () { + const category = Category(id: 'sports', name: 'Sports'); + expect( + Category.fromJson(const {'id': 'sports', 'name': 'Sports'}), + equals(category), + ); + }); - test('returns technology', () { - expect( - Category.fromString('technology'), - equals(Category.technology), - ); - }); + test('can be serialized', () { + const category = Category(id: 'sports', name: 'Sports'); + expect( + category.toJson(), + equals(const {'id': 'sports', 'name': 'Sports'}), + ); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/news_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/news_block_test.dart index ee1ea57b2..dde52f2b5 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/news_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/news_block_test.dart @@ -77,8 +77,9 @@ void main() { }); test('returns VideoIntroductionBlock', () { + const category = Category(id: 'technology', name: 'Technology'); final block = VideoIntroductionBlock( - category: PostCategory.technology, + categoryId: category.id, title: 'title', videoUrl: 'videoUrl', ); @@ -86,8 +87,9 @@ void main() { }); test('returns ArticleIntroductionBlock', () { + const category = Category(id: 'technology', name: 'Technology'); final block = ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 9), imageUrl: 'imageUrl', @@ -97,9 +99,10 @@ void main() { }); test('returns PostLargeBlock', () { + const category = Category(id: 'technology', name: 'Technology'); final block = PostLargeBlock( id: 'id', - category: PostCategory.technology, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 9), imageUrl: 'imageUrl', @@ -110,9 +113,10 @@ void main() { }); test('returns PostMediumBlock', () { + const category = Category(id: 'sports', name: 'Sports'); final block = PostMediumBlock( id: 'id', - category: PostCategory.sports, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 10), imageUrl: 'imageUrl', @@ -123,9 +127,10 @@ void main() { }); test('returns PostSmallBlock', () { + const category = Category(id: 'health', name: 'Health'); final block = PostSmallBlock( id: 'id', - category: PostCategory.health, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', @@ -136,12 +141,13 @@ void main() { }); test('returns PostGridGroupBlock', () { + const category = Category(id: 'science', name: 'Science'); final block = PostGridGroupBlock( - category: PostCategory.science, + categoryId: category.id, tiles: [ PostGridTileBlock( id: 'id', - category: PostCategory.science, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 12), imageUrl: 'imageUrl', @@ -154,9 +160,10 @@ void main() { }); test('returns PostGridTileBlock', () { + const category = Category(id: 'science', name: 'Science'); final block = PostGridTileBlock( id: 'id', - category: PostCategory.science, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 12), imageUrl: 'imageUrl', @@ -173,9 +180,7 @@ void main() { }); test('returns BannerAdBlock', () { - final block = BannerAdBlock( - size: BannerAdSize.normal, - ); + final block = BannerAdBlock(size: BannerAdSize.normal); expect(NewsBlock.fromJson(block.toJson()), equals(block)); }); @@ -214,9 +219,10 @@ void main() { }); test('returns TrendingStoryBlock', () { + const category = Category(id: 'health', name: 'Health'); final content = PostSmallBlock( id: 'id', - category: PostCategory.health, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/news_blocks_converter_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/news_blocks_converter_test.dart index 7cb3e2ad8..6def202ab 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/news_blocks_converter_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/news_blocks_converter_test.dart @@ -7,13 +7,14 @@ void main() { group('NewsBlocksConverter', () { test('can (de)serialize List', () { final converter = NewsBlocksConverter(); + const category = Category(id: 'health', name: 'Health'); final newsBlocks = [ SectionHeaderBlock(title: 'title'), DividerHorizontalBlock(), SpacerBlock(spacing: Spacing.medium), PostSmallBlock( id: 'id', - category: PostCategory.health, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_block_test.dart index 75bfcdc0d..e6a8e664a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_block_test.dart @@ -5,14 +5,14 @@ import 'package:test/test.dart'; class CustomPostBlock extends PostBlock { CustomPostBlock({super.action}) - : super( - id: 'id', - category: PostCategory.technology, - author: 'author', - publishedAt: DateTime(2022, 03, 09), - title: 'title', - type: 'type', - ); + : super( + id: 'id', + categoryId: Category(id: 'health', name: 'Health').id, + author: 'author', + publishedAt: DateTime(2022, 03, 09), + title: 'title', + type: 'type', + ); @override Map toJson() => throw UnimplementedError(); @@ -26,8 +26,7 @@ void main() { }); group('PostBlockActions', () { - test( - 'hasNavigationAction returns true ' + test('hasNavigationAction returns true ' 'when BlockActionType is navigation', () { expect( CustomPostBlock( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_grid_group_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_grid_group_block_test.dart index 7aa331852..8e418d53f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_grid_group_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_grid_group_block_test.dart @@ -4,12 +4,13 @@ import 'package:test/test.dart'; void main() { group('PostGridGroupBlock', () { test('can be (de)serialized', () { + const category = Category(id: 'science', name: 'Science'); final block = PostGridGroupBlock( - category: PostCategory.science, + categoryId: category.id, tiles: [ PostGridTileBlock( id: 'id', - category: PostCategory.science, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 12), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_grid_tile_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_grid_tile_block_test.dart index c94b8ecaa..eebe4ecb4 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_grid_tile_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_grid_tile_block_test.dart @@ -4,9 +4,10 @@ import 'package:test/test.dart'; void main() { group('PostGridTileBlock', () { test('can be (de)serialized', () { + const category = Category(id: 'science', name: 'Science'); final block = PostGridTileBlock( id: 'id', - category: PostCategory.science, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 12), imageUrl: 'imageUrl', @@ -18,7 +19,7 @@ void main() { group('PostGridTitleBlockExt', () { const id = 'id'; - const category = PostCategory.science; + const category = Category(id: 'science', name: 'Science'); const author = 'author'; final publishedAt = DateTime(2022, 3, 12); const imageUrl = 'imageUrl'; @@ -28,7 +29,7 @@ void main() { final gridTile = PostGridTileBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -40,7 +41,7 @@ void main() { test('toPostLargeBlock creates PostLargeBlock instance', () { final largeBlock = PostLargeBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -56,7 +57,7 @@ void main() { test('toPostMediumBlock creates PostMediumBlock instance', () { final mediumBlock = PostMediumBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_large_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_large_block_test.dart index 39275304d..62be91cf8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_large_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_large_block_test.dart @@ -4,9 +4,10 @@ import 'package:test/test.dart'; void main() { group('PostLargeBlock', () { test('can be (de)serialized', () { + const category = Category(id: 'technology', name: 'Technology'); final block = PostLargeBlock( id: 'id', - category: PostCategory.technology, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 9), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_medium_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_medium_block_test.dart index bfa71d3d2..03827632b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_medium_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_medium_block_test.dart @@ -4,9 +4,10 @@ import 'package:test/test.dart'; void main() { group('PostMediumBlock', () { test('can be (de)serialized', () { + const category = Category(id: 'sports', name: 'Sports'); final block = PostMediumBlock( id: 'id', - category: PostCategory.sports, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 10), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_small_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_small_block_test.dart index 4c1d7f260..c545cd895 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_small_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/post_small_block_test.dart @@ -5,9 +5,10 @@ import 'package:test/test.dart'; void main() { group('PostSmallBlock', () { test('can be (de)serialized', () { + const category = Category(id: 'health', name: 'Health'); final block = PostSmallBlock( id: 'id', - category: PostCategory.health, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/section_header_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/section_header_block_test.dart index f11f9671d..088b2c3ad 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/section_header_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/section_header_block_test.dart @@ -6,7 +6,9 @@ import 'package:test/test.dart'; void main() { group('SectionHeaderBlock', () { test('can be (de)serialized', () { - final action = NavigateToFeedCategoryAction(category: Category.top); + const category = Category(id: 'sports', name: 'Sports'); + + final action = NavigateToFeedCategoryAction(category: category); final block = SectionHeaderBlock(title: 'example_title', action: action); expect(SectionHeaderBlock.fromJson(block.toJson()), equals(block)); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/slide_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/slide_block_test.dart index 3d9a1452e..c2b6afa56 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/slide_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/slide_block_test.dart @@ -12,10 +12,7 @@ void main() { imageUrl: 'imageUrl', ); - expect( - SlideBlock.fromJson(block.toJson()), - equals(block), - ); + expect(SlideBlock.fromJson(block.toJson()), equals(block)); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/slideshow_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/slideshow_block_test.dart index 1119b4d99..258458b64 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/slideshow_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/slideshow_block_test.dart @@ -13,10 +13,7 @@ void main() { ); final block = SlideshowBlock(title: 'title', slides: [slide, slide]); - expect( - SlideshowBlock.fromJson(block.toJson()), - equals(block), - ); + expect(SlideshowBlock.fromJson(block.toJson()), equals(block)); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/trending_story_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/trending_story_block_test.dart index e4d9a043f..9e7b0cc5f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/trending_story_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/trending_story_block_test.dart @@ -1,14 +1,13 @@ -// ignore_for_file: prefer_const_constructors - import 'package:news_blocks/news_blocks.dart'; import 'package:test/test.dart'; void main() { group('TrendingStoryBlock', () { test('can be (de)serialized', () { + const category = Category(id: 'health', name: 'Health'); final content = PostSmallBlock( id: 'id', - category: PostCategory.health, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/video_intro_block_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/video_intro_block_test.dart index d1eaca521..4681c872d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/video_intro_block_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/packages/news_blocks/test/src/video_intro_block_test.dart @@ -1,13 +1,12 @@ -// ignore_for_file: prefer_const_constructors - import 'package:news_blocks/news_blocks.dart'; import 'package:test/test.dart'; void main() { group('VideoIntroductionBlock', () { test('can be (de)serialized', () { + const category = Category(id: 'technology', name: 'Technology'); final block = VideoIntroductionBlock( - category: PostCategory.technology, + categoryId: category.id, title: 'title', videoUrl: 'videoUrl', ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/pubspec.lock b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/pubspec.lock index d1aa092c4..b643f094b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/pubspec.lock +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/pubspec.lock @@ -5,31 +5,26 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 + sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e" url: "https://pub.dev" source: hosted - version: "72.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.2" + version: "92.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 + sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e" url: "https://pub.dev" source: hosted - version: "6.7.0" + version: "9.0.0" args: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.7.0" async: dependency: transitive description: @@ -50,18 +45,18 @@ packages: dependency: transitive description: name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "4.0.3" build_config: dependency: transitive description: name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" build_daemon: dependency: transitive description: @@ -70,30 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" - url: "https://pub.dev" - source: hosted - version: "2.4.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + sha256: "110c56ef29b5eb367b4d17fc79375fa8c18a6cd7acd92c05bb3986c17a079057" url: "https://pub.dev" source: hosted - version: "2.4.9" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 - url: "https://pub.dev" - source: hosted - version: "7.2.11" + version: "2.10.4" built_collection: dependency: transitive description: @@ -106,10 +85,10 @@ packages: dependency: transitive description: name: built_value - sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 + sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d url: "https://pub.dev" source: hosted - version: "8.6.3" + version: "8.12.0" checked_yaml: dependency: transitive description: @@ -118,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" code_builder: dependency: transitive description: @@ -130,10 +117,10 @@ packages: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" convert: dependency: transitive description: @@ -146,10 +133,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.15.0" crypto: dependency: transitive description: @@ -162,18 +149,18 @@ packages: dependency: "direct main" description: name: dart_frog - sha256: a5da81b1ea4f3e8f03dfcd0bc9b44ba83a6576ce6dae1bb138a6a8f7b70fe727 + sha256: "4ab2323ad74f935f5f76cb2de7e699d0319245e8029753878b0ddc35984f9cfe" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.6" dart_style: dependency: transitive description: name: dart_style - sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "3.1.3" equatable: dependency: "direct main" description: @@ -226,18 +213,18 @@ packages: dependency: transitive description: name: hotreloader - sha256: "728c0613556c1d153f7e7f4a367cffacc3f5a677d7f6497a1c2b35add4e6dacf" + sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "4.3.0" http: dependency: "direct main" description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.6.0" http_methods: dependency: transitive description: @@ -270,14 +257,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" json_annotation: dependency: "direct main" description: @@ -290,10 +269,10 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: c2fcb3920cf2b6ae6845954186420fca40bc0a8abcc84903b7801f17d7050d7c + sha256: "6b253f7851cf1626a05c8b49c792e04a14897349798c03798137f2b5f7e0b5b1" url: "https://pub.dev" source: hosted - version: "6.9.0" + version: "6.11.3" logging: dependency: transitive description: @@ -302,14 +281,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" - macros: - dependency: transitive - description: - name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" - url: "https://pub.dev" - source: hosted - version: "0.1.2-main.4" matcher: dependency: transitive description: @@ -409,10 +380,10 @@ packages: dependency: transitive description: name: shelf_hotreload - sha256: "5e7a68273d64a095384325d769c6a4885ea15c6aafee361098c0d8a5ecb5dcf6" + sha256: "449f68ce2d087a030c2bfbb40e6925c63fac3dcb502a67388a7139ce72102e7a" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.6.0" shelf_packages_handler: dependency: transitive description: @@ -441,18 +412,18 @@ packages: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "4.1.1" source_helper: dependency: transitive description: name: source_helper - sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + sha256: e82b1996c63da42aa3e6a34cc1ec17427728a1baf72ed017717a5669a7123f0d url: "https://pub.dev" source: hosted - version: "1.3.4" + version: "1.3.9" source_map_stack_trace: dependency: transitive description: @@ -521,34 +492,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "22eb7769bee38c7e032d532e8daa2e1cc901b799f603550a4db8f3a5f5173ea2" + sha256: "77cc98ea27006c84e71a7356cf3daf9ddbde2d91d84f77dbfe64cf0e4d9611ae" url: "https://pub.dev" source: hosted - version: "1.25.12" + version: "1.28.0" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.8" test_core: dependency: transitive description: name: test_core - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + sha256: f1072617a6657e5fc09662e721307f7fb009b4ed89b19f47175d11d5254a62d4 url: "https://pub.dev" source: hosted - version: "0.6.8" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" + version: "0.6.14" typed_data: dependency: transitive description: @@ -561,18 +524,18 @@ packages: dependency: "direct dev" description: name: very_good_analysis - sha256: "1fb637c0022034b1f19ea2acb42a3603cbd8314a470646a59a2fb01f5f3a8629" + sha256: "96245839dbcc45dfab1af5fa551603b5c7a282028a64746c19c547d21a7f1e3a" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "10.0.0" vm_service: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "15.0.2" watcher: dependency: transitive description: @@ -585,10 +548,10 @@ packages: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.1" web_socket_channel: dependency: transitive description: @@ -609,9 +572,9 @@ packages: dependency: transitive description: name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.9.2 <4.0.0" diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/pubspec.yaml index 3f29c92d5..4844dcd15 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/pubspec.yaml @@ -4,23 +4,23 @@ version: 0.1.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: - collection: ^1.16.0 - dart_frog: ^1.1.0 + collection: ^1.19.0 + dart_frog: ^1.2.6 equatable: ^2.0.7 - http: ^1.2.2 + http: ^1.6.0 json_annotation: ^4.9.0 news_blocks: path: packages/news_blocks dev_dependencies: - build_runner: ^2.4.9 - json_serializable: ^6.9.0 + build_runner: ^2.10.4 + json_serializable: ^6.11.3 mocktail: ^1.0.4 - test: ^1.25.12 - very_good_analysis: ^6.0.0 + test: ^1.28.0 + very_good_analysis: ^10.0.0 dependency_overrides: frontend_server_client: ^4.0.0 \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/routes/api/v1/feed/index.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/routes/api/v1/feed/index.dart index 326cdd085..df11efa82 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/routes/api/v1/feed/index.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/routes/api/v1/feed/index.dart @@ -10,15 +10,14 @@ Future onRequest(RequestContext context) async { final queryParams = context.request.url.queryParameters; final categoryQueryParam = queryParams['category']; - final category = Category.values.firstWhere( - (category) => category.name == categoryQueryParam, - orElse: () => Category.top, - ); + final limit = int.tryParse(queryParams['limit'] ?? '') ?? 20; final offset = int.tryParse(queryParams['offset'] ?? '') ?? 0; - final feed = await context - .read() - .getFeed(category: category, limit: limit, offset: offset); + final feed = await context.read().getFeed( + categoryId: categoryQueryParam ?? '', + limit: limit, + offset: offset, + ); final response = FeedResponse( feed: feed.blocks, totalCount: feed.totalBlocks, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/routes/api/v1/subscriptions/index.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/routes/api/v1/subscriptions/index.dart index cbba25dd1..8e9f20498 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/routes/api/v1/subscriptions/index.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/routes/api/v1/subscriptions/index.dart @@ -19,9 +19,9 @@ Future _onPostRequest(RequestContext context) async { } await context.read().createSubscription( - userId: user.id, - subscriptionId: subscriptionId, - ); + userId: user.id, + subscriptionId: subscriptionId, + ); return Response(statusCode: HttpStatus.created); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/_middleware_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/_middleware_test.dart index 282abadaf..de0de1e09 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/_middleware_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/_middleware_test.dart @@ -12,18 +12,17 @@ void main() { test('provides NewsDataSource instance.', () async { final context = _MockRequestContext(); - final handler = middleware( - (_) { - expect(context.read(), isNotNull); - return Response(); - }, - ); + final handler = middleware((_) { + expect(context.read(), isNotNull); + return Response(); + }); final request = Request('GET', Uri.parse('http://127.0.0.1/')); when(() => context.request).thenReturn(request); - when(() => context.read()) - .thenReturn(InMemoryNewsDataSource()); + when( + () => context.read(), + ).thenReturn(InMemoryNewsDataSource()); when(() => context.provide(any())).thenReturn(context); when(() => context.provide(any())).thenReturn(context); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/articles/[id]/index_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/articles/[id]/index_test.dart index 2d15e55d4..b34f51fe5 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/articles/[id]/index_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/articles/[id]/index_test.dart @@ -49,8 +49,7 @@ void main() { expect(response.statusCode, equals(HttpStatus.notFound)); }); - test( - 'responds with a 200 and full article ' + test('responds with a 200 and full article ' 'when article is not premium', () async { final url = Uri.parse('https://dailyglobe.com'); final article = Article( @@ -82,8 +81,7 @@ void main() { expect(await response.json(), equals(expected.toJson())); }); - test( - 'responds with a 200 and article preview ' + test('responds with a 200 and article preview ' 'when article preview is requested', () async { final url = Uri.parse('https://dailyglobe.com'); final article = Article( @@ -108,9 +106,9 @@ void main() { ); final request = Request( 'GET', - Uri.parse('http://127.0.0.1/').replace( - queryParameters: {'preview': 'true'}, - ), + Uri.parse( + 'http://127.0.0.1/', + ).replace(queryParameters: {'preview': 'true'}), ); final context = _MockRequestContext(); when(() => context.request).thenReturn(request); @@ -121,8 +119,7 @@ void main() { expect(await response.json(), equals(expected.toJson())); }); - test( - 'responds with a 200 and article preview ' + test('responds with a 200 and article preview ' 'when article is premium and user is anonymous', () async { final url = Uri.parse('https://dailyglobe.com'); final article = Article( @@ -155,8 +152,7 @@ void main() { expect(await response.json(), equals(expected.toJson())); }); - test( - 'responds with a 200 and article preview ' + test('responds with a 200 and article preview ' 'when article is premium and user is not found', () async { const userId = '__test_user_id__'; final url = Uri.parse('https://dailyglobe.com'); @@ -196,8 +192,7 @@ void main() { expect(await response.json(), equals(expected.toJson())); }); - test( - 'responds with a 200 and article preview ' + test('responds with a 200 and article preview ' 'when article is premium and user has no subscription plan', () async { const userId = '__test_user_id__'; const user = User(id: userId, subscription: SubscriptionPlan.none); @@ -238,8 +233,7 @@ void main() { expect(await response.json(), equals(expected.toJson())); }); - test( - 'responds with a 200 and full article ' + test('responds with a 200 and full article ' 'when article is premium and user has a subscription plan', () async { const userId = '__test_user_id__'; const user = User(id: userId, subscription: SubscriptionPlan.basic); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/categories/index_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/categories/index_test.dart index 9357ee17d..7a70d6f4c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/categories/index_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/categories/index_test.dart @@ -20,7 +20,10 @@ void main() { }); test('responds with a 200 and categories.', () async { - const categories = [Category.sports, Category.entertainment]; + const sportsCategory = Category(id: 'sports', name: 'Sports'); + const sportsEntertainment = Category(id: 'sports', name: 'Sports'); + + const categories = [sportsCategory, sportsEntertainment]; when( () => newsDataSource.getCategories(), ).thenAnswer((_) async => categories); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/feed/index_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/feed/index_test.dart index 6cbad7e02..7dbb95195 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/feed/index_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/feed/index_test.dart @@ -18,10 +18,6 @@ void main() { group('GET /api/v1/feed', () { late NewsDataSource newsDataSource; - setUpAll(() { - registerFallbackValue(Category.top); - }); - setUp(() { newsDataSource = _MockNewsDataSource(); }); @@ -33,7 +29,7 @@ void main() { when(() => feed.totalBlocks).thenReturn(blocks.length); when( () => newsDataSource.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), limit: any(named: 'limit'), offset: any(named: 'offset'), ), @@ -50,7 +46,8 @@ void main() { }); test('parses category, limit, and offset correctly.', () async { - const category = Category.entertainment; + const category = Category(id: 'sports', name: 'Sports'); + const limit = 42; const offset = 7; final blocks = []; @@ -59,7 +56,7 @@ void main() { when(() => feed.totalBlocks).thenReturn(blocks.length); when( () => newsDataSource.getFeed( - category: any(named: 'category'), + categoryId: category.id, limit: any(named: 'limit'), offset: any(named: 'offset'), ), @@ -70,7 +67,7 @@ void main() { 'GET', Uri.parse('http://127.0.0.1/').replace( queryParameters: { - 'category': category.name, + 'category': category.id, 'limit': '$limit', 'offset': '$offset', }, @@ -84,7 +81,7 @@ void main() { expect(await response.json(), equals(expected.toJson())); verify( () => newsDataSource.getFeed( - category: category, + categoryId: category.id, limit: limit, offset: offset, ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/search/popular_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/search/popular_test.dart index dd53a8deb..c15e23c0c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/search/popular_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/search/popular_test.dart @@ -25,9 +25,9 @@ void main() { articles: popularArticles.map((item) => item.post).toList(), topics: popularTopics, ); - when(() => newsDataSource.getPopularArticles()).thenAnswer( - (_) async => expected.articles, - ); + when( + () => newsDataSource.getPopularArticles(), + ).thenAnswer((_) async => expected.articles); when( () => newsDataSource.getPopularTopics(), ).thenAnswer((_) async => expected.topics); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/search/relevant_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/search/relevant_test.dart index 67e364967..7b615b27f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/search/relevant_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/search/relevant_test.dart @@ -44,9 +44,9 @@ void main() { ).thenAnswer((_) async => expected.topics); final request = Request( 'GET', - Uri.parse('http://127.0.0.1/').replace( - queryParameters: {'q': term}, - ), + Uri.parse( + 'http://127.0.0.1/', + ).replace(queryParameters: {'q': term}), ); final context = _MockRequestContext(); when(() => context.request).thenReturn(request); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/subscriptions/index_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/subscriptions/index_test.dart index a5b787f5b..ff24730d0 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/subscriptions/index_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/routes/subscriptions/index_test.dart @@ -27,10 +27,7 @@ void main() { final subscription = Subscription( id: 'a', name: SubscriptionPlan.plus, - cost: SubscriptionCost( - annual: 4200, - monthly: 1200, - ), + cost: SubscriptionCost(annual: 4200, monthly: 1200), benefits: const ['benefitA'], ); @@ -76,9 +73,7 @@ void main() { final request = Request( 'POST', Uri.parse('http://127.0.0.1/').replace( - queryParameters: { - 'subscriptionId': subscriptionId, - }, + queryParameters: {'subscriptionId': subscriptionId}, ), ); final user = _MockRequestUser(); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/client/{{#snakeCase}}{{project_name}}{{/snakeCase}}_api_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/client/{{#snakeCase}}{{project_name}}{{/snakeCase}}_api_client_test.dart index 63c5d3066..53b622693 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/client/{{#snakeCase}}{{project_name}}{{/snakeCase}}_api_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/client/{{#snakeCase}}{{project_name}}{{/snakeCase}}_api_client_test.dart @@ -67,8 +67,9 @@ void main() { }); test('has correct baseUrl', () async { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response( jsonEncode(const FeedResponse(feed: [], totalCount: 0)), HttpStatus.ok, @@ -99,8 +100,9 @@ void main() { }); test('has correct baseUrl.', () async { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response( jsonEncode(const FeedResponse(feed: [], totalCount: 0)), HttpStatus.ok, @@ -114,11 +116,7 @@ void main() { await apiClient.getFeed(); verify( () => httpClient.get( - any( - that: isAUriHaving( - authority: '{{api_url}}', - ), - ), + any(that: isAUriHaving(authority: '{{api_url}}')), headers: any(named: 'headers', that: areJsonHeaders()), ), ).called(1); @@ -140,8 +138,9 @@ void main() { const path = '/api/v1/articles/$articleId'; const query = 'preview=false'; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(articleResponse), HttpStatus.ok), ); @@ -150,7 +149,9 @@ void main() { verify( () => httpClient.get( - any(that: isAUriHaving(path: path, query: query)), + any( + that: isAUriHaving(path: path, query: query), + ), headers: any(named: 'headers', that: areJsonHeaders()), ), ).called(1); @@ -163,8 +164,9 @@ void main() { const path = '/api/v1/articles/$articleId'; const query = 'limit=$limit&offset=$offset&preview=true'; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(articleResponse), HttpStatus.ok), ); @@ -178,7 +180,9 @@ void main() { verify( () => httpClient.get( - any(that: isAUriHaving(path: path, query: query)), + any( + that: isAUriHaving(path: path, query: query), + ), headers: any(named: 'headers', that: areJsonHeaders()), ), ).called(1); @@ -191,8 +195,9 @@ void main() { tokenProvider = () async => token; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(articleResponse), HttpStatus.ok), ); @@ -204,7 +209,9 @@ void main() { verify( () => httpClient.get( - any(that: isAUriHaving(path: path, query: query)), + any( + that: isAUriHaving(path: path, query: query), + ), headers: any( named: 'headers', that: areJsonHeaders(authorizationToken: token), @@ -213,14 +220,12 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiMalformedResponse ' + test('throws {{project_name.pascalCase()}}ApiMalformedResponse ' 'when response body is malformed.', () { const articleId = '__article_id__'; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response('', HttpStatus.ok), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response('', HttpStatus.ok)); expect( () => apiClient.getArticle(id: articleId), @@ -228,16 +233,14 @@ void main() { ); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-200 status code.', () { const articleId = '__article_id__'; const statusCode = HttpStatus.internalServerError; final body = {}; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response(json.encode(body), statusCode), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response(json.encode(body), statusCode)); expect( () => apiClient.getArticle(id: articleId), @@ -259,8 +262,9 @@ void main() { isPremium: false, isPreview: false, ); - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response( json.encode(expectedResponse.toJson()), HttpStatus.ok, @@ -285,8 +289,9 @@ void main() { const path = '/api/v1/articles/$articleId/related'; const query = ''; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(relatedArticlesResponse), HttpStatus.ok), ); @@ -295,7 +300,9 @@ void main() { verify( () => httpClient.get( - any(that: isAUriHaving(path: path, query: query)), + any( + that: isAUriHaving(path: path, query: query), + ), headers: any(named: 'headers', that: areJsonHeaders()), ), ).called(1); @@ -308,8 +315,9 @@ void main() { const path = '/api/v1/articles/$articleId/related'; const query = 'limit=$limit&offset=$offset'; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(relatedArticlesResponse), HttpStatus.ok), ); @@ -322,7 +330,9 @@ void main() { verify( () => httpClient.get( - any(that: isAUriHaving(path: path, query: query)), + any( + that: isAUriHaving(path: path, query: query), + ), headers: any(named: 'headers', that: areJsonHeaders()), ), ).called(1); @@ -335,8 +345,9 @@ void main() { tokenProvider = () async => token; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(relatedArticlesResponse), HttpStatus.ok), ); @@ -348,7 +359,9 @@ void main() { verify( () => httpClient.get( - any(that: isAUriHaving(path: path, query: query)), + any( + that: isAUriHaving(path: path, query: query), + ), headers: any( named: 'headers', that: areJsonHeaders(authorizationToken: token), @@ -357,14 +370,12 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiMalformedResponse ' + test('throws {{project_name.pascalCase()}}ApiMalformedResponse ' 'when response body is malformed.', () { const articleId = '__article_id__'; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response('', HttpStatus.ok), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response('', HttpStatus.ok)); expect( () => apiClient.getRelatedArticles(id: articleId), @@ -372,16 +383,14 @@ void main() { ); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-200 status code.', () { const articleId = '__article_id__'; const statusCode = HttpStatus.internalServerError; final body = {}; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response(json.encode(body), statusCode), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response(json.encode(body), statusCode)); expect( () => apiClient.getRelatedArticles(id: articleId), @@ -399,8 +408,9 @@ void main() { relatedArticles: [], totalCount: 0, ); - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response( json.encode(expectedResponse.toJson()), HttpStatus.ok, @@ -415,17 +425,15 @@ void main() { }); group('getFeed', () { - const feedResponse = FeedResponse( - feed: [], - totalCount: 0, - ); + const feedResponse = FeedResponse(feed: [], totalCount: 0); test('makes correct http request (no query params).', () async { const path = '/api/v1/feed'; const query = ''; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(feedResponse), HttpStatus.ok), ); @@ -433,33 +441,38 @@ void main() { verify( () => httpClient.get( - any(that: isAUriHaving(path: path, query: query)), + any( + that: isAUriHaving(path: path, query: query), + ), headers: any(named: 'headers', that: areJsonHeaders()), ), ).called(1); }); test('makes correct http request (with query params).', () async { - const category = Category.science; + const category = Category(id: 'sports', name: 'Sports'); const limit = 42; const offset = 7; const path = '/api/v1/feed'; - final query = 'category=${category.name}&limit=$limit&offset=$offset'; + final query = 'category=${category.id}&limit=$limit&offset=$offset'; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(feedResponse), HttpStatus.ok), ); await apiClient.getFeed( - category: category, + categoryId: category.id, limit: limit, offset: offset, ); verify( () => httpClient.get( - any(that: isAUriHaving(path: path, query: query)), + any( + that: isAUriHaving(path: path, query: query), + ), headers: any(named: 'headers', that: areJsonHeaders()), ), ).called(1); @@ -471,8 +484,9 @@ void main() { tokenProvider = () async => token; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(feedResponse), HttpStatus.ok), ); @@ -483,7 +497,9 @@ void main() { verify( () => httpClient.get( - any(that: isAUriHaving(path: path, query: query)), + any( + that: isAUriHaving(path: path, query: query), + ), headers: any( named: 'headers', that: areJsonHeaders(authorizationToken: token), @@ -492,13 +508,11 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiMalformedResponse ' + test('throws {{project_name.pascalCase()}}ApiMalformedResponse ' 'when response body is malformed.', () { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response('', HttpStatus.ok), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response('', HttpStatus.ok)); expect( apiClient.getFeed, @@ -506,15 +520,13 @@ void main() { ); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-200 status code.', () { const statusCode = HttpStatus.internalServerError; final body = {}; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response(json.encode(body), statusCode), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response(json.encode(body), statusCode)); expect( apiClient.getFeed, @@ -528,8 +540,9 @@ void main() { test('returns a FeedResponse on a 200 response.', () { const expectedResponse = FeedResponse(feed: [], totalCount: 0); - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response( json.encode(expectedResponse.toJson()), HttpStatus.ok, @@ -544,8 +557,9 @@ void main() { const categoriesResponse = CategoriesResponse(categories: []); test('makes correct http request.', () async { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(categoriesResponse), HttpStatus.ok), ); @@ -563,8 +577,9 @@ void main() { test('makes correct http request (with authorization token).', () async { tokenProvider = () async => token; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(categoriesResponse), HttpStatus.ok), ); @@ -585,13 +600,11 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiMalformedResponse ' + test('throws {{project_name.pascalCase()}}ApiMalformedResponse ' 'when response body is malformed.', () { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response('', HttpStatus.ok), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response('', HttpStatus.ok)); expect( apiClient.getCategories, @@ -599,15 +612,13 @@ void main() { ); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-200 status code.', () { const statusCode = HttpStatus.internalServerError; final body = {}; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response(json.encode(body), statusCode), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response(json.encode(body), statusCode)); expect( apiClient.getCategories, @@ -620,11 +631,15 @@ void main() { }); test('returns a CategoriesResponse on a 200 response.', () { + const sportsCategory = Category(id: 'sports', name: 'Sports'); + const topCategory = Category(id: 'top', name: 'Top'); + const expectedResponse = CategoriesResponse( - categories: [Category.business, Category.top], + categories: [sportsCategory, topCategory], ); - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response( json.encode(expectedResponse.toJson()), HttpStatus.ok, @@ -644,10 +659,8 @@ void main() { when( () => httpClient.get(any(), headers: any(named: 'headers')), ).thenAnswer( - (_) async => http.Response( - jsonEncode(currentUserResponse), - HttpStatus.ok, - ), + (_) async => + http.Response(jsonEncode(currentUserResponse), HttpStatus.ok), ); await apiClient.getCurrentUser(); @@ -666,10 +679,8 @@ void main() { when( () => httpClient.get(any(), headers: any(named: 'headers')), ).thenAnswer( - (_) async => http.Response( - jsonEncode(currentUserResponse), - HttpStatus.ok, - ), + (_) async => + http.Response(jsonEncode(currentUserResponse), HttpStatus.ok), ); await {{project_name.pascalCase()}}ApiClient( @@ -688,13 +699,11 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiMalformedResponse ' + test('throws {{project_name.pascalCase()}}ApiMalformedResponse ' 'when response body is malformed.', () { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response('', HttpStatus.ok), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response('', HttpStatus.ok)); expect( apiClient.getCurrentUser, @@ -702,15 +711,13 @@ void main() { ); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-200 status code.', () { const statusCode = HttpStatus.internalServerError; final body = {}; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response(json.encode(body), statusCode), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response(json.encode(body), statusCode)); expect( apiClient.getCurrentUser, @@ -726,8 +733,9 @@ void main() { const expectedResponse = CurrentUserResponse( user: User(id: 'id', subscription: SubscriptionPlan.basic), ); - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response( json.encode(expectedResponse.toJson()), HttpStatus.ok, @@ -746,10 +754,8 @@ void main() { test('makes correct http request.', () async { when(() => httpClient.get(any())).thenAnswer( - (_) async => http.Response( - jsonEncode(subscriptionsResponse), - HttpStatus.ok, - ), + (_) async => + http.Response(jsonEncode(subscriptionsResponse), HttpStatus.ok), ); await apiClient.getSubscriptions(); @@ -761,12 +767,11 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiMalformedResponse ' + test('throws {{project_name.pascalCase()}}ApiMalformedResponse ' 'when response body is malformed.', () { - when(() => httpClient.get(any())).thenAnswer( - (_) async => http.Response('', HttpStatus.ok), - ); + when( + () => httpClient.get(any()), + ).thenAnswer((_) async => http.Response('', HttpStatus.ok)); expect( apiClient.getSubscriptions, @@ -774,14 +779,13 @@ void main() { ); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-200 status code.', () { const statusCode = HttpStatus.internalServerError; final body = {}; - when(() => httpClient.get(any())).thenAnswer( - (_) async => http.Response(json.encode(body), statusCode), - ); + when( + () => httpClient.get(any()), + ).thenAnswer((_) async => http.Response(json.encode(body), statusCode)); expect( apiClient.getSubscriptions, @@ -825,8 +829,9 @@ void main() { ); test('makes correct http request.', () async { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(popularSearchResponse), HttpStatus.ok), ); @@ -844,8 +849,9 @@ void main() { test('makes correct http request (with authorization token).', () async { tokenProvider = () async => token; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(popularSearchResponse), HttpStatus.ok), ); @@ -866,13 +872,11 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiMalformedResponse ' + test('throws {{project_name.pascalCase()}}ApiMalformedResponse ' 'when response body is malformed.', () { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response('', HttpStatus.ok), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response('', HttpStatus.ok)); expect( apiClient.popularSearch, @@ -880,15 +884,13 @@ void main() { ); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-200 status code.', () { const statusCode = HttpStatus.internalServerError; final body = {}; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response(json.encode(body), statusCode), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response(json.encode(body), statusCode)); expect( apiClient.popularSearch, @@ -905,8 +907,9 @@ void main() { articles: [], topics: [], ); - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response( json.encode(expectedResponse.toJson()), HttpStatus.ok, @@ -926,8 +929,9 @@ void main() { ); test('makes correct http request.', () async { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(relevantSearchResponse), HttpStatus.ok), ); @@ -950,8 +954,9 @@ void main() { test('makes correct http request (with authorization token).', () async { tokenProvider = () async => token; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response(jsonEncode(relevantSearchResponse), HttpStatus.ok), ); @@ -977,13 +982,11 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiMalformedResponse ' + test('throws {{project_name.pascalCase()}}ApiMalformedResponse ' 'when response body is malformed.', () { - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response('', HttpStatus.ok), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response('', HttpStatus.ok)); expect( () => apiClient.relevantSearch(term: term), @@ -991,15 +994,13 @@ void main() { ); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-200 status code.', () { const statusCode = HttpStatus.internalServerError; final body = {}; - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response(json.encode(body), statusCode), - ); + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response(json.encode(body), statusCode)); expect( () => apiClient.relevantSearch(term: term), @@ -1016,8 +1017,9 @@ void main() { articles: [], topics: [], ); - when(() => httpClient.get(any(), headers: any(named: 'headers'))) - .thenAnswer( + when( + () => httpClient.get(any(), headers: any(named: 'headers')), + ).thenAnswer( (_) async => http.Response( json.encode(expectedResponse.toJson()), HttpStatus.ok, @@ -1041,9 +1043,7 @@ void main() { body: any(named: 'body'), headers: any(named: 'headers'), ), - ).thenAnswer( - (_) async => http.Response('', HttpStatus.created), - ); + ).thenAnswer((_) async => http.Response('', HttpStatus.created)); await apiClient.subscribeToNewsletter(email: email); @@ -1065,9 +1065,7 @@ void main() { body: any(named: 'body'), headers: any(named: 'headers'), ), - ).thenAnswer( - (_) async => http.Response('', HttpStatus.created), - ); + ).thenAnswer((_) async => http.Response('', HttpStatus.created)); await {{project_name.pascalCase()}}ApiClient( httpClient: httpClient, @@ -1086,8 +1084,7 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-201 status code.', () { const statusCode = HttpStatus.internalServerError; when( @@ -1096,9 +1093,7 @@ void main() { body: any(named: 'body'), headers: any(named: 'headers'), ), - ).thenAnswer( - (_) async => http.Response('', statusCode), - ); + ).thenAnswer((_) async => http.Response('', statusCode)); expect( () => apiClient.subscribeToNewsletter(email: email), @@ -1117,9 +1112,7 @@ void main() { body: any(named: 'body'), headers: any(named: 'headers'), ), - ).thenAnswer( - (_) async => http.Response('', HttpStatus.created), - ); + ).thenAnswer((_) async => http.Response('', HttpStatus.created)); expect(apiClient.subscribeToNewsletter(email: email), completes); }); @@ -1132,9 +1125,7 @@ void main() { when( () => httpClient.post(any(), headers: any(named: 'headers')), - ).thenAnswer( - (_) async => http.Response('', HttpStatus.created), - ); + ).thenAnswer((_) async => http.Response('', HttpStatus.created)); await apiClient.createSubscription(subscriptionId: subscriptionId); @@ -1156,9 +1147,7 @@ void main() { when( () => httpClient.post(any(), headers: any(named: 'headers')), - ).thenAnswer( - (_) async => http.Response('', HttpStatus.created), - ); + ).thenAnswer((_) async => http.Response('', HttpStatus.created)); await {{project_name.pascalCase()}}ApiClient( httpClient: httpClient, @@ -1178,14 +1167,12 @@ void main() { ).called(1); }); - test( - 'throws {{project_name.pascalCase()}}ApiRequestFailure ' + test('throws {{project_name.pascalCase()}}ApiRequestFailure ' 'when response has a non-201 status code.', () { const statusCode = HttpStatus.internalServerError; - when(() => httpClient.post(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response('', statusCode), - ); + when( + () => httpClient.post(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response('', statusCode)); expect( () => apiClient.createSubscription(subscriptionId: 'subscriptionId'), @@ -1198,10 +1185,9 @@ void main() { }); test('resolves on a 201 response.', () { - when(() => httpClient.post(any(), headers: any(named: 'headers'))) - .thenAnswer( - (_) async => http.Response('', HttpStatus.created), - ); + when( + () => httpClient.post(any(), headers: any(named: 'headers')), + ).thenAnswer((_) async => http.Response('', HttpStatus.created)); expect( apiClient.createSubscription(subscriptionId: 'subscriptionId'), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/data/news_data_source_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/data/news_data_source_test.dart index bfef18d35..16cf8665c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/data/news_data_source_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/data/news_data_source_test.dart @@ -31,7 +31,7 @@ class MyNewsDataSource extends NewsDataSource { @override Future getFeed({ - Category category = Category.top, + required String categoryId, int limit = 20, int offset = 0, }) { @@ -83,48 +83,42 @@ class MyNewsDataSource extends NewsDataSource { void main() { Matcher articleHaving({required List blocks, int? totalBlocks}) { - return predicate
( - (article) { - totalBlocks ??= article.totalBlocks; - if (blocks.length != article.blocks.length) return false; - if (totalBlocks != article.totalBlocks) return false; - for (var i = 0; i < blocks.length; i++) { - if (blocks[i] != article.blocks[i]) return false; - } - return true; - }, - ); + return predicate
((article) { + totalBlocks ??= article.totalBlocks; + if (blocks.length != article.blocks.length) return false; + if (totalBlocks != article.totalBlocks) return false; + for (var i = 0; i < blocks.length; i++) { + if (blocks[i] != article.blocks[i]) return false; + } + return true; + }); } Matcher relatedArticlesHaving({ required List blocks, int? totalBlocks, }) { - return predicate( - (relatedArticles) { - totalBlocks ??= relatedArticles.totalBlocks; - if (blocks.length != relatedArticles.blocks.length) return false; - if (totalBlocks != relatedArticles.totalBlocks) return false; - for (var i = 0; i < blocks.length; i++) { - if (blocks[i] != relatedArticles.blocks[i]) return false; - } - return true; - }, - ); + return predicate((relatedArticles) { + totalBlocks ??= relatedArticles.totalBlocks; + if (blocks.length != relatedArticles.blocks.length) return false; + if (totalBlocks != relatedArticles.totalBlocks) return false; + for (var i = 0; i < blocks.length; i++) { + if (blocks[i] != relatedArticles.blocks[i]) return false; + } + return true; + }); } Matcher feedHaving({required List blocks, int? totalBlocks}) { - return predicate( - (feed) { - totalBlocks ??= feed.totalBlocks; - if (blocks.length != feed.blocks.length) return false; - if (totalBlocks != feed.totalBlocks) return false; - for (var i = 0; i < blocks.length; i++) { - if (blocks[i] != feed.blocks[i]) return false; - } - return true; - }, - ); + return predicate((feed) { + totalBlocks ??= feed.totalBlocks; + if (blocks.length != feed.blocks.length) return false; + if (totalBlocks != feed.totalBlocks) return false; + for (var i = 0; i < blocks.length; i++) { + if (blocks[i] != feed.blocks[i]) return false; + } + return true; + }); } Matcher isAnEmptyFeed() { @@ -168,8 +162,7 @@ void main() { }); group('getUser', () { - test( - 'completes with empty user ' + test('completes with empty user ' 'when subscription does not exist', () async { const userId = 'userId'; expect( @@ -187,60 +180,52 @@ void main() { ); expect( newsDataSource.getUser(userId: userId), - completion( - equals( - User(id: userId, subscription: subscription.name), - ), - ), + completion(equals(User(id: userId, subscription: subscription.name))), ); }); }); group('getFeed', () { - test('returns stubbed feed (default category)', () { - expect( - newsDataSource.getFeed(limit: 100), - completion(feedHaving(blocks: topNewsFeedBlocks)), + test('returns stubbed feed (Category.technology)', () { + final technologyCategory = Category( + id: 'technology', + name: 'Technology', ); - }); - test('returns stubbed feed (Category.technology)', () { expect( - newsDataSource.getFeed(category: Category.technology), + newsDataSource.getFeed(categoryId: technologyCategory.id), completion(feedHaving(blocks: technologyFeedBlocks)), ); }); test('returns stubbed feed (Category.sports)', () { + final sportsCategory = Category(id: 'sports', name: 'Sports'); + expect( - newsDataSource.getFeed(category: Category.sports), + newsDataSource.getFeed(categoryId: sportsCategory.id), completion(feedHaving(blocks: sportsFeedBlocks)), ); }); - test('returns empty feed for remaining categories', () async { - final emptyCategories = [ - Category.business, - Category.entertainment, - ]; - for (final category in emptyCategories) { - await expectLater( - newsDataSource.getFeed(category: category), - completion(isAnEmptyFeed()), - ); - } + test('returns empty feed for unknown category', () async { + expect( + newsDataSource.getFeed(categoryId: 'categoryUnknown'), + completion(isAnEmptyFeed()), + ); }); test('returns correct feed when limit is specified', () { + final topCategory = Category(id: 'top', name: 'Top'); + expect( - newsDataSource.getFeed(limit: 0), + newsDataSource.getFeed(categoryId: topCategory.id, limit: 0), completion( feedHaving(blocks: [], totalBlocks: topNewsFeedBlocks.length), ), ); expect( - newsDataSource.getFeed(limit: 1), + newsDataSource.getFeed(categoryId: topCategory.id, limit: 1), completion( feedHaving( blocks: topNewsFeedBlocks.take(1).toList(), @@ -250,7 +235,7 @@ void main() { ); expect( - newsDataSource.getFeed(limit: 100), + newsDataSource.getFeed(categoryId: topCategory.id, limit: 100), completion( feedHaving( blocks: topNewsFeedBlocks, @@ -261,8 +246,14 @@ void main() { }); test('returns correct feed when offset is specified', () { + final topCategory = Category(id: 'top', name: 'Top'); + expect( - newsDataSource.getFeed(offset: 1, limit: 100), + newsDataSource.getFeed( + categoryId: topCategory.id, + offset: 1, + limit: 100, + ), completion( feedHaving( blocks: topNewsFeedBlocks.sublist(1), @@ -272,7 +263,11 @@ void main() { ); expect( - newsDataSource.getFeed(offset: 2, limit: 100), + newsDataSource.getFeed( + categoryId: topCategory.id, + offset: 2, + limit: 100, + ), completion( feedHaving( blocks: topNewsFeedBlocks.sublist(2), @@ -282,12 +277,13 @@ void main() { ); expect( - newsDataSource.getFeed(offset: 100, limit: 100), + newsDataSource.getFeed( + categoryId: topCategory.id, + offset: 100, + limit: 100, + ), completion( - feedHaving( - blocks: [], - totalBlocks: topNewsFeedBlocks.length, - ), + feedHaving(blocks: [], totalBlocks: topNewsFeedBlocks.length), ), ); }); @@ -298,11 +294,11 @@ void main() { expect( newsDataSource.getCategories(), completion([ - Category.top, - Category.technology, - Category.sports, - Category.health, - Category.science, + Category(id: 'top', name: 'Top'), + Category(id: 'sports', name: 'Sports'), + Category(id: 'technology', name: 'Technology'), + Category(id: 'health', name: 'Health'), + Category(id: 'science', name: 'Science'), ]), ); }); @@ -316,8 +312,7 @@ void main() { ); }); - test( - 'returns content when article exists ' + test('returns content when article exists ' 'and preview is false', () { final item = healthItems.first; expect( @@ -331,8 +326,7 @@ void main() { ); }); - test( - 'returns content preview when article exists ' + test('returns content preview when article exists ' 'and preview is true', () { final item = healthItems.first; expect( @@ -381,8 +375,7 @@ void main() { ); }); - test( - 'returns true when article exists ' + test('returns true when article exists ' 'and isPremium is true', () { final item = technologySmallItems.last; expect( @@ -391,8 +384,7 @@ void main() { ); }); - test( - 'returns false when article exists ' + test('returns false when article exists ' 'and isPremium is false', () { final item = healthItems.last; expect( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/news_data_source_provider_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/news_data_source_provider_test.dart index 9b4e72405..c8d92149c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/news_data_source_provider_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/news_data_source_provider_test.dart @@ -8,16 +8,12 @@ void main() { group('newsDataSourceProvider', () { test('provides a NewsDataSource instance', () async { NewsDataSource? value; - final context = TestRequestContext( - path: 'http://localhost/', - ); + final context = TestRequestContext(path: 'http://localhost/'); - final handler = newsDataSourceProvider()( - (_) { - value = context.read(); - return Response(body: ''); - }, - ); + final handler = newsDataSourceProvider()((_) { + value = context.read(); + return Response(body: ''); + }); context.mockProvide(InMemoryNewsDataSource()); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/test_request_context.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/test_request_context.dart index e44e4653b..e2bce2e28 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/test_request_context.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/test_request_context.dart @@ -34,11 +34,8 @@ class TestRequestContext implements RequestContext { } @override - Request get request => Request( - method.name.toUpperCase(), - Uri.parse(path), - headers: headers, - ); + Request get request => + Request(method.name.toUpperCase(), Uri.parse(path), headers: headers); @override Map get mountedParams => {}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/user_provider_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/user_provider_test.dart index edba37f30..fa8df7d7f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/user_provider_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/api/test/src/middleware/user_provider_test.dart @@ -9,20 +9,15 @@ class _MockRequestUser extends Mock implements RequestUser {} void main() { group('userProvider', () { - test( - 'provides RequestUser.anonymous ' + test('provides RequestUser.anonymous ' 'when authorization header is missing.', () async { - final context = TestRequestContext( - path: 'http://localhost/', - ); + final context = TestRequestContext(path: 'http://localhost/'); RequestUser? value; - final handler = userProvider()( - (_) { - value = context.read(); - return Response(body: ''); - }, - ); + final handler = userProvider()((_) { + value = context.read(); + return Response(body: ''); + }); context.mockProvide(RequestUser.anonymous); @@ -30,20 +25,17 @@ void main() { expect(value, equals(RequestUser.anonymous)); }); - test( - 'provides RequestUser.anonymous ' + test('provides RequestUser.anonymous ' 'when authorization header is malformed (no bearer).', () async { final context = TestRequestContext( path: 'http://localhost/', headers: {'Authorization': 'some token'}, ); RequestUser? value; - final handler = userProvider()( - (_) { - value = context.read(); - return Response(body: ''); - }, - ); + final handler = userProvider()((_) { + value = context.read(); + return Response(body: ''); + }); context.mockProvide(RequestUser.anonymous); @@ -54,32 +46,30 @@ void main() { }); test( - 'provides RequestUser.anonymous ' - 'when authorization header is malformed (too many segments).', - () async { - final context = TestRequestContext( - path: 'http://localhost/', - headers: {'Authorization': 'bearer some token'}, - ); - - RequestUser? value; - final handler = userProvider()( - (_) { + 'provides RequestUser.anonymous ' + 'when authorization header is malformed (too many segments).', + () async { + final context = TestRequestContext( + path: 'http://localhost/', + headers: {'Authorization': 'bearer some token'}, + ); + + RequestUser? value; + final handler = userProvider()((_) { value = context.read(); return Response(body: ''); - }, - ); + }); - context.mockProvide(RequestUser.anonymous); + context.mockProvide(RequestUser.anonymous); - await handler(context); + await handler(context); - expect(value, equals(RequestUser.anonymous)); - expect(value!.isAnonymous, isTrue); - }); + expect(value, equals(RequestUser.anonymous)); + expect(value!.isAnonymous, isTrue); + }, + ); - test( - 'provides correct RequestUser ' + test('provides correct RequestUser ' 'when authorization header is valid.', () async { const userId = '__user_id__'; @@ -93,12 +83,10 @@ void main() { context.mockProvide(requestUser); RequestUser? value; - final handler = userProvider()( - (_) { - value = context.read(); - return Response(body: ''); - }, - ); + final handler = userProvider()((_) { + value = context.read(); + return Response(body: ''); + }); await handler(context); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/ios/Flutter/AppFrameworkInfo.plist b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/ios/Flutter/AppFrameworkInfo.plist index 4f8d4d245..8c6e56146 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/ios/Flutter/AppFrameworkInfo.plist +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/ios/Podfile b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/ios/Podfile index 7f1403aaa..09e5d3fce 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/ios/Podfile +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/ios/Podfile @@ -28,7 +28,7 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup target 'Runner' do - pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '11.0.0' + pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '11.4.0' use_frameworks! use_modular_headers! diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/bloc/full_screen_ads_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/bloc/full_screen_ads_bloc.dart index ae5e860d7..9d7da0185 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/bloc/full_screen_ads_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/bloc/full_screen_ads_bloc.dart @@ -10,18 +10,20 @@ part 'full_screen_ads_event.dart'; part 'full_screen_ads_state.dart'; /// Signature for the interstitial ad loader. -typedef InterstitialAdLoader = Future Function({ - required String adUnitId, - required ads.InterstitialAdLoadCallback adLoadCallback, - required ads.AdRequest request, -}); +typedef InterstitialAdLoader = + Future Function({ + required String adUnitId, + required ads.InterstitialAdLoadCallback adLoadCallback, + required ads.AdRequest request, + }); /// Signature for the rewarded ad loader. -typedef RewardedAdLoader = Future Function({ - required String adUnitId, - required ads.RewardedAdLoadCallback rewardedAdLoadCallback, - required ads.AdRequest request, -}); +typedef RewardedAdLoader = + Future Function({ + required String adUnitId, + required ads.RewardedAdLoadCallback rewardedAdLoadCallback, + required ads.AdRequest request, + }); /// A bloc that manages pre-loading and showing interstitial and rewarded ads /// with an [_adsRetryPolicy]. @@ -32,13 +34,13 @@ class FullScreenAdsBloc extends Bloc { required RewardedAdLoader rewardedAdLoader, required LocalPlatform localPlatform, FullScreenAdsConfig? fullScreenAdsConfig, - }) : _adsRetryPolicy = adsRetryPolicy, - _interstitialAdLoader = interstitialAdLoader, - _rewardedAdLoader = rewardedAdLoader, - _localPlatform = localPlatform, - _fullScreenAdsConfig = - fullScreenAdsConfig ?? const FullScreenAdsConfig(), - super(const FullScreenAdsState.initial()) { + }) : _adsRetryPolicy = adsRetryPolicy, + _interstitialAdLoader = interstitialAdLoader, + _rewardedAdLoader = rewardedAdLoader, + _localPlatform = localPlatform, + _fullScreenAdsConfig = + fullScreenAdsConfig ?? const FullScreenAdsConfig(), + super(const FullScreenAdsState.initial()) { on(_onLoadInterstitialAdRequested); on(_onLoadRewardedAdRequested); on(_onShowInterstitialAdRequested); @@ -71,7 +73,8 @@ class FullScreenAdsBloc extends Bloc { emit(state.copyWith(status: FullScreenAdsStatus.loadingInterstitialAd)); await _interstitialAdLoader( - adUnitId: _fullScreenAdsConfig.interstitialAdUnitId ?? + adUnitId: + _fullScreenAdsConfig.interstitialAdUnitId ?? (_localPlatform.isAndroid ? FullScreenAdsConfig.androidTestInterstitialAdUnitId : FullScreenAdsConfig.iosTestInterstitialAdUnitId), @@ -90,11 +93,9 @@ class FullScreenAdsBloc extends Bloc { status: FullScreenAdsStatus.loadingInterstitialAdSucceeded, ), ); - } catch (error, stackTrace) { + } on Object catch (error, stackTrace) { emit( - state.copyWith( - status: FullScreenAdsStatus.loadingInterstitialAdFailed, - ), + state.copyWith(status: FullScreenAdsStatus.loadingInterstitialAdFailed), ); addError(error, stackTrace); @@ -119,7 +120,8 @@ class FullScreenAdsBloc extends Bloc { emit(state.copyWith(status: FullScreenAdsStatus.loadingRewardedAd)); await _rewardedAdLoader( - adUnitId: _fullScreenAdsConfig.rewardedAdUnitId ?? + adUnitId: + _fullScreenAdsConfig.rewardedAdUnitId ?? (_localPlatform.isAndroid ? FullScreenAdsConfig.androidTestRewardedAdUnitId : FullScreenAdsConfig.iosTestRewardedAdUnitId), @@ -138,12 +140,8 @@ class FullScreenAdsBloc extends Bloc { status: FullScreenAdsStatus.loadingRewardedAdSucceeded, ), ); - } catch (error, stackTrace) { - emit( - state.copyWith( - status: FullScreenAdsStatus.loadingRewardedAdFailed, - ), - ); + } on Object catch (error, stackTrace) { + emit(state.copyWith(status: FullScreenAdsStatus.loadingRewardedAdFailed)); addError(error, stackTrace); @@ -166,12 +164,12 @@ class FullScreenAdsBloc extends Bloc { state.interstitialAd?.fullScreenContentCallback = ads.FullScreenContentCallback( - onAdDismissedFullScreenContent: (ad) => ad.dispose(), - onAdFailedToShowFullScreenContent: (ad, error) { - ad.dispose(); - addError(error); - }, - ); + onAdDismissedFullScreenContent: (ad) => ad.dispose(), + onAdFailedToShowFullScreenContent: (ad, error) async { + await ad.dispose(); + addError(error); + }, + ); // Show currently available interstitial ad. await state.interstitialAd?.show(); @@ -184,11 +182,9 @@ class FullScreenAdsBloc extends Bloc { // Load the next interstitial ad. add(const LoadInterstitialAdRequested()); - } catch (error, stackTrace) { + } on Object catch (error, stackTrace) { emit( - state.copyWith( - status: FullScreenAdsStatus.showingInterstitialAdFailed, - ), + state.copyWith(status: FullScreenAdsStatus.showingInterstitialAdFailed), ); addError(error, stackTrace); @@ -204,34 +200,27 @@ class FullScreenAdsBloc extends Bloc { state.rewardedAd?.fullScreenContentCallback = ads.FullScreenContentCallback( - onAdDismissedFullScreenContent: (ad) => ad.dispose(), - onAdFailedToShowFullScreenContent: (ad, error) { - ad.dispose(); - addError(error); - }, - ); + onAdDismissedFullScreenContent: (ad) => ad.dispose(), + onAdFailedToShowFullScreenContent: (ad, error) async { + await ad.dispose(); + addError(error); + }, + ); // Show currently available rewarded ad. await state.rewardedAd?.show( - onUserEarnedReward: (ad, earnedReward) => add( - EarnedReward(earnedReward), - ), + onUserEarnedReward: (ad, earnedReward) => + add(EarnedReward(earnedReward)), ); emit( - state.copyWith( - status: FullScreenAdsStatus.showingRewardedAdSucceeded, - ), + state.copyWith(status: FullScreenAdsStatus.showingRewardedAdSucceeded), ); // Load the next rewarded ad. add(const LoadRewardedAdRequested()); - } catch (error, stackTrace) { - emit( - state.copyWith( - status: FullScreenAdsStatus.showingRewardedAdFailed, - ), - ); + } on Object catch (error, stackTrace) { + emit(state.copyWith(status: FullScreenAdsStatus.showingRewardedAdFailed)); addError(error, stackTrace); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/bloc/full_screen_ads_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/bloc/full_screen_ads_state.dart index 0179ebf25..1c8326138 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/bloc/full_screen_ads_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/bloc/full_screen_ads_state.dart @@ -25,7 +25,7 @@ class FullScreenAdsState extends Equatable { }); const FullScreenAdsState.initial() - : this(status: FullScreenAdsStatus.initial); + : this(status: FullScreenAdsStatus.initial); final ads.InterstitialAd? interstitialAd; final ads.RewardedAd? rewardedAd; @@ -40,20 +40,16 @@ class FullScreenAdsState extends Equatable { ads.RewardedAd? rewardedAd, ads.RewardItem? earnedReward, FullScreenAdsStatus? status, - }) => - FullScreenAdsState( - interstitialAd: interstitialAd ?? this.interstitialAd, - rewardedAd: rewardedAd ?? this.rewardedAd, - earnedReward: earnedReward ?? this.earnedReward, - status: status ?? this.status, - ); + }) => FullScreenAdsState( + interstitialAd: interstitialAd ?? this.interstitialAd, + rewardedAd: rewardedAd ?? this.rewardedAd, + earnedReward: earnedReward ?? this.earnedReward, + status: status ?? this.status, + ); } class FullScreenAdsConfig { - const FullScreenAdsConfig({ - this.interstitialAdUnitId, - this.rewardedAdUnitId, - }); + const FullScreenAdsConfig({this.interstitialAdUnitId, this.rewardedAdUnitId}); /// The unit id of an interstitial ad. final String? interstitialAdUnitId; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/widgets/sticky_ad.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/widgets/sticky_ad.dart index ccd3e14ab..458f77e28 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/widgets/sticky_ad.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/ads/widgets/sticky_ad.dart @@ -76,7 +76,7 @@ class StickyAdContainer extends StatelessWidget { boxShadow: [ if (shadowEnabled) BoxShadow( - color: AppColors.black.withOpacity(0.3), + color: AppColors.black.withValues(alpha: 0.3), blurRadius: 3, spreadRadius: 1, offset: const Offset(0, 1), @@ -87,10 +87,7 @@ class StickyAdContainer extends StatelessWidget { left: false, top: false, right: false, - child: Padding( - padding: StickyAd.padding, - child: child, - ), + child: Padding(padding: StickyAd.padding, child: child), ), ), ); @@ -98,10 +95,7 @@ class StickyAdContainer extends StatelessWidget { } class StickyAdCloseIcon extends StatelessWidget { - const StickyAdCloseIcon({ - required this.onAdClosed, - super.key, - }); + const StickyAdCloseIcon({required this.onAdClosed, super.key}); final VoidCallback onAdClosed; @@ -128,9 +122,7 @@ class StickyAdCloseIcon extends StatelessWidget { @visibleForTesting class StickyAdCloseIconBackground extends StatelessWidget { - const StickyAdCloseIconBackground({ - super.key, - }); + const StickyAdCloseIconBackground({super.key}); @override Widget build(BuildContext context) { @@ -141,7 +133,7 @@ class StickyAdCloseIconBackground extends StatelessWidget { borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( - color: AppColors.black.withOpacity(0.3), + color: AppColors.black.withValues(alpha: 0.3), blurRadius: 3, spreadRadius: 1, offset: const Offset(0, 1), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/analytics/bloc/analytics_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/analytics/bloc/analytics_bloc.dart index 62d9148bb..5a5ad4b30 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/analytics/bloc/analytics_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/analytics/bloc/analytics_bloc.dart @@ -12,8 +12,8 @@ class AnalyticsBloc extends Bloc { AnalyticsBloc({ required analytics.AnalyticsRepository analyticsRepository, required UserRepository userRepository, - }) : _analyticsRepository = analyticsRepository, - super(AnalyticsInitial()) { + }) : _analyticsRepository = analyticsRepository, + super(AnalyticsInitial()) { on(_onTrackAnalyticsEvent); _userSubscription = userRepository.user.listen(_onUserChanged); @@ -24,9 +24,10 @@ class AnalyticsBloc extends Bloc { Future _onUserChanged(User user) async { try { - await _analyticsRepository - .setUserId(user != User.anonymous ? user.id : null); - } catch (error, stackTrace) { + await _analyticsRepository.setUserId( + user != User.anonymous ? user.id : null, + ); + } on Exception catch (error, stackTrace) { addError(error, stackTrace); } } @@ -37,14 +38,14 @@ class AnalyticsBloc extends Bloc { ) async { try { await _analyticsRepository.track(event.event); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { addError(error, stackTrace); } } @override - Future close() { - _userSubscription.cancel(); + Future close() async { + await _userSubscription.cancel(); return super.close(); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/bloc/app_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/bloc/app_bloc.dart index 5a63b760b..7567980aa 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/bloc/app_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/bloc/app_bloc.dart @@ -14,13 +14,13 @@ class AppBloc extends Bloc { required UserRepository userRepository, required NotificationsRepository notificationsRepository, required User user, - }) : _userRepository = userRepository, - _notificationsRepository = notificationsRepository, - super( - user == User.anonymous - ? const AppState.unauthenticated() - : AppState.authenticated(user), - ) { + }) : _userRepository = userRepository, + _notificationsRepository = notificationsRepository, + super( + user == User.anonymous + ? const AppState.unauthenticated() + : AppState.authenticated(user), + ) { on(_onUserChanged); on(_onOnboardingCompleted); on(_onLogoutRequested); @@ -51,8 +51,8 @@ class AppBloc extends Bloc { return user != User.anonymous && user.isNewUser ? emit(AppState.onboardingRequired(user)) : user == User.anonymous - ? emit(const AppState.unauthenticated()) - : emit(AppState.authenticated(user)); + ? emit(const AppState.unauthenticated()) + : emit(AppState.authenticated(user)); } } @@ -85,7 +85,7 @@ class AppBloc extends Bloc { // account is deleted. unawaited(_notificationsRepository.toggleNotifications(enable: false)); await _userRepository.deleteAccount(); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { await _userRepository.logOut(); addError(error, stackTrace); } @@ -106,8 +106,8 @@ class AppBloc extends Bloc { } @override - Future close() { - _userSubscription.cancel(); + Future close() async { + await _userSubscription.cancel(); return super.close(); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/bloc/app_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/bloc/app_state.dart index 79c121f6e..61d2a017c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/bloc/app_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/bloc/app_state.dart @@ -3,7 +3,8 @@ part of 'app_bloc.dart'; enum AppStatus { onboardingRequired(), authenticated(), - unauthenticated(); + unauthenticated() + ; bool get isLoggedIn => this == AppStatus.authenticated || this == AppStatus.onboardingRequired; @@ -16,18 +17,11 @@ class AppState extends Equatable { this.showLoginOverlay = false, }); - const AppState.authenticated( - User user, - ) : this( - status: AppStatus.authenticated, - user: user, - ); + const AppState.authenticated(User user) + : this(status: AppStatus.authenticated, user: user); const AppState.onboardingRequired(User user) - : this( - status: AppStatus.onboardingRequired, - user: user, - ); + : this(status: AppStatus.onboardingRequired, user: user); const AppState.unauthenticated() : this(status: AppStatus.unauthenticated); @@ -37,18 +31,9 @@ class AppState extends Equatable { bool get isUserSubscribed => user.subscriptionPlan != SubscriptionPlan.none; @override - List get props => [ - status, - user, - showLoginOverlay, - isUserSubscribed, - ]; - - AppState copyWith({ - AppStatus? status, - User? user, - bool? showLoginOverlay, - }) { + List get props => [status, user, showLoginOverlay, isUserSubscribed]; + + AppState copyWith({AppStatus? status, User? user, bool? showLoginOverlay}) { return AppState( status: status ?? this.status, user: user ?? this.user, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/view/app.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/view/app.dart index 6357c2f5f..32f4f623a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/view/app.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/view/app.dart @@ -8,6 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:{{project_name.snakeCase()}}/ads/ads.dart'; import 'package:{{project_name.snakeCase()}}/analytics/analytics.dart'; import 'package:{{project_name.snakeCase()}}/app/app.dart'; +import 'package:{{project_name.snakeCase()}}/categories/categories.dart'; import 'package:{{project_name.snakeCase()}}/l10n/l10n.dart'; import 'package:{{project_name.snakeCase()}}/login/login.dart' hide LoginEvent; import 'package:{{project_name.snakeCase()}}/theme_selector/theme_selector.dart'; @@ -30,14 +31,14 @@ class App extends StatelessWidget { required AdsConsentClient adsConsentClient, required User user, super.key, - }) : _userRepository = userRepository, - _newsRepository = newsRepository, - _notificationsRepository = notificationsRepository, - _articleRepository = articleRepository, - _inAppPurchaseRepository = inAppPurchaseRepository, - _analyticsRepository = analyticsRepository, - _adsConsentClient = adsConsentClient, - _user = user; + }) : _userRepository = userRepository, + _newsRepository = newsRepository, + _notificationsRepository = notificationsRepository, + _articleRepository = articleRepository, + _inAppPurchaseRepository = inAppPurchaseRepository, + _analyticsRepository = analyticsRepository, + _adsConsentClient = adsConsentClient, + _user = user; final UserRepository _userRepository; final NewsRepository _newsRepository; @@ -71,9 +72,8 @@ class App extends StatelessWidget { ), BlocProvider(create: (_) => ThemeModeBloc()), BlocProvider( - create: (_) => LoginWithEmailLinkBloc( - userRepository: _userRepository, - ), + create: (_) => + LoginWithEmailLinkBloc(userRepository: _userRepository), lazy: false, ), BlocProvider( @@ -84,16 +84,22 @@ class App extends StatelessWidget { lazy: false, ), BlocProvider( - create: (context) => FullScreenAdsBloc( - interstitialAdLoader: ads.InterstitialAd.load, - rewardedAdLoader: ads.RewardedAd.load, - adsRetryPolicy: const AdsRetryPolicy(), - localPlatform: const LocalPlatform(), - ) - ..add(const LoadInterstitialAdRequested()) - ..add(const LoadRewardedAdRequested()), + create: (context) => + FullScreenAdsBloc( + interstitialAdLoader: ads.InterstitialAd.load, + rewardedAdLoader: ads.RewardedAd.load, + adsRetryPolicy: const AdsRetryPolicy(), + localPlatform: const LocalPlatform(), + ) + ..add(const LoadInterstitialAdRequested()) + ..add(const LoadRewardedAdRequested()), lazy: false, ), + BlocProvider( + create: (context) => + CategoriesBloc(newsRepository: context.read()) + ..add(const CategoriesRequested()), + ), ], child: const AppView(), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/widgets/authenticated_user_listener.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/widgets/authenticated_user_listener.dart index e9f310545..f08278e6d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/widgets/authenticated_user_listener.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/app/widgets/authenticated_user_listener.dart @@ -4,10 +4,7 @@ import 'package:{{project_name.snakeCase()}}/analytics/analytics.dart'; import 'package:{{project_name.snakeCase()}}/app/app.dart'; class AuthenticatedUserListener extends StatelessWidget { - const AuthenticatedUserListener({ - required this.child, - super.key, - }); + const AuthenticatedUserListener({required this.child, super.key}); final Widget child; @@ -17,10 +14,10 @@ class AuthenticatedUserListener extends StatelessWidget { listener: (context, state) { if (state.status.isLoggedIn) { context.read().add( - TrackAnalyticsEvent( - state.user.isNewUser ? RegistrationEvent() : LoginEvent(), - ), - ); + TrackAnalyticsEvent( + state.user.isNewUser ? RegistrationEvent() : LoginEvent(), + ), + ); } }, listenWhen: (previous, current) => previous.status != current.status, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_bloc.dart index a7b02dee0..d569cb79e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_bloc.dart @@ -19,10 +19,10 @@ class ArticleBloc extends HydratedBloc { required String articleId, required ArticleRepository articleRepository, required ShareLauncher shareLauncher, - }) : _articleId = articleId, - _articleRepository = articleRepository, - _shareLauncher = shareLauncher, - super(const ArticleState.initial()) { + }) : _articleId = articleId, + _articleRepository = articleRepository, + _shareLauncher = shareLauncher, + super(const ArticleState.initial()) { on(_onArticleRequested, transformer: sequential()); on(_onArticleContentSeen); on(_onArticleRewardedAdWatched); @@ -103,7 +103,7 @@ class ArticleBloc extends HydratedBloc { isPremium: response.isPremium, ), ); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: ArticleStatus.failure)); addError(error, stackTrace); } @@ -132,7 +132,7 @@ class ArticleBloc extends HydratedBloc { hasReachedArticleViewsLimit: hasReachedArticleViewsLimit, ), ); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: ArticleStatus.rewardedAdWatchedFailure)); addError(error, stackTrace); } @@ -143,8 +143,7 @@ class ArticleBloc extends HydratedBloc { FutureOr _onArticleCommented( ArticleCommented event, Emitter emit, - ) => - Future.value(); + ) => Future.value(); FutureOr _onShareRequested( ShareRequested event, @@ -152,7 +151,7 @@ class ArticleBloc extends HydratedBloc { ) async { try { await _shareLauncher.share(text: event.uri.toString()); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: ArticleStatus.shareFailure)); addError(error, stackTrace); } @@ -168,7 +167,8 @@ class ArticleBloc extends HydratedBloc { final resetAt = currentArticleViews.resetAt; final now = clock.now(); - final shouldResetArticleViews = resetAt == null || + final shouldResetArticleViews = + resetAt == null || now.isAfter(resetAt.add(_resetArticleViewsAfterDuration)); if (shouldResetArticleViews) { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_bloc.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_bloc.g.dart index 13e6ca9cc..7d481cde7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_bloc.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_bloc.g.dart @@ -7,43 +7,45 @@ part of 'article_bloc.dart'; // ************************************************************************** ArticleState _$ArticleStateFromJson(Map json) => ArticleState( - status: $enumDecode(_$ArticleStatusEnumMap, json['status']), - title: json['title'] as String?, - content: (json['content'] as List?) - ?.map((e) => NewsBlock.fromJson(e as Map)) - .toList() ?? - const [], - contentTotalCount: (json['contentTotalCount'] as num?)?.toInt(), - contentSeenCount: (json['contentSeenCount'] as num?)?.toInt() ?? 0, - relatedArticles: (json['relatedArticles'] as List?) - ?.map((e) => NewsBlock.fromJson(e as Map)) - .toList() ?? - const [], - hasMoreContent: json['hasMoreContent'] as bool? ?? true, - uri: json['uri'] == null ? null : Uri.parse(json['uri'] as String), - hasReachedArticleViewsLimit: - json['hasReachedArticleViewsLimit'] as bool? ?? false, - isPreview: json['isPreview'] as bool? ?? false, - isPremium: json['isPremium'] as bool? ?? false, - showInterstitialAd: json['showInterstitialAd'] as bool? ?? false, - ); + status: $enumDecode(_$ArticleStatusEnumMap, json['status']), + title: json['title'] as String?, + content: + (json['content'] as List?) + ?.map((e) => NewsBlock.fromJson(e as Map)) + .toList() ?? + const [], + contentTotalCount: (json['contentTotalCount'] as num?)?.toInt(), + contentSeenCount: (json['contentSeenCount'] as num?)?.toInt() ?? 0, + relatedArticles: + (json['relatedArticles'] as List?) + ?.map((e) => NewsBlock.fromJson(e as Map)) + .toList() ?? + const [], + hasMoreContent: json['hasMoreContent'] as bool? ?? true, + uri: json['uri'] == null ? null : Uri.parse(json['uri'] as String), + hasReachedArticleViewsLimit: + json['hasReachedArticleViewsLimit'] as bool? ?? false, + isPreview: json['isPreview'] as bool? ?? false, + isPremium: json['isPremium'] as bool? ?? false, + showInterstitialAd: json['showInterstitialAd'] as bool? ?? false, +); -Map _$ArticleStateToJson(ArticleState instance) => - { - 'status': _$ArticleStatusEnumMap[instance.status]!, - 'title': instance.title, - 'content': instance.content.map((e) => e.toJson()).toList(), - 'contentTotalCount': instance.contentTotalCount, - 'contentSeenCount': instance.contentSeenCount, - 'relatedArticles': - instance.relatedArticles.map((e) => e.toJson()).toList(), - 'hasMoreContent': instance.hasMoreContent, - 'uri': instance.uri?.toString(), - 'hasReachedArticleViewsLimit': instance.hasReachedArticleViewsLimit, - 'isPreview': instance.isPreview, - 'isPremium': instance.isPremium, - 'showInterstitialAd': instance.showInterstitialAd, - }; +Map _$ArticleStateToJson( + ArticleState instance, +) => { + 'status': _$ArticleStatusEnumMap[instance.status]!, + 'title': instance.title, + 'content': instance.content.map((e) => e.toJson()).toList(), + 'contentTotalCount': instance.contentTotalCount, + 'contentSeenCount': instance.contentSeenCount, + 'relatedArticles': instance.relatedArticles.map((e) => e.toJson()).toList(), + 'hasMoreContent': instance.hasMoreContent, + 'uri': instance.uri?.toString(), + 'hasReachedArticleViewsLimit': instance.hasReachedArticleViewsLimit, + 'isPreview': instance.isPreview, + 'isPremium': instance.isPremium, + 'showInterstitialAd': instance.showInterstitialAd, +}; const _$ArticleStatusEnumMap = { ArticleStatus.initial: 'initial', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_event.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_event.dart index 00e12dbca..4998127ef 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_event.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_event.dart @@ -12,9 +12,7 @@ class ArticleRequested extends ArticleEvent { } class ArticleContentSeen extends ArticleEvent { - const ArticleContentSeen({ - required this.contentIndex, - }); + const ArticleContentSeen({required this.contentIndex}); final int contentIndex; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_state.dart index d773ef56a..268ae2191 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/bloc/article_state.dart @@ -29,10 +29,7 @@ class ArticleState extends Equatable { factory ArticleState.fromJson(Map json) => _$ArticleStateFromJson(json); - const ArticleState.initial() - : this( - status: ArticleStatus.initial, - ); + const ArticleState.initial() : this(status: ArticleStatus.initial); final ArticleStatus status; final String? title; @@ -53,19 +50,19 @@ class ArticleState extends Equatable { @override List get props => [ - status, - title, - content, - relatedArticles, - hasMoreContent, - uri, - hasReachedArticleViewsLimit, - isPreview, - isPremium, - contentTotalCount, - contentSeenCount, - showInterstitialAd, - ]; + status, + title, + content, + relatedArticles, + hasMoreContent, + uri, + hasReachedArticleViewsLimit, + isPreview, + isPremium, + contentTotalCount, + contentSeenCount, + showInterstitialAd, + ]; ArticleState copyWith({ ArticleStatus? status, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/view/article_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/view/article_page.dart index f5a383d29..becd0d1b7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/view/article_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/view/article_page.dart @@ -43,21 +43,20 @@ class ArticlePage extends StatelessWidget { bool isVideoArticle = false, InterstitialAdBehavior interstitialAdBehavior = InterstitialAdBehavior.onOpen, - }) => - MaterialPageRoute( - builder: (_) => ArticlePage( - id: id, - isVideoArticle: isVideoArticle, - interstitialAdBehavior: interstitialAdBehavior, - ), - ); + }) => MaterialPageRoute( + builder: (_) => ArticlePage( + id: id, + isVideoArticle: isVideoArticle, + interstitialAdBehavior: interstitialAdBehavior, + ), + ); @override Widget build(BuildContext context) { return BlocProvider( create: (_) => ArticleBloc( articleId: id, - shareLauncher: const ShareLauncher(), + shareLauncher: ShareLauncher(), articleRepository: context.read(), )..add(const ArticleRequested()), child: ArticleView( @@ -80,16 +79,19 @@ class ArticleView extends StatelessWidget { @override Widget build(BuildContext context) { - final backgroundColor = - isVideoArticle ? AppColors.darkBackground : AppColors.white; - final foregroundColor = - isVideoArticle ? AppColors.white : AppColors.highEmphasisSurface; + final backgroundColor = isVideoArticle + ? AppColors.darkBackground + : AppColors.white; + final foregroundColor = isVideoArticle + ? AppColors.white + : AppColors.highEmphasisSurface; final uri = context.select((ArticleBloc bloc) => bloc.state.uri); - final isSubscriber = - context.select((bloc) => bloc.state.isUserSubscribed); + final isSubscriber = context.select( + (bloc) => bloc.state.isUserSubscribed, + ); return PopScope( - onPopInvokedWithResult: (_, __) => _onPop(context), + onPopInvokedWithResult: (_, _) => _onPop(context), child: HasToShowInterstitialAdListener( interstitialAdBehavior: interstitialAdBehavior, child: HasReachedArticleLimitListener( @@ -98,18 +100,16 @@ class ArticleView extends StatelessWidget { backgroundColor: backgroundColor, appBar: AppBar( systemOverlayStyle: SystemUiOverlayStyle( - statusBarIconBrightness: - isVideoArticle ? Brightness.light : Brightness.dark, - statusBarBrightness: - isVideoArticle ? Brightness.dark : Brightness.light, + statusBarIconBrightness: isVideoArticle + ? Brightness.light + : Brightness.dark, + statusBarBrightness: isVideoArticle + ? Brightness.dark + : Brightness.light, ), leading: isVideoArticle - ? AppBackButton.light( - onPressed: Navigator.of(context).pop, - ) - : AppBackButton( - onPressed: Navigator.of(context).pop, - ), + ? AppBackButton.light(onPressed: Navigator.of(context).pop) + : AppBackButton(onPressed: Navigator.of(context).pop), actions: [ if (uri != null && uri.toString().isNotEmpty) Padding( @@ -118,9 +118,9 @@ class ArticleView extends StatelessWidget { child: ShareButton( shareText: context.l10n.shareText, color: foregroundColor, - onPressed: () => context - .read() - .add(ShareRequested(uri: uri)), + onPressed: () => context.read().add( + ShareRequested(uri: uri), + ), ), ), if (!isSubscriber) const ArticleSubscribeButton(), @@ -141,9 +141,9 @@ class ArticleView extends StatelessWidget { final state = context.read().state; if (state.showInterstitialAd && interstitialAdBehavior == InterstitialAdBehavior.onClose) { - context - .read() - .add(const ShowInterstitialAdRequested()); + context.read().add( + const ShowInterstitialAdRequested(), + ); } } } @@ -228,9 +228,9 @@ class HasToShowInterstitialAdListener extends StatelessWidget { listener: (context, state) { if (state.showInterstitialAd && interstitialAdBehavior == InterstitialAdBehavior.onOpen) { - context - .read() - .add(const ShowInterstitialAdRequested()); + context.read().add( + const ShowInterstitialAdRequested(), + ); } }, listenWhen: (previous, current) => diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_comments.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_comments.dart index dd5207375..d47e77f65 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_comments.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_comments.dart @@ -19,9 +19,7 @@ class ArticleComments extends StatelessWidget { ), const SizedBox(height: AppSpacing.lg), ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 148, - ), + constraints: const BoxConstraints(maxHeight: 148), child: AppTextField( hintText: context.l10n.commentEntryHint, onSubmitted: (_) { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content.dart index 95f83a0ff..39513682f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content.dart @@ -1,9 +1,12 @@ +import 'dart:async'; + import 'package:app_ui/app_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:{{project_name.snakeCase()}}/ads/ads.dart'; import 'package:{{project_name.snakeCase()}}/analytics/analytics.dart'; import 'package:{{project_name.snakeCase()}}/article/article.dart'; +import 'package:{{project_name.snakeCase()}}/categories/categories.dart'; import 'package:{{project_name.snakeCase()}}/l10n/l10n.dart'; import 'package:{{project_name.snakeCase()}}/network_error/network_error.dart'; import 'package:{{project_name.snakeCase()}}_api/client.dart'; @@ -15,11 +18,18 @@ class ArticleContent extends StatelessWidget { @override Widget build(BuildContext context) { final status = context.select((ArticleBloc bloc) => bloc.state.status); + final categoriesStatus = context.select( + (CategoriesBloc bloc) => bloc.state.status, + ); - final hasMoreContent = - context.select((ArticleBloc bloc) => bloc.state.hasMoreContent); + final hasMoreContent = context.select( + (ArticleBloc bloc) => bloc.state.hasMoreContent, + ); - if (status == ArticleStatus.initial) { + /// Show loader while categories are loading as articles may need to consume + /// them + if (status == ArticleStatus.initial || + categoriesStatus == CategoriesStatus.initial) { return const ArticleContentLoaderItem( key: Key('articleContent_empty_loaderItem'), ); @@ -29,12 +39,14 @@ class ArticleContent extends StatelessWidget { child: BlocListener( listener: (context, state) { if (state.status == ArticleStatus.failure && state.content.isEmpty) { - Navigator.of(context).push( - NetworkError.route( - onRetry: () { - context.read().add(const ArticleRequested()); - Navigator.of(context).pop(); - }, + unawaited( + Navigator.of(context).push( + NetworkError.route( + onRetry: () { + context.read().add(const ArticleRequested()); + Navigator.of(context).pop(); + }, + ), ), ); } else if (state.status == ArticleStatus.shareFailure) { @@ -65,19 +77,14 @@ class ArticleContent extends StatelessWidget { ..showSnackBar( SnackBar( key: const Key('articleContent_shareFailure_snackBar'), - content: Text( - context.l10n.shareFailure, - ), + content: Text(context.l10n.shareFailure), ), ); } } class ArticleContentSeenListener extends StatelessWidget { - const ArticleContentSeenListener({ - required this.child, - super.key, - }); + const ArticleContentSeenListener({required this.child, super.key}); final Widget child; @@ -85,13 +92,13 @@ class ArticleContentSeenListener extends StatelessWidget { Widget build(BuildContext context) { return BlocListener( listener: (context, state) => context.read().add( - TrackAnalyticsEvent( - ArticleMilestoneEvent( - milestonePercentage: state.contentMilestone, - articleTitle: state.title!, - ), - ), + TrackAnalyticsEvent( + ArticleMilestoneEvent( + milestonePercentage: state.contentMilestone, + articleTitle: state.title!, ), + ), + ), listenWhen: (previous, current) => previous.contentMilestone != current.contentMilestone, child: child, @@ -107,57 +114,54 @@ class ArticleContentItemList extends StatelessWidget { final isFailure = context.select( (ArticleBloc bloc) => bloc.state.status == ArticleStatus.failure, ); - final hasMoreContent = - context.select((ArticleBloc bloc) => bloc.state.hasMoreContent); + final hasMoreContent = context.select( + (ArticleBloc bloc) => bloc.state.hasMoreContent, + ); final status = context.select((ArticleBloc bloc) => bloc.state.status); final content = context.select((ArticleBloc bloc) => bloc.state.content); final uri = context.select((ArticleBloc bloc) => bloc.state.uri); - final isArticlePreview = - context.select((ArticleBloc bloc) => bloc.state.isPreview); + final isArticlePreview = context.select( + (ArticleBloc bloc) => bloc.state.isPreview, + ); return SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == content.length) { - if (isFailure) { - return NetworkError( - onRetry: () { - context.read().add(const ArticleRequested()); - }, - ); - } - return hasMoreContent - ? Padding( - padding: EdgeInsets.only( - top: content.isEmpty ? AppSpacing.xxxlg : 0, - ), - child: ArticleContentLoaderItem( - key: const Key( - 'articleContent_moreContent_loaderItem', - ), - onPresented: () { - if (status != ArticleStatus.loading) { - context - .read() - .add(const ArticleRequested()); - } - }, - ), - ) - : const SizedBox(); + delegate: SliverChildBuilderDelegate((context, index) { + if (index == content.length) { + if (isFailure) { + return NetworkError( + onRetry: () { + context.read().add(const ArticleRequested()); + }, + ); } + return hasMoreContent + ? Padding( + padding: EdgeInsets.only( + top: content.isEmpty ? AppSpacing.xxxlg : 0, + ), + child: ArticleContentLoaderItem( + key: const Key('articleContent_moreContent_loaderItem'), + onPresented: () { + if (status != ArticleStatus.loading) { + context.read().add( + const ArticleRequested(), + ); + } + }, + ), + ) + : const SizedBox(); + } - return _buildArticleItem( - context, - index, - content, - uri, - isArticlePreview, - ); - }, - childCount: content.length + 1, - ), + return _buildArticleItem( + context, + index, + content, + uri, + isArticlePreview, + ); + }, childCount: content.length + 1), ); } @@ -175,17 +179,15 @@ class ArticleContentItemList extends StatelessWidget { key: ValueKey(block), onVisibilityChanged: (visibility) { if (!visibility.visibleBounds.isEmpty) { - context - .read() - .add(ArticleContentSeen(contentIndex: index)); + context.read().add( + ArticleContentSeen(contentIndex: index), + ); } }, child: ArticleContentItem( block: block, onSharePressed: uri != null && uri.toString().isNotEmpty - ? () => context.read().add( - ShareRequested(uri: uri), - ) + ? () => context.read().add(ShareRequested(uri: uri)) : null, ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content_item.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content_item.dart index e3da1194a..3258941fd 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content_item.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content_item.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart' hide Image, Spacer; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:{{project_name.snakeCase()}}/article/article.dart'; +import 'package:{{project_name.snakeCase()}}/categories/categories.dart'; import 'package:{{project_name.snakeCase()}}/l10n/l10n.dart'; import 'package:{{project_name.snakeCase()}}/newsletter/newsletter.dart'; import 'package:{{project_name.snakeCase()}}/slideshow/slideshow.dart'; @@ -32,8 +34,9 @@ class ArticleContentItem extends StatelessWidget { } else if (newsBlock is VideoBlock) { return Video(block: newsBlock); } else if (newsBlock is TextCaptionBlock) { - final articleThemeColors = - Theme.of(context).extension()!; + final articleThemeColors = Theme.of( + context, + ).extension()!; return TextCaption( block: newsBlock, colorValues: { @@ -48,12 +51,19 @@ class ArticleContentItem extends StatelessWidget { } else if (newsBlock is TextParagraphBlock) { return TextParagraph(block: newsBlock); } else if (newsBlock is ArticleIntroductionBlock) { + final categoryName = context.read().state.getCategoryName( + newsBlock.categoryId, + ); return ArticleIntroduction( block: newsBlock, + categoryName: categoryName, premiumText: context.l10n.newsBlockPremiumText, ); } else if (newsBlock is VideoIntroductionBlock) { - return VideoIntroduction(block: newsBlock); + final categoryName = context.read().state.getCategoryName( + newsBlock.categoryId, + ); + return VideoIntroduction(block: newsBlock, categoryName: categoryName); } else if (newsBlock is BannerAdBlock) { return BannerAd( block: newsBlock, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content_loader_item.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content_loader_item.dart index a55492eac..010de986a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content_loader_item.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_content_loader_item.dart @@ -25,9 +25,7 @@ class _ArticleContentLoaderItemState extends State { Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.symmetric(vertical: AppSpacing.lg), - child: Center( - child: CircularProgressIndicator(), - ), + child: Center(child: CircularProgressIndicator()), ); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_theme_override.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_theme_override.dart index 8159b88ca..eb6396a43 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_theme_override.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_theme_override.dart @@ -60,10 +60,7 @@ class ArticleThemeColors extends ThemeExtension final Color captionLight; @override - ArticleThemeColors copyWith({ - Color? captionNormal, - Color? captionLight, - }) { + ArticleThemeColors copyWith({Color? captionNormal, Color? captionLight}) { return ArticleThemeColors( captionNormal: captionNormal ?? this.captionNormal, captionLight: captionLight ?? this.captionLight, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_trailing_content.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_trailing_content.dart index c4cd1f8a8..1b7169538 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_trailing_content.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/article/widgets/article_trailing_content.dart @@ -13,18 +13,23 @@ class ArticleTrailingContent extends StatelessWidget { @override Widget build(BuildContext context) { - final relatedArticles = - context.select((ArticleBloc bloc) => bloc.state.relatedArticles); - final isArticlePreview = - context.select((ArticleBloc bloc) => bloc.state.isPreview); + final relatedArticles = context.select( + (ArticleBloc bloc) => bloc.state.relatedArticles, + ); + final isArticlePreview = context.select( + (ArticleBloc bloc) => bloc.state.isPreview, + ); - final hasReachedArticleViewsLimit = context - .select((ArticleBloc bloc) => bloc.state.hasReachedArticleViewsLimit); - final isUserSubscribed = - context.select((AppBloc bloc) => bloc.state.isUserSubscribed); + final hasReachedArticleViewsLimit = context.select( + (ArticleBloc bloc) => bloc.state.hasReachedArticleViewsLimit, + ); + final isUserSubscribed = context.select( + (AppBloc bloc) => bloc.state.isUserSubscribed, + ); - final isArticlePremium = - context.select((ArticleBloc bloc) => bloc.state.isPremium); + final isArticlePremium = context.select( + (ArticleBloc bloc) => bloc.state.isPremium, + ); final showSubscribeWithArticleLimitModal = hasReachedArticleViewsLimit && !isUserSubscribed; @@ -60,15 +65,13 @@ class ArticleTrailingContent extends StatelessWidget { ], if (isArticlePreview) ...[ SliverList( - delegate: SliverChildListDelegate( - [ - const SizedBox(height: AppSpacing.xlg), - if (showSubscribeModal) - const SubscribeModal() - else if (showSubscribeWithArticleLimitModal) - const SubscribeWithArticleLimitModal(), - ], - ), + delegate: SliverChildListDelegate([ + const SizedBox(height: AppSpacing.xlg), + if (showSubscribeModal) + const SubscribeModal() + else if (showSubscribeWithArticleLimitModal) + const SubscribeWithArticleLimitModal(), + ]), ), ], ], @@ -95,8 +98,8 @@ class ArticleTrailingShadow extends StatelessWidget { decoration: BoxDecoration( gradient: LinearGradient( colors: [ - AppColors.white.withOpacity(0), - AppColors.white.withOpacity(1), + AppColors.white.withValues(alpha: 0), + AppColors.white.withValues(alpha: 1), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_bloc.dart index 105068d19..e2755908d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_bloc.dart @@ -2,19 +2,16 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; -import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:json_annotation/json_annotation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:news_repository/news_repository.dart'; part 'categories_event.dart'; part 'categories_state.dart'; -part 'categories_bloc.g.dart'; -class CategoriesBloc extends HydratedBloc { - CategoriesBloc({ - required NewsRepository newsRepository, - }) : _newsRepository = newsRepository, - super(const CategoriesState.initial()) { +class CategoriesBloc extends Bloc { + CategoriesBloc({required NewsRepository newsRepository}) + : _newsRepository = newsRepository, + super(const CategoriesState.initial()) { on(_onCategoriesRequested); on(_onCategorySelected); } @@ -36,7 +33,7 @@ class CategoriesBloc extends HydratedBloc { selectedCategory: response.categories.firstOrNull, ), ); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: CategoriesStatus.failure)); addError(error, stackTrace); } @@ -45,13 +42,5 @@ class CategoriesBloc extends HydratedBloc { void _onCategorySelected( CategorySelected event, Emitter emit, - ) => - emit(state.copyWith(selectedCategory: event.category)); - - @override - CategoriesState? fromJson(Map json) => - CategoriesState.fromJson(json); - - @override - Map? toJson(CategoriesState state) => state.toJson(); + ) => emit(state.copyWith(selectedCategory: event.category)); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_bloc.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_bloc.g.dart deleted file mode 100644 index 684c8f19f..000000000 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_bloc.g.dart +++ /dev/null @@ -1,42 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'categories_bloc.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -CategoriesState _$CategoriesStateFromJson(Map json) => - CategoriesState( - status: $enumDecode(_$CategoriesStatusEnumMap, json['status']), - categories: (json['categories'] as List?) - ?.map((e) => $enumDecode(_$CategoryEnumMap, e)) - .toList(), - selectedCategory: - $enumDecodeNullable(_$CategoryEnumMap, json['selectedCategory']), - ); - -Map _$CategoriesStateToJson(CategoriesState instance) => - { - 'status': _$CategoriesStatusEnumMap[instance.status]!, - 'categories': - instance.categories?.map((e) => _$CategoryEnumMap[e]!).toList(), - 'selectedCategory': _$CategoryEnumMap[instance.selectedCategory], - }; - -const _$CategoriesStatusEnumMap = { - CategoriesStatus.initial: 'initial', - CategoriesStatus.loading: 'loading', - CategoriesStatus.populated: 'populated', - CategoriesStatus.failure: 'failure', -}; - -const _$CategoryEnumMap = { - Category.business: 'business', - Category.entertainment: 'entertainment', - Category.top: 'top', - Category.health: 'health', - Category.science: 'science', - Category.sports: 'sports', - Category.technology: 'technology', -}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_state.dart index 15465da41..961880141 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/bloc/categories_state.dart @@ -1,13 +1,7 @@ part of 'categories_bloc.dart'; -enum CategoriesStatus { - initial, - loading, - populated, - failure, -} +enum CategoriesStatus { initial, loading, populated, failure } -@JsonSerializable() class CategoriesState extends Equatable { const CategoriesState({ required this.status, @@ -15,24 +9,24 @@ class CategoriesState extends Equatable { this.selectedCategory, }); - const CategoriesState.initial() - : this( - status: CategoriesStatus.initial, - ); - - factory CategoriesState.fromJson(Map json) => - _$CategoriesStateFromJson(json); + const CategoriesState.initial() : this(status: CategoriesStatus.initial); final CategoriesStatus status; final List? categories; final Category? selectedCategory; + String? getCategoryName(String categoryId) { + try { + return categories + ?.firstWhere((category) => category.id == categoryId) + .name; + } on Object catch (_) { + return null; + } + } + @override - List get props => [ - status, - categories, - selectedCategory, - ]; + List get props => [status, categories, selectedCategory]; CategoriesState copyWith({ CategoriesStatus? status, @@ -45,6 +39,4 @@ class CategoriesState extends Equatable { selectedCategory: selectedCategory ?? this.selectedCategory, ); } - - Map toJson() => _$CategoriesStateToJson(this); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/widgets/categories_tab_bar.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/widgets/categories_tab_bar.dart index 8c20860dc..3f34339b0 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/widgets/categories_tab_bar.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/categories/widgets/categories_tab_bar.dart @@ -25,11 +25,7 @@ class CategoriesTabBar extends StatelessWidget implements PreferredSizeWidget { } class CategoryTab extends StatelessWidget { - const CategoryTab({ - required this.categoryName, - this.onDoubleTap, - super.key, - }); + const CategoryTab({required this.categoryName, this.onDoubleTap, super.key}); final String categoryName; final VoidCallback? onDoubleTap; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_bloc.dart index 2acb7750e..53a4e857d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_bloc.dart @@ -12,15 +12,11 @@ part 'feed_event.dart'; part 'feed_state.dart'; class FeedBloc extends HydratedBloc { - FeedBloc({ - required NewsRepository newsRepository, - }) : _newsRepository = newsRepository, - super(const FeedState.initial()) { + FeedBloc({required NewsRepository newsRepository}) + : _newsRepository = newsRepository, + super(const FeedState.initial()) { on(_onFeedRequested, transformer: sequential()); - on( - _onFeedRefreshRequested, - transformer: droppable(), - ); + on(_onFeedRefreshRequested, transformer: droppable()); on(_onFeedResumed, transformer: droppable()); } @@ -31,7 +27,7 @@ class FeedBloc extends HydratedBloc { Emitter emit, ) async { emit(state.copyWith(status: FeedStatus.loading)); - return _updateFeed(category: event.category, emit: emit); + return _updateFeed(categoryId: event.category.id, emit: emit); } FutureOr _onFeedResumed( @@ -40,19 +36,19 @@ class FeedBloc extends HydratedBloc { ) async { await Future.wait( state.feed.keys.map( - (category) => _updateFeed(category: category, emit: emit), + (category) => _updateFeed(categoryId: category, emit: emit), ), ); } Future _updateFeed({ - required Category category, + required String categoryId, required Emitter emit, }) async { try { - final categoryFeed = state.feed[category] ?? []; + final categoryFeed = state.feed[categoryId] ?? []; final response = await _newsRepository.getFeed( - category: category, + categoryId: categoryId, offset: categoryFeed.length, ); @@ -65,13 +61,13 @@ class FeedBloc extends HydratedBloc { emit( state.copyWith( status: FeedStatus.populated, - feed: Map>.from(state.feed) - ..addAll({category: updatedCategoryFeed}), - hasMoreNews: Map.from(state.hasMoreNews) - ..addAll({category: hasMoreNewsForCategory}), + feed: Map>.from(state.feed) + ..addAll({categoryId: updatedCategoryFeed}), + hasMoreNews: Map.from(state.hasMoreNews) + ..addAll({categoryId: hasMoreNewsForCategory}), ), ); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: FeedStatus.failure)); addError(error, stackTrace); } @@ -93,7 +89,7 @@ class FeedBloc extends HydratedBloc { final category = event.category; final response = await _newsRepository.getFeed( - category: category, + categoryId: category.id, offset: 0, ); @@ -104,13 +100,13 @@ class FeedBloc extends HydratedBloc { emit( state.copyWith( status: FeedStatus.populated, - feed: Map>.of(state.feed) - ..addAll({category: refreshedCategoryFeed}), - hasMoreNews: Map.of(state.hasMoreNews) - ..addAll({category: hasMoreNewsForCategory}), + feed: Map>.of(state.feed) + ..addAll({category.id: refreshedCategoryFeed}), + hasMoreNews: Map.of(state.hasMoreNews) + ..addAll({category.id: hasMoreNewsForCategory}), ), ); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: FeedStatus.failure)); addError(error, stackTrace); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_bloc.g.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_bloc.g.dart index fcb9c5c44..6fa3f81c2 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_bloc.g.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_bloc.g.dart @@ -7,28 +7,31 @@ part of 'feed_bloc.dart'; // ************************************************************************** FeedState _$FeedStateFromJson(Map json) => FeedState( - status: $enumDecode(_$FeedStatusEnumMap, json['status']), - feed: (json['feed'] as Map?)?.map( - (k, e) => MapEntry( - $enumDecode(_$CategoryEnumMap, k), - (e as List) - .map((e) => NewsBlock.fromJson(e as Map)) - .toList()), - ) ?? - const {}, - hasMoreNews: (json['hasMoreNews'] as Map?)?.map( - (k, e) => MapEntry($enumDecode(_$CategoryEnumMap, k), e as bool), - ) ?? - const {}, - ); + status: $enumDecode(_$FeedStatusEnumMap, json['status']), + feed: + (json['feed'] as Map?)?.map( + (k, e) => MapEntry( + k, + (e as List) + .map((e) => NewsBlock.fromJson(e as Map)) + .toList(), + ), + ) ?? + const {}, + hasMoreNews: + (json['hasMoreNews'] as Map?)?.map( + (k, e) => MapEntry(k, e as bool), + ) ?? + const {}, +); Map _$FeedStateToJson(FeedState instance) => { - 'status': _$FeedStatusEnumMap[instance.status]!, - 'feed': instance.feed.map((k, e) => - MapEntry(_$CategoryEnumMap[k]!, e.map((e) => e.toJson()).toList())), - 'hasMoreNews': instance.hasMoreNews - .map((k, e) => MapEntry(_$CategoryEnumMap[k]!, e)), - }; + 'status': _$FeedStatusEnumMap[instance.status]!, + 'feed': instance.feed.map( + (k, e) => MapEntry(k, e.map((e) => e.toJson()).toList()), + ), + 'hasMoreNews': instance.hasMoreNews, +}; const _$FeedStatusEnumMap = { FeedStatus.initial: 'initial', @@ -36,13 +39,3 @@ const _$FeedStatusEnumMap = { FeedStatus.populated: 'populated', FeedStatus.failure: 'failure', }; - -const _$CategoryEnumMap = { - Category.business: 'business', - Category.entertainment: 'entertainment', - Category.top: 'top', - Category.health: 'health', - Category.science: 'science', - Category.sports: 'sports', - Category.technology: 'technology', -}; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_event.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_event.dart index 088df0ab6..a633912e5 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_event.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_event.dart @@ -5,9 +5,7 @@ abstract class FeedEvent extends Equatable { } class FeedRequested extends FeedEvent { - const FeedRequested({ - required this.category, - }); + const FeedRequested({required this.category}); final Category category; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_state.dart index a15f2391a..722410255 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/bloc/feed_state.dart @@ -1,11 +1,13 @@ part of 'feed_bloc.dart'; -enum FeedStatus { - initial, - loading, - populated, - failure, -} +enum FeedStatus { initial, loading, populated, failure } + +/// A map of category id to news blocks. +typedef Feed = Map>; + +/// A map of category id to a boolean indicating if there are more news items +/// to fetch for that category. +typedef HasMoreNews = Map; @JsonSerializable() class FeedState extends Equatable { @@ -15,29 +17,22 @@ class FeedState extends Equatable { this.hasMoreNews = const {}, }); - const FeedState.initial() - : this( - status: FeedStatus.initial, - ); + const FeedState.initial() : this(status: FeedStatus.initial); factory FeedState.fromJson(Map json) => _$FeedStateFromJson(json); final FeedStatus status; - final Map> feed; - final Map hasMoreNews; + final Feed feed; + final HasMoreNews hasMoreNews; @override - List get props => [ - status, - feed, - hasMoreNews, - ]; + List get props => [status, feed, hasMoreNews]; FeedState copyWith({ FeedStatus? status, - Map>? feed, - Map? hasMoreNews, + Map>? feed, + Map? hasMoreNews, }) { return FeedState( status: status ?? this.status, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/view/feed_view.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/view/feed_view.dart index a8d9ef25f..ed7705e6d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/view/feed_view.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/view/feed_view.dart @@ -22,10 +22,7 @@ class FeedView extends StatelessWidget { @visibleForTesting class FeedViewPopulated extends StatefulWidget { - const FeedViewPopulated({ - required this.categories, - super.key, - }); + const FeedViewPopulated({required this.categories, super.key}); final List categories; @@ -72,9 +69,9 @@ class _FeedViewPopulatedState extends State super.dispose(); } - void _onTabChanged() => context - .read() - .add(CategorySelected(category: widget.categories[_tabController.index])); + void _onTabChanged() => context.read().add( + CategorySelected(category: widget.categories[_tabController.index]), + ); @override Widget build(BuildContext context) { @@ -82,12 +79,14 @@ class _FeedViewPopulatedState extends State listener: (context, state) { final selectedCategory = state.selectedCategory; if (selectedCategory != null) { - final selectedCategoryIndex = - widget.categories.indexOf(selectedCategory); + final selectedCategoryIndex = widget.categories.indexOf( + selectedCategory, + ); if (selectedCategoryIndex != -1 && selectedCategoryIndex != _tabController.index) { - _tabController - .animateTo(widget.categories.indexOf(selectedCategory)); + _tabController.animateTo( + widget.categories.indexOf(selectedCategory), + ); } } }, @@ -101,8 +100,8 @@ class _FeedViewPopulatedState extends State .map( (category) => CategoryTab( categoryName: category.name, - onDoubleTap: () { - _controllers[category]?.animateTo( + onDoubleTap: () async { + await _controllers[category]?.animateTo( 0, duration: _categoryScrollToTopDuration, curve: Curves.ease, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed.dart index 518d173e8..950c1e370 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:app_ui/app_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -19,34 +21,39 @@ class CategoryFeed extends StatelessWidget { @override Widget build(BuildContext context) { final categoryFeed = - context.select((FeedBloc bloc) => bloc.state.feed[category]) ?? []; + context.select((FeedBloc bloc) => bloc.state.feed[category.id]) ?? []; final hasMoreNews = - context.select((FeedBloc bloc) => bloc.state.hasMoreNews[category]) ?? - true; + context.select( + (FeedBloc bloc) => bloc.state.hasMoreNews[category.id], + ) ?? + true; - final isFailure = context - .select((FeedBloc bloc) => bloc.state.status == FeedStatus.failure); + final isFailure = context.select( + (FeedBloc bloc) => bloc.state.status == FeedStatus.failure, + ); return BlocListener( listener: (context, state) { if (state.status == FeedStatus.failure && state.feed.isEmpty) { - Navigator.of(context).push( - NetworkError.route( - onRetry: () { - context - .read() - .add(FeedRefreshRequested(category: category)); - Navigator.of(context).pop(); - }, + unawaited( + Navigator.of(context).push( + NetworkError.route( + onRetry: () { + context.read().add( + FeedRefreshRequested(category: category), + ); + Navigator.of(context).pop(); + }, + ), ), ); } }, child: RefreshIndicator( - onRefresh: () async => context - .read() - .add(FeedRefreshRequested(category: category)), + onRefresh: () async => context.read().add( + FeedRefreshRequested(category: category), + ), displacement: 0, color: AppColors.mediumHighEmphasisSurface, child: SelectionArea( @@ -78,9 +85,9 @@ class CategoryFeed extends StatelessWidget { if (isFailure) { result = NetworkError( onRetry: () { - context - .read() - .add(FeedRefreshRequested(category: category)); + context.read().add( + FeedRefreshRequested(category: category), + ); }, ); } else { @@ -91,9 +98,9 @@ class CategoryFeed extends StatelessWidget { ), child: CategoryFeedLoaderItem( key: ValueKey(index), - onPresented: () => context - .read() - .add(FeedRequested(category: category)), + onPresented: () => context.read().add( + FeedRequested(category: category), + ), ), ) : const SizedBox(); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed_item.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed_item.dart index 5a3beeb95..aea03c08f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed_item.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed_item.dart @@ -18,8 +18,9 @@ class CategoryFeedItem extends StatelessWidget { Widget build(BuildContext context) { final newsBlock = block; - final isUserSubscribed = - context.select((AppBloc bloc) => bloc.state.isUserSubscribed); + final isUserSubscribed = context.select( + (AppBloc bloc) => bloc.state.isUserSubscribed, + ); late Widget widget; @@ -33,8 +34,12 @@ class CategoryFeedItem extends StatelessWidget { onPressed: (action) => _onFeedItemAction(context, action), ); } else if (newsBlock is PostLargeBlock) { + final categoryName = context.read().state.getCategoryName( + newsBlock.categoryId, + ); widget = PostLarge( block: newsBlock, + categoryName: categoryName, premiumText: context.l10n.newsBlockPremiumText, isLocked: newsBlock.isPremium && !isUserSubscribed, onPressed: (action) => _onFeedItemAction(context, action), @@ -50,8 +55,12 @@ class CategoryFeedItem extends StatelessWidget { onPressed: (action) => _onFeedItemAction(context, action), ); } else if (newsBlock is PostGridGroupBlock) { + final categoryName = context.read().state.getCategoryName( + newsBlock.categoryId, + ); widget = PostGrid( gridGroupBlock: newsBlock, + categoryName: categoryName, premiumText: context.l10n.newsBlockPremiumText, onPressed: (action) => _onFeedItemAction(context, action), ); @@ -78,17 +87,17 @@ class CategoryFeedItem extends StatelessWidget { BlockAction action, ) async { if (action is NavigateToArticleAction) { - await Navigator.of(context).push( - ArticlePage.route(id: action.articleId), - ); + await Navigator.of( + context, + ).push(ArticlePage.route(id: action.articleId)); } else if (action is NavigateToVideoArticleAction) { await Navigator.of(context).push( ArticlePage.route(id: action.articleId, isVideoArticle: true), ); } else if (action is NavigateToFeedCategoryAction) { - context - .read() - .add(CategorySelected(category: action.category)); + context.read().add( + CategorySelected(category: action.category), + ); } } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed_loader_item.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed_loader_item.dart index 8df984271..df84a8b01 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed_loader_item.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/feed/widgets/category_feed_loader_item.dart @@ -24,9 +24,7 @@ class _CategoryFeedLoaderItemState extends State { Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.symmetric(vertical: AppSpacing.lg), - child: Center( - child: CircularProgressIndicator(), - ), + child: Center(child: CircularProgressIndicator()), ); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/cubit/home_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/cubit/home_state.dart index fe7c5fa96..6400b864d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/cubit/home_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/cubit/home_state.dart @@ -3,7 +3,8 @@ part of 'home_cubit.dart'; enum HomeState { topStories(0), search(1), - subscribe(2); + subscribe(2) + ; const HomeState(this.tabIndex); final int tabIndex; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/view/home_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/view/home_page.dart index 636aca0a2..680dfb3c9 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/view/home_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/view/home_page.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:{{project_name.snakeCase()}}/categories/categories.dart'; import 'package:{{project_name.snakeCase()}}/feed/feed.dart'; import 'package:{{project_name.snakeCase()}}/home/home.dart'; import 'package:news_repository/news_repository.dart'; @@ -15,14 +14,8 @@ class HomePage extends StatelessWidget { return MultiBlocProvider( providers: [ BlocProvider( - create: (context) => CategoriesBloc( - newsRepository: context.read(), - )..add(const CategoriesRequested()), - ), - BlocProvider( - create: (context) => FeedBloc( - newsRepository: context.read(), - ), + create: (context) => + FeedBloc(newsRepository: context.read()), ), BlocProvider(create: (_) => HomeCubit()), ], diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/view/home_view.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/view/home_view.dart index 20a42b7d8..a676bba0d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/view/home_view.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/home/view/home_view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:app_ui/app_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -14,8 +16,9 @@ class HomeView extends StatelessWidget { @override Widget build(BuildContext context) { - final selectedTab = - context.select((HomeCubit cubit) => cubit.state.tabIndex); + final selectedTab = context.select( + (HomeCubit cubit) => cubit.state.tabIndex, + ); return MultiBlocListener( listeners: [ BlocListener( @@ -23,10 +26,12 @@ class HomeView extends StatelessWidget { previous.showLoginOverlay != current.showLoginOverlay, listener: (context, state) { if (state.showLoginOverlay) { - showAppModal( - context: context, - builder: (context) => const LoginModal(), - routeSettings: const RouteSettings(name: LoginModal.name), + unawaited( + showAppModal( + context: context, + builder: (context) => const LoginModal(), + routeSettings: const RouteSettings(name: LoginModal.name), + ), ); } }, @@ -46,10 +51,7 @@ class HomeView extends StatelessWidget { drawer: const NavDrawer(), body: IndexedStack( index: selectedTab, - children: const [ - FeedView(), - SearchPage(), - ], + children: const [FeedView(), SearchPage()], ), bottomNavigationBar: BottomNavBar( currentIndex: selectedTab, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_en.arb b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_en.arb index 9d0dd3732..7d5742331 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_en.arb +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_en.arb @@ -605,7 +605,7 @@ "type": "String", "placeholders": {} }, - "networkError": "A network error has occured.\nCheck your connection and try again.", + "networkError": "A network error has occurred.\nCheck your connection and try again.", "@networkError": { "description": "Text displayed when a network error occurs.", "type": "String", diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_localizations.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_localizations.dart new file mode 100644 index 000000000..6157d94bd --- /dev/null +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_localizations.dart @@ -0,0 +1,770 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'arb/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations of(BuildContext context) { + return Localizations.of(context, AppLocalizations)!; + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [Locale('en')]; + + /// The name of this application + /// + /// In en, this message translates to: + /// **'{{app_name}}'** + String get appName; + + /// Message shown when there is an error at login + /// + /// In en, this message translates to: + /// **'Authentication failure'** + String get authenticationFailure; + + /// Text displayed when an unexpected error occurs + /// + /// In en, this message translates to: + /// **'An unexpected error occurred. Please try again.'** + String get unexpectedFailure; + + /// Text displayed when a share error occurs + /// + /// In en, this message translates to: + /// **'Problem sharing your content. Please try again.'** + String get shareFailure; + + /// Text displayed when loading an ad fails + /// + /// In en, this message translates to: + /// **'Failed to load this ad.'** + String get adLoadFailure; + + /// Greeting shown on the login page. + /// + /// In en, this message translates to: + /// **'Welcome to\n{{app_name}}'** + String get loginWelcomeText; + + /// Login Button Text + /// + /// In en, this message translates to: + /// **'LOGIN'** + String get loginButtonText; + + /// Header title shown on Login with email form + /// + /// In en, this message translates to: + /// **'Please enter your\nemail address.'** + String get loginWithEmailHeaderText; + + /// Hint text shown on the email text field on Login with email form + /// + /// In en, this message translates to: + /// **'Your email address'** + String get loginWithEmailTextFieldHint; + + /// Subtitle shown on Login with email form + /// + /// In en, this message translates to: + /// **'By logging in, you agree to our '** + String get loginWithEmailSubtitleText; + + /// Text terms and privacy policy shown on Login with email form + /// + /// In en, this message translates to: + /// **'Terms of Use and Privacy Policy'** + String get loginWithEmailTermsAndPrivacyPolicyText; + + /// Message shown when there is an error creating an account + /// + /// In en, this message translates to: + /// **'Unable to create an account'** + String get loginWithEmailFailure; + + /// Title shown on the TOS modal + /// + /// In en, this message translates to: + /// **'Terms of Use &\nPrivacy Policy'** + String get termsOfServiceModalTitle; + + /// Log in with email button text shown on the log in modal + /// + /// In en, this message translates to: + /// **'Continue with Email'** + String get loginWithEmailButtonText; + + /// The option to use system-wide theme in the theme selector menu + /// + /// In en, this message translates to: + /// **'System'** + String get systemOption; + + /// The option for light mode in the theme selector menu + /// + /// In en, this message translates to: + /// **'Light'** + String get lightModeOption; + + /// The option for dark mode in the theme selector menu + /// + /// In en, this message translates to: + /// **'Dark'** + String get darkModeOption; + + /// Text shown as title on the onboarding page + /// + /// In en, this message translates to: + /// **'Welcome to\nThe Daily Globe!'** + String get onboardingWelcomeTitle; + + /// Text shown as subtitle on the onboarding page + /// + /// In en, this message translates to: + /// **'Please set your preferences to\nget the app up and running.'** + String get onboardingSubtitle; + + /// Text of the first number page in the onboarding. + /// + /// In en, this message translates to: + /// **'1 OF 2'** + String get onboardingItemFirstNumberTitle; + + /// Text of the second number page in the onboarding. + /// + /// In en, this message translates to: + /// **'2 OF 2'** + String get onboardingItemSecondNumberTitle; + + /// Text of the first page title of the onboarding. + /// + /// In en, this message translates to: + /// **'ADD YOUR TITLE FOR\nAD TRACKING PERMISSIONS'** + String get onboardingItemFirstTitle; + + /// Text of the first page subtitle of the onboarding. + /// + /// In en, this message translates to: + /// **'ADD YOUR DESCRIPTION FOR\nAD TRACKING PERMISSIONS'** + String get onboardingItemFirstSubtitleTitle; + + /// Text of the primary button on the first page of the onboarding. + /// + /// In en, this message translates to: + /// **'ADD YOUR CALL TO ACTION'** + String get onboardingItemFirstButtonTitle; + + /// Text of the primary title on the second page of the onboarding. + /// + /// In en, this message translates to: + /// **'ADD YOUR TITLE FOR\nNOTIFICATION PERMISSIONS'** + String get onboardingItemSecondTitle; + + /// Text of the second page subtitle of the onboarding. + /// + /// In en, this message translates to: + /// **'ADD YOUR DESCRIPTION FOR\nNOTIFICATION PERMISSIONS'** + String get onboardingItemSecondSubtitleTitle; + + /// Text of the primary button on the second page of the onboarding. + /// + /// In en, this message translates to: + /// **'ADD YOUR CALL TO ACTION'** + String get onboardingItemSecondButtonTitle; + + /// Text of the secondary button of the onboarding. + /// + /// In en, this message translates to: + /// **'ADD YOUR DECLINE TEXT'** + String get onboardingItemSecondaryButtonTitle; + + /// Tooltip shown on the login button + /// + /// In en, this message translates to: + /// **'Login'** + String get loginTooltip; + + /// Tooltip shown on the open profile button + /// + /// In en, this message translates to: + /// **'Open profile'** + String get openProfileTooltip; + + /// Log in modal title + /// + /// In en, this message translates to: + /// **'Log In'** + String get loginModalTitle; + + /// Log in modal subtitle + /// + /// In en, this message translates to: + /// **'Log in to access articles and save\nyour preferences.'** + String get loginModalSubtitle; + + /// Continue with email button text shown on the log in modal + /// + /// In en, this message translates to: + /// **'Continue with Email'** + String get continueWithEmailButtonText; + + /// Navigation drawer sections title + /// + /// In en, this message translates to: + /// **'SECTIONS'** + String get navigationDrawerSectionsTitle; + + /// Navigation drawer subscribe title + /// + /// In en, this message translates to: + /// **'Become A Subscriber'** + String get navigationDrawerSubscribeTitle; + + /// Navigation drawer subscribe subtitle + /// + /// In en, this message translates to: + /// **'Subscribe to access premium content and exclusive online events.'** + String get navigationDrawerSubscribeSubtitle; + + /// Subscribe button text + /// + /// In en, this message translates to: + /// **'Subscribe'** + String get subscribeButtonText; + + /// Log in with email next button shown on the login with email form + /// + /// In en, this message translates to: + /// **'Next'** + String get nextButtonText; + + /// User profile page title + /// + /// In en, this message translates to: + /// **'Your Info'** + String get userProfileTitle; + + /// User profile logout button text + /// + /// In en, this message translates to: + /// **'Logout'** + String get userProfileLogoutButtonText; + + /// User profile settings section subtitle + /// + /// In en, this message translates to: + /// **'Settings'** + String get userProfileSettingsSubtitle; + + /// User profile subscription details section subtitle + /// + /// In en, this message translates to: + /// **'Subscription Details'** + String get userProfileSubscriptionDetailsSubtitle; + + /// User profile not a subscriber subtitle + /// + /// In en, this message translates to: + /// **'You are not currently a subscriber.'** + String get userProfileSubscribeBoxSubtitle; + + /// User profile not a subscriber message + /// + /// In en, this message translates to: + /// **'Become a subscriber to access premium content and exclusive online events.'** + String get userProfileSubscribeBoxMessage; + + /// User profile settings subscribe button text + /// + /// In en, this message translates to: + /// **'Subscribe Now'** + String get userProfileSubscribeNowButtonText; + + /// Manage subscription section - subscription item title + /// + /// In en, this message translates to: + /// **'Manage Subscription'** + String get manageSubscriptionTile; + + /// Manage subscription section - subscription body text + /// + /// In en, this message translates to: + /// **'Manage your subscription through the Subscriptions manager on your device.'** + String get manageSubscriptionBodyText; + + /// Manage subscription link text + /// + /// In en, this message translates to: + /// **'Subscriptions'** + String get manageSubscriptionLinkText; + + /// User profile settings section - notifications item title + /// + /// In en, this message translates to: + /// **'Notifications'** + String get userProfileSettingsNotificationsTitle; + + /// User profile settings section - notification preferences item title + /// + /// In en, this message translates to: + /// **'Notification Preferences'** + String get notificationPreferencesTitle; + + /// User profile settings categories notifications subtitle + /// + /// In en, this message translates to: + /// **'Notifications will be sent for all active categories below.'** + String get notificationPreferencesCategoriesSubtitle; + + /// User profile legal section subtitle + /// + /// In en, this message translates to: + /// **'Legal'** + String get userProfileLegalSubtitle; + + /// User profile legal section - terms of use and privacy policy item title + /// + /// In en, this message translates to: + /// **'Terms of Use & Privacy Policy'** + String get userProfileLegalTermsOfUseAndPrivacyPolicyTitle; + + /// User profile legal section - about item title + /// + /// In en, this message translates to: + /// **'About'** + String get userProfileLegalAboutTitle; + + /// User profile checkbox on title + /// + /// In en, this message translates to: + /// **'On'** + String get checkboxOnTitle; + + /// User profile checkbox off title + /// + /// In en, this message translates to: + /// **'Off'** + String get userProfileCheckboxOffTitle; + + /// Header title shown in the magic link prompt UI + /// + /// In en, this message translates to: + /// **'Check your email!'** + String get magicLinkPromptHeader; + + /// Title shown in the magic link prompt UI + /// + /// In en, this message translates to: + /// **'We sent an email to'** + String get magicLinkPromptTitle; + + /// Subtitle shown in the magic link prompt UI + /// + /// In en, this message translates to: + /// **'It contains a special link. Click it to\ncomplete the log in process.'** + String get magicLinkPromptSubtitle; + + /// Open mail app button text shown in the magic link prompt UI + /// + /// In en, this message translates to: + /// **'Open Mail App'** + String get openMailAppButtonText; + + /// Premium text shown on the news block widgets + /// + /// In en, this message translates to: + /// **'Premium'** + String get newsBlockPremiumText; + + /// Share text shown on the news block widgets and article page + /// + /// In en, this message translates to: + /// **'Share'** + String get shareText; + + /// Subscribe header shown in subscribe box + /// + /// In en, this message translates to: + /// **'ADD YOUR EMAIL SIGNUP PROMPT TITLE'** + String get subscribeEmailHeader; + + /// Text shown as a hint in subscribe email text field + /// + /// In en, this message translates to: + /// **'Email address'** + String get subscribeEmailHint; + + /// Subscribe body shown in subscribe box + /// + /// In en, this message translates to: + /// **'ADD YOUR EMAIL SIGNUP PROMPT DESCRIPTION'** + String get subscribeEmailBody; + + /// Text shown in a button of a subscribe box + /// + /// In en, this message translates to: + /// **'ADD YOUR CALL TO ACTION'** + String get subscribeEmailButtonText; + + /// Subscribe successful header shown in subscribe box + /// + /// In en, this message translates to: + /// **'Thank you for signing up!'** + String get subscribeSuccessfulHeader; + + /// Subscribe body shown in subscribe box + /// + /// In en, this message translates to: + /// **'Check your email for all of your newsletter details. '** + String get subscribeSuccessfulEmailBody; + + /// Message displayed when error ocurred during subscribing to newsletter + /// + /// In en, this message translates to: + /// **'Problem ocurred when subscribing to the newsletter'** + String get subscribeErrorMessage; + + /// Text displayed at the top of the popular searches. + /// + /// In en, this message translates to: + /// **'Popular Searches'** + String get searchPopularSearches; + + /// Text displayed at the top of the popular articles. + /// + /// In en, this message translates to: + /// **'Popular Articles'** + String get searchPopularArticles; + + /// Text displayed at the top of the relevant search. + /// + /// In en, this message translates to: + /// **'Relevant Topics / Sections'** + String get searchRelevantTopics; + + /// Text displayed at the top of the relevant articles. + /// + /// In en, this message translates to: + /// **'Relevant Articles'** + String get searchRelevantArticles; + + /// Hint displayed in search text field. + /// + /// In en, this message translates to: + /// **'Search by keyword'** + String get searchByKeyword; + + /// Message displayed when error occurred during fetching search results. + /// + /// In en, this message translates to: + /// **'Problem ocurred finding search results.'** + String get searchErrorMessage; + + /// Top stories text shown in the bottom nav bar widget. + /// + /// In en, this message translates to: + /// **'Top Stories'** + String get bottomNavBarTopStories; + + /// Search text shown in the bottom nav bar widget. + /// + /// In en, this message translates to: + /// **'Search'** + String get bottomNavBarSearch; + + /// Header text shown in the bottom of article content. + /// + /// In en, this message translates to: + /// **'Related Stories'** + String get relatedStories; + + /// Title text shown in the subscribe modal widget. + /// + /// In en, this message translates to: + /// **'ADD YOUR SUBSCRIPTION\nPROMPT TITLE'** + String get subscribeModalTitle; + + /// Subtitle text shown in the subscribe modal widget. + /// + /// In en, this message translates to: + /// **'ADD YOUR SUBSCRIPTION\nPROMPT DESCRIPTION'** + String get subscribeModalSubtitle; + + /// Text shown in log in button on the subscribe modal widget. + /// + /// In en, this message translates to: + /// **'Log In'** + String get subscribeModalLogInButton; + + /// Title text shown in the subscribe limit modal widget. + /// + /// In en, this message translates to: + /// **'You\'ve reached your\n4 article limit.'** + String get subscribeWithArticleLimitModalTitle; + + /// Subtitle text shown in the subscribe limit modal modal widget. + /// + /// In en, this message translates to: + /// **'ADD YOUR SUBSCRIPTION\nPROMPT DESCRIPTION'** + String get subscribeWithArticleLimitModalSubtitle; + + /// Text shown in log in button on the subscribe limit modal widget. + /// + /// In en, this message translates to: + /// **'Log In'** + String get subscribeWithArticleLimitModalLogInButton; + + /// Text shown in watch video button on the subscribe limit modal widget. + /// + /// In en, this message translates to: + /// **'Watch a video to view this article'** + String get subscribeWithArticleLimitModalWatchVideoButton; + + /// Header text shown in the bottom of article content above comment entry field. + /// + /// In en, this message translates to: + /// **'Discussion'** + String get discussion; + + /// Text shown inside comment entry text field. + /// + /// In en, this message translates to: + /// **'Enter comment'** + String get commentEntryHint; + + /// Title shown in the header of trending story article. + /// + /// In en, this message translates to: + /// **'TRENDING STORY'** + String get trendingStoryTitle; + + /// Text shown in the title of the subscription purchase overlay. + /// + /// In en, this message translates to: + /// **'Subscribe today!'** + String get subscriptionPurchaseTitle; + + /// Text shown bellow the title of the subscription purchase overlay. + /// + /// In en, this message translates to: + /// **'Become a subscriber to access premium content and exclusive online events.'** + String get subscriptionPurchaseSubtitle; + + /// Text shown as a header for subscription benefits. + /// + /// In en, this message translates to: + /// **'Benefits'** + String get subscriptionPurchaseBenefits; + + /// Text shown bellow the subscription benefits. + /// + /// In en, this message translates to: + /// **'Cancel Anytime'** + String get subscriptionPurchaseCancelAnytime; + + /// Text shown in button on subscription purchase overlay. + /// + /// In en, this message translates to: + /// **'Start Free Trial'** + String get subscriptionPurchaseButton; + + /// Text shown in button on subscription purchase overlay when user is unauthenticated. + /// + /// In en, this message translates to: + /// **'Log in to subscribe'** + String get subscriptionUnauthenticatedPurchaseButton; + + /// Text shown in unimplemented button on subscription purchase overlay. + /// + /// In en, this message translates to: + /// **'View Details'** + String get subscriptionViewDetailsButton; + + /// Text shown when clicked on view details button. + /// + /// In en, this message translates to: + /// **'Configure additional subscription packages.'** + String get subscriptionViewDetailsButtonSnackBar; + + /// Text shown subscription purchase was completed. + /// + /// In en, this message translates to: + /// **'Purchase completed!'** + String get subscriptionPurchaseCompleted; + + /// Text displayed where month abbreviation is used. + /// + /// In en, this message translates to: + /// **'mo'** + String get monthAbbreviation; + + /// Text displayed where year abbreviation is used. + /// + /// In en, this message translates to: + /// **'yr'** + String get yearAbbreviation; + + /// Slideshow text shown on slideshow introduction widgets. + /// + /// In en, this message translates to: + /// **'Slideshow'** + String get slideshow; + + /// Text displayed on the number of pages in the slideshow page. + /// + /// In en, this message translates to: + /// **'of'** + String get slideshow_of_title; + + /// Text displayed when a network error occurs. + /// + /// In en, this message translates to: + /// **'A network error has occurred.\nCheck your connection and try again.'** + String get networkError; + + /// Text displayed on the refresh button when a network error occurs. + /// + /// In en, this message translates to: + /// **'Try Again'** + String get networkErrorButton; + + /// Delete account dialog cancel button text + /// + /// In en, this message translates to: + /// **'Cancel'** + String get deleteAccountDialogCancelButtonText; + + /// Delete account dialog subtitle + /// + /// In en, this message translates to: + /// **'ADD YOUR DELETE DIALOG SUBTITLE'** + String get deleteAccountDialogSubtitle; + + /// Delete account dialog title + /// + /// In en, this message translates to: + /// **'ADD YOUR DELETE DIALOG TITLE'** + String get deleteAccountDialogTitle; + + /// User profile delete account button title + /// + /// In en, this message translates to: + /// **'Delete account'** + String get userProfileDeleteAccountButton; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.', + ); +} diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_localizations_en.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_localizations_en.dart new file mode 100644 index 000000000..37a20150b --- /dev/null +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/arb/app_localizations_en.dart @@ -0,0 +1,355 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get appName => '{{app_name}}'; + + @override + String get authenticationFailure => 'Authentication failure'; + + @override + String get unexpectedFailure => + 'An unexpected error occurred. Please try again.'; + + @override + String get shareFailure => 'Problem sharing your content. Please try again.'; + + @override + String get adLoadFailure => 'Failed to load this ad.'; + + @override + String get loginWelcomeText => 'Welcome to\n{{app_name}}'; + + @override + String get loginButtonText => 'LOGIN'; + + @override + String get loginWithEmailHeaderText => 'Please enter your\nemail address.'; + + @override + String get loginWithEmailTextFieldHint => 'Your email address'; + + @override + String get loginWithEmailSubtitleText => 'By logging in, you agree to our '; + + @override + String get loginWithEmailTermsAndPrivacyPolicyText => + 'Terms of Use and Privacy Policy'; + + @override + String get loginWithEmailFailure => 'Unable to create an account'; + + @override + String get termsOfServiceModalTitle => 'Terms of Use &\nPrivacy Policy'; + + @override + String get loginWithEmailButtonText => 'Continue with Email'; + + @override + String get systemOption => 'System'; + + @override + String get lightModeOption => 'Light'; + + @override + String get darkModeOption => 'Dark'; + + @override + String get onboardingWelcomeTitle => 'Welcome to\nThe Daily Globe!'; + + @override + String get onboardingSubtitle => + 'Please set your preferences to\nget the app up and running.'; + + @override + String get onboardingItemFirstNumberTitle => '1 OF 2'; + + @override + String get onboardingItemSecondNumberTitle => '2 OF 2'; + + @override + String get onboardingItemFirstTitle => + 'ADD YOUR TITLE FOR\nAD TRACKING PERMISSIONS'; + + @override + String get onboardingItemFirstSubtitleTitle => + 'ADD YOUR DESCRIPTION FOR\nAD TRACKING PERMISSIONS'; + + @override + String get onboardingItemFirstButtonTitle => 'ADD YOUR CALL TO ACTION'; + + @override + String get onboardingItemSecondTitle => + 'ADD YOUR TITLE FOR\nNOTIFICATION PERMISSIONS'; + + @override + String get onboardingItemSecondSubtitleTitle => + 'ADD YOUR DESCRIPTION FOR\nNOTIFICATION PERMISSIONS'; + + @override + String get onboardingItemSecondButtonTitle => 'ADD YOUR CALL TO ACTION'; + + @override + String get onboardingItemSecondaryButtonTitle => 'ADD YOUR DECLINE TEXT'; + + @override + String get loginTooltip => 'Login'; + + @override + String get openProfileTooltip => 'Open profile'; + + @override + String get loginModalTitle => 'Log In'; + + @override + String get loginModalSubtitle => + 'Log in to access articles and save\nyour preferences.'; + + @override + String get continueWithEmailButtonText => 'Continue with Email'; + + @override + String get navigationDrawerSectionsTitle => 'SECTIONS'; + + @override + String get navigationDrawerSubscribeTitle => 'Become A Subscriber'; + + @override + String get navigationDrawerSubscribeSubtitle => + 'Subscribe to access premium content and exclusive online events.'; + + @override + String get subscribeButtonText => 'Subscribe'; + + @override + String get nextButtonText => 'Next'; + + @override + String get userProfileTitle => 'Your Info'; + + @override + String get userProfileLogoutButtonText => 'Logout'; + + @override + String get userProfileSettingsSubtitle => 'Settings'; + + @override + String get userProfileSubscriptionDetailsSubtitle => 'Subscription Details'; + + @override + String get userProfileSubscribeBoxSubtitle => + 'You are not currently a subscriber.'; + + @override + String get userProfileSubscribeBoxMessage => + 'Become a subscriber to access premium content and exclusive online events.'; + + @override + String get userProfileSubscribeNowButtonText => 'Subscribe Now'; + + @override + String get manageSubscriptionTile => 'Manage Subscription'; + + @override + String get manageSubscriptionBodyText => + 'Manage your subscription through the Subscriptions manager on your device.'; + + @override + String get manageSubscriptionLinkText => 'Subscriptions'; + + @override + String get userProfileSettingsNotificationsTitle => 'Notifications'; + + @override + String get notificationPreferencesTitle => 'Notification Preferences'; + + @override + String get notificationPreferencesCategoriesSubtitle => + 'Notifications will be sent for all active categories below.'; + + @override + String get userProfileLegalSubtitle => 'Legal'; + + @override + String get userProfileLegalTermsOfUseAndPrivacyPolicyTitle => + 'Terms of Use & Privacy Policy'; + + @override + String get userProfileLegalAboutTitle => 'About'; + + @override + String get checkboxOnTitle => 'On'; + + @override + String get userProfileCheckboxOffTitle => 'Off'; + + @override + String get magicLinkPromptHeader => 'Check your email!'; + + @override + String get magicLinkPromptTitle => 'We sent an email to'; + + @override + String get magicLinkPromptSubtitle => + 'It contains a special link. Click it to\ncomplete the log in process.'; + + @override + String get openMailAppButtonText => 'Open Mail App'; + + @override + String get newsBlockPremiumText => 'Premium'; + + @override + String get shareText => 'Share'; + + @override + String get subscribeEmailHeader => 'ADD YOUR EMAIL SIGNUP PROMPT TITLE'; + + @override + String get subscribeEmailHint => 'Email address'; + + @override + String get subscribeEmailBody => 'ADD YOUR EMAIL SIGNUP PROMPT DESCRIPTION'; + + @override + String get subscribeEmailButtonText => 'ADD YOUR CALL TO ACTION'; + + @override + String get subscribeSuccessfulHeader => 'Thank you for signing up!'; + + @override + String get subscribeSuccessfulEmailBody => + 'Check your email for all of your newsletter details. '; + + @override + String get subscribeErrorMessage => + 'Problem ocurred when subscribing to the newsletter'; + + @override + String get searchPopularSearches => 'Popular Searches'; + + @override + String get searchPopularArticles => 'Popular Articles'; + + @override + String get searchRelevantTopics => 'Relevant Topics / Sections'; + + @override + String get searchRelevantArticles => 'Relevant Articles'; + + @override + String get searchByKeyword => 'Search by keyword'; + + @override + String get searchErrorMessage => 'Problem ocurred finding search results.'; + + @override + String get bottomNavBarTopStories => 'Top Stories'; + + @override + String get bottomNavBarSearch => 'Search'; + + @override + String get relatedStories => 'Related Stories'; + + @override + String get subscribeModalTitle => 'ADD YOUR SUBSCRIPTION\nPROMPT TITLE'; + + @override + String get subscribeModalSubtitle => + 'ADD YOUR SUBSCRIPTION\nPROMPT DESCRIPTION'; + + @override + String get subscribeModalLogInButton => 'Log In'; + + @override + String get subscribeWithArticleLimitModalTitle => + 'You\'ve reached your\n4 article limit.'; + + @override + String get subscribeWithArticleLimitModalSubtitle => + 'ADD YOUR SUBSCRIPTION\nPROMPT DESCRIPTION'; + + @override + String get subscribeWithArticleLimitModalLogInButton => 'Log In'; + + @override + String get subscribeWithArticleLimitModalWatchVideoButton => + 'Watch a video to view this article'; + + @override + String get discussion => 'Discussion'; + + @override + String get commentEntryHint => 'Enter comment'; + + @override + String get trendingStoryTitle => 'TRENDING STORY'; + + @override + String get subscriptionPurchaseTitle => 'Subscribe today!'; + + @override + String get subscriptionPurchaseSubtitle => + 'Become a subscriber to access premium content and exclusive online events.'; + + @override + String get subscriptionPurchaseBenefits => 'Benefits'; + + @override + String get subscriptionPurchaseCancelAnytime => 'Cancel Anytime'; + + @override + String get subscriptionPurchaseButton => 'Start Free Trial'; + + @override + String get subscriptionUnauthenticatedPurchaseButton => 'Log in to subscribe'; + + @override + String get subscriptionViewDetailsButton => 'View Details'; + + @override + String get subscriptionViewDetailsButtonSnackBar => + 'Configure additional subscription packages.'; + + @override + String get subscriptionPurchaseCompleted => 'Purchase completed!'; + + @override + String get monthAbbreviation => 'mo'; + + @override + String get yearAbbreviation => 'yr'; + + @override + String get slideshow => 'Slideshow'; + + @override + String get slideshow_of_title => 'of'; + + @override + String get networkError => + 'A network error has occurred.\nCheck your connection and try again.'; + + @override + String get networkErrorButton => 'Try Again'; + + @override + String get deleteAccountDialogCancelButtonText => 'Cancel'; + + @override + String get deleteAccountDialogSubtitle => 'ADD YOUR DELETE DIALOG SUBTITLE'; + + @override + String get deleteAccountDialogTitle => 'ADD YOUR DELETE DIALOG TITLE'; + + @override + String get userProfileDeleteAccountButton => 'Delete account'; +} diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/l10n.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/l10n.dart index 5ca96bd5e..70c6d9f3d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/l10n.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/l10n/l10n.dart @@ -1,7 +1,7 @@ -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter/widgets.dart'; +import 'package:{{project_name.snakeCase()}}/l10n/arb/app_localizations.dart'; -export 'package:flutter_gen/gen_l10n/app_localizations.dart'; +export 'package:{{project_name.snakeCase()}}/l10n/arb/app_localizations.dart'; extension AppLocalizationsX on BuildContext { AppLocalizations get l10n => AppLocalizations.of(this); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_bloc.dart index 522fa9ed5..2755e3b02 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_bloc.dart @@ -10,10 +10,9 @@ part 'login_event.dart'; part 'login_state.dart'; class LoginBloc extends Bloc { - LoginBloc({ - required UserRepository userRepository, - }) : _userRepository = userRepository, - super(const LoginState()) { + LoginBloc({required UserRepository userRepository}) + : _userRepository = userRepository, + super(const LoginState()) { on(_onEmailChanged); on(_onSendEmailLinkSubmitted); on(_onGoogleSubmitted); @@ -26,12 +25,7 @@ class LoginBloc extends Bloc { void _onEmailChanged(LoginEmailChanged event, Emitter emit) { final email = Email.dirty(event.email); - emit( - state.copyWith( - email: email, - valid: Formz.validate([email]), - ), - ); + emit(state.copyWith(email: email, valid: Formz.validate([email]))); } Future _onSendEmailLinkSubmitted( @@ -41,11 +35,9 @@ class LoginBloc extends Bloc { if (!state.valid) return; emit(state.copyWith(status: FormzSubmissionStatus.inProgress)); try { - await _userRepository.sendLoginEmailLink( - email: state.email.value, - ); + await _userRepository.sendLoginEmailLink(email: state.email.value); emit(state.copyWith(status: FormzSubmissionStatus.success)); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: FormzSubmissionStatus.failure)); addError(error, stackTrace); } @@ -61,7 +53,7 @@ class LoginBloc extends Bloc { emit(state.copyWith(status: FormzSubmissionStatus.success)); } on LogInWithGoogleCanceled { emit(state.copyWith(status: FormzSubmissionStatus.canceled)); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: FormzSubmissionStatus.failure)); addError(error, stackTrace); } @@ -75,7 +67,7 @@ class LoginBloc extends Bloc { try { await _userRepository.logInWithApple(); emit(state.copyWith(status: FormzSubmissionStatus.success)); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: FormzSubmissionStatus.failure)); addError(error, stackTrace); } @@ -91,7 +83,7 @@ class LoginBloc extends Bloc { emit(state.copyWith(status: FormzSubmissionStatus.success)); } on LogInWithTwitterCanceled { emit(state.copyWith(status: FormzSubmissionStatus.canceled)); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: FormzSubmissionStatus.failure)); addError(error, stackTrace); } @@ -107,7 +99,7 @@ class LoginBloc extends Bloc { emit(state.copyWith(status: FormzSubmissionStatus.success)); } on LogInWithFacebookCanceled { emit(state.copyWith(status: FormzSubmissionStatus.canceled)); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: FormzSubmissionStatus.failure)); addError(error, stackTrace); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_with_email_link_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_with_email_link_bloc.dart index d1de79d36..d3081a478 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_with_email_link_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_with_email_link_bloc.dart @@ -10,10 +10,9 @@ part 'login_with_email_link_state.dart'; class LoginWithEmailLinkBloc extends Bloc { - LoginWithEmailLinkBloc({ - required UserRepository userRepository, - }) : _userRepository = userRepository, - super(const LoginWithEmailLinkState()) { + LoginWithEmailLinkBloc({required UserRepository userRepository}) + : _userRepository = userRepository, + super(const LoginWithEmailLinkState()) { on(_onLoginWithEmailLinkSubmitted); _incomingEmailLinksSub = _userRepository.incomingEmailLinks @@ -35,9 +34,7 @@ class LoginWithEmailLinkBloc final currentUser = await _userRepository.user.first; if (!currentUser.isAnonymous) { throw LogInWithEmailLinkFailure( - Exception( - 'The user is already logged in', - ), + Exception('The user is already logged in'), ); } @@ -50,14 +47,13 @@ class LoginWithEmailLinkBloc ); } - final redirectUrl = - Uri.tryParse(emailLink.queryParameters['continueUrl']!); + final redirectUrl = Uri.tryParse( + emailLink.queryParameters['continueUrl']!, + ); if (!(redirectUrl?.queryParameters.containsKey('email') ?? false)) { throw LogInWithEmailLinkFailure( - Exception( - 'No `email` parameter found in the received email link', - ), + Exception('No `email` parameter found in the received email link'), ); } @@ -67,15 +63,15 @@ class LoginWithEmailLinkBloc ); emit(state.copyWith(status: LoginWithEmailLinkStatus.success)); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(state.copyWith(status: LoginWithEmailLinkStatus.failure)); addError(error, stackTrace); } } @override - Future close() { - _incomingEmailLinksSub.cancel(); + Future close() async { + await _incomingEmailLinksSub.cancel(); return super.close(); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_with_email_link_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_with_email_link_state.dart index ff17208d3..440f3038a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_with_email_link_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/bloc/login_with_email_link_state.dart @@ -1,11 +1,6 @@ part of 'login_with_email_link_bloc.dart'; -enum LoginWithEmailLinkStatus { - initial, - loading, - success, - failure, -} +enum LoginWithEmailLinkStatus { initial, loading, success, failure } class LoginWithEmailLinkState extends Equatable { const LoginWithEmailLinkState({ @@ -17,11 +12,7 @@ class LoginWithEmailLinkState extends Equatable { @override List get props => [status]; - LoginWithEmailLinkState copyWith({ - LoginWithEmailLinkStatus? status, - }) { - return LoginWithEmailLinkState( - status: status ?? this.status, - ); + LoginWithEmailLinkState copyWith({LoginWithEmailLinkStatus? status}) { + return LoginWithEmailLinkState(status: status ?? this.status); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/view/login_modal.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/view/login_modal.dart index 6101e5489..d4fbe181a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/view/login_modal.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/view/login_modal.dart @@ -14,9 +14,7 @@ class LoginModal extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (_) => LoginBloc( - userRepository: context.read(), - ), + create: (_) => LoginBloc(userRepository: context.read()), child: const LoginForm(), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/view/login_with_email_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/view/login_with_email_page.dart index 15827d2e9..d37d21982 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/view/login_with_email_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/view/login_with_email_page.dart @@ -13,9 +13,7 @@ class LoginWithEmailPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (_) => LoginBloc( - userRepository: context.read(), - ), + create: (_) => LoginBloc(userRepository: context.read()), child: Scaffold( appBar: AppBar( leading: const AppBackButton(), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/widgets/login_form.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/widgets/login_form.dart index b83ee573b..f915c4655 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/widgets/login_form.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/widgets/login_form.dart @@ -16,8 +16,9 @@ class LoginForm extends StatelessWidget { listener: (context, state) { if (state.status.isLoggedIn) { // Pop all routes on top of [LoginModal], then pop the modal itself. - Navigator.of(context) - .popUntil((route) => route.settings.name == LoginModal.name); + Navigator.of( + context, + ).popUntil((route) => route.settings.name == LoginModal.name); Navigator.of(context).pop(); } }, @@ -201,9 +202,8 @@ class _ContinueWithEmailLoginButton extends StatelessWidget { Widget build(BuildContext context) { return AppButton.outlinedTransparentDarkAqua( key: const Key('loginForm_emailLogin_appButton'), - onPressed: () => Navigator.of(context).push( - LoginWithEmailPage.route(), - ), + onPressed: () => + Navigator.of(context).push(LoginWithEmailPage.route()), textStyle: Theme.of(context).textTheme.titleMedium, child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/widgets/login_with_email_form.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/widgets/login_with_email_form.dart index 290297b09..a4a52309a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/widgets/login_with_email_form.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/login/widgets/login_with_email_form.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:app_ui/app_ui.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -17,8 +19,10 @@ class LoginWithEmailForm extends StatelessWidget { return BlocListener( listener: (context, state) { if (state.status.isSuccess) { - Navigator.of(context).push( - MagicLinkPromptPage.route(email: email), + unawaited( + Navigator.of( + context, + ).push(MagicLinkPromptPage.route(email: email)), ); } else if (state.status.isFailure) { ScaffoldMessenger.of(context) @@ -135,14 +139,11 @@ class _TermsAndPrivacyPolicyLinkTexts extends StatelessWidget { ), recognizer: TapGestureRecognizer() ..onTap = () => showAppModal( - context: context, - builder: (context) => const TermsOfServiceModal(), - ), - ), - TextSpan( - text: '.', - style: theme.textTheme.bodyLarge, + context: context, + builder: (context) => const TermsOfServiceModal(), + ), ), + TextSpan(text: '.', style: theme.textTheme.bodyLarge), ], ), ), @@ -175,17 +176,15 @@ class _NextButton extends StatelessWidget { @visibleForTesting class ClearIconButton extends StatelessWidget { - const ClearIconButton({ - required this.onPressed, - super.key, - }); + const ClearIconButton({required this.onPressed, super.key}); final VoidCallback? onPressed; @override Widget build(BuildContext context) { - final suffixVisible = - context.select((LoginBloc bloc) => bloc.state.email.value.isNotEmpty); + final suffixVisible = context.select( + (LoginBloc bloc) => bloc.state.email.value.isNotEmpty, + ); return Padding( key: const Key('loginWithEmailForm_clearIconButton'), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/magic_link_prompt/view/magic_link_prompt_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/magic_link_prompt/view/magic_link_prompt_page.dart index f4d8a25a4..86f85d8f9 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/magic_link_prompt/view/magic_link_prompt_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/magic_link_prompt/view/magic_link_prompt_page.dart @@ -23,14 +23,13 @@ class MagicLinkPromptPage extends StatelessWidget { IconButton( key: const Key('magicLinkPrompt_closeIcon'), icon: const Icon(Icons.close), - onPressed: () => Navigator.of(context) - .popUntil((route) => route.settings.name == LoginModal.name), + onPressed: () => Navigator.of( + context, + ).popUntil((route) => route.settings.name == LoginModal.name), ), ], ), - body: MagicLinkPromptView( - email: email, - ), + body: MagicLinkPromptView(email: email), ); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/magic_link_prompt/view/magic_link_prompt_view.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/magic_link_prompt/view/magic_link_prompt_view.dart index 22ac22afa..0971ca4dc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/magic_link_prompt/view/magic_link_prompt_view.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/magic_link_prompt/view/magic_link_prompt_view.dart @@ -73,9 +73,7 @@ class MagicLinkPromptSubtitle extends StatelessWidget { Text( email, textAlign: TextAlign.center, - style: theme.textTheme.bodyLarge?.apply( - color: AppColors.darkAqua, - ), + style: theme.textTheme.bodyLarge?.apply(color: AppColors.darkAqua), ), const SizedBox(height: AppSpacing.xxlg), Text( @@ -90,10 +88,8 @@ class MagicLinkPromptSubtitle extends StatelessWidget { @visibleForTesting class MagicLinkPromptOpenEmailButton extends StatelessWidget { - MagicLinkPromptOpenEmailButton({ - EmailLauncher? emailLauncher, - super.key, - }) : _emailLauncher = emailLauncher ?? EmailLauncher(); + MagicLinkPromptOpenEmailButton({EmailLauncher? emailLauncher, super.key}) + : _emailLauncher = emailLauncher ?? EmailLauncher(); final EmailLauncher _emailLauncher; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/bootstrap/app_bloc_observer.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/bootstrap/app_bloc_observer.dart index 26832cff8..62fbb3ddf 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/bootstrap/app_bloc_observer.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/bootstrap/app_bloc_observer.dart @@ -1,12 +1,12 @@ +import 'dart:async'; import 'dart:developer'; import 'package:analytics_repository/analytics_repository.dart'; import 'package:bloc/bloc.dart'; class AppBlocObserver extends BlocObserver { - AppBlocObserver({ - required AnalyticsRepository analyticsRepository, - }) : _analyticsRepository = analyticsRepository; + AppBlocObserver({required AnalyticsRepository analyticsRepository}) + : _analyticsRepository = analyticsRepository; final AnalyticsRepository _analyticsRepository; @@ -29,12 +29,16 @@ class AppBlocObserver extends BlocObserver { void onChange(BlocBase bloc, Change change) { super.onChange(bloc, change); final dynamic state = change.nextState; - if (state is AnalyticsEventMixin) _analyticsRepository.track(state.event); + if (state is AnalyticsEventMixin) { + unawaited(_analyticsRepository.track(state.event)); + } } @override void onEvent(Bloc bloc, Object? event) { super.onEvent(bloc, event); - if (event is AnalyticsEventMixin) _analyticsRepository.track(event.event); + if (event is AnalyticsEventMixin) { + unawaited(_analyticsRepository.track(event.event)); + } } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/bootstrap/bootstrap.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/bootstrap/bootstrap.dart index ca5460120..cd610425f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/bootstrap/bootstrap.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/bootstrap/bootstrap.dart @@ -14,53 +14,51 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -typedef AppBuilder = Future Function( - // Ignore, as we currently only use dynamic links for e-mail login. - // e-mail login will be replaced but not deprecated. - // source:https://firebase.google.com/support/dynamic-links-faq#im_currently_using_or_need_to_use_dynamic_links_for_email_link_authentication_in_firebase_authentication_will_this_feature_continue_to_work_after_the_sunset - // ignore: deprecated_member_use - FirebaseDynamicLinks firebaseDynamicLinks, - FirebaseMessaging firebaseMessaging, - SharedPreferences sharedPreferences, - AnalyticsRepository analyticsRepository, -); +typedef AppBuilder = + Future Function( + // Ignore, as we currently only use dynamic links for e-mail login. + // e-mail login will be replaced but not deprecated. + // source:https://firebase.google.com/support/dynamic-links-faq#im_currently_using_or_need_to_use_dynamic_links_for_email_link_authentication_in_firebase_authentication_will_this_feature_continue_to_work_after_the_sunset + // ignore: deprecated_member_use + FirebaseDynamicLinks firebaseDynamicLinks, + FirebaseMessaging firebaseMessaging, + SharedPreferences sharedPreferences, + AnalyticsRepository analyticsRepository, + ); Future bootstrap(AppBuilder builder) async { - await runZonedGuarded>( - () async { - WidgetsFlutterBinding.ensureInitialized(); + await runZonedGuarded>(() async { + WidgetsFlutterBinding.ensureInitialized(); - await Firebase.initializeApp(); - final analyticsRepository = - AnalyticsRepository(FirebaseAnalytics.instance); - final blocObserver = AppBlocObserver( - analyticsRepository: analyticsRepository, - ); - Bloc.observer = blocObserver; - HydratedBloc.storage = await HydratedStorage.build( - storageDirectory: await getApplicationSupportDirectory(), - ); + await Firebase.initializeApp(); + final analyticsRepository = AnalyticsRepository(FirebaseAnalytics.instance); + final blocObserver = AppBlocObserver( + analyticsRepository: analyticsRepository, + ); + Bloc.observer = blocObserver; + final storageDirectory = await getApplicationSupportDirectory(); + HydratedBloc.storage = await HydratedStorage.build( + storageDirectory: HydratedStorageDirectory(storageDirectory.path), + ); - if (kDebugMode) { - await HydratedBloc.storage.clear(); - } + if (kDebugMode) { + await HydratedBloc.storage.clear(); + } - await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); - FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError; + await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); + FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError; - final sharedPreferences = await SharedPreferences.getInstance(); + final sharedPreferences = await SharedPreferences.getInstance(); - unawaited(MobileAds.instance.initialize()); - runApp( - await builder( - // ignore: deprecated_member_use - FirebaseDynamicLinks.instance, - FirebaseMessaging.instance, - sharedPreferences, - analyticsRepository, - ), - ); - }, - (_, __) {}, - ); + unawaited(MobileAds.instance.initialize()); + runApp( + await builder( + // ignore: deprecated_member_use + FirebaseDynamicLinks.instance, + FirebaseMessaging.instance, + sharedPreferences, + analyticsRepository, + ), + ); + }, (_, _) {}); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/{{#flavors}}main_{{{name}}}.dart{{/flavors}} b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/{{#flavors}}main_{{{name}}}.dart{{/flavors}} index 910330a18..9f2e6a5ab 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/{{#flavors}}main_{{{name}}}.dart{{/flavors}} +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/main/{{#flavors}}main_{{{name}}}.dart{{/flavors}} @@ -18,90 +18,86 @@ import 'package:purchase_client/purchase_client.dart'; import 'package:token_storage/token_storage.dart'; import 'package:user_repository/user_repository.dart'; -void main() { - bootstrap( - ( - firebaseDynamicLinks, - firebaseMessaging, - sharedPreferences, - analyticsRepository, - ) async { - final tokenStorage = InMemoryTokenStorage(); - - final apiClient = {{project_name.pascalCase()}}ApiClient.localhost( - tokenProvider: tokenStorage.readToken, - ); - - const permissionClient = PermissionClient(); - - final persistentStorage = PersistentStorage( - sharedPreferences: sharedPreferences, - ); - - final packageInfoClient = PackageInfoClient( - appName: '{{app_name}} [DEV]', - packageName: '{{reverse_domain}}.dev', - packageVersion: packageVersion, - ); - - final deepLinkService = DeepLinkService( - deepLinkClient: FirebaseDeepLinkClient( - firebaseDynamicLinks: firebaseDynamicLinks, - ), - ); - - final userStorage = UserStorage(storage: persistentStorage); - - final authenticationClient = FirebaseAuthenticationClient( - tokenStorage: tokenStorage, - ); - - final notificationsClient = FirebaseNotificationsClient( - firebaseMessaging: firebaseMessaging, - ); - - final userRepository = UserRepository( - apiClient: apiClient, - authenticationClient: authenticationClient, - packageInfoClient: packageInfoClient, - deepLinkService: deepLinkService, - storage: userStorage, - ); - - final newsRepository = NewsRepository( - apiClient: apiClient, - ); - - final notificationsRepository = NotificationsRepository( - permissionClient: permissionClient, - storage: NotificationsStorage(storage: persistentStorage), - notificationsClient: notificationsClient, - apiClient: apiClient, - ); - - final articleRepository = ArticleRepository( - storage: ArticleStorage(storage: persistentStorage), - apiClient: apiClient, - ); - - final inAppPurchaseRepository = InAppPurchaseRepository( - authenticationClient: authenticationClient, - apiClient: apiClient, - inAppPurchase: PurchaseClient(), - ); - - final adsConsentClient = AdsConsentClient(); - - return App( - userRepository: userRepository, - newsRepository: newsRepository, - notificationsRepository: notificationsRepository, - articleRepository: articleRepository, - analyticsRepository: analyticsRepository, - inAppPurchaseRepository: inAppPurchaseRepository, - adsConsentClient: adsConsentClient, - user: await userRepository.user.first, - ); - }, - ); +void main() async { + await bootstrap(( + firebaseDynamicLinks, + firebaseMessaging, + sharedPreferences, + analyticsRepository, + ) async { + final tokenStorage = InMemoryTokenStorage(); + + final apiClient = {{project_name.pascalCase()}}ApiClient.localhost( + tokenProvider: tokenStorage.readToken, + ); + + const permissionClient = PermissionClient(); + + final persistentStorage = PersistentStorage( + sharedPreferences: sharedPreferences, + ); + + final packageInfoClient = PackageInfoClient( + appName: '{{app_name}} [DEV]', + packageName: '{{reverse_domain}}.dev', + packageVersion: packageVersion, + ); + + final deepLinkService = DeepLinkService( + deepLinkClient: FirebaseDeepLinkClient( + firebaseDynamicLinks: firebaseDynamicLinks, + ), + ); + + final userStorage = UserStorage(storage: persistentStorage); + + final authenticationClient = FirebaseAuthenticationClient( + tokenStorage: tokenStorage, + ); + + final notificationsClient = FirebaseNotificationsClient( + firebaseMessaging: firebaseMessaging, + ); + + final userRepository = UserRepository( + apiClient: apiClient, + authenticationClient: authenticationClient, + packageInfoClient: packageInfoClient, + deepLinkService: deepLinkService, + storage: userStorage, + ); + + final newsRepository = NewsRepository(apiClient: apiClient); + + final notificationsRepository = NotificationsRepository( + permissionClient: permissionClient, + storage: NotificationsStorage(storage: persistentStorage), + notificationsClient: notificationsClient, + apiClient: apiClient, + ); + + final articleRepository = ArticleRepository( + storage: ArticleStorage(storage: persistentStorage), + apiClient: apiClient, + ); + + final inAppPurchaseRepository = InAppPurchaseRepository( + authenticationClient: authenticationClient, + apiClient: apiClient, + inAppPurchase: PurchaseClient(), + ); + + final adsConsentClient = AdsConsentClient(); + + return App( + userRepository: userRepository, + newsRepository: newsRepository, + notificationsRepository: notificationsRepository, + articleRepository: articleRepository, + analyticsRepository: analyticsRepository, + inAppPurchaseRepository: inAppPurchaseRepository, + adsConsentClient: adsConsentClient, + user: await userRepository.user.first, + ); + }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/view/bottom_nav_bar.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/view/bottom_nav_bar.dart index 3c8d697b6..68ea432f9 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/view/bottom_nav_bar.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/view/bottom_nav_bar.dart @@ -21,10 +21,7 @@ class BottomNavBar extends StatelessWidget { label: context.l10n.bottomNavBarTopStories, ), BottomNavigationBarItem( - icon: const Icon( - Icons.search, - key: Key('bottomNavBar_search'), - ), + icon: const Icon(Icons.search, key: Key('bottomNavBar_search')), label: context.l10n.bottomNavBarSearch, ), ], diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/view/nav_drawer.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/view/nav_drawer.dart index 0af84fef5..469d66988 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/view/nav_drawer.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/view/nav_drawer.dart @@ -7,12 +7,13 @@ import 'package:{{project_name.snakeCase()}}/navigation/navigation.dart'; class NavDrawer extends StatelessWidget { const NavDrawer({super.key}); - static const _contentPadding = AppSpacing.lg; + static const double _contentPadding = AppSpacing.lg; @override Widget build(BuildContext context) { - final isUserSubscribed = - context.select((AppBloc bloc) => bloc.state.isUserSubscribed); + final isUserSubscribed = context.select( + (AppBloc bloc) => bloc.state.isUserSubscribed, + ); return ClipRRect( borderRadius: const BorderRadius.only( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/widgets/nav_drawer_sections.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/widgets/nav_drawer_sections.dart index 67164fbed..d5b9c1ef5 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/widgets/nav_drawer_sections.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/widgets/nav_drawer_sections.dart @@ -14,8 +14,9 @@ class NavDrawerSections extends StatelessWidget { final categories = context.select((CategoriesBloc bloc) => bloc.state.categories) ?? []; - final selectedCategory = - context.select((CategoriesBloc bloc) => bloc.state.selectedCategory); + final selectedCategory = context.select( + (CategoriesBloc bloc) => bloc.state.selectedCategory, + ); return Column( children: [ @@ -29,9 +30,9 @@ class NavDrawerSections extends StatelessWidget { onTap: () { Scaffold.of(context).closeDrawer(); context.read().setTab(0); - context - .read() - .add(CategorySelected(category: category)); + context.read().add( + CategorySelected(category: category), + ); }, ), ], @@ -55,9 +56,9 @@ class NavDrawerSectionsTitle extends StatelessWidget { ), child: Text( context.l10n.navigationDrawerSectionsTitle, - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: AppColors.primaryContainer, - ), + style: Theme.of( + context, + ).textTheme.titleMedium?.copyWith(color: AppColors.primaryContainer), ), ), ); @@ -94,22 +95,20 @@ class NavDrawerSectionItem extends StatelessWidget { vertical: AppSpacing.lg + AppSpacing.xxs, ), shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(_borderRadius), - ), + borderRadius: BorderRadius.all(Radius.circular(_borderRadius)), ), horizontalTitleGap: AppSpacing.md, minLeadingWidth: AppSpacing.xlg, - selectedTileColor: AppColors.white.withOpacity(0.08), + selectedTileColor: AppColors.white.withValues(alpha: 0.08), selected: selected, onTap: onTap, title: Text( title, style: Theme.of(context).textTheme.labelLarge?.copyWith( - color: selected - ? AppColors.highEmphasisPrimary - : AppColors.mediumEmphasisPrimary, - ), + color: selected + ? AppColors.highEmphasisPrimary + : AppColors.mediumEmphasisPrimary, + ), ), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/widgets/nav_drawer_subscribe.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/widgets/nav_drawer_subscribe.dart index 4caac1bd1..bd93769d3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/widgets/nav_drawer_subscribe.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/navigation/widgets/nav_drawer_subscribe.dart @@ -35,8 +35,8 @@ class NavDrawerSubscribeTitle extends StatelessWidget { child: Text( context.l10n.navigationDrawerSubscribeTitle, style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: AppColors.highEmphasisPrimary, - ), + color: AppColors.highEmphasisPrimary, + ), ), ), ); @@ -50,14 +50,12 @@ class NavDrawerSubscribeSubtitle extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - horizontal: AppSpacing.lg, - ), + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), child: Text( context.l10n.navigationDrawerSubscribeSubtitle, style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: AppColors.mediumEmphasisPrimary, - ), + color: AppColors.mediumEmphasisPrimary, + ), ), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/network_error/view/network_error.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/network_error/view/network_error.dart index fe6e28385..e97a68343 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/network_error/view/network_error.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/network_error/view/network_error.dart @@ -15,14 +15,10 @@ class NetworkError extends StatelessWidget { /// Route constructor to display the widget inside a [Scaffold]. static Route route({VoidCallback? onRetry}) { return PageRouteBuilder( - pageBuilder: (_, __, ___) => Scaffold( + pageBuilder: (_, _, _) => Scaffold( backgroundColor: AppColors.background, - appBar: AppBar( - leading: const AppBackButton(), - ), - body: Center( - child: NetworkError(onRetry: onRetry), - ), + appBar: AppBar(leading: const AppBackButton()), + body: Center(child: NetworkError(onRetry: onRetry)), ), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/bloc/newsletter_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/bloc/newsletter_bloc.dart index 7d891a168..6878498de 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/bloc/newsletter_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/bloc/newsletter_bloc.dart @@ -9,9 +9,8 @@ part 'newsletter_event.dart'; part 'newsletter_state.dart'; class NewsletterBloc extends Bloc { - NewsletterBloc({ - required this.newsRepository, - }) : super(const NewsletterState()) { + NewsletterBloc({required this.newsRepository}) + : super(const NewsletterState()) { on(_onNewsletterSubscribed); on(_onEmailChanged); } @@ -27,7 +26,7 @@ class NewsletterBloc extends Bloc { try { await newsRepository.subscribeToNewsletter(email: state.email.value); emit(state.copyWith(status: NewsletterStatus.success)); - } catch (error, stackTrace) { + } on Object catch (error, stackTrace) { emit(state.copyWith(status: NewsletterStatus.failure)); addError(error, stackTrace); } @@ -38,11 +37,6 @@ class NewsletterBloc extends Bloc { Emitter emit, ) async { final email = Email.dirty(event.email); - emit( - state.copyWith( - email: email, - isValid: Formz.validate([email]), - ), - ); + emit(state.copyWith(email: email, isValid: Formz.validate([email]))); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/bloc/newsletter_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/bloc/newsletter_state.dart index 20c1bc969..dcd56d9f4 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/bloc/newsletter_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/bloc/newsletter_state.dart @@ -1,11 +1,6 @@ part of 'newsletter_bloc.dart'; -enum NewsletterStatus { - initial, - loading, - success, - failure, -} +enum NewsletterStatus { initial, loading, success, failure } class NewsletterState extends Equatable { const NewsletterState({ @@ -25,10 +20,9 @@ class NewsletterState extends Equatable { NewsletterStatus? status, Email? email, bool? isValid, - }) => - NewsletterState( - status: status ?? this.status, - email: email ?? this.email, - isValid: isValid ?? this.isValid, - ); + }) => NewsletterState( + status: status ?? this.status, + email: email ?? this.email, + isValid: isValid ?? this.isValid, + ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/view/newsletter.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/view/newsletter.dart index 4f3dc7ca8..a5c4bc2f7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/view/newsletter.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/newsletter/view/newsletter.dart @@ -15,9 +15,8 @@ class Newsletter extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => NewsletterBloc( - newsRepository: context.read(), - ), + create: (context) => + NewsletterBloc(newsRepository: context.read()), child: const NewsletterView(), ); } @@ -35,8 +34,9 @@ class _NewsletterViewState extends State { @override Widget build(BuildContext context) { - final isEmailValid = - context.select((bloc) => bloc.state.isValid); + final isEmailValid = context.select( + (bloc) => bloc.state.isValid, + ); return VisibilityDetector( key: const Key('newsletterView'), @@ -44,9 +44,9 @@ class _NewsletterViewState extends State { ? null : (visibility) { if (!visibility.visibleBounds.isEmpty) { - context - .read() - .add(TrackAnalyticsEvent(NewsletterEvent.impression())); + context.read().add( + TrackAnalyticsEvent(NewsletterEvent.impression()), + ); setState(() { _newsletterShown = true; }); @@ -61,9 +61,9 @@ class _NewsletterViewState extends State { SnackBar(content: Text(context.l10n.subscribeErrorMessage)), ); } else if (state.status == NewsletterStatus.success) { - context - .read() - .add(TrackAnalyticsEvent(NewsletterEvent.signUp())); + context.read().add( + TrackAnalyticsEvent(NewsletterEvent.signUp()), + ); } }, builder: (context, state) { @@ -83,15 +83,15 @@ class _NewsletterViewState extends State { bodyText: context.l10n.subscribeEmailBody, email: AppEmailTextField( hintText: context.l10n.subscribeEmailHint, - onChanged: (email) => context - .read() - .add(EmailChanged(email: email)), + onChanged: (email) => context.read().add( + EmailChanged(email: email), + ), ), buttonText: context.l10n.subscribeEmailButtonText, onPressed: isEmailValid - ? () => context - .read() - .add(const NewsletterSubscribed()) + ? () => context.read().add( + const NewsletterSubscribed(), + ) : null, ); }, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/bloc/notification_preferences_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/bloc/notification_preferences_bloc.dart index 2b36e7a3e..c9960a704 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/bloc/notification_preferences_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/bloc/notification_preferences_bloc.dart @@ -13,14 +13,10 @@ class NotificationPreferencesBloc NotificationPreferencesBloc({ required NewsRepository newsRepository, required NotificationsRepository notificationsRepository, - }) : _notificationsRepository = notificationsRepository, - _newsRepository = newsRepository, - super( - NotificationPreferencesState.initial(), - ) { - on( - _onCategoriesPreferenceToggled, - ); + }) : _notificationsRepository = notificationsRepository, + _newsRepository = newsRepository, + super(NotificationPreferencesState.initial()) { + on(_onCategoriesPreferenceToggled); on( _onInitialCategoriesPreferencesRequested, ); @@ -42,8 +38,9 @@ class NotificationPreferencesBloc : updatedCategories.add(event.category); try { - await _notificationsRepository - .setCategoriesPreferences(updatedCategories); + await _notificationsRepository.setCategoriesPreferences( + updatedCategories, + ); emit( state.copyWith( @@ -51,10 +48,8 @@ class NotificationPreferencesBloc selectedCategories: updatedCategories, ), ); - } catch (error, stackTrace) { - emit( - state.copyWith(status: NotificationPreferencesStatus.failure), - ); + } on Exception catch (error, stackTrace) { + emit(state.copyWith(status: NotificationPreferencesStatus.failure)); addError(error, stackTrace); } } @@ -69,15 +64,13 @@ class NotificationPreferencesBloc late Set selectedCategories; late CategoriesResponse categoriesResponse; - await Future.wait( - [ - (() async => selectedCategories = - await _notificationsRepository.fetchCategoriesPreferences() ?? - {})(), - (() async => - categoriesResponse = await _newsRepository.getCategories())(), - ], - ); + await Future.wait([ + (() async => selectedCategories = + await _notificationsRepository.fetchCategoriesPreferences() ?? + {})(), + (() async => + categoriesResponse = await _newsRepository.getCategories())(), + ]); emit( state.copyWith( @@ -86,10 +79,8 @@ class NotificationPreferencesBloc categories: categoriesResponse.categories.toSet(), ), ); - } catch (error, stackTrace) { - emit( - state.copyWith(status: NotificationPreferencesStatus.failure), - ); + } on Exception catch (error, stackTrace) { + emit(state.copyWith(status: NotificationPreferencesStatus.failure)); addError(error, stackTrace); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/bloc/notification_preferences_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/bloc/notification_preferences_state.dart index 001810c5c..5f9837d77 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/bloc/notification_preferences_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/bloc/notification_preferences_state.dart @@ -1,11 +1,6 @@ part of 'notification_preferences_bloc.dart'; -enum NotificationPreferencesStatus { - initial, - loading, - success, - failure, -} +enum NotificationPreferencesStatus { initial, loading, success, failure } class NotificationPreferencesState extends Equatable { const NotificationPreferencesState({ @@ -15,11 +10,11 @@ class NotificationPreferencesState extends Equatable { }); NotificationPreferencesState.initial() - : this( - selectedCategories: {}, - status: NotificationPreferencesStatus.initial, - categories: {}, - ); + : this( + selectedCategories: {}, + status: NotificationPreferencesStatus.initial, + categories: {}, + ); final NotificationPreferencesStatus status; final Set categories; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/view/notification_preferences_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/view/notification_preferences_page.dart index 04d7398f7..dcc8cd4f5 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/view/notification_preferences_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/view/notification_preferences_page.dart @@ -36,9 +36,7 @@ class NotificationPreferencesView extends StatelessWidget { final l10n = context.l10n; final theme = Theme.of(context); return Scaffold( - appBar: AppBar( - leading: const AppBackButton(), - ), + appBar: AppBar(leading: const AppBackButton()), body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), @@ -58,8 +56,10 @@ class NotificationPreferencesView extends StatelessWidget { ), ), const SizedBox(height: AppSpacing.lg), - BlocBuilder( + BlocBuilder< + NotificationPreferencesBloc, + NotificationPreferencesState + >( builder: (context, state) => Expanded( child: ListView( children: state.categories @@ -69,8 +69,9 @@ class NotificationPreferencesView extends StatelessWidget { trailing: AppSwitch( onText: l10n.checkboxOnTitle, offText: l10n.userProfileCheckboxOffTitle, - value: - state.selectedCategories.contains(category), + value: state.selectedCategories.contains( + category, + ), onChanged: (value) => context .read() .add( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/widgets/notification_category_tile.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/widgets/notification_category_tile.dart index 08e4ddd45..3cb11e1d4 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/widgets/notification_category_tile.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/notification_preferences/widgets/notification_category_tile.dart @@ -22,9 +22,7 @@ class NotificationCategoryTile extends StatelessWidget { visualDensity: const VisualDensity( vertical: VisualDensity.minimumDensity, ), - contentPadding: const EdgeInsets.symmetric( - vertical: AppSpacing.lg, - ), + contentPadding: const EdgeInsets.symmetric(vertical: AppSpacing.lg), horizontalTitleGap: 0, onTap: onTap, title: Text( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/onboarding/bloc/onboarding_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/onboarding/bloc/onboarding_bloc.dart index a6d6fb37c..0476b7533 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/onboarding/bloc/onboarding_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/onboarding/bloc/onboarding_bloc.dart @@ -14,9 +14,9 @@ class OnboardingBloc extends Bloc { OnboardingBloc({ required NotificationsRepository notificationsRepository, required AdsConsentClient adsConsentClient, - }) : _notificationsRepository = notificationsRepository, - _adsConsentClient = adsConsentClient, - super(const OnboardingInitial()) { + }) : _notificationsRepository = notificationsRepository, + _adsConsentClient = adsConsentClient, + super(const OnboardingInitial()) { on( _onEnableAdTrackingRequested, transformer: droppable(), @@ -39,7 +39,7 @@ class OnboardingBloc extends Bloc { ? const EnablingAdTrackingSucceeded() : const EnablingAdTrackingFailed(), ); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(const EnablingAdTrackingFailed()); addError(error, stackTrace); } @@ -53,7 +53,7 @@ class OnboardingBloc extends Bloc { emit(const EnablingNotifications()); await _notificationsRepository.toggleNotifications(enable: true); emit(const EnablingNotificationsSucceeded()); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit(const EnablingNotificationsFailed()); addError(error, stackTrace); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/onboarding/view/onboarding_view.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/onboarding/view/onboarding_view.dart index 232472629..e643afde7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/onboarding/view/onboarding_view.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/onboarding/view/onboarding_view.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:app_ui/app_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -27,10 +29,12 @@ class _OnboardingViewState extends State { if ((state is EnablingAdTrackingSucceeded || state is EnablingAdTrackingFailed) && _controller.page != _onboardingPageTwo) { - _controller.animateToPage( - _onboardingPageTwo, - duration: _onboardingItemSwitchDuration, - curve: Curves.easeInOut, + unawaited( + _controller.animateToPage( + _onboardingPageTwo, + duration: _onboardingItemSwitchDuration, + curve: Curves.easeInOut, + ), ); } else if (state is EnablingNotificationsSucceeded) { context.read().add(const AppOnboardingCompleted()); @@ -58,11 +62,12 @@ class _OnboardingViewState extends State { title: l10n.onboardingItemFirstTitle, subtitle: l10n.onboardingItemFirstSubtitleTitle, primaryButton: AppButton.darkAqua( - key: - const Key('onboardingItem_primaryButton_pageOne'), - onPressed: () => context - .read() - .add(const EnableAdTrackingRequested()), + key: const Key( + 'onboardingItem_primaryButton_pageOne', + ), + onPressed: () => context.read().add( + const EnableAdTrackingRequested(), + ), child: Text(l10n.onboardingItemFirstButtonTitle), ), secondaryButton: AppButton.smallTransparent( @@ -85,20 +90,21 @@ class _OnboardingViewState extends State { title: l10n.onboardingItemSecondTitle, subtitle: l10n.onboardingItemSecondSubtitleTitle, primaryButton: AppButton.darkAqua( - key: - const Key('onboardingItem_primaryButton_pageTwo'), - onPressed: () => context - .read() - .add(const EnableNotificationsRequested()), + key: const Key( + 'onboardingItem_primaryButton_pageTwo', + ), + onPressed: () => context.read().add( + const EnableNotificationsRequested(), + ), child: Text(l10n.onboardingItemSecondButtonTitle), ), secondaryButton: AppButton.smallTransparent( key: const Key( 'onboardingItem_secondaryButton_pageTwo', ), - onPressed: () => context - .read() - .add(const AppOnboardingCompleted()), + onPressed: () => context.read().add( + const AppOnboardingCompleted(), + ), child: Text( context.l10n.onboardingItemSecondaryButtonTitle, ), @@ -137,9 +143,7 @@ class _OnboardingTitle extends StatelessWidget { ), child: Text( context.l10n.onboardingWelcomeTitle, - style: theme.textTheme.displayLarge?.apply( - color: AppColors.white, - ), + style: theme.textTheme.displayLarge?.apply(color: AppColors.white), textAlign: TextAlign.center, ), ); @@ -155,14 +159,10 @@ class _OnboardingSubtitle extends StatelessWidget { return Padding( key: const Key('onboardingView_onboardingSubtitle'), - padding: const EdgeInsets.only( - top: AppSpacing.xlg, - ), + padding: const EdgeInsets.only(top: AppSpacing.xlg), child: Text( context.l10n.onboardingSubtitle, - style: theme.textTheme.titleMedium?.apply( - color: AppColors.white, - ), + style: theme.textTheme.titleMedium?.apply(color: AppColors.white), textAlign: TextAlign.center, ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/bloc/search_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/bloc/search_bloc.dart index 3b845e598..55e8199f7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/bloc/search_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/bloc/search_bloc.dart @@ -23,10 +23,9 @@ EventTransformer restartableDebounce( } class SearchBloc extends Bloc { - SearchBloc({ - required NewsRepository newsRepository, - }) : _newsRepository = newsRepository, - super(const SearchState.initial()) { + SearchBloc({required NewsRepository newsRepository}) + : _newsRepository = newsRepository, + super(const SearchState.initial()) { on( (event, emit) async { event.searchTerm.isEmpty @@ -61,7 +60,7 @@ class SearchBloc extends Bloc { status: SearchStatus.populated, ), ); - } catch (error, stackTrace) { + } on Object catch (error, stackTrace) { emit(state.copyWith(status: SearchStatus.failure)); addError(error, stackTrace); } @@ -88,7 +87,7 @@ class SearchBloc extends Bloc { status: SearchStatus.populated, ), ); - } catch (error, stackTrace) { + } on Object catch (error, stackTrace) { emit(state.copyWith(status: SearchStatus.failure)); addError(error, stackTrace); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/bloc/search_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/bloc/search_state.dart index 12b83cba8..d0994fe61 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/bloc/search_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/bloc/search_state.dart @@ -1,16 +1,8 @@ part of 'search_bloc.dart'; -enum SearchStatus { - initial, - loading, - populated, - failure, -} +enum SearchStatus { initial, loading, populated, failure } -enum SearchType { - popular, - relevant, -} +enum SearchType { popular, relevant } class SearchState extends Equatable { const SearchState({ @@ -21,12 +13,12 @@ class SearchState extends Equatable { }); const SearchState.initial() - : this( - articles: const [], - topics: const [], - status: SearchStatus.initial, - searchType: SearchType.popular, - ); + : this( + articles: const [], + topics: const [], + status: SearchStatus.initial, + searchType: SearchType.popular, + ); final List articles; @@ -44,11 +36,10 @@ class SearchState extends Equatable { List? topics, SearchStatus? status, SearchType? searchType, - }) => - SearchState( - articles: articles ?? this.articles, - topics: topics ?? this.topics, - status: status ?? this.status, - searchType: searchType ?? this.searchType, - ); + }) => SearchState( + articles: articles ?? this.articles, + topics: topics ?? this.topics, + status: status ?? this.status, + searchType: searchType ?? this.searchType, + ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/view/search_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/view/search_page.dart index f2efd0050..babe7da57 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/view/search_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/view/search_page.dart @@ -12,9 +12,9 @@ class SearchPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => SearchBloc( - newsRepository: context.read(), - )..add(const SearchTermChanged()), + create: (context) => + SearchBloc(newsRepository: context.read()) + ..add(const SearchTermChanged()), child: const SearchView(), ); } @@ -34,9 +34,9 @@ class _SearchViewState extends State { void initState() { super.initState(); _controller.addListener( - () => context - .read() - .add(SearchTermChanged(searchTerm: _controller.text)), + () => context.read().add( + SearchTermChanged(searchTerm: _controller.text), + ), ); } @@ -64,44 +64,42 @@ class _SearchViewState extends State { return CustomScrollView( slivers: [ SliverList( - delegate: SliverChildListDelegate( - [ - SearchTextField( - key: const Key('searchPage_searchTextField'), - controller: _controller, + delegate: SliverChildListDelegate([ + SearchTextField( + key: const Key('searchPage_searchTextField'), + controller: _controller, + ), + SearchHeadlineText( + headerText: state.searchType == SearchType.popular + ? l10n.searchPopularSearches + : l10n.searchRelevantTopics, + ), + Padding( + padding: const EdgeInsets.fromLTRB( + AppSpacing.lg, + 0, + AppSpacing.lg, + AppSpacing.lg, ), - SearchHeadlineText( - headerText: state.searchType == SearchType.popular - ? l10n.searchPopularSearches - : l10n.searchRelevantTopics, + child: Wrap( + spacing: AppSpacing.sm, + children: state.topics + .map( + (topic) => SearchFilterChip( + key: Key('searchFilterChip_$topic'), + chipText: topic, + onSelected: (text) => _controller.text = text, + ), + ) + .toList(), ), - Padding( - padding: const EdgeInsets.fromLTRB( - AppSpacing.lg, - 0, - AppSpacing.lg, - AppSpacing.lg, - ), - child: Wrap( - spacing: AppSpacing.sm, - children: state.topics - .map( - (topic) => SearchFilterChip( - key: Key('searchFilterChip_$topic'), - chipText: topic, - onSelected: (text) => _controller.text = text, - ), - ) - .toList(), - ), - ), - SearchHeadlineText( - headerText: state.searchType == SearchType.popular - ? l10n.searchPopularArticles - : l10n.searchRelevantArticles, - ), - ], - ), + ), + SearchHeadlineText( + headerText: state.searchType == SearchType.popular + ? l10n.searchPopularArticles + : l10n.searchRelevantArticles, + ), + ]), ), ...state.articles.map( (newsBlock) => CategoryFeedItem(block: newsBlock), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/widgets/search_filter_chip.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/widgets/search_filter_chip.dart index d2ad3bd88..cb9b132e1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/widgets/search_filter_chip.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/widgets/search_filter_chip.dart @@ -18,10 +18,7 @@ class SearchFilterChip extends StatelessWidget { padding: const EdgeInsets.symmetric( vertical: AppSpacing.xs + AppSpacing.xxs, ), - label: Text( - chipText, - style: Theme.of(context).textTheme.labelLarge, - ), + label: Text(chipText, style: Theme.of(context).textTheme.labelLarge), onSelected: (_) => onSelected(chipText), backgroundColor: AppColors.transparent, shape: RoundedRectangleBorder( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/widgets/search_headline_text.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/widgets/search_headline_text.dart index 29cbdcafc..11f57d953 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/widgets/search_headline_text.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/search/widgets/search_headline_text.dart @@ -17,9 +17,9 @@ class SearchHeadlineText extends StatelessWidget { ), child: Text( headerText.toUpperCase(), - style: Theme.of(context).textTheme.titleSmall?.copyWith( - color: AppColors.secondary, - ), + style: Theme.of( + context, + ).textTheme.titleSmall?.copyWith(color: AppColors.secondary), ), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/slideshow/view/slideshow_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/slideshow/view/slideshow_page.dart index 8900c3340..eee249a4a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/slideshow/view/slideshow_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/slideshow/view/slideshow_page.dart @@ -18,10 +18,7 @@ class SlideshowPage extends StatelessWidget { required String articleId, }) { return MaterialPageRoute( - builder: (_) => SlideshowPage( - slideshow: slideshow, - articleId: articleId, - ), + builder: (_) => SlideshowPage(slideshow: slideshow, articleId: articleId), ); } @@ -33,12 +30,10 @@ class SlideshowPage extends StatelessWidget { return BlocProvider( create: (context) => ArticleBloc( articleId: articleId, - shareLauncher: const ShareLauncher(), + shareLauncher: ShareLauncher(), articleRepository: context.read(), ), - child: SlideshowView( - block: slideshow, - ), + child: SlideshowView(block: slideshow), ); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/slideshow/view/slideshow_view.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/slideshow/view/slideshow_view.dart index f2b19087f..da4f758fb 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/slideshow/view/slideshow_view.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/slideshow/view/slideshow_view.dart @@ -15,8 +15,9 @@ class SlideshowView extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = context.l10n; - final isSubscriber = - context.select((bloc) => bloc.state.isUserSubscribed); + final isSubscriber = context.select( + (bloc) => bloc.state.isUserSubscribed, + ); final uri = context.select((ArticleBloc bloc) => bloc.state.uri); return Scaffold( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/bloc/subscriptions_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/bloc/subscriptions_bloc.dart index 3384c7e22..ce82247d1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/bloc/subscriptions_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/bloc/subscriptions_bloc.dart @@ -12,27 +12,26 @@ class SubscriptionsBloc extends Bloc { SubscriptionsBloc({ required InAppPurchaseRepository inAppPurchaseRepository, required UserRepository userRepository, - }) : _inAppPurchaseRepository = inAppPurchaseRepository, - _userRepository = userRepository, - super( - SubscriptionsState.initial(), - ) { + }) : _inAppPurchaseRepository = inAppPurchaseRepository, + _userRepository = userRepository, + super(SubscriptionsState.initial()) { on(_onSubscriptionsRequested); on(_onSubscriptionPurchaseRequested); on(_onSubscriptionPurchaseUpdated); - _subscriptionPurchaseUpdateSubscription = - _inAppPurchaseRepository.purchaseUpdate.listen( - (purchase) => add(SubscriptionPurchaseUpdated(purchase: purchase)), - onError: addError, - ); + _subscriptionPurchaseUpdateSubscription = _inAppPurchaseRepository + .purchaseUpdate + .listen( + (purchase) => add(SubscriptionPurchaseUpdated(purchase: purchase)), + onError: addError, + ); } final InAppPurchaseRepository _inAppPurchaseRepository; final UserRepository _userRepository; late StreamSubscription - _subscriptionPurchaseUpdateSubscription; + _subscriptionPurchaseUpdateSubscription; FutureOr _onSubscriptionsRequested( SubscriptionsRequested event, @@ -41,7 +40,7 @@ class SubscriptionsBloc extends Bloc { try { final subscriptions = await _inAppPurchaseRepository.fetchSubscriptions(); emit(state.copyWith(subscriptions: subscriptions)); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { addError(error, stackTrace); } } @@ -53,7 +52,7 @@ class SubscriptionsBloc extends Bloc { try { emit(state.copyWith(purchaseStatus: PurchaseStatus.pending)); await _inAppPurchaseRepository.purchase(subscription: event.subscription); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { addError(error, stackTrace); } } @@ -65,30 +64,18 @@ class SubscriptionsBloc extends Bloc { final purchase = event.purchase; if (purchase is PurchasePurchased) { - emit( - state.copyWith( - purchaseStatus: PurchaseStatus.pending, - ), - ); + emit(state.copyWith(purchaseStatus: PurchaseStatus.pending)); } else if (purchase is PurchaseDelivered) { await _userRepository.updateSubscriptionPlan(); - emit( - state.copyWith( - purchaseStatus: PurchaseStatus.completed, - ), - ); + emit(state.copyWith(purchaseStatus: PurchaseStatus.completed)); } else if (purchase is PurchaseFailed || purchase is PurchaseCanceled) { - emit( - state.copyWith( - purchaseStatus: PurchaseStatus.failed, - ), - ); + emit(state.copyWith(purchaseStatus: PurchaseStatus.failed)); } } @override - Future close() { - _subscriptionPurchaseUpdateSubscription.cancel(); + Future close() async { + await _subscriptionPurchaseUpdateSubscription.cancel(); return super.close(); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/bloc/subscriptions_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/bloc/subscriptions_state.dart index f7ab017f6..c2874712b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/bloc/subscriptions_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/bloc/subscriptions_state.dart @@ -1,11 +1,6 @@ part of 'subscriptions_bloc.dart'; -enum PurchaseStatus { - none, - pending, - completed, - failed, -} +enum PurchaseStatus { none, pending, completed, failed } class SubscriptionsState extends Equatable { const SubscriptionsState({ @@ -14,10 +9,7 @@ class SubscriptionsState extends Equatable { }); SubscriptionsState.initial() - : this( - subscriptions: [], - purchaseStatus: PurchaseStatus.none, - ); + : this(subscriptions: [], purchaseStatus: PurchaseStatus.none); final List subscriptions; final PurchaseStatus purchaseStatus; @@ -28,9 +20,8 @@ class SubscriptionsState extends Equatable { SubscriptionsState copyWith({ List? subscriptions, PurchaseStatus? purchaseStatus, - }) => - SubscriptionsState( - subscriptions: subscriptions ?? this.subscriptions, - purchaseStatus: purchaseStatus ?? this.purchaseStatus, - ); + }) => SubscriptionsState( + subscriptions: subscriptions ?? this.subscriptions, + purchaseStatus: purchaseStatus ?? this.purchaseStatus, + ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/view/purchase_subscription_dialog.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/view/purchase_subscription_dialog.dart index 2d423f730..4a49f5996 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/view/purchase_subscription_dialog.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/dialog/view/purchase_subscription_dialog.dart @@ -11,18 +11,19 @@ import 'package:user_repository/user_repository.dart'; Future showPurchaseSubscriptionDialog({ required BuildContext context, -}) async => - showGeneralDialog( - context: context, - pageBuilder: (_, __, ___) => const PurchaseSubscriptionDialog(), - transitionBuilder: (context, anim1, anim2, child) { - return SlideTransition( - position: - Tween(begin: const Offset(0, 1), end: Offset.zero).animate(anim1), - child: child, - ); - }, +}) async => showGeneralDialog( + context: context, + pageBuilder: (_, _, _) => const PurchaseSubscriptionDialog(), + transitionBuilder: (context, anim1, anim2, child) { + return SlideTransition( + position: Tween( + begin: const Offset(0, 1), + end: Offset.zero, + ).animate(anim1), + child: child, ); + }, +); class PurchaseSubscriptionDialog extends StatelessWidget { const PurchaseSubscriptionDialog({super.key}); @@ -82,50 +83,55 @@ class PurchaseSubscriptionDialogView extends StatelessWidget { Expanded( child: BlocConsumer( - listener: (context, state) { - if (state.purchaseStatus == - PurchaseStatus.completed) { - context.read().add( + listener: (context, state) { + if (state.purchaseStatus == + PurchaseStatus.completed) { + context.read().add( TrackAnalyticsEvent( UserSubscriptionConversionEvent(), ), ); - showDialog( - context: context, - builder: (context) => - const PurchaseCompletedDialog(), - ).then( - (_) { - if (context.mounted) { - Navigator.maybePop(context); - } - }, - ); - } - }, - builder: (context, state) { - if (state.subscriptions.isEmpty) { - return const Center( - child: CircularProgressIndicator(), - ); - } else { - return CustomScrollView( - slivers: [ - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) => SubscriptionCard( - key: ValueKey(state.subscriptions[index]), - subscription: state.subscriptions[index], - isExpanded: index == 0, + unawaited( + showDialog( + context: context, + builder: (context) => + const PurchaseCompletedDialog(), + ).then((_) { + if (context.mounted) { + unawaited( + Navigator.maybePop(context), + ); + } + }), + ); + } + }, + builder: (context, state) { + if (state.subscriptions.isEmpty) { + return const Center( + child: CircularProgressIndicator(), + ); + } else { + return CustomScrollView( + slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) => SubscriptionCard( + key: ValueKey( + state.subscriptions[index], + ), + subscription: + state.subscriptions[index], + isExpanded: index == 0, + ), + childCount: state.subscriptions.length, + ), ), - childCount: state.subscriptions.length, - ), - ), - ], - ); - } - }, - ), + ], + ); + } + }, + ), ), ], ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/view/manage_subscription_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/view/manage_subscription_page.dart index 7ada9bb6f..4ee548250 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/view/manage_subscription_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/view/manage_subscription_page.dart @@ -6,9 +6,7 @@ class ManageSubscriptionPage extends StatelessWidget { const ManageSubscriptionPage({super.key}); static MaterialPageRoute route() { - return MaterialPageRoute( - builder: (_) => const ManageSubscriptionPage(), - ); + return MaterialPageRoute(builder: (_) => const ManageSubscriptionPage()); } @override @@ -26,9 +24,7 @@ class ManageSubscriptionView extends StatelessWidget { final l10n = context.l10n; final theme = Theme.of(context); return Scaffold( - appBar: AppBar( - leading: const AppBackButton(), - ), + appBar: AppBar(leading: const AppBackButton()), body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), @@ -57,13 +53,14 @@ class ManageSubscriptionView extends StatelessWidget { ), title: Text( l10n.manageSubscriptionLinkText, - style: theme.textTheme.titleSmall - ?.copyWith(color: AppColors.darkAqua), + style: theme.textTheme.titleSmall?.copyWith( + color: AppColors.darkAqua, + ), ), - onTap: () { + onTap: () async { // No possibility to revoke subscriptions from the app. // Navigate the user to "Manage Subscriptions" page instead. - Navigator.maybePop(context); + await Navigator.maybePop(context); }, ), ], diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscribe_modal.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscribe_modal.dart index d87a33502..81f56c7a6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscribe_modal.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscribe_modal.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:app_ui/app_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -37,13 +39,13 @@ class _SubscribeModalState extends State { : (visibility) { if (!visibility.visibleBounds.isEmpty) { context.read().add( - TrackAnalyticsEvent( - PaywallPromptEvent.impression( - impression: PaywallPromptImpression.subscription, - articleTitle: articleTitle ?? '', - ), - ), - ); + TrackAnalyticsEvent( + PaywallPromptEvent.impression( + impression: PaywallPromptImpression.subscription, + articleTitle: articleTitle ?? '', + ), + ), + ); setState(() => _modalShown = true); } }, @@ -59,8 +61,9 @@ class _SubscribeModalState extends State { padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xlg), child: Text( l10n.subscribeModalTitle, - style: theme.textTheme.displaySmall - ?.apply(color: AppColors.white), + style: theme.textTheme.displaySmall?.apply( + color: AppColors.white, + ), ), ), const SizedBox(height: AppSpacing.sm + AppSpacing.xxs), @@ -68,8 +71,9 @@ class _SubscribeModalState extends State { padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xlg), child: Text( l10n.subscribeModalSubtitle, - style: theme.textTheme.titleMedium - ?.apply(color: AppColors.mediumEmphasisPrimary), + style: theme.textTheme.titleMedium?.apply( + color: AppColors.mediumEmphasisPrimary, + ), ), ), const SizedBox(height: AppSpacing.lg + AppSpacing.lg), @@ -82,13 +86,13 @@ class _SubscribeModalState extends State { child: Text(l10n.subscribeButtonText), onPressed: () { context.read().add( - TrackAnalyticsEvent( - PaywallPromptEvent.click( - articleTitle: articleTitle ?? '', - ), - ), - ); - showPurchaseSubscriptionDialog(context: context); + TrackAnalyticsEvent( + PaywallPromptEvent.click( + articleTitle: articleTitle ?? '', + ), + ), + ); + unawaited(showPurchaseSubscriptionDialog(context: context)); }, ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscribe_with_article_limit_modal.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscribe_with_article_limit_modal.dart index cf9c29445..52f2e4c28 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscribe_with_article_limit_modal.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscribe_with_article_limit_modal.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:app_ui/app_ui.dart' show AppButton, AppColors, AppSpacing, Assets, showAppModal; import 'package:flutter/material.dart'; @@ -28,8 +30,9 @@ class _SubscribeWithArticleLimitModalState Widget build(BuildContext context) { final theme = Theme.of(context); final l10n = context.l10n; - final isLoggedIn = - context.select((AppBloc bloc) => bloc.state.status.isLoggedIn); + final isLoggedIn = context.select( + (AppBloc bloc) => bloc.state.status.isLoggedIn, + ); final articleTitle = context.select((ArticleBloc bloc) => bloc.state.title); @@ -43,13 +46,13 @@ class _SubscribeWithArticleLimitModalState : (visibility) { if (!visibility.visibleBounds.isEmpty) { context.read().add( - TrackAnalyticsEvent( - PaywallPromptEvent.impression( - impression: PaywallPromptImpression.rewarded, - articleTitle: articleTitle ?? '', - ), - ), - ); + TrackAnalyticsEvent( + PaywallPromptEvent.impression( + impression: PaywallPromptImpression.rewarded, + articleTitle: articleTitle ?? '', + ), + ), + ); setState(() => _modalShown = true); } }, @@ -65,8 +68,9 @@ class _SubscribeWithArticleLimitModalState padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xlg), child: Text( l10n.subscribeWithArticleLimitModalTitle, - style: theme.textTheme.displaySmall - ?.apply(color: AppColors.white), + style: theme.textTheme.displaySmall?.apply( + color: AppColors.white, + ), ), ), const SizedBox(height: AppSpacing.sm + AppSpacing.xxs), @@ -74,8 +78,9 @@ class _SubscribeWithArticleLimitModalState padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xlg), child: Text( l10n.subscribeWithArticleLimitModalSubtitle, - style: theme.textTheme.titleMedium - ?.apply(color: AppColors.mediumEmphasisPrimary), + style: theme.textTheme.titleMedium?.apply( + color: AppColors.mediumEmphasisPrimary, + ), ), ), const SizedBox(height: AppSpacing.lg + AppSpacing.lg), @@ -89,14 +94,14 @@ class _SubscribeWithArticleLimitModalState ), child: Text(l10n.subscribeButtonText), onPressed: () { - showPurchaseSubscriptionDialog(context: context); + unawaited(showPurchaseSubscriptionDialog(context: context)); context.read().add( - TrackAnalyticsEvent( - PaywallPromptEvent.click( - articleTitle: articleTitle ?? '', - ), - ), - ); + TrackAnalyticsEvent( + PaywallPromptEvent.click( + articleTitle: articleTitle ?? '', + ), + ), + ); }, ), ), @@ -107,8 +112,9 @@ class _SubscribeWithArticleLimitModalState horizontal: AppSpacing.lg + AppSpacing.xxs, ), child: AppButton.outlinedTransparentWhite( - key: - const Key('subscribeWithArticleLimitModal_logInButton'), + key: const Key( + 'subscribeWithArticleLimitModal_logInButton', + ), child: Text(l10n.subscribeWithArticleLimitModalLogInButton), onPressed: () => showAppModal( context: context, @@ -127,9 +133,9 @@ class _SubscribeWithArticleLimitModalState key: const Key( 'subscribeWithArticleLimitModal_watchVideoButton', ), - onPressed: () => context - .read() - .add(const ShowRewardedAdRequested()), + onPressed: () => context.read().add( + const ShowRewardedAdRequested(), + ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscription_card.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscription_card.dart index 3e8e13719..52e1cffba 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscription_card.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/subscriptions/widgets/subscription_card.dart @@ -31,9 +31,7 @@ class SubscriptionCard extends StatelessWidget { final annualCost = NumberFormat.currency( decimalDigits: subscription.cost.annual % 100 == 0 ? 0 : 2, symbol: r'$', - ).format( - subscription.cost.annual / 100, - ); + ).format(subscription.cost.annual / 100); final isLoggedIn = context.select( (AppBloc bloc) => bloc.state.status.isLoggedIn, ); @@ -41,9 +39,7 @@ class SubscriptionCard extends StatelessWidget { return Card( shadowColor: AppColors.black, shape: RoundedRectangleBorder( - side: const BorderSide( - color: AppColors.borderOutline, - ), + side: const BorderSide(color: AppColors.borderOutline), borderRadius: BorderRadius.circular(AppSpacing.lg), ), child: Padding( @@ -54,21 +50,21 @@ class SubscriptionCard extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox( - height: AppSpacing.xlg + AppSpacing.sm, - ), + const SizedBox(height: AppSpacing.xlg + AppSpacing.sm), Text( subscription.name.name.toUpperCase(), - style: theme.textTheme.titleLarge - ?.copyWith(color: AppColors.secondary), + style: theme.textTheme.titleLarge?.copyWith( + color: AppColors.secondary, + ), ), Text( '$monthlyCost/${l10n.monthAbbreviation} | $annualCost/${l10n.yearAbbreviation}', style: theme.textTheme.headlineSmall, ), if (isBestValue) ...[ - Assets.icons.bestValue - .svg(key: const Key('subscriptionCard_bestValueSvg')), + Assets.icons.bestValue.svg( + key: const Key('subscriptionCard_bestValueSvg'), + ), const SizedBox(height: AppSpacing.md), ], if (isExpanded) ...[ @@ -76,8 +72,9 @@ class SubscriptionCard extends StatelessWidget { const SizedBox(height: AppSpacing.md), Text( l10n.subscriptionPurchaseBenefits.toUpperCase(), - style: theme.textTheme.titleSmall - ?.copyWith(fontWeight: FontWeight.w600), + style: theme.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), ), ], for (final paragraph in subscription.benefits) @@ -115,16 +112,17 @@ class SubscriptionCard extends StatelessWidget { key: const Key('subscriptionCard_subscribe_appButton'), onPressed: isLoggedIn ? () => context.read().add( - SubscriptionPurchaseRequested( - subscription: subscription, - ), - ) + SubscriptionPurchaseRequested( + subscription: subscription, + ), + ) : () => showAppModal( - context: context, - builder: (_) => const LoginModal(), - routeSettings: - const RouteSettings(name: LoginModal.name), + context: context, + builder: (_) => const LoginModal(), + routeSettings: const RouteSettings( + name: LoginModal.name, ), + ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -142,12 +140,8 @@ class SubscriptionCard extends StatelessWidget { ..hideCurrentSnackBar() ..showSnackBar( SnackBar( - key: const Key( - 'subscriptionCard_unimplemented_snackBar', - ), - content: Text( - l10n.subscriptionViewDetailsButtonSnackBar, - ), + key: const Key('subscriptionCard_unimplemented_snackBar'), + content: Text(l10n.subscriptionViewDetailsButtonSnackBar), ), ), child: Row( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/view/terms_of_service_modal.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/view/terms_of_service_modal.dart index d3787c2e5..61a39d99d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/view/terms_of_service_modal.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/view/terms_of_service_modal.dart @@ -13,9 +13,7 @@ class TermsOfServiceModal extends StatelessWidget { child: SafeArea( top: false, child: Padding( - padding: EdgeInsets.symmetric( - horizontal: AppSpacing.lg, - ), + padding: EdgeInsets.symmetric(horizontal: AppSpacing.lg), child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -41,10 +39,7 @@ class TermsOfServiceModalHeader extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); return Padding( - padding: const EdgeInsets.only( - top: AppSpacing.lg, - bottom: AppSpacing.sm, - ), + padding: const EdgeInsets.only(top: AppSpacing.lg, bottom: AppSpacing.sm), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/view/terms_of_service_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/view/terms_of_service_page.dart index 28fbaac28..0b19de472 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/view/terms_of_service_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/view/terms_of_service_page.dart @@ -6,16 +6,13 @@ import 'package:{{project_name.snakeCase()}}/terms_of_service/terms_of_service.d class TermsOfServicePage extends StatelessWidget { const TermsOfServicePage({super.key}); - static Route route() => MaterialPageRoute( - builder: (_) => const TermsOfServicePage(), - ); + static Route route() => + MaterialPageRoute(builder: (_) => const TermsOfServicePage()); @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - leading: const AppBackButton(), - ), + appBar: AppBar(leading: const AppBackButton()), body: const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/widgets/terms_of_service_body.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/widgets/terms_of_service_body.dart index 86758c3c4..deaf7450a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/widgets/terms_of_service_body.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/terms_of_service/widgets/terms_of_service_body.dart @@ -4,10 +4,7 @@ part '../terms_of_service_mock_text.dart'; @visibleForTesting class TermsOfServiceBody extends StatelessWidget { - const TermsOfServiceBody({ - super.key, - this.contentPadding = EdgeInsets.zero, - }); + const TermsOfServiceBody({super.key, this.contentPadding = EdgeInsets.zero}); final EdgeInsets contentPadding; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/bloc/user_profile_bloc.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/bloc/user_profile_bloc.dart index 71306f319..6806c7ea0 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/bloc/user_profile_bloc.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/bloc/user_profile_bloc.dart @@ -12,8 +12,8 @@ class UserProfileBloc extends Bloc { UserProfileBloc({ required UserRepository userRepository, required NotificationsRepository notificationsRepository, - }) : _notificationsRepository = notificationsRepository, - super(const UserProfileState.initial()) { + }) : _notificationsRepository = notificationsRepository, + super(const UserProfileState.initial()) { on(_onUserProfileUpdated); on(_onFetchNotificationsEnabled); on(_onToggleNotifications); @@ -31,10 +31,7 @@ class UserProfileBloc extends Bloc { Emitter emit, ) { emit( - state.copyWith( - user: event.user, - status: UserProfileStatus.userUpdated, - ), + state.copyWith(user: event.user, status: UserProfileStatus.userUpdated), ); } @@ -44,13 +41,11 @@ class UserProfileBloc extends Bloc { ) async { try { emit( - state.copyWith( - status: UserProfileStatus.fetchingNotificationsEnabled, - ), + state.copyWith(status: UserProfileStatus.fetchingNotificationsEnabled), ); - final notificationsEnabled = - await _notificationsRepository.fetchNotificationsEnabled(); + final notificationsEnabled = await _notificationsRepository + .fetchNotificationsEnabled(); emit( state.copyWith( @@ -58,7 +53,7 @@ class UserProfileBloc extends Bloc { notificationsEnabled: notificationsEnabled, ), ); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit( state.copyWith( status: UserProfileStatus.fetchingNotificationsEnabledFailed, @@ -92,7 +87,7 @@ class UserProfileBloc extends Bloc { status: UserProfileStatus.togglingNotificationsSucceeded, ), ); - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { emit( state.copyWith( status: UserProfileStatus.togglingNotificationsFailed, @@ -104,8 +99,8 @@ class UserProfileBloc extends Bloc { } @override - Future close() { - _userSubscription.cancel(); + Future close() async { + await _userSubscription.cancel(); return super.close(); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/bloc/user_profile_state.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/bloc/user_profile_state.dart index 74de71c3e..69e84e38c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/bloc/user_profile_state.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/bloc/user_profile_state.dart @@ -19,10 +19,7 @@ class UserProfileState extends Equatable { }); const UserProfileState.initial() - : this( - status: UserProfileStatus.initial, - user: User.anonymous, - ); + : this(status: UserProfileStatus.initial, user: User.anonymous); final UserProfileStatus status; final bool notificationsEnabled; @@ -35,10 +32,9 @@ class UserProfileState extends Equatable { UserProfileStatus? status, bool? notificationsEnabled, User? user, - }) => - UserProfileState( - status: status ?? this.status, - notificationsEnabled: notificationsEnabled ?? this.notificationsEnabled, - user: user ?? this.user, - ); + }) => UserProfileState( + status: status ?? this.status, + notificationsEnabled: notificationsEnabled ?? this.notificationsEnabled, + user: user ?? this.user, + ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/view/user_profile_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/view/user_profile_page.dart index 5407233a0..6d4e9a398 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/view/user_profile_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/view/user_profile_page.dart @@ -67,8 +67,9 @@ class _UserProfileViewState extends State @override Widget build(BuildContext context) { final user = context.select((UserProfileBloc bloc) => bloc.state.user); - final notificationsEnabled = context - .select((UserProfileBloc bloc) => bloc.state.notificationsEnabled); + final notificationsEnabled = context.select( + (UserProfileBloc bloc) => bloc.state.notificationsEnabled, + ); final isUserSubscribed = context.select( (bloc) => bloc.state.isUserSubscribed, ); @@ -79,9 +80,9 @@ class _UserProfileViewState extends State listener: (context, state) { if (state.status == UserProfileStatus.togglingNotificationsSucceeded && state.notificationsEnabled) { - context - .read() - .add(TrackAnalyticsEvent(PushNotificationSubscriptionEvent())); + context.read().add( + TrackAnalyticsEvent(PushNotificationSubscriptionEvent()), + ); } }, child: BlocListener( @@ -91,9 +92,7 @@ class _UserProfileViewState extends State } }, child: Scaffold( - appBar: AppBar( - leading: const AppBackButton(), - ), + appBar: AppBar(leading: const AppBackButton()), body: CustomScrollView( slivers: [ SliverToBoxAdapter( @@ -120,9 +119,9 @@ class _UserProfileViewState extends State key: const Key('userProfilePage_subscriptionItem'), title: l10n.manageSubscriptionTile, trailing: const Icon(Icons.chevron_right), - onTap: () => Navigator.of(context).push( - ManageSubscriptionPage.route(), - ), + onTap: () => Navigator.of( + context, + ).push(ManageSubscriptionPage.route()), ) else UserProfileSubscribeBox( @@ -141,9 +140,9 @@ class _UserProfileViewState extends State onText: l10n.checkboxOnTitle, offText: l10n.userProfileCheckboxOffTitle, value: notificationsEnabled, - onChanged: (_) => context - .read() - .add(const ToggleNotifications()), + onChanged: (_) => context.read().add( + const ToggleNotifications(), + ), ), ), UserProfileItem( @@ -152,9 +151,9 @@ class _UserProfileViewState extends State ), title: l10n.notificationPreferencesTitle, trailing: const Icon(Icons.chevron_right), - onTap: () => Navigator.of(context).push( - NotificationPreferencesPage.route(), - ), + onTap: () => Navigator.of( + context, + ).push(NotificationPreferencesPage.route()), ), const _UserProfileDivider(), UserProfileSubtitle( @@ -165,8 +164,9 @@ class _UserProfileViewState extends State leading: Assets.icons.termsOfUseIcon.svg(), title: l10n.userProfileLegalTermsOfUseAndPrivacyPolicyTitle, - onTap: () => Navigator.of(context) - .push(TermsOfServicePage.route()), + onTap: () => Navigator.of( + context, + ).push(TermsOfServicePage.route()), ), UserProfileItem( key: const Key('userProfilePage_aboutItem'), @@ -176,16 +176,14 @@ class _UserProfileViewState extends State Align( child: AppButton.smallTransparent( key: const Key('userProfilePage_deleteAccountButton'), - onPressed: () { - showDialog( + onPressed: () async { + await showDialog( context: context, builder: (_) => const UserProfileDeleteAccountDialog(), ); }, - child: Text( - l10n.userProfileDeleteAccountButton, - ), + child: Text(l10n.userProfileDeleteAccountButton), ), ), const SizedBox(height: AppSpacing.lg), @@ -233,10 +231,7 @@ class UserProfileSubtitle extends StatelessWidget { AppSpacing.lg, AppSpacing.md, ), - child: Text( - subtitle, - style: theme.textTheme.titleSmall, - ), + child: Text(subtitle, style: theme.textTheme.titleSmall), ); } } @@ -251,7 +246,7 @@ class UserProfileItem extends StatelessWidget { super.key, }); - static const _leadingWidth = AppSpacing.xxxlg + AppSpacing.sm; + static const double _leadingWidth = AppSpacing.xxxlg + AppSpacing.sm; final String title; final Widget? leading; @@ -264,10 +259,7 @@ class UserProfileItem extends StatelessWidget { return ListTile( dense: true, - leading: SizedBox( - width: hasLeading ? _leadingWidth : 0, - child: leading, - ), + leading: SizedBox(width: hasLeading ? _leadingWidth : 0, child: leading), trailing: trailing, visualDensity: const VisualDensity( vertical: VisualDensity.minimumDensity, @@ -283,9 +275,9 @@ class UserProfileItem extends StatelessWidget { onTap: onTap, title: Text( title, - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: AppColors.highEmphasisSurface, - ), + style: Theme.of( + context, + ).textTheme.titleMedium?.copyWith(color: AppColors.highEmphasisSurface), ), ); } @@ -324,11 +316,7 @@ class _UserProfileDivider extends StatelessWidget { Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.symmetric(horizontal: AppSpacing.lg), - child: Divider( - color: AppColors.borderOutline, - indent: 0, - endIndent: 0, - ), + child: Divider(color: AppColors.borderOutline, indent: 0, endIndent: 0), ); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/widgets/user_profile_subscribe_box.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/widgets/user_profile_subscribe_box.dart index 2a9670d55..fd67d9511 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/widgets/user_profile_subscribe_box.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/lib/user_profile/widgets/user_profile_subscribe_box.dart @@ -26,8 +26,9 @@ class UserProfileSubscribeBox extends StatelessWidget { const SizedBox(height: AppSpacing.lg), Text( l10n.userProfileSubscribeBoxMessage, - style: theme.textTheme.bodyMedium - ?.copyWith(color: AppColors.mediumEmphasisSurface), + style: theme.textTheme.bodyMedium?.copyWith( + color: AppColors.mediumEmphasisSurface, + ), ), const SizedBox(height: AppSpacing.lg), AppButton.smallRedWine( @@ -35,9 +36,7 @@ class UserProfileSubscribeBox extends StatelessWidget { onPressed: onSubscribePressed, child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(l10n.userProfileSubscribeNowButtonText), - ], + children: [Text(l10n.userProfileSubscribeNowButtonText)], ), ), const SizedBox(height: AppSpacing.lg), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/analysis_options.yaml index bb7209144..a095ce460 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/analysis_options.yaml @@ -1 +1,4 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml +analyzer: + errors: + document_ignores: ignore diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/lib/src/ads_consent_client.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/lib/src/ads_consent_client.dart index 13b3b7901..b1c94a3e5 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/lib/src/ads_consent_client.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/lib/src/ads_consent_client.dart @@ -22,10 +22,11 @@ class RequestConsentFailure extends AdsContentFailure { } /// Signature for the content form provider. -typedef ConsentFormProvider = void Function( - OnConsentFormLoadSuccessListener successListener, - OnConsentFormLoadFailureListener failureListener, -); +typedef ConsentFormProvider = + void Function( + OnConsentFormLoadSuccessListener successListener, + OnConsentFormLoadFailureListener failureListener, + ); /// {@template ads_consent_client} /// A client that handles requesting ads consent on a device. @@ -35,10 +36,10 @@ class AdsConsentClient { AdsConsentClient({ ConsentInformation? adsConsentInformation, ConsentFormProvider? adsConsentFormProvider, - }) : _adsConsentInformation = - adsConsentInformation ?? ConsentInformation.instance, - _adsConsentFormProvider = - adsConsentFormProvider ?? ConsentForm.loadConsentForm; + }) : _adsConsentInformation = + adsConsentInformation ?? ConsentInformation.instance, + _adsConsentFormProvider = + adsConsentFormProvider ?? ConsentForm.loadConsentForm; final ConsentInformation _adsConsentInformation; final ConsentFormProvider _adsConsentFormProvider; @@ -82,27 +83,22 @@ class AdsConsentClient { Future _loadConsentForm() async { final completer = Completer(); - _adsConsentFormProvider( - (consentForm) async { - final status = await _adsConsentInformation.getConsentStatus(); - if (status.isRequired) { - consentForm.show( - (error) async { - if (error != null) { - completer.completeError(error, StackTrace.current); - } else { - final updatedStatus = - await _adsConsentInformation.getConsentStatus(); - completer.complete(updatedStatus.isDetermined); - } - }, - ); - } else { - completer.complete(status.isDetermined); - } - }, - (error) => completer.completeError(error, StackTrace.current), - ); + _adsConsentFormProvider((consentForm) async { + final status = await _adsConsentInformation.getConsentStatus(); + if (status.isRequired) { + consentForm.show((error) async { + if (error != null) { + completer.completeError(error, StackTrace.current); + } else { + final updatedStatus = await _adsConsentInformation + .getConsentStatus(); + completer.complete(updatedStatus.isDetermined); + } + }); + } else { + completer.complete(status.isDetermined); + } + }, (error) => completer.completeError(error, StackTrace.current)); return completer.future; } @@ -111,11 +107,10 @@ class AdsConsentClient { FormError error, { required Completer completer, StackTrace? stackTrace, - }) => - completer.completeError( - RequestConsentFailure(error), - stackTrace ?? StackTrace.current, - ); + }) => completer.completeError( + RequestConsentFailure(error), + stackTrace ?? StackTrace.current, + ); } extension on ConsentStatus { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/pubspec.yaml index cdaaa86a2..286773a1e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/pubspec.yaml @@ -4,15 +4,15 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: flutter: sdk: flutter - google_mobile_ads: ^5.0.0 + google_mobile_ads: ^5.3.1 dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/test/src/ads_consent_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/test/src/ads_consent_client_test.dart index d2fc55312..4c4761b04 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/test/src/ads_consent_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/ads_consent_client/test/src/ads_consent_client_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: prefer_const_constructors, avoid_dynamic_calls +// ignore_for_file: avoid_dynamic_calls import 'package:ads_consent_client/ads_consent_client.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -26,8 +26,7 @@ void main() { }); group('requestConsent', () { - group( - 'when ConsentInformation.requestConsentInfoUpdate succeeds ' + group('when ConsentInformation.requestConsentInfoUpdate succeeds ' 'and ConsentInformation.isConsentFormAvailable returns true', () { setUp(() { when( @@ -36,24 +35,23 @@ void main() { any(), any(), ), - ).thenAnswer( - (invocation) { - final successListener = invocation.positionalArguments[1]; - successListener(); - }, - ); + ).thenAnswer((invocation) { + final successListener = invocation.positionalArguments[1]; + successListener(); + }); - when(adsConsentInformation.isConsentFormAvailable) - .thenAnswer((_) async => true); + when( + adsConsentInformation.isConsentFormAvailable, + ).thenAnswer((_) async => true); }); - group( - 'and ConsentInformation.getConsentStatus returns required ' + group('and ConsentInformation.getConsentStatus returns required ' 'and ConsentForm.show succeeds', () { setUp(() { adsConsentFormProvider = (successListener, failureListener) async { - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => ConsentStatus.required); + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => ConsentStatus.required); successListener(adsConsentForm); }; }); @@ -64,8 +62,9 @@ void main() { when(() => adsConsentForm.show(any())).thenAnswer((invocation) { // Update consent status to obtained // when the consent form is dismissed. - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => updatedConsentStatus); + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => updatedConsentStatus); final dismissedListener = invocation.positionalArguments.first; dismissedListener(null); @@ -96,8 +95,9 @@ void main() { when(() => adsConsentForm.show(any())).thenAnswer((invocation) { // Update consent status to unknown // when the consent form is dismissed. - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => updatedConsentStatus); + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => updatedConsentStatus); final dismissedListener = invocation.positionalArguments.first; dismissedListener(null); @@ -123,13 +123,13 @@ void main() { }); }); - group( - 'and ConsentInformation.getConsentStatus returns required ' + group('and ConsentInformation.getConsentStatus returns required ' 'and ConsentForm.show fails', () { setUp(() { adsConsentFormProvider = (successListener, failureListener) async { - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => ConsentStatus.required); + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => ConsentStatus.required); successListener(adsConsentForm); }; @@ -150,8 +150,7 @@ void main() { }); }); - group( - 'and ConsentInformation.getConsentStatus returns required ' + group('and ConsentInformation.getConsentStatus returns required ' 'and ConsentFormProvider fails', () { setUp(() { adsConsentFormProvider = (successListener, failureListener) async { @@ -171,92 +170,97 @@ void main() { }); test( - 'returns true ' - 'when ConsentInformation.getConsentStatus returns notRequired', - () async { - adsConsentFormProvider = (successListener, failureListener) async { - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => ConsentStatus.notRequired); - successListener(adsConsentForm); - }; - - final adsConsentDetermined = await AdsConsentClient( - adsConsentInformation: adsConsentInformation, - adsConsentFormProvider: adsConsentFormProvider, - ).requestConsent(); - - verify( - () => adsConsentInformation.requestConsentInfoUpdate( - ConsentRequestParameters(), - any(), - any(), - ), - ).called(1); - verify(adsConsentInformation.isConsentFormAvailable).called(1); - verify(adsConsentInformation.getConsentStatus).called(1); + 'returns true ' + 'when ConsentInformation.getConsentStatus returns notRequired', + () async { + adsConsentFormProvider = (successListener, failureListener) async { + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => ConsentStatus.notRequired); + successListener(adsConsentForm); + }; - expect(adsConsentDetermined, isTrue); - }); + final adsConsentDetermined = await AdsConsentClient( + adsConsentInformation: adsConsentInformation, + adsConsentFormProvider: adsConsentFormProvider, + ).requestConsent(); + + verify( + () => adsConsentInformation.requestConsentInfoUpdate( + ConsentRequestParameters(), + any(), + any(), + ), + ).called(1); + verify(adsConsentInformation.isConsentFormAvailable).called(1); + verify(adsConsentInformation.getConsentStatus).called(1); + + expect(adsConsentDetermined, isTrue); + }, + ); test( - 'returns true ' - 'when ConsentInformation.getConsentStatus returns obtained', - () async { - adsConsentFormProvider = (successListener, failureListener) async { - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => ConsentStatus.obtained); - successListener(adsConsentForm); - }; - - final adsConsentDetermined = await AdsConsentClient( - adsConsentInformation: adsConsentInformation, - adsConsentFormProvider: adsConsentFormProvider, - ).requestConsent(); - - verify( - () => adsConsentInformation.requestConsentInfoUpdate( - ConsentRequestParameters(), - any(), - any(), - ), - ).called(1); - verify(adsConsentInformation.isConsentFormAvailable).called(1); - verify(adsConsentInformation.getConsentStatus).called(1); + 'returns true ' + 'when ConsentInformation.getConsentStatus returns obtained', + () async { + adsConsentFormProvider = (successListener, failureListener) async { + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => ConsentStatus.obtained); + successListener(adsConsentForm); + }; - expect(adsConsentDetermined, isTrue); - }); + final adsConsentDetermined = await AdsConsentClient( + adsConsentInformation: adsConsentInformation, + adsConsentFormProvider: adsConsentFormProvider, + ).requestConsent(); + + verify( + () => adsConsentInformation.requestConsentInfoUpdate( + ConsentRequestParameters(), + any(), + any(), + ), + ).called(1); + verify(adsConsentInformation.isConsentFormAvailable).called(1); + verify(adsConsentInformation.getConsentStatus).called(1); + + expect(adsConsentDetermined, isTrue); + }, + ); test( - 'returns false ' - 'when ConsentInformation.getConsentStatus returns unknown', - () async { - adsConsentFormProvider = (successListener, failureListener) async { - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => ConsentStatus.unknown); - successListener(adsConsentForm); - }; - - final adsConsentDetermined = await AdsConsentClient( - adsConsentInformation: adsConsentInformation, - adsConsentFormProvider: adsConsentFormProvider, - ).requestConsent(); - - verify( - () => adsConsentInformation.requestConsentInfoUpdate( - ConsentRequestParameters(), - any(), - any(), - ), - ).called(1); - verify(adsConsentInformation.isConsentFormAvailable).called(1); - verify(adsConsentInformation.getConsentStatus).called(1); + 'returns false ' + 'when ConsentInformation.getConsentStatus returns unknown', + () async { + adsConsentFormProvider = (successListener, failureListener) async { + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => ConsentStatus.unknown); + successListener(adsConsentForm); + }; - expect(adsConsentDetermined, isFalse); - }); + final adsConsentDetermined = await AdsConsentClient( + adsConsentInformation: adsConsentInformation, + adsConsentFormProvider: adsConsentFormProvider, + ).requestConsent(); + + verify( + () => adsConsentInformation.requestConsentInfoUpdate( + ConsentRequestParameters(), + any(), + any(), + ), + ).called(1); + verify(adsConsentInformation.isConsentFormAvailable).called(1); + verify(adsConsentInformation.getConsentStatus).called(1); + + expect(adsConsentDetermined, isFalse); + }, + ); }); - group( - 'when ConsentInformation.requestConsentInfoUpdate succeeds ' + group('when ConsentInformation.requestConsentInfoUpdate succeeds ' 'and ConsentInformation.isConsentFormAvailable returns false', () { setUp(() { when( @@ -265,91 +269,96 @@ void main() { any(), any(), ), - ).thenAnswer( - (invocation) { - final successListener = invocation.positionalArguments[1]; - successListener(); - }, - ); + ).thenAnswer((invocation) { + final successListener = invocation.positionalArguments[1]; + successListener(); + }); - when(adsConsentInformation.isConsentFormAvailable) - .thenAnswer((_) async => false); + when( + adsConsentInformation.isConsentFormAvailable, + ).thenAnswer((_) async => false); }); test( - 'returns true ' - 'when ConsentInformation.getConsentStatus returns notRequired', - () async { - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => ConsentStatus.notRequired); - - final adsConsentDetermined = await AdsConsentClient( - adsConsentInformation: adsConsentInformation, - adsConsentFormProvider: adsConsentFormProvider, - ).requestConsent(); - - verify( - () => adsConsentInformation.requestConsentInfoUpdate( - ConsentRequestParameters(), - any(), - any(), - ), - ).called(1); - verify(adsConsentInformation.isConsentFormAvailable).called(1); - verify(adsConsentInformation.getConsentStatus).called(1); + 'returns true ' + 'when ConsentInformation.getConsentStatus returns notRequired', + () async { + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => ConsentStatus.notRequired); - expect(adsConsentDetermined, isTrue); - }); + final adsConsentDetermined = await AdsConsentClient( + adsConsentInformation: adsConsentInformation, + adsConsentFormProvider: adsConsentFormProvider, + ).requestConsent(); + + verify( + () => adsConsentInformation.requestConsentInfoUpdate( + ConsentRequestParameters(), + any(), + any(), + ), + ).called(1); + verify(adsConsentInformation.isConsentFormAvailable).called(1); + verify(adsConsentInformation.getConsentStatus).called(1); + + expect(adsConsentDetermined, isTrue); + }, + ); test( - 'returns true ' - 'when ConsentInformation.getConsentStatus returns obtained', - () async { - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => ConsentStatus.obtained); - - final adsConsentDetermined = await AdsConsentClient( - adsConsentInformation: adsConsentInformation, - adsConsentFormProvider: adsConsentFormProvider, - ).requestConsent(); - - verify( - () => adsConsentInformation.requestConsentInfoUpdate( - ConsentRequestParameters(), - any(), - any(), - ), - ).called(1); - verify(adsConsentInformation.isConsentFormAvailable).called(1); - verify(adsConsentInformation.getConsentStatus).called(1); + 'returns true ' + 'when ConsentInformation.getConsentStatus returns obtained', + () async { + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => ConsentStatus.obtained); - expect(adsConsentDetermined, isTrue); - }); + final adsConsentDetermined = await AdsConsentClient( + adsConsentInformation: adsConsentInformation, + adsConsentFormProvider: adsConsentFormProvider, + ).requestConsent(); + + verify( + () => adsConsentInformation.requestConsentInfoUpdate( + ConsentRequestParameters(), + any(), + any(), + ), + ).called(1); + verify(adsConsentInformation.isConsentFormAvailable).called(1); + verify(adsConsentInformation.getConsentStatus).called(1); + + expect(adsConsentDetermined, isTrue); + }, + ); test( - 'returns false ' - 'when ConsentInformation.getConsentStatus returns unknown', - () async { - when(adsConsentInformation.getConsentStatus) - .thenAnswer((_) async => ConsentStatus.unknown); - - final adsConsentDetermined = await AdsConsentClient( - adsConsentInformation: adsConsentInformation, - adsConsentFormProvider: adsConsentFormProvider, - ).requestConsent(); - - verify( - () => adsConsentInformation.requestConsentInfoUpdate( - ConsentRequestParameters(), - any(), - any(), - ), - ).called(1); - verify(adsConsentInformation.isConsentFormAvailable).called(1); - verify(adsConsentInformation.getConsentStatus).called(1); + 'returns false ' + 'when ConsentInformation.getConsentStatus returns unknown', + () async { + when( + adsConsentInformation.getConsentStatus, + ).thenAnswer((_) async => ConsentStatus.unknown); - expect(adsConsentDetermined, isFalse); - }); + final adsConsentDetermined = await AdsConsentClient( + adsConsentInformation: adsConsentInformation, + adsConsentFormProvider: adsConsentFormProvider, + ).requestConsent(); + + verify( + () => adsConsentInformation.requestConsentInfoUpdate( + ConsentRequestParameters(), + any(), + any(), + ), + ).called(1); + verify(adsConsentInformation.isConsentFormAvailable).called(1); + verify(adsConsentInformation.getConsentStatus).called(1); + + expect(adsConsentDetermined, isFalse); + }, + ); }); group('when ConsentInformation.requestConsentInfoUpdate fails', () { @@ -360,12 +369,10 @@ void main() { any(), any(), ), - ).thenAnswer( - (invocation) { - final failureListener = invocation.positionalArguments.last; - failureListener(FormError(errorCode: 1, message: 'message')); - }, - ); + ).thenAnswer((invocation) { + final failureListener = invocation.positionalArguments.last; + failureListener(FormError(errorCode: 1, message: 'message')); + }); }); test('throws a RequestConsentFailure', () async { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/analysis_options.yaml index bb7209144..abaf72432 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/analysis_options.yaml @@ -1 +1,4 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/lib/src/analytics_repository.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/lib/src/analytics_repository.dart index 9a79be104..4fc7549f9 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/lib/src/analytics_repository.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/lib/src/analytics_repository.dart @@ -38,17 +38,14 @@ class SetUserIdFailure extends AnalyticsFailure { class AnalyticsRepository { /// {@macro analytics_repository} const AnalyticsRepository(FirebaseAnalytics analytics) - : _analytics = analytics; + : _analytics = analytics; final FirebaseAnalytics _analytics; /// Tracks the provided [AnalyticsEvent]. Future track(AnalyticsEvent event) async { try { - await _analytics.logEvent( - name: event.name, - parameters: event.properties, - ); + await _analytics.logEvent(name: event.name, parameters: event.properties); } catch (error, stackTrace) { Error.throwWithStackTrace(TrackEventFailure(error), stackTrace); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/lib/src/models/ntg_event.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/lib/src/models/ntg_event.dart index 7828b213c..df485afa2 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/lib/src/models/ntg_event.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/lib/src/models/ntg_event.dart @@ -15,16 +15,16 @@ abstract class NTGEvent extends AnalyticsEvent { Object? value, String? hitType, }) : super( - name, - properties: { - 'eventCategory': category, - 'eventAction': action, - 'nonInteraction': '$nonInteraction', - if (label != null) 'eventLabel': label, - if (value != null) 'eventValue': value, - if (hitType != null) 'hitType': hitType, - }, - ); + name, + properties: { + 'eventCategory': category, + 'eventAction': action, + 'nonInteraction': '$nonInteraction', + 'eventLabel': ?label, + 'eventValue': ?value, + 'hitType': ?hitType, + }, + ); } /// {@template newsletter_event} @@ -33,23 +33,23 @@ abstract class NTGEvent extends AnalyticsEvent { class NewsletterEvent extends NTGEvent { /// An analytics event for tracking newsletter sign up. NewsletterEvent.signUp() - : super( - name: 'newsletter_signup', - category: 'NTG newsletter', - action: 'newsletter signup', - label: 'success', - nonInteraction: false, - ); + : super( + name: 'newsletter_signup', + category: 'NTG newsletter', + action: 'newsletter signup', + label: 'success', + nonInteraction: false, + ); /// An analytics event for tracking newsletter impression. NewsletterEvent.impression({String? articleTitle}) - : super( - name: 'newsletter_impression', - category: 'NTG newsletter', - action: 'newsletter modal impression 3', - label: articleTitle ?? '', - nonInteraction: false, - ); + : super( + name: 'newsletter_impression', + category: 'NTG newsletter', + action: 'newsletter modal impression 3', + label: articleTitle ?? '', + nonInteraction: false, + ); } /// {@template login_event} @@ -58,13 +58,13 @@ class NewsletterEvent extends NTGEvent { class LoginEvent extends NTGEvent { /// {@macro login_event} LoginEvent() - : super( - name: 'login', - category: 'NTG account', - action: 'login', - label: 'success', - nonInteraction: false, - ); + : super( + name: 'login', + category: 'NTG account', + action: 'login', + label: 'success', + nonInteraction: false, + ); } /// {@template registration_event} @@ -73,13 +73,13 @@ class LoginEvent extends NTGEvent { class RegistrationEvent extends NTGEvent { /// {@macro registration_event} RegistrationEvent() - : super( - name: 'registration', - category: 'NTG account', - action: 'registration', - label: 'success', - nonInteraction: false, - ); + : super( + name: 'registration', + category: 'NTG account', + action: 'registration', + label: 'success', + nonInteraction: false, + ); } /// {@template article_milestone_event} @@ -91,14 +91,14 @@ class ArticleMilestoneEvent extends NTGEvent { required int milestonePercentage, required String articleTitle, }) : super( - name: 'article_milestone', - category: 'NTG article milestone', - action: '$milestonePercentage%', - label: articleTitle, - value: milestonePercentage, - nonInteraction: true, - hitType: 'event', - ); + name: 'article_milestone', + category: 'NTG article milestone', + action: '$milestonePercentage%', + label: articleTitle, + value: milestonePercentage, + nonInteraction: true, + hitType: 'event', + ); } /// {@template article_comment_event} @@ -107,13 +107,13 @@ class ArticleMilestoneEvent extends NTGEvent { class ArticleCommentEvent extends NTGEvent { /// {@macro article_comment_event} ArticleCommentEvent({required String articleTitle}) - : super( - name: 'comment', - category: 'NTG user', - action: 'comment added', - label: articleTitle, - nonInteraction: false, - ); + : super( + name: 'comment', + category: 'NTG user', + action: 'comment added', + label: articleTitle, + nonInteraction: false, + ); } /// {@template social_share_event} @@ -122,13 +122,13 @@ class ArticleCommentEvent extends NTGEvent { class SocialShareEvent extends NTGEvent { /// {@macro social_share_event} SocialShareEvent() - : super( - name: 'social_share', - category: 'NTG social', - action: 'social share', - label: 'OS share menu', - nonInteraction: false, - ); + : super( + name: 'social_share', + category: 'NTG social', + action: 'social share', + label: 'OS share menu', + nonInteraction: false, + ); } /// {@template push_notification_subscription_event} @@ -137,12 +137,12 @@ class SocialShareEvent extends NTGEvent { class PushNotificationSubscriptionEvent extends NTGEvent { /// {@macro push_notification_subscription_event} PushNotificationSubscriptionEvent() - : super( - name: 'push_notification_click', - category: 'NTG push notification', - action: 'click', - nonInteraction: false, - ); + : super( + name: 'push_notification_click', + category: 'NTG push notification', + action: 'click', + nonInteraction: false, + ); } /// {@template paywall_prompt_event} @@ -154,22 +154,22 @@ class PaywallPromptEvent extends NTGEvent { required PaywallPromptImpression impression, required String articleTitle, }) : super( - name: 'paywall_impression', - category: 'NTG paywall', - action: 'paywall modal impression $impression', - label: articleTitle, - nonInteraction: true, - ); + name: 'paywall_impression', + category: 'NTG paywall', + action: 'paywall modal impression $impression', + label: articleTitle, + nonInteraction: true, + ); /// An analytics event for tracking paywall prompt click. PaywallPromptEvent.click({required String articleTitle}) - : super( - name: 'paywall_click', - category: 'NTG paywall', - action: 'click', - label: articleTitle, - nonInteraction: false, - ); + : super( + name: 'paywall_click', + category: 'NTG paywall', + action: 'click', + label: articleTitle, + nonInteraction: false, + ); } /// {@template paywall_prompt_impression} @@ -196,11 +196,11 @@ enum PaywallPromptImpression { class UserSubscriptionConversionEvent extends NTGEvent { /// {@macro user_subscription_conversion_event} UserSubscriptionConversionEvent() - : super( - name: 'subscription_submit', - category: 'NTG subscription', - action: 'submit', - label: 'success', - nonInteraction: false, - ); + : super( + name: 'subscription_submit', + category: 'NTG subscription', + action: 'submit', + label: 'success', + nonInteraction: false, + ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/pubspec.yaml index f9dc8a9b0..34548ce60 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/pubspec.yaml @@ -3,11 +3,11 @@ description: Package which manages the analytics domain. publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: equatable: ^2.0.0 - firebase_analytics: ^11.3.1 + firebase_analytics: ^11.3.6 flutter: sdk: flutter @@ -15,4 +15,4 @@ dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/test/analytics_repository_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/test/analytics_repository_test.dart index 77dbb9507..3af5ee46a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/test/analytics_repository_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/test/analytics_repository_test.dart @@ -1,25 +1,10 @@ import 'package:analytics_repository/analytics_repository.dart'; -import 'package:equatable/equatable.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; class MockFirebaseAnalytics extends Mock implements FirebaseAnalytics {} -class TestEvent extends Equatable with AnalyticsEventMixin { - const TestEvent({required this.id}); - - final String id; - - @override - AnalyticsEvent get event { - return AnalyticsEvent( - 'TestEvent', - properties: {'test-key': id}, - ); - } -} - void main() { group('AnalyticsRepository', () { late FirebaseAnalytics firebaseAnalytics; @@ -46,13 +31,13 @@ void main() { }); group('track', () { - test('tracks event successfully', () { + test('tracks event successfully', () async { const event = AnalyticsEvent( 'TestEvent', properties: {'test-key': 'mock-id'}, ); - analyticsRepository.track(event); + await analyticsRepository.track(event); verify( () => firebaseAnalytics.logEvent( @@ -62,8 +47,7 @@ void main() { ).called(1); }); - test( - 'throws TrackEventFailure ' + test('throws TrackEventFailure ' 'when logEvent throws exception', () async { when( () => firebaseAnalytics.logEvent( @@ -88,18 +72,15 @@ void main() { }); group('setUserId', () { - test('sets user identifier successfully', () { + test('sets user identifier successfully', () async { const userId = 'userId'; - analyticsRepository.setUserId(userId); + await analyticsRepository.setUserId(userId); - verify( - () => firebaseAnalytics.setUserId(id: userId), - ).called(1); + verify(() => firebaseAnalytics.setUserId(id: userId)).called(1); }); - test( - 'throws SetUserIdFailure ' + test('throws SetUserIdFailure ' 'when setUserId throws exception', () async { when( () => firebaseAnalytics.setUserId(id: any(named: 'id')), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/test/models/ntg_event_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/test/models/ntg_event_test.dart index ff186ed6f..08603a2a3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/test/models/ntg_event_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/analytics_repository/test/models/ntg_event_test.dart @@ -23,8 +23,7 @@ void main() { }); group('impression', () { - test( - 'has correct values ' + test('has correct values ' 'when articleTitle is empty', () { final event = NewsletterEvent.impression(); expect(event.name, equals('newsletter_impression')); @@ -37,8 +36,7 @@ void main() { expect(event.properties!['nonInteraction'], equals('false')); }); - test( - 'has correct values ' + test('has correct values ' 'when articleTitle is not empty', () { const articleTitle = 'articleTitle'; final event = NewsletterEvent.impression(articleTitle: articleTitle); @@ -159,9 +157,7 @@ void main() { group('click', () { test('has correct values', () { const articleTitle = 'articleTitle'; - final event = PaywallPromptEvent.click( - articleTitle: articleTitle, - ); + final event = PaywallPromptEvent.click(articleTitle: articleTitle); expect(event.name, equals('paywall_click')); expect(event.properties!['eventCategory'], equals('NTG paywall')); expect(event.properties!['eventAction'], equals('click')); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/analysis_options.yaml index 1a86ad22e..89138eb27 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/analysis_options.yaml @@ -1,5 +1,7 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml analyzer: + errors: + document_ignores: ignore exclude: - lib/src/generated/** diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/analysis_options.yaml index 3a6b5d4c4..7e773a09f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/analysis_options.yaml @@ -1,4 +1,7 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml +analyzer: + errors: + document_ignores: ignore linter: rules: public_member_api_docs: false diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/colors/colors_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/colors/colors_page.dart index 8afed9e49..d2a941190 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/colors/colors_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/colors/colors_page.dart @@ -72,14 +72,8 @@ class ColorsPage extends StatelessWidget { name: 'Disabled Foreground', color: AppColors.disabledForeground, ), - _ColorItem( - name: 'Disabled Button', - color: AppColors.disabledButton, - ), - _ColorItem( - name: 'Disabled Surface', - color: AppColors.disabledSurface, - ), + _ColorItem(name: 'Disabled Button', color: AppColors.disabledButton), + _ColorItem(name: 'Disabled Surface', color: AppColors.disabledSurface), ]; return Scaffold( @@ -162,10 +156,11 @@ class _ColorSquare extends StatelessWidget { } String get hexCode { - if (color.value.toRadixString(16).length <= 2) { + if (color.toARGB32().toRadixString(16).length <= 2) { return '--'; } else { - return '#${color.value.toRadixString(16).substring(2).toUpperCase()}'; + final c = color.toARGB32().toRadixString(16).substring(2).toUpperCase(); + return '#$c'; } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/main.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/main.dart index 8d2de17f1..2c0c4bf5b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/main.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/main.dart @@ -57,7 +57,7 @@ class RootPage extends StatelessWidget { body: ListView.separated( itemCount: pages.length, itemBuilder: (_, index) => pages[index], - separatorBuilder: (_, __) => const Divider(), + separatorBuilder: (_, _) => const Divider(), ), ); } @@ -80,9 +80,7 @@ class _ListItem extends StatelessWidget { Widget build(BuildContext context) { return ListTile( leading: IconTheme( - data: IconThemeData( - color: Theme.of(context).iconTheme.color, - ), + data: IconThemeData(color: Theme.of(context).iconTheme.color), child: icon, ), title: title, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_button_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_button_page.dart index 207561e78..bc5111440 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_button_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_button_page.dart @@ -73,10 +73,7 @@ class AppButtonPage extends StatelessWidget { ], ), ), - const _AppButtonItem( - buttonType: ButtonType.login, - child: Text('Log in'), - ), + const _AppButtonItem(buttonType: ButtonType.login, child: Text('Log in')), const _AppButtonItem( buttonType: ButtonType.subscribe, child: Text('Subscribe'), @@ -190,7 +187,7 @@ enum ButtonType { cancel, watchVideo, watchVideoDark, - logInSubscribe + logInSubscribe, } class _AppButtonItem extends StatelessWidget { @@ -199,25 +196,13 @@ class _AppButtonItem extends StatelessWidget { AppButton get appButton { switch (buttonType) { case ButtonType.google: - return AppButton.outlinedWhite( - onPressed: () {}, - child: child, - ); + return AppButton.outlinedWhite(onPressed: () {}, child: child); case ButtonType.apple: - return AppButton.black( - child: child, - onPressed: () {}, - ); + return AppButton.black(child: child, onPressed: () {}); case ButtonType.facebook: - return AppButton.blueDress( - child: child, - onPressed: () {}, - ); + return AppButton.blueDress(child: child, onPressed: () {}); case ButtonType.twitter: - return AppButton.crystalBlue( - onPressed: () {}, - child: child, - ); + return AppButton.crystalBlue(onPressed: () {}, child: child); case ButtonType.email: return AppButton.outlinedTransparentDarkAqua( onPressed: () {}, @@ -229,45 +214,24 @@ class _AppButtonItem extends StatelessWidget { child: child, ); case ButtonType.subscribe: - return AppButton.redWine( - child: child, - onPressed: () {}, - ); + return AppButton.redWine(child: child, onPressed: () {}); case ButtonType.information: - return AppButton.darkAqua( - onPressed: () {}, - child: child, - ); + return AppButton.darkAqua(onPressed: () {}, child: child); case ButtonType.trial: - return AppButton.smallRedWine( - onPressed: () {}, - child: child, - ); + return AppButton.smallRedWine(onPressed: () {}, child: child); case ButtonType.logout: - return AppButton.smallDarkAqua( - onPressed: () {}, - child: child, - ); + return AppButton.smallDarkAqua(onPressed: () {}, child: child); case ButtonType.details: return AppButton.smallOutlineTransparent( onPressed: () {}, child: child, ); case ButtonType.cancel: - return AppButton.smallTransparent( - onPressed: () {}, - child: child, - ); + return AppButton.smallTransparent(onPressed: () {}, child: child); case ButtonType.watchVideo: - return AppButton.transparentDarkAqua( - onPressed: () {}, - child: child, - ); + return AppButton.transparentDarkAqua(onPressed: () {}, child: child); case ButtonType.watchVideoDark: - return AppButton.transparentWhite( - onPressed: () {}, - child: child, - ); + return AppButton.transparentWhite(onPressed: () {}, child: child); case ButtonType.logInSubscribe: return AppButton.outlinedTransparentWhite( onPressed: () {}, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_logo_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_logo_page.dart index 421a98328..0ed37254a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_logo_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_logo_page.dart @@ -11,9 +11,7 @@ class AppLogoPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Logo'), - ), + appBar: AppBar(title: const Text('Logo')), body: ColoredBox( color: AppColors.darkBackground, child: Center( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_switch_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_switch_page.dart index 2567fbf7b..f0c254b2c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_switch_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_switch_page.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_field_initializers_in_const_classes - import 'package:app_ui/app_ui.dart'; import 'package:flutter/material.dart'; @@ -26,12 +24,7 @@ class AppSwitchPage extends StatelessWidget { } } -enum SwitchType { - on, - off, - noLabelOn, - noLabelOff, -} +enum SwitchType { on, off, noLabelOn, noLabelOff } class _AppSwitch extends StatelessWidget { const _AppSwitch({required this.switchType}); @@ -39,28 +32,14 @@ class _AppSwitch extends StatelessWidget { AppSwitch get appSwitch { switch (switchType) { case SwitchType.on: - return AppSwitch( - onChanged: (_) {}, - onText: 'On', - value: true, - ); + return AppSwitch(onChanged: (_) {}, onText: 'On', value: true); case SwitchType.off: - return AppSwitch( - onChanged: (_) {}, - onText: 'Off', - value: false, - ); + return AppSwitch(onChanged: (_) {}, onText: 'Off', value: false); case SwitchType.noLabelOn: - return AppSwitch( - onChanged: (_) {}, - value: true, - ); + return AppSwitch(onChanged: (_) {}, value: true); case SwitchType.noLabelOff: - return AppSwitch( - onChanged: (_) {}, - value: false, - ); + return AppSwitch(onChanged: (_) {}, value: false); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_text_field_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_text_field_page.dart index 88d3b4ff6..ab0f71a50 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_text_field_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/app_text_field_page.dart @@ -11,20 +11,13 @@ class AppTextFieldPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text( - 'Text Field', - ), - ), + appBar: AppBar(title: const Text('Text Field')), body: Padding( padding: const EdgeInsets.all(AppSpacing.lg), child: Column( children: [ const AppEmailTextField(), - AppTextField( - hintText: 'Default text field', - onChanged: (_) {}, - ), + AppTextField(hintText: 'Default text field', onChanged: (_) {}), ], ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/show_app_modal_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/show_app_modal_page.dart index afa310802..5a0aaa848 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/show_app_modal_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/show_app_modal_page.dart @@ -22,8 +22,10 @@ class ShowAppModalPage extends StatelessWidget { horizontal: AppSpacing.xxlg + contentSpace, vertical: AppSpacing.xlg, ), - textStyle: - const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + textStyle: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), ), child: const Text('Show app modal'), ), @@ -31,9 +33,7 @@ class ShowAppModalPage extends StatelessWidget { ]; return Scaffold( - appBar: AppBar( - title: const Text('Modal App'), - ), + appBar: AppBar(title: const Text('Modal App')), body: ColoredBox( color: AppColors.white, child: Align( @@ -46,10 +46,8 @@ class ShowAppModalPage extends StatelessWidget { ); } - void _showModal({ - required BuildContext context, - }) { - showAppModal( + Future _showModal({required BuildContext context}) async { + await showAppModal( context: context, builder: (context) => Column( mainAxisSize: MainAxisSize.min, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/widgets_page.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/widgets_page.dart index 6a20cf7e2..83c6a1eb3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/widgets_page.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/lib/widgets/widgets_page.dart @@ -49,7 +49,7 @@ class WidgetsPage extends StatelessWidget { body: ListView.separated( itemCount: widgets.length, itemBuilder: (_, index) => widgets[index], - separatorBuilder: (_, __) => const Divider(), + separatorBuilder: (_, _) => const Divider(), ), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/pubspec.yaml index 360f02cdc..c591d9823 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/gallery/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: app_ui: @@ -13,7 +13,7 @@ dependencies: sdk: flutter dev_dependencies: - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 flutter: uses-material-design: true diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/generated/assets.gen.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/generated/assets.gen.dart index c18ffdcba..dddfef893 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/generated/assets.gen.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/generated/assets.gen.dart @@ -76,24 +76,24 @@ class $AssetsIconsGen { /// List of all assets List get values => [ - aboutIcon, - apple, - backIcon, - bestValue, - closeCircle, - closeCircleFilled, - emailOutline, - envelopeOpen, - facebook, - google, - logInIcon, - logOutIcon, - notificationsIcon, - profileIcon, - termsOfUseIcon, - twitter, - video - ]; + aboutIcon, + apple, + backIcon, + bestValue, + closeCircle, + closeCircleFilled, + emailOutline, + envelopeOpen, + facebook, + google, + logInIcon, + logOutIcon, + notificationsIcon, + profileIcon, + termsOfUseIcon, + twitter, + video, + ]; } class $AssetsImagesGen { @@ -125,13 +125,13 @@ class $AssetsImagesGen { /// List of all assets List get values => [ - continueWithApple, - continueWithFacebook, - continueWithGoogle, - continueWithTwitter, - logoDark, - logoLight - ]; + continueWithApple, + continueWithFacebook, + continueWithGoogle, + continueWithTwitter, + logoDark, + logoLight, + ]; } class Assets { @@ -199,15 +199,8 @@ class AssetGenImage { ); } - ImageProvider provider({ - AssetBundle? bundle, - String? package = 'app_ui', - }) { - return AssetImage( - _assetName, - bundle: bundle, - package: package, - ); + ImageProvider provider({AssetBundle? bundle, String? package = 'app_ui'}) { + return AssetImage(_assetName, bundle: bundle, package: package); } String get path => _assetName; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/theme/app_theme.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/theme/app_theme.dart index 9d440f0ee..d1eb51ddb 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/theme/app_theme.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/theme/app_theme.dart @@ -45,9 +45,7 @@ class AppTheme { SnackBarThemeData get _snackBarTheme { return SnackBarThemeData( - contentTextStyle: UITextStyle.bodyText1.copyWith( - color: AppColors.white, - ), + contentTextStyle: UITextStyle.bodyText1.copyWith(color: AppColors.white), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppSpacing.sm), ), @@ -75,9 +73,7 @@ class AppTheme { } IconThemeData get _iconTheme { - return const IconThemeData( - color: AppColors.onBackground, - ); + return const IconThemeData(color: AppColors.onBackground); } DividerThemeData get _dividerTheme { @@ -93,46 +89,48 @@ class AppTheme { TextTheme get _textTheme => uiTextTheme; /// The Content text theme based on [ContentTextStyle]. - static final contentTextTheme = TextTheme( - displayLarge: ContentTextStyle.headline1, - displayMedium: ContentTextStyle.headline2, - displaySmall: ContentTextStyle.headline3, - headlineMedium: ContentTextStyle.headline4, - headlineSmall: ContentTextStyle.headline5, - titleLarge: ContentTextStyle.headline6, - titleMedium: ContentTextStyle.subtitle1, - titleSmall: ContentTextStyle.subtitle2, - bodyLarge: ContentTextStyle.bodyText1, - bodyMedium: ContentTextStyle.bodyText2, - labelLarge: ContentTextStyle.button, - bodySmall: ContentTextStyle.caption, - labelSmall: ContentTextStyle.overline, - ).apply( - bodyColor: AppColors.black, - displayColor: AppColors.black, - decorationColor: AppColors.black, - ); + static final TextTheme contentTextTheme = + TextTheme( + displayLarge: ContentTextStyle.headline1, + displayMedium: ContentTextStyle.headline2, + displaySmall: ContentTextStyle.headline3, + headlineMedium: ContentTextStyle.headline4, + headlineSmall: ContentTextStyle.headline5, + titleLarge: ContentTextStyle.headline6, + titleMedium: ContentTextStyle.subtitle1, + titleSmall: ContentTextStyle.subtitle2, + bodyLarge: ContentTextStyle.bodyText1, + bodyMedium: ContentTextStyle.bodyText2, + labelLarge: ContentTextStyle.button, + bodySmall: ContentTextStyle.caption, + labelSmall: ContentTextStyle.overline, + ).apply( + bodyColor: AppColors.black, + displayColor: AppColors.black, + decorationColor: AppColors.black, + ); /// The UI text theme based on [UITextStyle]. - static final uiTextTheme = TextTheme( - displayLarge: UITextStyle.headline1, - displayMedium: UITextStyle.headline2, - displaySmall: UITextStyle.headline3, - headlineMedium: UITextStyle.headline4, - headlineSmall: UITextStyle.headline5, - titleLarge: UITextStyle.headline6, - titleMedium: UITextStyle.subtitle1, - titleSmall: UITextStyle.subtitle2, - bodyLarge: UITextStyle.bodyText1, - bodyMedium: UITextStyle.bodyText2, - labelLarge: UITextStyle.button, - bodySmall: UITextStyle.caption, - labelSmall: UITextStyle.overline, - ).apply( - bodyColor: AppColors.black, - displayColor: AppColors.black, - decorationColor: AppColors.black, - ); + static final TextTheme uiTextTheme = + TextTheme( + displayLarge: UITextStyle.headline1, + displayMedium: UITextStyle.headline2, + displaySmall: UITextStyle.headline3, + headlineMedium: UITextStyle.headline4, + headlineSmall: UITextStyle.headline5, + titleLarge: UITextStyle.headline6, + titleMedium: UITextStyle.subtitle1, + titleSmall: UITextStyle.subtitle2, + bodyLarge: UITextStyle.bodyText1, + bodyMedium: UITextStyle.bodyText2, + labelLarge: UITextStyle.button, + bodySmall: UITextStyle.caption, + labelSmall: UITextStyle.overline, + ).apply( + bodyColor: AppColors.black, + displayColor: AppColors.black, + decorationColor: AppColors.black, + ); InputDecorationTheme get _inputDecorationTheme { return InputDecorationTheme( @@ -222,8 +220,9 @@ class AppTheme { } return AppColors.grey; }), - trackOutlineColor: - WidgetStateProperty.resolveWith((Set states) { + trackOutlineColor: WidgetStateProperty.resolveWith(( + Set states, + ) { if (states.contains(WidgetState.selected)) { return AppColors.primaryContainer; } @@ -239,8 +238,8 @@ class AppTheme { ); } - TabBarTheme get _tabBarTheme { - return TabBarTheme( + TabBarThemeData get _tabBarTheme { + return TabBarThemeData( labelStyle: UITextStyle.button, labelColor: AppColors.darkAqua, labelPadding: const EdgeInsets.symmetric( @@ -250,10 +249,7 @@ class AppTheme { unselectedLabelStyle: UITextStyle.button, unselectedLabelColor: AppColors.mediumEmphasisSurface, indicator: const UnderlineTabIndicator( - borderSide: BorderSide( - width: 3, - color: AppColors.darkAqua, - ), + borderSide: BorderSide(width: 3, color: AppColors.darkAqua), ), indicatorSize: TabBarIndicatorSize.label, ); @@ -261,24 +257,19 @@ class AppTheme { } InputBorder get _textFieldBorder => const UnderlineInputBorder( - borderSide: BorderSide( - width: 1.5, - color: AppColors.darkAqua, - ), - ); + borderSide: BorderSide(width: 1.5, color: AppColors.darkAqua), +); BottomNavigationBarThemeData get _bottomAppBarTheme { return BottomNavigationBarThemeData( backgroundColor: AppColors.darkBackground, selectedItemColor: AppColors.white, - unselectedItemColor: AppColors.white.withOpacity(0.74), + unselectedItemColor: AppColors.white.withValues(alpha: 0.74), ); } ChipThemeData get _chipTheme { - return const ChipThemeData( - backgroundColor: AppColors.transparent, - ); + return const ChipThemeData(backgroundColor: AppColors.transparent); } /// {@template app_dark_theme} @@ -309,9 +300,7 @@ class AppDarkTheme extends AppTheme { @override SnackBarThemeData get _snackBarTheme { return SnackBarThemeData( - contentTextStyle: UITextStyle.bodyText1.copyWith( - color: AppColors.black, - ), + contentTextStyle: UITextStyle.bodyText1.copyWith(color: AppColors.black), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppSpacing.sm), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_back_button.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_back_button.dart index 2cbbd1385..ae44a10a7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_back_button.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_back_button.dart @@ -8,24 +8,12 @@ import 'package:flutter/material.dart'; class AppBackButton extends StatelessWidget { /// Creates a default instance of [AppBackButton]. - const AppBackButton({ - Key? key, - VoidCallback? onPressed, - }) : this._( - key: key, - isLight: false, - onPressed: onPressed, - ); + const AppBackButton({Key? key, VoidCallback? onPressed}) + : this._(key: key, isLight: false, onPressed: onPressed); /// Creates a light instance of [AppBackButton]. - const AppBackButton.light({ - Key? key, - VoidCallback? onPressed, - }) : this._( - key: key, - isLight: true, - onPressed: onPressed, - ); + const AppBackButton.light({Key? key, VoidCallback? onPressed}) + : this._(key: key, isLight: true, onPressed: onPressed); /// {@macro app_back_button} const AppBackButton._({required this.isLight, this.onPressed, super.key}); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_button.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_button.dart index f051bba16..943723c48 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_button.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_button.dart @@ -20,17 +20,17 @@ class AppButton extends StatelessWidget { Size? minimumSize, EdgeInsets? padding, super.key, - }) : _buttonColor = buttonColor ?? Colors.white, - _disabledButtonColor = disabledButtonColor ?? AppColors.disabledButton, - _borderSide = borderSide, - _foregroundColor = foregroundColor ?? AppColors.black, - _disabledForegroundColor = - disabledForegroundColor ?? AppColors.disabledForeground, - _elevation = elevation ?? 0, - _textStyle = textStyle, - _maximumSize = maximumSize ?? _defaultMaximumSize, - _minimumSize = minimumSize ?? _defaultMinimumSize, - _padding = padding ?? _defaultPadding; + }) : _buttonColor = buttonColor ?? Colors.white, + _disabledButtonColor = disabledButtonColor ?? AppColors.disabledButton, + _borderSide = borderSide, + _foregroundColor = foregroundColor ?? AppColors.black, + _disabledForegroundColor = + disabledForegroundColor ?? AppColors.disabledForeground, + _elevation = elevation ?? 0, + _textStyle = textStyle, + _maximumSize = maximumSize ?? _defaultMaximumSize, + _minimumSize = minimumSize ?? _defaultMinimumSize, + _padding = padding ?? _defaultPadding; /// Filled black button. const AppButton.black({ @@ -40,14 +40,14 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.black, - child: child, - foregroundColor: AppColors.white, - elevation: elevation, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.black, + child: child, + foregroundColor: AppColors.white, + elevation: elevation, + textStyle: textStyle, + ); /// Filled blue dress button. const AppButton.blueDress({ @@ -57,14 +57,14 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.blueDress, - child: child, - foregroundColor: AppColors.white, - elevation: elevation, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.blueDress, + child: child, + foregroundColor: AppColors.white, + elevation: elevation, + textStyle: textStyle, + ); /// Filled crystal blue button. const AppButton.crystalBlue({ @@ -74,14 +74,14 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.crystalBlue, - child: child, - foregroundColor: AppColors.white, - elevation: elevation, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.crystalBlue, + child: child, + foregroundColor: AppColors.white, + elevation: elevation, + textStyle: textStyle, + ); /// Filled red wine button. const AppButton.redWine({ @@ -91,14 +91,14 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.redWine, - child: child, - foregroundColor: AppColors.white, - elevation: elevation, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.redWine, + child: child, + foregroundColor: AppColors.white, + elevation: elevation, + textStyle: textStyle, + ); /// Filled secondary button. const AppButton.secondary({ @@ -109,18 +109,18 @@ class AppButton extends StatelessWidget { TextStyle? textStyle, Color? disabledButtonColor, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.secondary, - child: child, - foregroundColor: AppColors.white, - disabledButtonColor: disabledButtonColor ?? AppColors.disabledSurface, - elevation: elevation, - textStyle: textStyle, - padding: _smallPadding, - maximumSize: _smallMaximumSize, - minimumSize: _smallMinimumSize, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.secondary, + child: child, + foregroundColor: AppColors.white, + disabledButtonColor: disabledButtonColor ?? AppColors.disabledSurface, + elevation: elevation, + textStyle: textStyle, + padding: _smallPadding, + maximumSize: _smallMaximumSize, + minimumSize: _smallMinimumSize, + ); /// Filled dark aqua button. const AppButton.darkAqua({ @@ -130,14 +130,14 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.darkAqua, - child: child, - foregroundColor: AppColors.white, - elevation: elevation, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.darkAqua, + child: child, + foregroundColor: AppColors.white, + elevation: elevation, + textStyle: textStyle, + ); /// Outlined white button. const AppButton.outlinedWhite({ @@ -147,17 +147,15 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - child: child, - buttonColor: AppColors.white, - borderSide: const BorderSide( - color: AppColors.pastelGrey, - ), - elevation: elevation, - foregroundColor: AppColors.lightBlack, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + child: child, + buttonColor: AppColors.white, + borderSide: const BorderSide(color: AppColors.pastelGrey), + elevation: elevation, + foregroundColor: AppColors.lightBlack, + textStyle: textStyle, + ); /// Outlined transparent dark aqua button. const AppButton.outlinedTransparentDarkAqua({ @@ -167,17 +165,15 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - child: child, - buttonColor: AppColors.transparent, - borderSide: const BorderSide( - color: AppColors.paleSky, - ), - elevation: elevation, - foregroundColor: AppColors.darkAqua, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + child: child, + buttonColor: AppColors.transparent, + borderSide: const BorderSide(color: AppColors.paleSky), + elevation: elevation, + foregroundColor: AppColors.darkAqua, + textStyle: textStyle, + ); /// Outlined transparent white button. const AppButton.outlinedTransparentWhite({ @@ -187,17 +183,15 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - child: child, - buttonColor: AppColors.transparent, - borderSide: const BorderSide( - color: AppColors.white, - ), - elevation: elevation, - foregroundColor: AppColors.white, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + child: child, + buttonColor: AppColors.transparent, + borderSide: const BorderSide(color: AppColors.white), + elevation: elevation, + foregroundColor: AppColors.white, + textStyle: textStyle, + ); /// Filled transparent dark aqua button. const AppButton.transparentDarkAqua({ @@ -207,14 +201,14 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - child: child, - buttonColor: AppColors.transparent, - elevation: elevation, - foregroundColor: AppColors.darkAqua, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + child: child, + buttonColor: AppColors.transparent, + elevation: elevation, + foregroundColor: AppColors.darkAqua, + textStyle: textStyle, + ); /// Filled transparent white button. const AppButton.transparentWhite({ @@ -224,16 +218,16 @@ class AppButton extends StatelessWidget { double? elevation, TextStyle? textStyle, }) : this._( - key: key, - onPressed: onPressed, - child: child, - disabledButtonColor: AppColors.transparent, - buttonColor: AppColors.transparent, - elevation: elevation, - foregroundColor: AppColors.white, - disabledForegroundColor: AppColors.white, - textStyle: textStyle, - ); + key: key, + onPressed: onPressed, + child: child, + disabledButtonColor: AppColors.transparent, + buttonColor: AppColors.transparent, + elevation: elevation, + foregroundColor: AppColors.white, + disabledForegroundColor: AppColors.white, + textStyle: textStyle, + ); /// Filled small red wine blue button. const AppButton.smallRedWine({ @@ -242,16 +236,16 @@ class AppButton extends StatelessWidget { VoidCallback? onPressed, double? elevation, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.redWine, - child: child, - foregroundColor: AppColors.white, - elevation: elevation, - maximumSize: _smallMaximumSize, - minimumSize: _smallMinimumSize, - padding: _smallPadding, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.redWine, + child: child, + foregroundColor: AppColors.white, + elevation: elevation, + maximumSize: _smallMaximumSize, + minimumSize: _smallMinimumSize, + padding: _smallPadding, + ); /// Filled small transparent button. const AppButton.smallDarkAqua({ @@ -260,16 +254,16 @@ class AppButton extends StatelessWidget { VoidCallback? onPressed, double? elevation, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.darkAqua, - child: child, - foregroundColor: AppColors.white, - elevation: elevation, - maximumSize: _smallMaximumSize, - minimumSize: _smallMinimumSize, - padding: _smallPadding, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.darkAqua, + child: child, + foregroundColor: AppColors.white, + elevation: elevation, + maximumSize: _smallMaximumSize, + minimumSize: _smallMinimumSize, + padding: _smallPadding, + ); /// Filled small transparent button. const AppButton.smallTransparent({ @@ -278,16 +272,16 @@ class AppButton extends StatelessWidget { VoidCallback? onPressed, double? elevation, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.transparent, - child: child, - foregroundColor: AppColors.darkAqua, - elevation: elevation, - maximumSize: _smallMaximumSize, - minimumSize: _smallMinimumSize, - padding: _smallPadding, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.transparent, + child: child, + foregroundColor: AppColors.darkAqua, + elevation: elevation, + maximumSize: _smallMaximumSize, + minimumSize: _smallMinimumSize, + padding: _smallPadding, + ); /// Filled small transparent button. const AppButton.smallOutlineTransparent({ @@ -296,19 +290,17 @@ class AppButton extends StatelessWidget { VoidCallback? onPressed, double? elevation, }) : this._( - key: key, - onPressed: onPressed, - buttonColor: AppColors.transparent, - child: child, - borderSide: const BorderSide( - color: AppColors.paleSky, - ), - foregroundColor: AppColors.darkAqua, - elevation: elevation, - maximumSize: _smallMaximumSize, - minimumSize: _smallMinimumSize, - padding: _smallPadding, - ); + key: key, + onPressed: onPressed, + buttonColor: AppColors.transparent, + child: child, + borderSide: const BorderSide(color: AppColors.paleSky), + foregroundColor: AppColors.darkAqua, + elevation: elevation, + maximumSize: _smallMaximumSize, + minimumSize: _smallMinimumSize, + padding: _smallPadding, + ); /// The maximum size of the small variant of the button. static const _smallMaximumSize = Size(double.infinity, 40); @@ -401,9 +393,7 @@ class AppButton extends StatelessWidget { : WidgetStateProperty.all(_foregroundColor), side: WidgetStateProperty.all(_borderSide), shape: WidgetStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(100), - ), + RoundedRectangleBorder(borderRadius: BorderRadius.circular(100)), ), ), child: child, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_email_text_field.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_email_text_field.dart index b06441455..861b70576 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_email_text_field.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_email_text_field.dart @@ -40,10 +40,7 @@ class AppEmailTextField extends StatelessWidget { autoFillHints: const [AutofillHints.email], autocorrect: false, prefix: const Padding( - padding: EdgeInsets.only( - left: AppSpacing.sm, - right: AppSpacing.sm, - ), + padding: EdgeInsets.only(left: AppSpacing.sm, right: AppSpacing.sm), child: Icon( Icons.email_outlined, color: AppColors.mediumEmphasisSurface, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_logo.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_logo.dart index 3694ad267..45408d7ff 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_logo.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_logo.dart @@ -19,10 +19,6 @@ class AppLogo extends StatelessWidget { @override Widget build(BuildContext context) { - return _logo.image( - fit: BoxFit.contain, - width: 172, - height: 24, - ); + return _logo.image(fit: BoxFit.contain, width: 172, height: 24); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_switch.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_switch.dart index a2202b847..b38044416 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_switch.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_switch.dart @@ -38,17 +38,14 @@ class AppSwitch extends StatelessWidget { ContentThemeOverrideBuilder( builder: (context) => Text( value ? onText : offText, - style: Theme.of(context).textTheme.labelLarge?.copyWith( - color: AppColors.eerieBlack, - ), + style: Theme.of( + context, + ).textTheme.labelLarge?.copyWith(color: AppColors.eerieBlack), ), ), Padding( padding: const EdgeInsets.only(left: AppSpacing.xs), - child: Switch( - value: value, - onChanged: onChanged, - ), + child: Switch(value: value, onChanged: onChanged), ), ], ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_text_field.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_text_field.dart index 9ba5db4fa..6369a2eb7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_text_field.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/app_text_field.dart @@ -93,9 +93,9 @@ class AppTextField extends StatelessWidget { readOnly: readOnly, autofillHints: autoFillHints, cursorColor: AppColors.darkAqua, - style: Theme.of(context).textTheme.titleLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: Theme.of( + context, + ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w500), onFieldSubmitted: onSubmitted, decoration: InputDecoration( hintText: hintText, @@ -106,9 +106,7 @@ class AppTextField extends StatelessWidget { width: 32, height: 32, ), - prefixIconConstraints: const BoxConstraints.tightFor( - width: 48, - ), + prefixIconConstraints: const BoxConstraints.tightFor(width: 48), ), onChanged: onChanged, onTap: onTap, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/content_theme_override_builder.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/content_theme_override_builder.dart index bcb55a19e..fec6c07b4 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/content_theme_override_builder.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/lib/src/widgets/content_theme_override_builder.dart @@ -19,9 +19,7 @@ class ContentThemeOverrideBuilder extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); return Theme( - data: theme.copyWith( - textTheme: AppTheme.contentTextTheme, - ), + data: theme.copyWith(textTheme: AppTheme.contentTextTheme), child: Builder(builder: builder), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/pubspec.yaml index 02fe08072..67e65ff54 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/pubspec.yaml @@ -3,21 +3,21 @@ description: App UI Component Library publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: flutter: sdk: flutter - flutter_svg: ^2.0.5 + flutter_svg: ^2.2.0 intl: ^0.20.0 - mockingjay: ^0.6.0 + mockingjay: ^2.1.0 dev_dependencies: - build_runner: ^2.0.3 + build_runner: ^2.4.15 flutter_gen_runner: ^5.2.0 flutter_test: sdk: flutter - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 flutter: uses-material-design: true diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/helpers/pump_app.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/helpers/pump_app.dart index e3b483545..5ffd92f7d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/helpers/pump_app.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/helpers/pump_app.dart @@ -12,14 +12,10 @@ extension AppTester on WidgetTester { MaterialApp( theme: theme, home: navigator == null - ? Scaffold( - body: widgetUnderTest, - ) + ? Scaffold(body: widgetUnderTest) : MockNavigatorProvider( navigator: navigator, - child: Scaffold( - body: widgetUnderTest, - ), + child: Scaffold(body: widgetUnderTest), ), ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_back_button_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_back_button_test.dart index cc8484f71..37f6a75aa 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_back_button_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_back_button_test.dart @@ -13,32 +13,18 @@ void main() { group('AppBackButton', () { testWidgets('renders IconButton', (tester) async { await tester.pumpApp( - Scaffold( - appBar: AppBar( - leading: const AppBackButton(), - ), - ), + Scaffold(appBar: AppBar(leading: const AppBackButton())), ); - expect( - find.byType(IconButton), - findsOneWidget, - ); + expect(find.byType(IconButton), findsOneWidget); }); testWidgets('renders IconButton when light', (tester) async { await tester.pumpApp( - Scaffold( - appBar: AppBar( - leading: const AppBackButton.light(), - ), - ), + Scaffold(appBar: AppBar(leading: const AppBackButton.light())), ); - expect( - find.byType(IconButton), - findsOneWidget, - ); + expect(find.byType(IconButton), findsOneWidget); }); group('navigates', () { @@ -46,10 +32,7 @@ void main() { final navigator = MockNavigator(); when(navigator.canPop).thenAnswer((_) => true); when(navigator.pop).thenAnswer((_) async {}); - await tester.pumpApp( - const AppBackButton(), - navigator: navigator, - ); + await tester.pumpApp(const AppBackButton(), navigator: navigator); await tester.tap(find.byType(IconButton)); await tester.pumpAndSettle(); verify(navigator.pop).called(1); @@ -57,21 +40,16 @@ void main() { testWidgets('call onPressed when is provided ', (tester) async { final onPressed = MockFunction(); - await tester.pumpApp( - AppBackButton(onPressed: onPressed.call), - ); + await tester.pumpApp(AppBackButton(onPressed: onPressed.call)); await tester.tap(find.byType(IconButton)); await tester.pumpAndSettle(); verify(onPressed.call).called(1); }); - testWidgets( - 'call onPressed when is provided ' + testWidgets('call onPressed when is provided ' 'and style is light', (tester) async { final onPressed = MockFunction(); - await tester.pumpApp( - AppBackButton.light(onPressed: onPressed.call), - ); + await tester.pumpApp(AppBackButton.light(onPressed: onPressed.call)); await tester.tap(find.byType(IconButton)); await tester.pumpAndSettle(); verify(onPressed.call).called(1); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_button_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_button_test.dart index df502b930..d07506658 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_button_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_button_test.dart @@ -1,4 +1,3 @@ -// ignore_for_file: prefer_const_literals_to_create_immutables // ignore_for_file: prefer_const_constructors // ignore_for_file: avoid_redundant_argument_values @@ -22,18 +21,10 @@ void main() { await tester.pumpApp( Column( children: [ - AppButton.black( - child: buttonText, - ), - AppButton.smallOutlineTransparent( - child: buttonText, - ), - AppButton.redWine( - child: buttonText, - ), - AppButton.blueDress( - child: buttonText, - ), + AppButton.black(child: buttonText), + AppButton.smallOutlineTransparent(child: buttonText), + AppButton.redWine(child: buttonText), + AppButton.blueDress(child: buttonText), ], ), ); @@ -41,248 +32,136 @@ void main() { expect(find.text('buttonText'), findsNWidgets(4)); }); - testWidgets( - 'renders black button ' + testWidgets('renders black button ' 'when `AppButton.black()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.black( - child: buttonText, - onPressed: () {}, - ), + AppButton.black(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.black, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.black); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders blueDress button ' + testWidgets('renders blueDress button ' 'when `AppButton.blueDress()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.blueDress( - child: buttonText, - onPressed: () {}, - ), + AppButton.blueDress(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.blueDress, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.blueDress); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders crystalBlue button ' + testWidgets('renders crystalBlue button ' 'when `AppButton.crystalBlue()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.crystalBlue( - child: buttonText, - onPressed: () {}, - ), + AppButton.crystalBlue(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.crystalBlue, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.crystalBlue); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders redWine button ' + testWidgets('renders redWine button ' 'when `AppButton.redWine()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.redWine( - child: buttonText, - onPressed: () {}, - ), + AppButton.redWine(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.redWine, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.redWine); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders secondary button ' + testWidgets('renders secondary button ' 'when `AppButton.secondary()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.secondary( - child: buttonText, - onPressed: () {}, - ), + AppButton.secondary(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.secondary, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 40), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(0, 40), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.secondary); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 40)); + expect(widget.style?.minimumSize?.resolve({}), Size(0, 40)); }); - testWidgets( - 'renders darkAqua button ' + testWidgets('renders darkAqua button ' 'when `AppButton.darkAqua()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.darkAqua( - child: buttonText, - onPressed: () {}, - ), + AppButton.darkAqua(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.darkAqua, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.darkAqua); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders outlinedWhite button ' + testWidgets('renders outlinedWhite button ' 'when `AppButton.outlinedWhite()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.outlinedWhite( - child: buttonText, - onPressed: () {}, - ), + AppButton.outlinedWhite(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.white, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.white); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders outlinedTransparentDarkAqua button ' - 'when `AppButton.outlinedTransparentDarkAqua()` called', - (tester) async { + testWidgets('renders outlinedTransparentDarkAqua button ' + 'when `AppButton.outlinedTransparentDarkAqua()` called', ( + tester, + ) async { final buttonText = Text('buttonText'); await tester.pumpApp( @@ -296,332 +175,179 @@ void main() { final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.transparent, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.transparent); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders outlinedTransparentWhite button ' + testWidgets('renders outlinedTransparentWhite button ' 'when `AppButton.outlinedTransparentWhite()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.outlinedTransparentWhite( - onPressed: () {}, - child: buttonText, - ), + AppButton.outlinedTransparentWhite(onPressed: () {}, child: buttonText), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.transparent, - ); - expect( - widget.style?.foregroundColor?.resolve({}), - AppColors.white, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.transparent); + expect(widget.style?.foregroundColor?.resolve({}), AppColors.white); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders smallRedWine button ' + testWidgets('renders smallRedWine button ' 'when `AppButton.smallRedWine()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.smallRedWine( - child: buttonText, - onPressed: () {}, - ), + AppButton.smallRedWine(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.redWine, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 40), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(0, 40), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.redWine); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 40)); + expect(widget.style?.minimumSize?.resolve({}), Size(0, 40)); }); - testWidgets( - 'renders smallDarkAqua button ' + testWidgets('renders smallDarkAqua button ' 'when `AppButton.smallDarkAqua()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.smallDarkAqua( - child: buttonText, - onPressed: () {}, - ), + AppButton.smallDarkAqua(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.darkAqua, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 40), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(0, 40), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.darkAqua); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 40)); + expect(widget.style?.minimumSize?.resolve({}), Size(0, 40)); }); - testWidgets( - 'renders smallTransparent button ' + testWidgets('renders smallTransparent button ' 'when `AppButton.smallTransparent()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.smallTransparent( - child: buttonText, - onPressed: () {}, - ), + AppButton.smallTransparent(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.transparent, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 40), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(0, 40), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.transparent); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 40)); + expect(widget.style?.minimumSize?.resolve({}), Size(0, 40)); }); - testWidgets( - 'renders smallOutlineTransparent button ' + testWidgets('renders smallOutlineTransparent button ' 'when `AppButton.smallOutlineTransparent()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.smallOutlineTransparent( - child: buttonText, - onPressed: () {}, - ), + AppButton.smallOutlineTransparent(child: buttonText, onPressed: () {}), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.transparent, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 40), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(0, 40), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.transparent); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 40)); + expect(widget.style?.minimumSize?.resolve({}), Size(0, 40)); }); - testWidgets( - 'renders transparentDarkAqua button ' + testWidgets('renders transparentDarkAqua button ' 'when `AppButton.transparentDarkAqua()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.transparentDarkAqua( - onPressed: () {}, - child: buttonText, - ), + AppButton.transparentDarkAqua(onPressed: () {}, child: buttonText), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.transparent, - ); - expect( - widget.style?.foregroundColor?.resolve({}), - AppColors.darkAqua, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.transparent); + expect(widget.style?.foregroundColor?.resolve({}), AppColors.darkAqua); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders transparentWhite button ' + testWidgets('renders transparentWhite button ' 'when `AppButton.transparentWhite()` called', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.transparentWhite( - onPressed: () {}, - child: buttonText, - ), + AppButton.transparentWhite(onPressed: () {}, child: buttonText), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.transparent, - ); - expect( - widget.style?.foregroundColor?.resolve({}), - AppColors.white, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.transparent); + expect(widget.style?.foregroundColor?.resolve({}), AppColors.white); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'renders disabled transparentWhite button ' + testWidgets('renders disabled transparentWhite button ' 'when `AppButton.transparentWhite()` called ' 'with onPressed equal to null', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.transparentWhite( - onPressed: null, - child: buttonText, - ), + AppButton.transparentWhite(onPressed: null, child: buttonText), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.transparent, - ); - expect( - widget.style?.foregroundColor?.resolve({}), - AppColors.white, - ); - expect( - widget.style?.textStyle?.resolve({}), - buttonTextTheme, - ); - expect( - widget.style?.maximumSize?.resolve({}), - Size(double.infinity, 56), - ); - expect( - widget.style?.minimumSize?.resolve({}), - Size(double.infinity, 56), - ); + expect(widget.style?.backgroundColor?.resolve({}), AppColors.transparent); + expect(widget.style?.foregroundColor?.resolve({}), AppColors.white); + expect(widget.style?.textStyle?.resolve({}), buttonTextTheme); + expect(widget.style?.maximumSize?.resolve({}), Size(double.infinity, 56)); + expect(widget.style?.minimumSize?.resolve({}), Size(double.infinity, 56)); }); - testWidgets( - 'changes background color to AppColors.black.withOpacity(.12) ' + testWidgets('changes background color to black with 0.12 alpha ' 'when `onPressed` is null', (tester) async { final buttonText = Text('buttonText'); await tester.pumpApp( - AppButton.smallOutlineTransparent( - child: buttonText, - ), + AppButton.smallOutlineTransparent(child: buttonText), theme: theme, ); final finder = find.byType(ElevatedButton); final widget = tester.widget(finder) as ElevatedButton; - expect( - widget.style?.backgroundColor?.resolve({}), - AppColors.black.withOpacity(.12), - ); + final actual = widget.style?.backgroundColor?.resolve({}); + + expect(actual?.r, 0.0); + expect(actual?.g, 0.0); + expect(actual?.b, 0.0); + expect(actual?.a.toStringAsFixed(2), '0.12'); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_email_text_field_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_email_text_field_test.dart index 421dc6d68..66be5f7e0 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_email_text_field_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_email_text_field_test.dart @@ -12,18 +12,14 @@ void main() { group('email', () { testWidgets('has keyboardType set to emailAddress', (tester) async { - await tester.pumpApp( - AppEmailTextField(), - ); + await tester.pumpApp(AppEmailTextField()); final field = tester.widget(find.byType(AppTextField)); expect(field.keyboardType, TextInputType.emailAddress); }); testWidgets('has autocorrect set to false', (tester) async { - await tester.pumpApp( - AppEmailTextField(), - ); + await tester.pumpApp(AppEmailTextField()); final field = tester.widget(find.byType(AppTextField)); expect(field.autocorrect, false); @@ -32,11 +28,7 @@ void main() { group('renders', () { testWidgets('hint text', (tester) async { - await tester.pumpApp( - AppEmailTextField( - hintText: hintText, - ), - ); + await tester.pumpApp(AppEmailTextField(hintText: hintText)); expect(find.text(hintText), findsOneWidget); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_switch_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_switch_test.dart index ad0260ccf..d2532fe45 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_switch_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_switch_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: prefer_const_constructors - import 'package:app_ui/app_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -8,45 +6,30 @@ import '../helpers/helpers.dart'; void main() { group('AppSwitch', () { - testWidgets( - 'renders onText ' + testWidgets('renders onText ' 'when enabled', (tester) async { await tester.pumpApp( - AppSwitch( - value: true, - onText: 'On', - onChanged: (_) {}, - ), + AppSwitch(value: true, onText: 'On', onChanged: (_) {}), ); expect(find.text('On'), findsOneWidget); }); - testWidgets( - 'renders offText ' + testWidgets('renders offText ' 'when disabled', (tester) async { await tester.pumpApp( - AppSwitch( - value: false, - offText: 'Off', - onChanged: (_) {}, - ), + AppSwitch(value: false, offText: 'Off', onChanged: (_) {}), ); expect(find.text('Off'), findsOneWidget); }); testWidgets('renders Switch', (tester) async { - await tester.pumpApp( - AppSwitch( - value: true, - onChanged: (_) {}, - ), - ); + await tester.pumpApp(AppSwitch(value: true, onChanged: (_) {})); expect( find.byWidgetPredicate( - (widget) => widget is Switch && widget.value == true, + (widget) => widget is Switch && widget.value, ), findsOneWidget, ); @@ -55,10 +38,7 @@ void main() { testWidgets('calls onChanged when tapped', (tester) async { var tapped = false; await tester.pumpApp( - AppSwitch( - value: true, - onChanged: (_) => tapped = true, - ), + AppSwitch(value: true, onChanged: (_) => tapped = true), ); await tester.tap(find.byType(Switch)); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_text_field_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_text_field_test.dart index 58d08c2a0..9dde28ef5 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_text_field_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/app_text_field_test.dart @@ -11,36 +11,26 @@ void main() { const hintText = 'Hint'; testWidgets('renders TextFormField', (tester) async { - await tester.pumpApp( - AppTextField(), - ); + await tester.pumpApp(AppTextField()); expect(find.byType(TextFormField), findsOneWidget); }); testWidgets('has autocorrect set to true', (tester) async { - await tester.pumpApp( - AppTextField(), - ); + await tester.pumpApp(AppTextField()); final field = tester.widget(find.byType(AppTextField)); expect(field.autocorrect, true); }); testWidgets('has hint text', (tester) async { - await tester.pumpApp( - AppTextField( - hintText: hintText, - ), - ); + await tester.pumpApp(AppTextField(hintText: hintText)); expect(find.text(hintText), findsOneWidget); }); testWidgets('calls onSubmitted when submitted', (tester) async { var onSubmittedCalled = false; await tester.pumpApp( - AppTextField( - onSubmitted: (_) => onSubmittedCalled = true, - ), + AppTextField(onSubmitted: (_) => onSubmittedCalled = true), ); await tester.showKeyboard(find.byType(TextField)); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/content_theme_override_builder_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/content_theme_override_builder_test.dart index 6c74e7ffd..1a4e32217 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/content_theme_override_builder_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/content_theme_override_builder_test.dart @@ -8,8 +8,9 @@ void main() { group('ContentThemeOverrideBuilder', () { final theme = const AppTheme().themeData; - testWidgets('overrides the text theme to AppTheme.contentTextTheme', - (tester) async { + testWidgets('overrides the text theme to AppTheme.contentTextTheme', ( + tester, + ) async { late BuildContext capturedContext; await tester.pumpApp( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/show_app_modal_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/show_app_modal_test.dart index dbbba72a8..a9a844c64 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/show_app_modal_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/src/widgets/show_app_modal_test.dart @@ -15,15 +15,15 @@ void main() { builder: (context) => ElevatedButton( onPressed: () => showAppModal( context: context, - builder: (context) => Container( - key: modalKey, - ), + builder: (context) => Container(key: modalKey), ), style: ElevatedButton.styleFrom( backgroundColor: AppColors.red, padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20), - textStyle: - const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + textStyle: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), ), child: const Text('Tap'), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/theme/app_theme_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/theme/app_theme_test.dart index 89094eb2b..307d48356 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/theme/app_theme_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/app_ui/test/theme/app_theme_test.dart @@ -7,10 +7,7 @@ void main() { group('themeData', () { group('color', () { test('primary is blue', () { - expect( - const AppTheme().themeData.primaryColor, - AppColors.blue, - ); + expect(const AppTheme().themeData.primaryColor, AppColors.blue); }); test('secondary is AppColors.secondary', () { @@ -23,10 +20,7 @@ void main() { group('divider', () { test('horizontal padding is AppSpacing.lg', () { - expect( - const AppTheme().themeData.dividerTheme.indent, - AppSpacing.lg, - ); + expect(const AppTheme().themeData.dividerTheme.indent, AppSpacing.lg); expect( const AppTheme().themeData.dividerTheme.endIndent, AppSpacing.lg, @@ -34,10 +28,7 @@ void main() { }); test('space is AppSpacing.lg', () { - expect( - const AppTheme().themeData.dividerTheme.space, - AppSpacing.lg, - ); + expect(const AppTheme().themeData.dividerTheme.space, AppSpacing.lg); }); }); @@ -45,11 +36,9 @@ void main() { group('thumbColor', () { test('returns darkAqua when selected', () { expect( - const AppTheme() - .themeData - .switchTheme - .thumbColor - ?.resolve({WidgetState.selected}), + const AppTheme().themeData.switchTheme.thumbColor?.resolve({ + WidgetState.selected, + }), equals(AppColors.darkAqua), ); }); @@ -65,11 +54,9 @@ void main() { group('trackColor', () { test('returns primaryContainer when selected', () { expect( - const AppTheme() - .themeData - .switchTheme - .trackColor - ?.resolve({WidgetState.selected}), + const AppTheme().themeData.switchTheme.trackColor?.resolve({ + WidgetState.selected, + }), equals(AppColors.primaryContainer), ); }); @@ -85,22 +72,18 @@ void main() { group('trackOutlineColor', () { test('returns primaryContainer when selected', () { expect( - const AppTheme() - .themeData - .switchTheme - .trackOutlineColor - ?.resolve({WidgetState.selected}), + const AppTheme().themeData.switchTheme.trackOutlineColor?.resolve( + {WidgetState.selected}, + ), equals(AppColors.primaryContainer), ); }); test('returns grey when not selected', () { expect( - const AppTheme() - .themeData - .switchTheme - .trackOutlineColor - ?.resolve({}), + const AppTheme().themeData.switchTheme.trackOutlineColor?.resolve( + {}, + ), equals(AppColors.grey), ); }); @@ -132,10 +115,7 @@ void main() { group('themeData', () { group('color', () { test('primary is blue', () { - expect( - const AppDarkTheme().themeData.primaryColor, - AppColors.blue, - ); + expect(const AppDarkTheme().themeData.primaryColor, AppColors.blue); }); test('secondary is AppColors.secondary', () { @@ -155,10 +135,7 @@ void main() { group('divider', () { test('horizontal padding is AppSpacing.lg', () { - expect( - const AppTheme().themeData.dividerTheme.indent, - AppSpacing.lg, - ); + expect(const AppTheme().themeData.dividerTheme.indent, AppSpacing.lg); expect( const AppTheme().themeData.dividerTheme.endIndent, AppSpacing.lg, @@ -166,10 +143,7 @@ void main() { }); test('space is AppSpacing.lg', () { - expect( - const AppTheme().themeData.dividerTheme.space, - AppSpacing.lg, - ); + expect(const AppTheme().themeData.dividerTheme.space, AppSpacing.lg); }); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/lib/src/article_repository.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/lib/src/article_repository.dart index d3d50b757..e2486ed22 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/lib/src/article_repository.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/lib/src/article_repository.dart @@ -106,8 +106,8 @@ class ArticleRepository { const ArticleRepository({ required {{project_name.pascalCase()}}ApiClient apiClient, required ArticleStorage storage, - }) : _apiClient = apiClient, - _storage = storage; + }) : _apiClient = apiClient, + _storage = storage; final {{project_name.pascalCase()}}ApiClient _apiClient; final ArticleStorage _storage; @@ -195,10 +195,7 @@ class ArticleRepository { _storage.setArticleViewsResetDate(clock.now()), ]); } catch (error, stackTrace) { - Error.throwWithStackTrace( - ResetArticleViewsFailure(error), - stackTrace, - ); + Error.throwWithStackTrace(ResetArticleViewsFailure(error), stackTrace); } } @@ -213,10 +210,7 @@ class ArticleRepository { ]); return ArticleViews(views, resetAt); } catch (error, stackTrace) { - Error.throwWithStackTrace( - FetchArticleViewsFailure(error), - stackTrace, - ); + Error.throwWithStackTrace(FetchArticleViewsFailure(error), stackTrace); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/lib/src/article_storage.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/lib/src/article_storage.dart index aa447a70c..185654345 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/lib/src/article_storage.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/lib/src/article_storage.dart @@ -17,48 +17,49 @@ abstract class ArticleStorageKeys { /// {@endtemplate} class ArticleStorage { /// {@macro article_storage} - const ArticleStorage({ - required Storage storage, - }) : _storage = storage; + const ArticleStorage({required Storage storage}) : _storage = storage; final Storage _storage; /// Sets the number of article views in Storage. Future setArticleViews(int views) => _storage.write( - key: ArticleStorageKeys.articleViews, - value: views.toString(), - ); + key: ArticleStorageKeys.articleViews, + value: views.toString(), + ); /// Fetches the number of article views from Storage. Future fetchArticleViews() async { - final articleViews = - await _storage.read(key: ArticleStorageKeys.articleViews); + final articleViews = await _storage.read( + key: ArticleStorageKeys.articleViews, + ); return articleViews != null ? int.parse(articleViews) : 0; } /// Sets the reset date of the number of article views in Storage. Future setArticleViewsResetDate(DateTime date) => _storage.write( - key: ArticleStorageKeys.articleViewsResetAt, - value: date.toIso8601String(), - ); + key: ArticleStorageKeys.articleViewsResetAt, + value: date.toIso8601String(), + ); /// Fetches the reset date of the number of article views from Storage. Future fetchArticleViewsResetDate() async { - final resetDate = - await _storage.read(key: ArticleStorageKeys.articleViewsResetAt); + final resetDate = await _storage.read( + key: ArticleStorageKeys.articleViewsResetAt, + ); return resetDate != null ? DateTime.parse(resetDate) : null; } /// Sets the number of total article views. Future setTotalArticleViews(int count) => _storage.write( - key: ArticleStorageKeys.totalArticleViews, - value: count.toString(), - ); + key: ArticleStorageKeys.totalArticleViews, + value: count.toString(), + ); /// Fetches the number of total article views value from storage. Future fetchTotalArticleViews() async { - final count = - await _storage.read(key: ArticleStorageKeys.totalArticleViews); + final count = await _storage.read( + key: ArticleStorageKeys.totalArticleViews, + ); return int.tryParse(count ?? '') ?? 0; } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/pubspec.yaml index e1e6e6ad3..223c8b593 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/pubspec.yaml @@ -4,10 +4,10 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: - clock: ^1.1.0 + clock: ^1.1.1 equatable: ^2.0.3 {{project_name.snakeCase()}}_api: path: ../../api @@ -18,4 +18,4 @@ dev_dependencies: coverage: ^1.3.2 mocktail: ^1.0.2 test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/test/src/article_repository_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/test/src/article_repository_test.dart index fafd2f7f1..8da3a6e97 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/test/src/article_repository_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/test/src/article_repository_test.dart @@ -19,8 +19,9 @@ void main() { apiClient = Mock{{project_name.pascalCase()}}ApiClient(); storage = MockArticleStorage(); when(() => storage.setArticleViews(any())).thenAnswer((_) async {}); - when(() => storage.setArticleViewsResetDate(any())) - .thenAnswer((_) async {}); + when( + () => storage.setArticleViewsResetDate(any()), + ).thenAnswer((_) async {}); articleRepository = ArticleRepository( apiClient: apiClient, @@ -29,8 +30,7 @@ void main() { }); group('getArticle', () { - test( - 'returns ArticleResponse ' + test('returns ArticleResponse ' 'from ApiClient.getArticle', () { const content = [ TextCaptionBlock(text: 'text', color: TextCaptionColor.normal), @@ -56,25 +56,16 @@ void main() { ).thenAnswer((_) async => articleResponse); expect( - articleRepository.getArticle( - id: 'id', - offset: 10, - limit: 20, - ), + articleRepository.getArticle(id: 'id', offset: 10, limit: 20), completion(equals(articleResponse)), ); verify( - () => apiClient.getArticle( - id: 'id', - offset: 10, - limit: 20, - ), + () => apiClient.getArticle(id: 'id', offset: 10, limit: 20), ).called(1); }); - test( - 'throws GetArticleFailure ' + test('throws GetArticleFailure ' 'if ApiClient.getArticle fails', () async { when( () => apiClient.getArticle( @@ -92,8 +83,7 @@ void main() { }); group('getRelatedArticles', () { - test( - 'returns RelatedArticlesResponse ' + test('returns RelatedArticlesResponse ' 'from ApiClient.getRelatedArticles', () async { const relatedArticlesResponse = RelatedArticlesResponse( relatedArticles: [ @@ -104,9 +94,7 @@ void main() { ); when( - () => apiClient.getRelatedArticles( - id: any(named: 'id'), - ), + () => apiClient.getRelatedArticles(id: any(named: 'id')), ).thenAnswer((_) async => relatedArticlesResponse); final response = await articleRepository.getRelatedArticles(id: 'id'); @@ -114,13 +102,10 @@ void main() { expect(response, equals(relatedArticlesResponse)); }); - test( - 'throws GetRelatedArticlesFailure ' + test('throws GetRelatedArticlesFailure ' 'if ApiClient.getRelatedArticles fails', () async { when( - () => apiClient.getRelatedArticles( - id: any(named: 'id'), - ), + () => apiClient.getRelatedArticles(id: any(named: 'id')), ).thenThrow(Exception()); expect( @@ -131,22 +116,22 @@ void main() { }); group('incrementArticleViews', () { - test( - 'calls ArticleStorage.setArticleViews ' + test('calls ArticleStorage.setArticleViews ' 'with current article views increased by 1', () async { const currentArticleViews = 3; - when(storage.fetchArticleViews) - .thenAnswer((_) async => currentArticleViews); + when( + storage.fetchArticleViews, + ).thenAnswer((_) async => currentArticleViews); await articleRepository.incrementArticleViews(); verify(storage.fetchArticleViews).called(1); - verify(() => storage.setArticleViews(currentArticleViews + 1)) - .called(1); + verify( + () => storage.setArticleViews(currentArticleViews + 1), + ).called(1); }); - test( - 'throws an IncrementArticleViewsFailure ' + test('throws an IncrementArticleViewsFailure ' 'when incrementing article views fails', () async { when(() => storage.setArticleViews(any())).thenThrow(Exception()); @@ -158,22 +143,22 @@ void main() { }); group('decrementArticleViews', () { - test( - 'calls ArticleStorage.setArticleViews ' + test('calls ArticleStorage.setArticleViews ' 'with current article views decreased by 1', () async { const currentArticleViews = 3; - when(storage.fetchArticleViews) - .thenAnswer((_) async => currentArticleViews); + when( + storage.fetchArticleViews, + ).thenAnswer((_) async => currentArticleViews); await articleRepository.decrementArticleViews(); verify(storage.fetchArticleViews).called(1); - verify(() => storage.setArticleViews(currentArticleViews - 1)) - .called(1); + verify( + () => storage.setArticleViews(currentArticleViews - 1), + ).called(1); }); - test( - 'throws a DecrementArticleViewsFailure ' + test('throws a DecrementArticleViewsFailure ' 'when decrementing article views fails', () async { when(() => storage.setArticleViews(any())).thenThrow(Exception()); @@ -185,15 +170,13 @@ void main() { }); group('resetArticleViews', () { - test( - 'calls ArticleStorage.setArticleViews ' + test('calls ArticleStorage.setArticleViews ' 'with 0 article views', () async { await articleRepository.resetArticleViews(); verify(() => storage.setArticleViews(0)).called(1); }); - test( - 'calls ArticleStorage.setArticleViewsResetDate ' + test('calls ArticleStorage.setArticleViewsResetDate ' 'with current date', () async { final now = DateTime(2022, 6, 7); await withClock(Clock.fixed(now), () async { @@ -202,8 +185,7 @@ void main() { }); }); - test( - 'throws a ResetArticleViewsFailure ' + test('throws a ResetArticleViewsFailure ' 'when resetting article views fails', () async { when(() => storage.setArticleViews(any())).thenThrow(Exception()); @@ -215,30 +197,29 @@ void main() { }); group('fetchArticleViews', () { - test( - 'returns the number of article views ' + test('returns the number of article views ' 'from ArticleStorage.fetchArticleViews', () async { const currentArticleViews = 3; - when(storage.fetchArticleViews) - .thenAnswer((_) async => currentArticleViews); + when( + storage.fetchArticleViews, + ).thenAnswer((_) async => currentArticleViews); when(storage.fetchArticleViewsResetDate).thenAnswer((_) async => null); final result = await articleRepository.fetchArticleViews(); expect(result.views, equals(currentArticleViews)); }); - test( - 'returns the reset date of the number of article views ' + test('returns the reset date of the number of article views ' 'from ArticleStorage.fetchArticleViewsResetDate', () async { final resetDate = DateTime(2022, 6, 7); when(storage.fetchArticleViews).thenAnswer((_) async => 0); - when(storage.fetchArticleViewsResetDate) - .thenAnswer((_) async => resetDate); + when( + storage.fetchArticleViewsResetDate, + ).thenAnswer((_) async => resetDate); final result = await articleRepository.fetchArticleViews(); expect(result.resetAt, equals(resetDate)); }); - test( - 'throws a FetchArticleViewsFailure ' + test('throws a FetchArticleViewsFailure ' 'when fetching article views fails', () async { when(storage.fetchArticleViews).thenThrow(Exception()); @@ -290,14 +271,15 @@ void main() { }); group('incrementTotalArticleViews', () { - test( - 'calls UserStorage.setTotalArticleViews ' + test('calls UserStorage.setTotalArticleViews ' 'with current total article views increased by 1', () async { const totalArticleViews = 3; - when(storage.fetchTotalArticleViews) - .thenAnswer((_) async => totalArticleViews); - when(() => storage.setTotalArticleViews(any())) - .thenAnswer((_) async {}); + when( + storage.fetchTotalArticleViews, + ).thenAnswer((_) async => totalArticleViews); + when( + () => storage.setTotalArticleViews(any()), + ).thenAnswer((_) async {}); await articleRepository.incrementTotalArticleViews(); @@ -307,8 +289,7 @@ void main() { ).called(1); }); - test( - 'throws an IncrementTotalArticleViewsFailure ' + test('throws an IncrementTotalArticleViewsFailure ' 'when incrementing total article views fails', () async { when(() => storage.setTotalArticleViews(any())).thenThrow(Exception()); @@ -320,18 +301,17 @@ void main() { }); group('fetchTotalArticleViews', () { - test( - 'returns the number of total article views ' + test('returns the number of total article views ' 'from UserStorage.fetchTotalArticleViews', () async { const currentArticleViews = 3; - when(storage.fetchTotalArticleViews) - .thenAnswer((_) async => currentArticleViews); + when( + storage.fetchTotalArticleViews, + ).thenAnswer((_) async => currentArticleViews); final result = await articleRepository.fetchTotalArticleViews(); expect(result, equals(currentArticleViews)); }); - test( - 'throws a FetchTotalArticleViewsFailure ' + test('throws a FetchTotalArticleViewsFailure ' 'when fetching total article views fails', () async { when(storage.fetchTotalArticleViews).thenThrow(Exception()); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/test/src/article_storage_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/test/src/article_storage_test.dart index f170d83a5..284a28f8f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/test/src/article_storage_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/article_repository/test/src/article_storage_test.dart @@ -41,13 +41,12 @@ void main() { () => storage.read(key: ArticleStorageKeys.articleViews), ).thenAnswer((_) async => '3'); - final result = - await ArticleStorage(storage: storage).fetchArticleViews(); + final result = await ArticleStorage( + storage: storage, + ).fetchArticleViews(); verify( - () => storage.read( - key: ArticleStorageKeys.articleViews, - ), + () => storage.read(key: ArticleStorageKeys.articleViews), ).called(1); expect(result, equals(3)); @@ -58,13 +57,12 @@ void main() { () => storage.read(key: ArticleStorageKeys.articleViews), ).thenAnswer((_) async => null); - final result = - await ArticleStorage(storage: storage).fetchArticleViews(); + final result = await ArticleStorage( + storage: storage, + ).fetchArticleViews(); verify( - () => storage.read( - key: ArticleStorageKeys.articleViews, - ), + () => storage.read(key: ArticleStorageKeys.articleViews), ).called(1); expect(result, isZero); @@ -94,13 +92,12 @@ void main() { () => storage.read(key: ArticleStorageKeys.articleViewsResetAt), ).thenAnswer((_) async => date.toIso8601String()); - final result = - await ArticleStorage(storage: storage).fetchArticleViewsResetDate(); + final result = await ArticleStorage( + storage: storage, + ).fetchArticleViewsResetDate(); verify( - () => storage.read( - key: ArticleStorageKeys.articleViewsResetAt, - ), + () => storage.read(key: ArticleStorageKeys.articleViewsResetAt), ).called(1); expect(result, equals(date)); @@ -111,13 +108,12 @@ void main() { () => storage.read(key: ArticleStorageKeys.articleViewsResetAt), ).thenAnswer((_) async => null); - final result = - await ArticleStorage(storage: storage).fetchArticleViewsResetDate(); + final result = await ArticleStorage( + storage: storage, + ).fetchArticleViewsResetDate(); verify( - () => storage.read( - key: ArticleStorageKeys.articleViewsResetAt, - ), + () => storage.read(key: ArticleStorageKeys.articleViewsResetAt), ).called(1); expect(result, isNull); @@ -145,13 +141,12 @@ void main() { () => storage.read(key: ArticleStorageKeys.totalArticleViews), ).thenAnswer((_) async => '3'); - final result = - await ArticleStorage(storage: storage).fetchTotalArticleViews(); + final result = await ArticleStorage( + storage: storage, + ).fetchTotalArticleViews(); verify( - () => storage.read( - key: ArticleStorageKeys.totalArticleViews, - ), + () => storage.read(key: ArticleStorageKeys.totalArticleViews), ).called(1); expect(result, equals(3)); @@ -162,13 +157,12 @@ void main() { () => storage.read(key: ArticleStorageKeys.totalArticleViews), ).thenAnswer((_) async => null); - final result = - await ArticleStorage(storage: storage).fetchTotalArticleViews(); + final result = await ArticleStorage( + storage: storage, + ).fetchTotalArticleViews(); verify( - () => storage.read( - key: ArticleStorageKeys.totalArticleViews, - ), + () => storage.read(key: ArticleStorageKeys.totalArticleViews), ).called(1); expect(result, isZero); @@ -179,13 +173,12 @@ void main() { () => storage.read(key: ArticleStorageKeys.totalArticleViews), ).thenAnswer((_) async => 'malformed'); - final result = - await ArticleStorage(storage: storage).fetchTotalArticleViews(); + final result = await ArticleStorage( + storage: storage, + ).fetchTotalArticleViews(); verify( - () => storage.read( - key: ArticleStorageKeys.totalArticleViews, - ), + () => storage.read(key: ArticleStorageKeys.totalArticleViews), ).called(1); expect(result, isZero); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/lib/src/authentication_client.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/lib/src/authentication_client.dart index e719f7f39..9f1bf31ec 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/lib/src/authentication_client.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/lib/src/authentication_client.dart @@ -151,9 +151,7 @@ abstract class AuthenticationClient { /// Checks if an incoming [emailLink] is a sign-in with email link. /// /// Throws a [IsLogInWithEmailLinkFailure] if an exception occurs. - bool isLogInWithEmailLink({ - required String emailLink, - }); + bool isLogInWithEmailLink({required String emailLink}); /// Signs in with the provided [email] and [emailLink]. /// diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/pubspec.yaml index a23403f5d..84d29fe1b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/pubspec.yaml @@ -3,11 +3,11 @@ description: An Authentication Client Interface publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: equatable: ^2.0.3 dev_dependencies: test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/test/src/authentication_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/test/src/authentication_client_test.dart index fcb12a8bf..76531a3ce 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/test/src/authentication_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/authentication_client/test/src/authentication_client_test.dart @@ -16,86 +16,50 @@ void main() { }); test('exports SendLoginEmailLinkFailure', () { - expect( - () => SendLoginEmailLinkFailure('oops'), - returnsNormally, - ); + expect(() => SendLoginEmailLinkFailure('oops'), returnsNormally); }); test('exports IsLogInWithEmailLinkFailure', () { - expect( - () => IsLogInWithEmailLinkFailure('oops'), - returnsNormally, - ); + expect(() => IsLogInWithEmailLinkFailure('oops'), returnsNormally); }); test('exports LogInWithEmailLinkFailure', () { - expect( - () => LogInWithEmailLinkFailure('oops'), - returnsNormally, - ); + expect(() => LogInWithEmailLinkFailure('oops'), returnsNormally); }); test('exports LogInWithAppleFailure', () { - expect( - () => LogInWithAppleFailure('oops'), - returnsNormally, - ); + expect(() => LogInWithAppleFailure('oops'), returnsNormally); }); test('exports LogInWithGoogleFailure', () { - expect( - () => LogInWithGoogleFailure('oops'), - returnsNormally, - ); + expect(() => LogInWithGoogleFailure('oops'), returnsNormally); }); test('exports LogInWithGoogleCanceled', () { - expect( - () => LogInWithGoogleCanceled('oops'), - returnsNormally, - ); + expect(() => LogInWithGoogleCanceled('oops'), returnsNormally); }); test('exports LogInWithFacebookFailure', () { - expect( - () => LogInWithFacebookFailure('oops'), - returnsNormally, - ); + expect(() => LogInWithFacebookFailure('oops'), returnsNormally); }); test('exports LogInWithFacebookCanceled', () { - expect( - () => LogInWithFacebookCanceled('oops'), - returnsNormally, - ); + expect(() => LogInWithFacebookCanceled('oops'), returnsNormally); }); test('exports LogInWithTwitterFailure', () { - expect( - () => LogInWithTwitterFailure('oops'), - returnsNormally, - ); + expect(() => LogInWithTwitterFailure('oops'), returnsNormally); }); test('exports LogInWithTwitterCanceled', () { - expect( - () => LogInWithTwitterCanceled('oops'), - returnsNormally, - ); + expect(() => LogInWithTwitterCanceled('oops'), returnsNormally); }); test('exports LogOutFailure', () { - expect( - () => LogOutFailure('oops'), - returnsNormally, - ); + expect(() => LogOutFailure('oops'), returnsNormally); }); test('exports DeleteAccountFailure', () { - expect( - () => DeleteAccountFailure('oops'), - returnsNormally, - ); + expect(() => DeleteAccountFailure('oops'), returnsNormally); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/lib/src/firebase_authentication_client.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/lib/src/firebase_authentication_client.dart index 2fceeda41..3f0a39dbc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/lib/src/firebase_authentication_client.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/lib/src/firebase_authentication_client.dart @@ -7,12 +7,13 @@ import 'package:token_storage/token_storage.dart'; import 'package:twitter_login/twitter_login.dart'; /// Signature for [SignInWithApple.getAppleIDCredential]. -typedef GetAppleCredentials = Future Function({ - required List scopes, - WebAuthenticationOptions webAuthenticationOptions, - String nonce, - String state, -}); +typedef GetAppleCredentials = + Future Function({ + required List scopes, + WebAuthenticationOptions webAuthenticationOptions, + String nonce, + String state, + }); /// {@template firebase_authentication_client} /// A Firebase implementation of the [AuthenticationClient] interface. @@ -26,18 +27,19 @@ class FirebaseAuthenticationClient implements AuthenticationClient { GetAppleCredentials? getAppleCredentials, FacebookAuth? facebookAuth, TwitterLogin? twitterLogin, - }) : _tokenStorage = tokenStorage, - _firebaseAuth = firebaseAuth ?? firebase_auth.FirebaseAuth.instance, - _googleSignIn = googleSignIn ?? GoogleSignIn.standard(), - _getAppleCredentials = - getAppleCredentials ?? SignInWithApple.getAppleIDCredential, - _facebookAuth = facebookAuth ?? FacebookAuth.instance, - _twitterLogin = twitterLogin ?? - TwitterLogin( - apiKey: const String.fromEnvironment('TWITTER_API_KEY'), - apiSecretKey: const String.fromEnvironment('TWITTER_API_SECRET'), - redirectURI: const String.fromEnvironment('TWITTER_REDIRECT_URI'), - ) { + }) : _tokenStorage = tokenStorage, + _firebaseAuth = firebaseAuth ?? firebase_auth.FirebaseAuth.instance, + _googleSignIn = googleSignIn ?? GoogleSignIn.standard(), + _getAppleCredentials = + getAppleCredentials ?? SignInWithApple.getAppleIDCredential, + _facebookAuth = facebookAuth ?? FacebookAuth.instance, + _twitterLogin = + twitterLogin ?? + TwitterLogin( + apiKey: const String.fromEnvironment('TWITTER_API_KEY'), + apiSecretKey: const String.fromEnvironment('TWITTER_API_SECRET'), + redirectURI: const String.fromEnvironment('TWITTER_REDIRECT_URI'), + ) { user.listen(_onUserChanged); } @@ -123,9 +125,7 @@ class FirebaseAuthenticationClient implements AuthenticationClient { Exception('Sign in with Facebook canceled'), ); } else if (loginResult.status == LoginStatus.failed) { - throw LogInWithFacebookFailure( - Exception(loginResult.message), - ); + throw LogInWithFacebookFailure(Exception(loginResult.message)); } final accessToken = loginResult.accessToken?.tokenString; @@ -137,8 +137,9 @@ class FirebaseAuthenticationClient implements AuthenticationClient { ); } - final credential = - firebase_auth.FacebookAuthProvider.credential(accessToken); + final credential = firebase_auth.FacebookAuthProvider.credential( + accessToken, + ); await _firebaseAuth.signInWithCredential(credential); } on LogInWithFacebookCanceled { @@ -161,9 +162,7 @@ class FirebaseAuthenticationClient implements AuthenticationClient { Exception('Sign in with Twitter canceled'), ); } else if (loginResult.status == TwitterLoginStatus.error) { - throw LogInWithTwitterFailure( - Exception(loginResult.errorMessage), - ); + throw LogInWithTwitterFailure(Exception(loginResult.errorMessage)); } final authToken = loginResult.authToken; @@ -262,10 +261,7 @@ class FirebaseAuthenticationClient implements AuthenticationClient { @override Future logOut() async { try { - await Future.wait([ - _firebaseAuth.signOut(), - _googleSignIn.signOut(), - ]); + await Future.wait([_firebaseAuth.signOut(), _googleSignIn.signOut()]); } catch (error, stackTrace) { Error.throwWithStackTrace(LogOutFailure(error), stackTrace); } @@ -277,9 +273,7 @@ class FirebaseAuthenticationClient implements AuthenticationClient { try { final user = _firebaseAuth.currentUser; if (user == null) { - throw DeleteAccountFailure( - Exception('User is not authenticated'), - ); + throw DeleteAccountFailure(Exception('User is not authenticated')); } await user.delete(); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/pubspec.yaml index 25fb7291d..c2c2614f6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/pubspec.yaml @@ -4,15 +4,15 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: authentication_client: path: ../authentication_client - firebase_auth: ^5.2.1 - firebase_auth_platform_interface: ^7.4.5 - firebase_core: ^3.4.1 - firebase_core_platform_interface: ^5.2.1 + firebase_auth: ^5.3.4 + firebase_auth_platform_interface: ^7.7.1 + firebase_core: ^3.15.0 + firebase_core_platform_interface: ^5.3.1 flutter: sdk: flutter flutter_facebook_auth: ^7.1.1 @@ -27,4 +27,4 @@ dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/test/firebase_authentication_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/test/firebase_authentication_client_test.dart index e7c524264..6c7baf73f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/test/firebase_authentication_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/firebase_authentication_client/test/firebase_authentication_client_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: must_be_immutable - import 'dart:async'; import 'package:authentication_client/authentication_client.dart'; @@ -120,22 +118,24 @@ void main() { googleSignIn = MockGoogleSignIn(); authorizationCredentialAppleID = MockAuthorizationCredentialAppleID(); getAppleCredentialsCalls = >[]; - getAppleCredentials = ({ - List scopes = const [], - WebAuthenticationOptions? webAuthenticationOptions, - String? nonce, - String? state, - }) async { - getAppleCredentialsCalls.add(scopes); - return authorizationCredentialAppleID; - }; + getAppleCredentials = + ({ + List scopes = const [], + WebAuthenticationOptions? webAuthenticationOptions, + String? nonce, + String? state, + }) async { + getAppleCredentialsCalls.add(scopes); + return authorizationCredentialAppleID; + }; facebookAuth = MockFacebookAuth(); twitterLogin = MockTwitterLogin(); authStateChangesController = StreamController.broadcast(); - when(firebaseAuth.authStateChanges) - .thenAnswer((_) => authStateChangesController.stream); + when( + firebaseAuth.authStateChanges, + ).thenAnswer((_) => authStateChangesController.stream); when(() => tokenStorage.saveToken(any())).thenAnswer((_) async {}); when(tokenStorage.clearToken).thenAnswer((_) async {}); @@ -150,34 +150,35 @@ void main() { ); }); - testWidgets( - 'creates FirebaseAuth instance internally when not injected', - (tester) async { - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( - MethodChannelFirebaseAuth.channel, - (call) async { - if (call.method == 'Auth#registerIdTokenListener' || - call.method == 'Auth#registerAuthStateListener') { - return 'mockAuthChannel'; - } - return null; - }, - ); + testWidgets('creates FirebaseAuth instance internally when not injected', ( + tester, + ) async { + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + MethodChannelFirebaseAuth.channel, + (call) async { + if (call.method == 'Auth#registerIdTokenListener' || + call.method == 'Auth#registerAuthStateListener') { + return 'mockAuthChannel'; + } + return null; + }, + ); - expect( - () => FirebaseAuthenticationClient(tokenStorage: tokenStorage), - isNot(throwsException), - ); - }, - ); + expect( + () => FirebaseAuthenticationClient(tokenStorage: tokenStorage), + isNot(throwsException), + ); + }); group('logInWithApple', () { setUp(() { - when(() => firebaseAuth.signInWithCredential(any())) - .thenAnswer((_) => Future.value(MockUserCredential())); + when( + () => firebaseAuth.signInWithCredential(any()), + ).thenAnswer((_) => Future.value(MockUserCredential())); when(() => authorizationCredentialAppleID.identityToken).thenReturn(''); - when(() => authorizationCredentialAppleID.authorizationCode) - .thenReturn(''); + when( + () => authorizationCredentialAppleID.authorizationCode, + ).thenReturn(''); }); test('calls getAppleCredentials with correct scopes', () async { @@ -186,24 +187,27 @@ void main() { [ AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName, - ] + ], ]); }); test('calls signInWithCredential with correct credential', () async { const identityToken = 'identity-token'; const accessToken = 'access-token'; - when(() => authorizationCredentialAppleID.identityToken) - .thenReturn(identityToken); - when(() => authorizationCredentialAppleID.authorizationCode) - .thenReturn(accessToken); + when( + () => authorizationCredentialAppleID.identityToken, + ).thenReturn(identityToken); + when( + () => authorizationCredentialAppleID.authorizationCode, + ).thenReturn(accessToken); await firebaseAuthenticationClient.logInWithApple(); verify(() => firebaseAuth.signInWithCredential(any())).called(1); }); test('throws LogInWithAppleFailure when exception occurs', () async { - when(() => firebaseAuth.signInWithCredential(any())) - .thenThrow(Exception()); + when( + () => firebaseAuth.signInWithCredential(any()), + ).thenThrow(Exception()); expect( () => firebaseAuthenticationClient.logInWithApple(), throwsA(isA()), @@ -218,15 +222,19 @@ void main() { setUp(() { final googleSignInAuthentication = MockGoogleSignInAuthentication(); final googleSignInAccount = MockGoogleSignInAccount(); - when(() => googleSignInAuthentication.accessToken) - .thenReturn(accessToken); + when( + () => googleSignInAuthentication.accessToken, + ).thenReturn(accessToken); when(() => googleSignInAuthentication.idToken).thenReturn(idToken); - when(() => googleSignInAccount.authentication) - .thenAnswer((_) async => googleSignInAuthentication); - when(() => googleSignIn.signIn()) - .thenAnswer((_) async => googleSignInAccount); - when(() => firebaseAuth.signInWithCredential(any())) - .thenAnswer((_) => Future.value(MockUserCredential())); + when( + () => googleSignInAccount.authentication, + ).thenAnswer((_) async => googleSignInAuthentication); + when( + () => googleSignIn.signIn(), + ).thenAnswer((_) async => googleSignInAccount); + when( + () => firebaseAuth.signInWithCredential(any()), + ).thenAnswer((_) => Future.value(MockUserCredential())); }); test('calls signIn authentication, and signInWithCredential', () async { @@ -240,8 +248,9 @@ void main() { }); test('throws LogInWithGoogleFailure when exception occurs', () async { - when(() => firebaseAuth.signInWithCredential(any())) - .thenThrow(Exception()); + when( + () => firebaseAuth.signInWithCredential(any()), + ).thenThrow(Exception()); expect( firebaseAuthenticationClient.logInWithGoogle(), throwsA(isA()), @@ -268,11 +277,13 @@ void main() { when(() => accessTokenResult.tokenString).thenReturn(accessToken); when(() => loginResult.accessToken).thenReturn(accessTokenResult); - when(() => loginResult.status) - .thenReturn(facebook_auth.LoginStatus.success); + when( + () => loginResult.status, + ).thenReturn(facebook_auth.LoginStatus.success); when(() => facebookAuth.login()).thenAnswer((_) async => loginResult); - when(() => firebaseAuth.signInWithCredential(any())) - .thenAnswer((_) => Future.value(MockUserCredential())); + when( + () => firebaseAuth.signInWithCredential(any()), + ).thenAnswer((_) => Future.value(MockUserCredential())); }); test('calls login authentication and signInWithCredential', () async { @@ -285,30 +296,29 @@ void main() { expect(firebaseAuthenticationClient.logInWithFacebook(), completes); }); - test( - 'throws LogInWithFacebookFailure ' + test('throws LogInWithFacebookFailure ' 'when signInWithCredential throws', () async { - when(() => firebaseAuth.signInWithCredential(any())) - .thenThrow(Exception()); + when( + () => firebaseAuth.signInWithCredential(any()), + ).thenThrow(Exception()); expect( firebaseAuthenticationClient.logInWithFacebook(), throwsA(isA()), ); }); - test( - 'throws LogInWithFacebookFailure ' + test('throws LogInWithFacebookFailure ' 'when login result status is failed', () async { - when(() => loginResult.status) - .thenReturn(facebook_auth.LoginStatus.failed); + when( + () => loginResult.status, + ).thenReturn(facebook_auth.LoginStatus.failed); expect( firebaseAuthenticationClient.logInWithFacebook(), throwsA(isA()), ); }); - test( - 'throws LogInWithFacebookFailure ' + test('throws LogInWithFacebookFailure ' 'when login result access token is empty', () async { when(() => loginResult.accessToken).thenReturn(null); expect( @@ -317,11 +327,11 @@ void main() { ); }); - test( - 'throws LogInWithFacebookCanceled ' + test('throws LogInWithFacebookCanceled ' 'when login result status is cancelled', () async { - when(() => loginResult.status) - .thenReturn(facebook_auth.LoginStatus.cancelled); + when( + () => loginResult.status, + ).thenReturn(facebook_auth.LoginStatus.cancelled); expect( firebaseAuthenticationClient.logInWithFacebook(), throwsA(isA()), @@ -339,11 +349,13 @@ void main() { when(() => loginResult.authToken).thenReturn(accessToken); when(() => loginResult.authTokenSecret).thenReturn(secret); - when(() => loginResult.status) - .thenReturn(twitter_auth.TwitterLoginStatus.loggedIn); + when( + () => loginResult.status, + ).thenReturn(twitter_auth.TwitterLoginStatus.loggedIn); when(() => twitterLogin.loginV2()).thenAnswer((_) async => loginResult); - when(() => firebaseAuth.signInWithCredential(any())) - .thenAnswer((_) => Future.value(MockUserCredential())); + when( + () => firebaseAuth.signInWithCredential(any()), + ).thenAnswer((_) => Future.value(MockUserCredential())); }); test('calls loginV2 authentication and signInWithCredential', () async { @@ -356,30 +368,29 @@ void main() { expect(firebaseAuthenticationClient.logInWithTwitter(), completes); }); - test( - 'throws LogInWithTwitterFailure ' + test('throws LogInWithTwitterFailure ' 'when signInWithCredential throws', () async { - when(() => firebaseAuth.signInWithCredential(any())) - .thenThrow(Exception()); + when( + () => firebaseAuth.signInWithCredential(any()), + ).thenThrow(Exception()); expect( firebaseAuthenticationClient.logInWithTwitter(), throwsA(isA()), ); }); - test( - 'throws LogInWithTwitterFailure ' + test('throws LogInWithTwitterFailure ' 'when login result status is error', () async { - when(() => loginResult.status) - .thenReturn(twitter_auth.TwitterLoginStatus.error); + when( + () => loginResult.status, + ).thenReturn(twitter_auth.TwitterLoginStatus.error); expect( firebaseAuthenticationClient.logInWithTwitter(), throwsA(isA()), ); }); - test( - 'throws LogInWithTwitterFailure ' + test('throws LogInWithTwitterFailure ' 'when login result auth token is empty', () async { when(() => loginResult.authToken).thenReturn(null); expect( @@ -388,8 +399,7 @@ void main() { ); }); - test( - 'throws LogInWithTwitterFailure ' + test('throws LogInWithTwitterFailure ' 'when login result auth token secret is empty', () async { when(() => loginResult.authTokenSecret).thenReturn(null); expect( @@ -398,11 +408,11 @@ void main() { ); }); - test( - 'throws LogInWithTwitterCanceled ' + test('throws LogInWithTwitterCanceled ' 'when login result status is cancelledByUser', () async { - when(() => loginResult.status) - .thenReturn(twitter_auth.TwitterLoginStatus.cancelledByUser); + when( + () => loginResult.status, + ).thenReturn(twitter_auth.TwitterLoginStatus.cancelledByUser); expect( firebaseAuthenticationClient.logInWithTwitter(), throwsA(isA()), @@ -467,8 +477,7 @@ void main() { ); }); - test( - 'throws SendLoginEmailLinkFailure ' + test('throws SendLoginEmailLinkFailure ' 'when sendSignInLinkToEmail throws', () async { when( () => firebaseAuth.sendSignInLinkToEmail( @@ -494,12 +503,8 @@ void main() { }); test('calls isSignInWithEmailLink', () { - firebaseAuthenticationClient.isLogInWithEmailLink( - emailLink: emailLink, - ); - verify( - () => firebaseAuth.isSignInWithEmailLink(emailLink), - ).called(1); + firebaseAuthenticationClient.isLogInWithEmailLink(emailLink: emailLink); + verify(() => firebaseAuth.isSignInWithEmailLink(emailLink)).called(1); }); test('succeeds when isSignInWithEmailLink succeeds', () async { @@ -511,8 +516,7 @@ void main() { ); }); - test( - 'throws IsLogInWithEmailLinkFailure ' + test('throws IsLogInWithEmailLinkFailure ' 'when isSignInWithEmailLink throws', () async { when( () => firebaseAuth.isSignInWithEmailLink(any()), @@ -559,8 +563,7 @@ void main() { ); }); - test( - 'throws LogInWithEmailLinkFailure ' + test('throws LogInWithEmailLinkFailure ' 'when signInWithEmailLink throws', () async { when( () => firebaseAuth.signInWithEmailLink( @@ -632,17 +635,21 @@ void main() { const userId = 'mock-uid'; const email = 'mock-email'; const newUser = AuthenticationUser(id: userId, email: email); - const returningUser = - AuthenticationUser(id: userId, email: email, isNewUser: false); + const returningUser = AuthenticationUser( + id: userId, + email: email, + isNewUser: false, + ); test('emits anonymous user when firebase user is null', () async { - when(firebaseAuth.authStateChanges) - .thenAnswer((_) => Stream.value(null)); + when( + firebaseAuth.authStateChanges, + ).thenAnswer((_) => Stream.value(null)); await expectLater( firebaseAuthenticationClient.user, - emitsInOrder( - const [AuthenticationUser.anonymous], - ), + emitsInOrder(const [ + AuthenticationUser.anonymous, + ]), ); }); @@ -656,8 +663,9 @@ void main() { when(() => userMetadata.lastSignInTime).thenReturn(creationTime); when(() => firebaseUser.photoURL).thenReturn(null); when(() => firebaseUser.metadata).thenReturn(userMetadata); - when(firebaseAuth.authStateChanges) - .thenAnswer((_) => Stream.value(firebaseUser)); + when( + firebaseAuth.authStateChanges, + ).thenAnswer((_) => Stream.value(firebaseUser)); await expectLater( firebaseAuthenticationClient.user, emitsInOrder(const [newUser]), @@ -675,16 +683,16 @@ void main() { when(() => userMetadata.lastSignInTime).thenReturn(lastSignInTime); when(() => firebaseUser.photoURL).thenReturn(null); when(() => firebaseUser.metadata).thenReturn(userMetadata); - when(firebaseAuth.authStateChanges) - .thenAnswer((_) => Stream.value(firebaseUser)); + when( + firebaseAuth.authStateChanges, + ).thenAnswer((_) => Stream.value(firebaseUser)); await expectLater( firebaseAuthenticationClient.user, emitsInOrder(const [returningUser]), ); }); - test( - 'calls saveToken on TokenStorage ' + test('calls saveToken on TokenStorage ' 'when user changes to authenticated', () async { final firebaseUser = MockFirebaseUser(); final userMetadata = MockUserMetadata(); @@ -703,8 +711,7 @@ void main() { verifyNever(tokenStorage.clearToken); }); - test( - 'calls clearToken on TokenStorage ' + test('calls clearToken on TokenStorage ' 'when user changes to unauthenticated', () async { authStateChangesController.add(null); await Future.microtask(() {}); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/pubspec.yaml index 01dc994af..0f74b231d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/pubspec.yaml @@ -4,10 +4,10 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dev_dependencies: coverage: ^1.3.2 mocktail: ^1.0.2 test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/test/src/token_storage_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/test/src/token_storage_test.dart index a2745244b..f640b78b3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/test/src/token_storage_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/authentication_client/token_storage/test/src/token_storage_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: prefer_const_constructors - import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; import 'package:token_storage/token_storage.dart'; @@ -15,15 +13,13 @@ void main() { }); group('InMemoryTokenStorage', () { - test( - 'readToken returns null ' + test('readToken returns null ' 'when no token is saved', () async { final storage = InMemoryTokenStorage(); expect(await storage.readToken(), isNull); }); - test( - 'readToken returns token ' + test('readToken returns token ' 'when token is saved with saveToken', () async { const token = 'token'; final storage = InMemoryTokenStorage(); @@ -31,8 +27,7 @@ void main() { expect(await storage.readToken(), equals(token)); }); - test( - 'readToken returns updated token ' + test('readToken returns updated token ' 'when token is overridden with saveToken', () async { const token = 'token'; const updatedToken = 'updatedToken'; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/analysis_options.yaml index 799268d3e..08f89e709 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/analysis_options.yaml @@ -1 +1,5 @@ include: package:very_good_analysis/analysis_options.5.1.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart index 4da62b071..4cc9e967f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart @@ -25,10 +25,9 @@ class DeepLinkClientFailure with EquatableMixin implements Exception { /// {@endtemplate} class DeepLinkService { /// {@macro deep_link_service} - DeepLinkService({ - required DeepLinkClient deepLinkClient, - }) : _deepLinkClient = deepLinkClient, - _deepLinkSubject = BehaviorSubject() { + DeepLinkService({required DeepLinkClient deepLinkClient}) + : _deepLinkClient = deepLinkClient, + _deepLinkSubject = BehaviorSubject() { unawaited(_getInitialLink()); _deepLinkClient.deepLinkStream.listen(_onAppLink).onError(_handleError); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/pubspec.yaml index 5fc8421cc..94aa747bc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/pubspec.yaml @@ -3,7 +3,7 @@ description: A deep link client interface publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: equatable: ^2.0.5 @@ -12,4 +12,4 @@ dependencies: dev_dependencies: mocktail: ^1.0.4 test: ^1.25.8 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/test/deep_link_service_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/test/deep_link_service_test.dart index 30ab3c1c7..d6a69593c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/test/deep_link_service_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/deep_link_client/test/deep_link_service_test.dart @@ -15,8 +15,9 @@ void main() { setUp(() { deepLinkClient = MockDeepLinkClient(); onDeepLinkStreamController = StreamController(); - when(() => deepLinkClient.deepLinkStream) - .thenAnswer((_) => onDeepLinkStreamController.stream); + when( + () => deepLinkClient.deepLinkStream, + ).thenAnswer((_) => onDeepLinkStreamController.stream); }); tearDown(() { @@ -26,9 +27,9 @@ void main() { group('DeepLinkService', () { test('retrieves and publishes latest link if present', () { final expectedUri = Uri.https('ham.app.test', '/test/path'); - when(deepLinkClient.getInitialLink).thenAnswer( - (_) => Future.value(expectedUri), - ); + when( + deepLinkClient.getInitialLink, + ).thenAnswer((_) => Future.value(expectedUri)); final service = DeepLinkService(deepLinkClient: deepLinkClient); expect(service.deepLinkStream, emits(expectedUri)); @@ -49,8 +50,11 @@ void main() { expect( deepLinkService.deepLinkStream, emitsError( - isA() - .having((failure) => failure.error, 'error', expectedError), + isA().having( + (failure) => failure.error, + 'error', + expectedError, + ), ), ); }); @@ -65,9 +69,12 @@ void main() { expect( deepLinkService.deepLinkStream, - emitsInOrder( - [expectedUri1, expectedUri1, expectedUri2, expectedUri1], - ), + emitsInOrder([ + expectedUri1, + expectedUri1, + expectedUri2, + expectedUri1, + ]), ); onDeepLinkStreamController @@ -82,10 +89,7 @@ void main() { final error = Exception('errorMessage'); test('has correct props', () { - expect( - DeepLinkClientFailure(error).props, - [error], - ); + expect(DeepLinkClientFailure(error).props, [error]); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart index f371c87a2..4b68975cf 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart @@ -11,7 +11,7 @@ import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; class FirebaseDeepLinkClient implements DeepLinkClient { /// {@macro firebase_deep_link_client} FirebaseDeepLinkClient({required FirebaseDynamicLinks firebaseDynamicLinks}) - : _firebaseDynamicLinks = firebaseDynamicLinks; + : _firebaseDynamicLinks = firebaseDynamicLinks; final FirebaseDynamicLinks _firebaseDynamicLinks; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml index 4d54fc85e..2fb99c4e4 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml @@ -3,19 +3,19 @@ description: A Dart package which provides a deep link stream publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: deep_link_client: path: ../deep_link_client equatable: ^2.0.3 - firebase_core: ^3.4.1 - firebase_dynamic_links: ^6.0.6 + firebase_core: ^3.15.0 + firebase_dynamic_links: ^6.1.8 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^5.2.1 + firebase_core_platform_interface: ^5.3.1 flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart index ff8a43e3b..ce3d14906 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart @@ -1,19 +1,13 @@ // ignore_for_file: deprecated_member_use import 'dart:async'; -import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; import 'package:firebase_deep_link_client/firebase_deep_link_client.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class MockFirebaseDynamicLinks extends Mock implements FirebaseDynamicLinks {} -class MockFirebaseCore extends Mock - with MockPlatformInterfaceMixin - implements FirebasePlatform {} - void main() { late MockFirebaseDynamicLinks dynamicLinks; late StreamController onLinkStreamController; @@ -21,12 +15,13 @@ void main() { setUp(() { dynamicLinks = MockFirebaseDynamicLinks(); onLinkStreamController = StreamController(); - when(() => dynamicLinks.onLink) - .thenAnswer((_) => onLinkStreamController.stream); + when( + () => dynamicLinks.onLink, + ).thenAnswer((_) => onLinkStreamController.stream); }); tearDown(() { - onLinkStreamController.close(); + unawaited(onLinkStreamController.close()); }); group('FirebaseDeepLinkClient', () { @@ -37,8 +32,9 @@ void main() { (_) => Future.value(PendingDynamicLinkData(link: expectedUri)), ); - final client = - FirebaseDeepLinkClient(firebaseDynamicLinks: dynamicLinks); + final client = FirebaseDeepLinkClient( + firebaseDynamicLinks: dynamicLinks, + ); final link = await client.getInitialLink(); expect(link, expectedUri); }); @@ -49,8 +45,9 @@ void main() { final expectedUri1 = Uri.https('news.app.test', '/test/1'); final expectedUri2 = Uri.https('news.app.test', '/test/2'); - final client = - FirebaseDeepLinkClient(firebaseDynamicLinks: dynamicLinks); + final client = FirebaseDeepLinkClient( + firebaseDynamicLinks: dynamicLinks, + ); onLinkStreamController ..add(PendingDynamicLinkData(link: expectedUri1)) @@ -60,9 +57,12 @@ void main() { expect( client.deepLinkStream, - emitsInOrder( - [expectedUri1, expectedUri1, expectedUri2, expectedUri1], - ), + emitsInOrder([ + expectedUri1, + expectedUri1, + expectedUri2, + expectedUri1, + ]), ); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/lib/src/email_launcher.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/lib/src/email_launcher.dart index 76f16e889..3096222d9 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/lib/src/email_launcher.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/lib/src/email_launcher.dart @@ -35,8 +35,8 @@ class EmailLauncher { EmailLauncher({ LaunchUrlProvider? launchUrlProvider, CanLaunchUrlProvider? canLaunchProvider, - }) : _launchUrlProvider = launchUrlProvider ?? launchUrl, - _canLaunchUrlProvider = canLaunchProvider ?? canLaunchUrl; + }) : _launchUrlProvider = launchUrlProvider ?? launchUrl, + _canLaunchUrlProvider = canLaunchProvider ?? canLaunchUrl; final LaunchUrlProvider _launchUrlProvider; final CanLaunchUrlProvider _canLaunchUrlProvider; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/pubspec.yaml index ee7580306..2994b05c7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/pubspec.yaml @@ -4,19 +4,19 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: android_intent_plus: ^5.0.1 flutter: sdk: flutter - platform: ^3.0.2 + platform: ^3.1.6 plugin_platform_interface: ^2.1.3 - url_launcher: ^6.1.5 + url_launcher: ^6.3.2 url_launcher_platform_interface: ^2.1.1 dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/test/helpers/mock_url_launcher.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/test/helpers/mock_url_launcher.dart index 92afd487b..77cac3884 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/test/helpers/mock_url_launcher.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/test/helpers/mock_url_launcher.dart @@ -65,10 +65,7 @@ class MockUrlLauncher extends Fake } /// Sets needed variables to use the `launchUrl` method. - void setLaunchUrlExpectations({ - required String url, - LaunchOptions? options, - }) { + void setLaunchUrlExpectations({required String url, LaunchOptions? options}) { canLaunchUrl = url; this.options = options; } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/test/src/email_launcher_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/test/src/email_launcher_test.dart index 156537ee0..ca774f3b6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/test/src/email_launcher_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/email_launcher/test/src/email_launcher_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: prefer_const_constructors - import 'package:email_launcher/email_launcher.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -52,9 +50,7 @@ void main() { final result = await canLaunchUrl(url); mock - ..setLaunchUrlExpectations( - url: url.toString(), - ) + ..setLaunchUrlExpectations(url: url.toString()) ..response = true; final launch = await launchUrl(url); await emailLauncher.launchEmailApp(); @@ -65,8 +61,7 @@ void main() { }); }); - test( - 'throws LaunchEmailAppFailure ' + test('throws LaunchEmailAppFailure ' 'on generic exception', () async { debugDefaultTargetPlatformOverride = TargetPlatform.iOS; final emailLauncher = EmailLauncher( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/lib/src/email.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/lib/src/email.dart index a804ef3e2..fdf4d7008 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/lib/src/email.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/lib/src/email.dart @@ -3,7 +3,7 @@ import 'package:formz/formz.dart'; /// Email Form Input Validation Error enum EmailValidationError { /// Email is invalid (generic validation error) - invalid + invalid, } /// {@template email} diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/pubspec.yaml index df2557e04..f08c21750 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/pubspec.yaml @@ -3,11 +3,11 @@ description: A Dart package which exposes reusable form inputs and validation ru publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: formz: ^0.8.0 dev_dependencies: test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/test/email_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/test/email_test.dart index 9d6c4271a..9a32b14b8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/test/email_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/form_inputs/test/email_test.dart @@ -21,24 +21,15 @@ void main() { group('validator', () { test('returns invalid error when email is empty', () { - expect( - Email.dirty().error, - EmailValidationError.invalid, - ); + expect(Email.dirty().error, EmailValidationError.invalid); }); test('returns invalid error when email is malformed', () { - expect( - Email.dirty('test').error, - EmailValidationError.invalid, - ); + expect(Email.dirty('test').error, EmailValidationError.invalid); }); test('is valid when email is valid', () { - expect( - Email.dirty(emailString).error, - isNull, - ); + expect(Email.dirty(emailString).error, isNull); }); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/lib/src/in_app_purchase_repository.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/lib/src/in_app_purchase_repository.dart index f99d4d95e..b871661b6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/lib/src/in_app_purchase_repository.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/lib/src/in_app_purchase_repository.dart @@ -94,9 +94,9 @@ class InAppPurchaseRepository { required AuthenticationClient authenticationClient, required {{project_name.pascalCase()}}ApiClient apiClient, required InAppPurchase inAppPurchase, - }) : _apiClient = apiClient, - _authenticationClient = authenticationClient, - _inAppPurchase = inAppPurchase { + }) : _apiClient = apiClient, + _authenticationClient = authenticationClient, + _inAppPurchase = inAppPurchase { _inAppPurchase.purchaseStream .expand((value) => value) .listen(_onPurchaseUpdated); @@ -131,10 +131,7 @@ class InAppPurchaseRepository { _cachedSubscriptions = response.subscriptions; return _cachedSubscriptions ?? []; } catch (error, stackTrace) { - Error.throwWithStackTrace( - FetchSubscriptionsFailure(error), - stackTrace, - ); + Error.throwWithStackTrace(FetchSubscriptionsFailure(error), stackTrace); } } @@ -143,11 +140,10 @@ class InAppPurchaseRepository { /// When the payment is successfully completed, the app informs /// the server about the purchased subscription. The server then verifies /// if the purchase was correct and updates user's subscription. - Future purchase({ - required Subscription subscription, - }) async { - final productDetailsResponse = - await _inAppPurchase.queryProductDetails({subscription.id}); + Future purchase({required Subscription subscription}) async { + final productDetailsResponse = await _inAppPurchase.queryProductDetails({ + subscription.id, + }); if (productDetailsResponse.error != null) { Error.throwWithStackTrace( @@ -186,8 +182,9 @@ class InAppPurchaseRepository { applicationUserName: user.id, ); - final isPurchaseRequestSuccessful = - await _inAppPurchase.buyNonConsumable(purchaseParam: purchaseParam); + final isPurchaseRequestSuccessful = await _inAppPurchase.buyNonConsumable( + purchaseParam: purchaseParam, + ); if (!isPurchaseRequestSuccessful) { Error.throwWithStackTrace( @@ -221,9 +218,7 @@ class InAppPurchaseRepository { if (purchase.status == PurchaseStatus.error) { _purchaseUpdateStreamController.add( PurchaseFailed( - failure: InternalInAppPurchaseFailure( - purchase.error.toString(), - ), + failure: InternalInAppPurchaseFailure(purchase.error.toString()), ), ); } @@ -231,8 +226,9 @@ class InAppPurchaseRepository { try { if (purchase.status == PurchaseStatus.purchased || purchase.status == PurchaseStatus.restored) { - final purchasedProduct = (await fetchSubscriptions()) - .firstWhere((product) => product.id == purchase.productID); + final purchasedProduct = (await fetchSubscriptions()).firstWhere( + (product) => product.id == purchase.productID, + ); _purchaseUpdateStreamController.add( PurchasePurchased(subscription: purchasedProduct), @@ -246,13 +242,9 @@ class InAppPurchaseRepository { PurchaseDelivered(subscription: purchasedProduct), ); } - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { _purchaseUpdateStreamController.addError( - PurchaseFailed( - failure: DeliverInAppPurchaseFailure( - error, - ), - ), + PurchaseFailed(failure: DeliverInAppPurchaseFailure(error)), stackTrace, ); } @@ -266,18 +258,12 @@ class InAppPurchaseRepository { await _inAppPurchase.completePurchase(purchase); _purchaseUpdateStreamController.add( - PurchaseCompleted( - subscription: purchasedSubscription, - ), + PurchaseCompleted(subscription: purchasedSubscription), ); } - } catch (error, stackTrace) { + } on Exception catch (error, stackTrace) { _purchaseUpdateStreamController.addError( - PurchaseFailed( - failure: CompleteInAppPurchaseFailure( - error, - ), - ), + PurchaseFailed(failure: CompleteInAppPurchaseFailure(error)), stackTrace, ); } @@ -297,9 +283,7 @@ abstract class PurchaseUpdate extends Equatable { /// {@endtemplate} class PurchaseDelivered extends PurchaseUpdate { /// {@macro purchase_delivered} - const PurchaseDelivered({ - required this.subscription, - }) : super(); + const PurchaseDelivered({required this.subscription}) : super(); /// A subscription associated with a purchase that was delivered. final Subscription subscription; @@ -313,9 +297,7 @@ class PurchaseDelivered extends PurchaseUpdate { /// {@endtemplate} class PurchaseCompleted extends PurchaseUpdate { /// {@macro purchase_completed} - const PurchaseCompleted({ - required this.subscription, - }) : super(); + const PurchaseCompleted({required this.subscription}) : super(); /// A subscription that was successfully purchased. final Subscription subscription; @@ -329,9 +311,7 @@ class PurchaseCompleted extends PurchaseUpdate { /// {@endtemplate} class PurchasePurchased extends PurchaseUpdate { /// {@macro purchase_purchased} - const PurchasePurchased({ - required this.subscription, - }) : super(); + const PurchasePurchased({required this.subscription}) : super(); /// A subscription that was successfully purchased. final Subscription subscription; @@ -356,9 +336,7 @@ class PurchaseCanceled extends PurchaseUpdate { /// {@endtemplate} class PurchaseFailed extends PurchaseUpdate { /// {@macro purchase_failed} - const PurchaseFailed({ - required this.failure, - }) : super(); + const PurchaseFailed({required this.failure}) : super(); /// A failure which occurred when purchasing a subscription. final InAppPurchaseFailure failure; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/pubspec.yaml index 0e8782b4e..c547f0a94 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: authentication_client: @@ -22,4 +22,4 @@ dev_dependencies: sdk: flutter mocktail: ^1.0.2 test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/test/src/in_app_purchase_repository_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/test/src/in_app_purchase_repository_test.dart index 27d7614fc..3f8ddb555 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/test/src/in_app_purchase_repository_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/in_app_purchase_repository/test/src/in_app_purchase_repository_test.dart @@ -25,9 +25,7 @@ void main() { test('InAppPurchaseFailure supports value comparisons', () { expect( InAppPurchaseException('error'), - equals( - InAppPurchaseException('error'), - ), + equals(InAppPurchaseException('error')), ); }); @@ -45,10 +43,7 @@ void main() { currencyCode: 'USD', ); - final user = AuthenticationUser( - id: '123', - name: 'name', - ); + final user = AuthenticationUser(id: '123', name: 'name'); final subscription = Subscription( id: product.id, @@ -58,19 +53,13 @@ void main() { ); final apiUserResponse = CurrentUserResponse( - user: api.User( - id: user.id, - subscription: subscription.name, - ), + user: api.User(id: user.id, subscription: subscription.name), ); setUpAll(() { registerFallbackValue(SubscriptionPlan.none); registerFallbackValue( - PurchaseParam( - productDetails: product, - applicationUserName: user.name, - ), + PurchaseParam(productDetails: product, applicationUserName: user.name), ); registerFallbackValue(FakePurchaseDetails()); }); @@ -86,13 +75,11 @@ void main() { ), ).thenAnswer((_) async {}); - when( - apiClient.getCurrentUser, - ).thenAnswer((_) async => apiUserResponse); + when(apiClient.getCurrentUser).thenAnswer((_) async => apiUserResponse); - when(() => authenticationClient.user).thenAnswer( - (_) => Stream.fromIterable([user]), - ); + when( + () => authenticationClient.user, + ).thenAnswer((_) => Stream.fromIterable([user])); when(() => inAppPurchase.purchaseStream).thenAnswer( (_) => Stream.value([ @@ -121,15 +108,12 @@ void main() { ); when(apiClient.getSubscriptions).thenAnswer( - (_) async => SubscriptionsResponse( - subscriptions: [subscription], - ), + (_) async => SubscriptionsResponse(subscriptions: [subscription]), ); }); group('calls apiClient.getSubscriptions ', () { - test( - 'which returns cachedProducts list ' + test('which returns cachedProducts list ' 'and calls getSubscriptions only once ' 'when cachedProducts is not empty', () async { final result = await repository.fetchSubscriptions(); @@ -137,25 +121,17 @@ void main() { verify(apiClient.getSubscriptions).called(1); - expect( - result, - equals(nextResult), - ); + expect(result, equals(nextResult)); }); - test( - 'and returns server products list ' + test('and returns server products list ' 'when cachedProducts is empty', () async { final result = await repository.fetchSubscriptions(); - expect( - result, - equals([subscription]), - ); + expect(result, equals([subscription])); }); - test( - 'and throws FetchSubscriptionsFailure ' + test('and throws FetchSubscriptionsFailure ' 'when getSubscriptions fails', () async { when(apiClient.getSubscriptions).thenThrow(Exception()); @@ -207,8 +183,7 @@ void main() { ).called(1); }); - test( - 'and throws InAppPurchaseBuyNonConsumableFailure ' + test('and throws InAppPurchaseBuyNonConsumableFailure ' 'when buyNonConsumable fails', () async { when( () => inAppPurchase.buyNonConsumable( @@ -253,10 +228,8 @@ void main() { any(that: isA>()), ), ).thenAnswer( - (_) async => ProductDetailsResponse( - productDetails: [], - notFoundIDs: [], - ), + (_) async => + ProductDetailsResponse(productDetails: [], notFoundIDs: []), ); expect( @@ -301,8 +274,7 @@ void main() { ).thenAnswer((_) async => true); }); - test( - 'calls InAppPurchase.restorePurchases ' + test('calls InAppPurchase.restorePurchases ' 'when user is not anonymous', () async { when( () => inAppPurchase.restorePurchases( @@ -310,23 +282,20 @@ void main() { ), ).thenAnswer((_) async {}); - when(() => authenticationClient.user).thenAnswer( - (_) => Stream.fromIterable([user]), - ); + when( + () => authenticationClient.user, + ).thenAnswer((_) => Stream.fromIterable([user])); await repository.restorePurchases(); verify( () => inAppPurchase.restorePurchases( - applicationUserName: any( - named: 'applicationUserName', - ), + applicationUserName: any(named: 'applicationUserName'), ), ).called(1); }); - test( - 'does not call InAppPurchase.restorePurchases ' + test('does not call InAppPurchase.restorePurchases ' 'when user is anonymous', () async { when( () => inAppPurchase.restorePurchases( @@ -342,9 +311,7 @@ void main() { verifyNever( () => inAppPurchase.restorePurchases( - applicationUserName: any( - named: 'applicationUserName', - ), + applicationUserName: any(named: 'applicationUserName'), ), ); }); @@ -364,15 +331,12 @@ void main() { setUp(() { when(apiClient.getSubscriptions).thenAnswer( - (_) async => SubscriptionsResponse( - subscriptions: [subscription], - ), + (_) async => SubscriptionsResponse(subscriptions: [subscription]), ); }); - test( - 'adds PurchaseCanceled event ' - 'when PurchaseDetails status is canceled', () { + test('adds PurchaseCanceled event ' + 'when PurchaseDetails status is canceled', () async { when(() => inAppPurchase.purchaseStream).thenAnswer( (_) => Stream.fromIterable([ [purchaseDetails.copyWith(status: PurchaseStatus.canceled)], @@ -385,15 +349,14 @@ void main() { inAppPurchase: inAppPurchase, ); - expectLater( + await expectLater( repository.purchaseUpdate, emits(isA()), ); }); - test( - 'adds PurchaseFailed event ' - 'when PurchaseDetails status is error', () { + test('adds PurchaseFailed event ' + 'when PurchaseDetails status is error', () async { when(() => inAppPurchase.purchaseStream).thenAnswer( (_) => Stream.fromIterable([ [purchaseDetails.copyWith(status: PurchaseStatus.error)], @@ -406,7 +369,7 @@ void main() { inAppPurchase: inAppPurchase, ); - expectLater( + await expectLater( repository.purchaseUpdate, emits(isA()), ); @@ -422,36 +385,34 @@ void main() { }); test( - 'adds PurchasePurchased event ' - 'calls apiClient.createSubscription ' - 'adds PurchaseDelivered event ' - 'adds purchased subscription to currentSubscriptionPlanStream', - () async { - final repository = InAppPurchaseRepository( - authenticationClient: authenticationClient, - apiClient: apiClient, - inAppPurchase: inAppPurchase, - ); - - await expectLater( - repository.purchaseUpdate, - emitsInOrder( - [ + 'adds PurchasePurchased event ' + 'calls apiClient.createSubscription ' + 'adds PurchaseDelivered event ' + 'adds purchased subscription to currentSubscriptionPlanStream', + () async { + final repository = InAppPurchaseRepository( + authenticationClient: authenticationClient, + apiClient: apiClient, + inAppPurchase: inAppPurchase, + ); + + await expectLater( + repository.purchaseUpdate, + emitsInOrder([ isA(), isA(), - ], - ), - ); + ]), + ); - verify( - () => apiClient.createSubscription( - subscriptionId: any(named: 'subscriptionId'), - ), - ).called(1); - }); + verify( + () => apiClient.createSubscription( + subscriptionId: any(named: 'subscriptionId'), + ), + ).called(1); + }, + ); - test( - 'adds PurchasePurchased event ' + test('adds PurchasePurchased event ' 'and throws PurchaseFailed ' 'when apiClient.createSubscription throws', () async { when( @@ -468,12 +429,10 @@ void main() { expect( repository.purchaseUpdate, - emitsInOrder( - [ - isA(), - emitsError(isA()), - ], - ), + emitsInOrder([ + isA(), + emitsError(isA()), + ]), ); }); }); @@ -488,36 +447,34 @@ void main() { }); test( - 'adds PurchasePurchased event ' - 'calls apiClient.createSubscription ' - 'adds PurchaseDelivered event ' - 'adds purchased subscription to currentSubscriptionPlanStream', - () async { - final repository = InAppPurchaseRepository( - authenticationClient: authenticationClient, - apiClient: apiClient, - inAppPurchase: inAppPurchase, - ); - - await expectLater( - repository.purchaseUpdate, - emitsInOrder( - [ + 'adds PurchasePurchased event ' + 'calls apiClient.createSubscription ' + 'adds PurchaseDelivered event ' + 'adds purchased subscription to currentSubscriptionPlanStream', + () async { + final repository = InAppPurchaseRepository( + authenticationClient: authenticationClient, + apiClient: apiClient, + inAppPurchase: inAppPurchase, + ); + + await expectLater( + repository.purchaseUpdate, + emitsInOrder([ isA(), isA(), - ], - ), - ); + ]), + ); - verify( - () => apiClient.createSubscription( - subscriptionId: any(named: 'subscriptionId'), - ), - ).called(1); - }); + verify( + () => apiClient.createSubscription( + subscriptionId: any(named: 'subscriptionId'), + ), + ).called(1); + }, + ); - test( - 'adds PurchasePurchased event ' + test('adds PurchasePurchased event ' 'and throws PurchaseFailed ' 'when apiClient.createSubscription throws', () async { when( @@ -556,8 +513,7 @@ void main() { ); }); - test( - 'emits PurchaseCompleted ' + test('emits PurchaseCompleted ' 'when inAppPurchase.completePurchase succeeds', () async { when( () => inAppPurchase.completePurchase(any()), @@ -575,8 +531,7 @@ void main() { ); }); - test( - 'throws PurchaseFailed ' + test('throws PurchaseFailed ' 'when inAppPurchase.completePurchase fails', () async { when( () => inAppPurchase.completePurchase(any()), @@ -590,14 +545,11 @@ void main() { await expectLater( repository.purchaseUpdate, - emitsError( - isA(), - ), + emitsError(isA()), ); }); - test( - 'calls apiClient.getSubscriptions once ' + test('calls apiClient.getSubscriptions once ' 'and returns cachedProducts on next ' 'apiClient.getSubscriptions call', () async { when( @@ -626,41 +578,36 @@ void main() { await expectLater( repository.purchaseUpdate, - emitsInOrder( - [ - isA(), - isA(), - ], - ), + emitsInOrder([ + isA(), + isA(), + ]), ); }); }); }); group('fetchSubscriptions', () { - test('returns subscription list from apiClient.getSubscriptions', - () async { - when(apiClient.getSubscriptions).thenAnswer( - (_) async => SubscriptionsResponse( - subscriptions: [subscription], - ), - ); - final repository = InAppPurchaseRepository( - authenticationClient: authenticationClient, - apiClient: apiClient, - inAppPurchase: inAppPurchase, - ); + test( + 'returns subscription list from apiClient.getSubscriptions', + () async { + when(apiClient.getSubscriptions).thenAnswer( + (_) async => SubscriptionsResponse(subscriptions: [subscription]), + ); + final repository = InAppPurchaseRepository( + authenticationClient: authenticationClient, + apiClient: apiClient, + inAppPurchase: inAppPurchase, + ); - final result = await repository.fetchSubscriptions(); + final result = await repository.fetchSubscriptions(); - expect(result, equals([subscription])); - verify( - apiClient.getSubscriptions, - ).called(1); - }); + expect(result, equals([subscription])); + verify(apiClient.getSubscriptions).called(1); + }, + ); - test( - 'throws FetchSubscriptionsFailure ' + test('throws FetchSubscriptionsFailure ' 'when apiClient.getSubscriptions throws', () async { when(apiClient.getSubscriptions).thenThrow(Exception()); @@ -684,10 +631,7 @@ void main() { final event1 = PurchaseDelivered( subscription: Subscription( benefits: const [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -695,10 +639,7 @@ void main() { final event2 = PurchaseDelivered( subscription: Subscription( benefits: const [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -713,10 +654,7 @@ void main() { final event1 = PurchaseCompleted( subscription: Subscription( benefits: const [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -724,10 +662,7 @@ void main() { final event2 = PurchaseCompleted( subscription: Subscription( benefits: const [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -742,10 +677,7 @@ void main() { final event1 = PurchasePurchased( subscription: Subscription( benefits: const [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -753,10 +685,7 @@ void main() { final event2 = PurchasePurchased( subscription: Subscription( benefits: const [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -802,11 +731,12 @@ extension _PurchaseDetailsCopyWith on PurchaseDetails { bool? pendingCompletePurchase, }) => PurchaseDetails( - purchaseID: purchaseID ?? this.purchaseID, - productID: productID ?? this.productID, - verificationData: verificationData ?? this.verificationData, - transactionDate: transactionDate ?? this.transactionDate, - status: status ?? this.status, - )..pendingCompletePurchase = - pendingCompletePurchase ?? this.pendingCompletePurchase; + purchaseID: purchaseID ?? this.purchaseID, + productID: productID ?? this.productID, + verificationData: verificationData ?? this.verificationData, + transactionDate: transactionDate ?? this.transactionDate, + status: status ?? this.status, + ) + ..pendingCompletePurchase = + pendingCompletePurchase ?? this.pendingCompletePurchase; } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/analysis_options.yaml index 1a86ad22e..89138eb27 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/analysis_options.yaml @@ -1,5 +1,7 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml analyzer: + errors: + document_ignores: ignore exclude: - lib/src/generated/** diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/article_introduction.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/article_introduction.dart index 44a81d88f..5ca2b6bf1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/article_introduction.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/article_introduction.dart @@ -10,6 +10,7 @@ class ArticleIntroduction extends StatelessWidget { /// {@macro article_introduction} const ArticleIntroduction({ required this.block, + required this.categoryName, required this.premiumText, super.key, }); @@ -17,6 +18,9 @@ class ArticleIntroduction extends StatelessWidget { /// The associated [ArticleIntroductionBlock] instance. final ArticleIntroductionBlock block; + /// The name of the category of the associated article. + final String? categoryName; + /// Text displayed when article is premium content. final String premiumText; @@ -28,7 +32,7 @@ class ArticleIntroduction extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), child: PostContent( - categoryName: block.category.name, + categoryName: categoryName, title: block.title, author: block.author, publishedAt: block.publishedAt, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/generated/assets.gen.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/generated/assets.gen.dart index 59b99b46c..d5e3d2b34 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/generated/assets.gen.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/generated/assets.gen.dart @@ -35,12 +35,12 @@ class $AssetsIconsGen { /// List of all assets List get values => [ - arrowLeftDisable, - arrowLeftEnable, - arrowRightDisable, - arrowRightEnable, - playIcon - ]; + arrowLeftDisable, + arrowLeftEnable, + arrowRightDisable, + arrowRightEnable, + playIcon, + ]; } class Assets { @@ -111,11 +111,7 @@ class AssetGenImage { AssetBundle? bundle, String? package = 'news_blocks_ui', }) { - return AssetImage( - _assetName, - bundle: bundle, - package: package, - ); + return AssetImage(_assetName, bundle: bundle, package: package); } String get path => _assetName; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/html.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/html.dart index ae154932f..59ffefbe3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/html.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/html.dart @@ -29,27 +29,19 @@ class Html extends StatelessWidget { }, data: block.content, style: { - 'p': flutter_html.Style.fromTextStyle( - theme.textTheme.bodyLarge!, - ), - 'h1': flutter_html.Style.fromTextStyle( - theme.textTheme.displayLarge!, - ), + 'p': flutter_html.Style.fromTextStyle(theme.textTheme.bodyLarge!), + 'h1': flutter_html.Style.fromTextStyle(theme.textTheme.displayLarge!), 'h2': flutter_html.Style.fromTextStyle( theme.textTheme.displayMedium!, ), - 'h3': flutter_html.Style.fromTextStyle( - theme.textTheme.displaySmall!, - ), + 'h3': flutter_html.Style.fromTextStyle(theme.textTheme.displaySmall!), 'h4': flutter_html.Style.fromTextStyle( theme.textTheme.headlineMedium!, ), 'h5': flutter_html.Style.fromTextStyle( theme.textTheme.headlineSmall!, ), - 'h6': flutter_html.Style.fromTextStyle( - theme.textTheme.titleLarge!, - ), + 'h6': flutter_html.Style.fromTextStyle(theme.textTheme.titleLarge!), }, ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/newsletter/newsletter_container.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/newsletter/newsletter_container.dart index c3f50de46..f11924e9b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/newsletter/newsletter_container.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/newsletter/newsletter_container.dart @@ -6,10 +6,7 @@ import 'package:flutter/material.dart'; /// {@endtemplate} class NewsletterContainer extends StatelessWidget { /// {@macro newsletter_container} - const NewsletterContainer({ - super.key, - this.child, - }); + const NewsletterContainer({super.key, this.child}); /// The widget displayed in newsletter container. final Widget? child; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/newsletter/newsletter_sign_up.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/newsletter/newsletter_sign_up.dart index a73f08229..ad51972f8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/newsletter/newsletter_sign_up.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/newsletter/newsletter_sign_up.dart @@ -41,15 +41,17 @@ class NewsletterSignUp extends StatelessWidget { Text( headerText, textAlign: TextAlign.center, - style: theme.textTheme.headlineMedium - ?.copyWith(color: AppColors.highEmphasisPrimary), + style: theme.textTheme.headlineMedium?.copyWith( + color: AppColors.highEmphasisPrimary, + ), ), const SizedBox(height: AppSpacing.lg), Text( bodyText, textAlign: TextAlign.center, - style: theme.textTheme.bodyLarge - ?.copyWith(color: AppColors.mediumEmphasisPrimary), + style: theme.textTheme.bodyLarge?.copyWith( + color: AppColors.mediumEmphasisPrimary, + ), ), const SizedBox(height: AppSpacing.lg), email, @@ -58,9 +60,7 @@ class NewsletterSignUp extends StatelessWidget { textStyle: theme.textTheme.labelLarge?.copyWith( color: AppColors.white, ), - child: Text( - buttonText, - ), + child: Text(buttonText), ), ], ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_grid.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_grid.dart index 2b2d7dc0e..45adcb8c4 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_grid.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_grid.dart @@ -11,6 +11,7 @@ class PostGrid extends StatelessWidget { /// {@macro post_grid} const PostGrid({ required this.gridGroupBlock, + required this.categoryName, required this.premiumText, this.isLocked = false, this.onPressed, @@ -20,6 +21,9 @@ class PostGrid extends StatelessWidget { /// The associated [PostGridGroupBlock] instance. final PostGridGroupBlock gridGroupBlock; + /// The name of the category of the associated article. + final String? categoryName; + /// Text displayed when post is premium content. final String premiumText; @@ -47,25 +51,23 @@ class PostGrid extends StatelessWidget { crossAxisSpacing: AppSpacing.md, childAspectRatio: 3 / 2, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - final block = gridGroupBlock.tiles[index]; - if (index == 0) { - return PostLarge( - block: block.toPostLargeBlock(), - premiumText: premiumText, - isLocked: isLocked, - onPressed: onPressed, - ); - } - - return PostMedium( - block: block.toPostMediumBlock(), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + final block = gridGroupBlock.tiles[index]; + if (index == 0) { + return PostLarge( + block: block.toPostLargeBlock(), + categoryName: categoryName, + premiumText: premiumText, + isLocked: isLocked, onPressed: onPressed, ); - }, - childCount: gridGroupBlock.tiles.length, - ), + } + + return PostMedium( + block: block.toPostMediumBlock(), + onPressed: onPressed, + ); + }, childCount: gridGroupBlock.tiles.length), ), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_large/post_large.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_large/post_large.dart index 03b6b554e..ba8e68edc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_large/post_large.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_large/post_large.dart @@ -11,6 +11,7 @@ class PostLarge extends StatelessWidget { /// {@macro post_large} const PostLarge({ required this.block, + required this.categoryName, required this.premiumText, required this.isLocked, this.onPressed, @@ -20,6 +21,9 @@ class PostLarge extends StatelessWidget { /// The associated [PostLargeBlock] instance. final PostLargeBlock block; + /// The name of the category of the associated article. + final String? categoryName; + /// Text displayed when post is premium content. final String premiumText; @@ -45,7 +49,7 @@ class PostLarge extends StatelessWidget { ), PostContent( author: block.author, - categoryName: block.category.name, + categoryName: categoryName, publishedAt: block.publishedAt, title: block.title, isPremium: block.isPremium, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_large/post_large_image.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_large/post_large_image.dart index fc951a884..be981c5a1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_large/post_large_image.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_large/post_large_image.dart @@ -30,7 +30,7 @@ class PostLargeImage extends StatelessWidget { if (isContentOverlaid) OverlaidImage( imageUrl: imageUrl, - gradientColor: AppColors.black.withOpacity(0.7), + gradientColor: AppColors.black.withValues(alpha: 0.7), ) else InlineImage(imageUrl: imageUrl), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_medium/post_medium_description_layout.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_medium/post_medium_description_layout.dart index a0e14e73d..7a62ac66f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_medium/post_medium_description_layout.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_medium/post_medium_description_layout.dart @@ -49,20 +49,20 @@ class PostMediumDescriptionLayout extends StatelessWidget { Expanded( child: Text( title, - style: textTheme.titleLarge - ?.copyWith(color: AppColors.highEmphasisSurface), + style: textTheme.titleLarge?.copyWith( + color: AppColors.highEmphasisSurface, + ), ), ), const SizedBox(width: AppSpacing.md), - Expanded( - child: InlineImage(imageUrl: imageUrl), - ), + Expanded(child: InlineImage(imageUrl: imageUrl)), ], ), Text( description ?? '', - style: textTheme.bodyMedium - ?.copyWith(color: AppColors.mediumEmphasisSurface), + style: textTheme.bodyMedium?.copyWith( + color: AppColors.mediumEmphasisSurface, + ), ), const SizedBox(height: AppSpacing.sm), PostFooter( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_medium/post_medium_overlaid_layout.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_medium/post_medium_overlaid_layout.dart index 87452402f..4c104b86f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_medium/post_medium_overlaid_layout.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_medium/post_medium_overlaid_layout.dart @@ -27,7 +27,7 @@ class PostMediumOverlaidLayout extends StatelessWidget { children: [ OverlaidImage( imageUrl: imageUrl, - gradientColor: AppColors.black.withOpacity(0.7), + gradientColor: AppColors.black.withValues(alpha: 0.7), ), Padding( padding: const EdgeInsets.all(AppSpacing.sm), @@ -35,8 +35,9 @@ class PostMediumOverlaidLayout extends StatelessWidget { title, maxLines: 3, overflow: TextOverflow.ellipsis, - style: textTheme.titleSmall - ?.copyWith(color: AppColors.highEmphasisPrimary), + style: textTheme.titleSmall?.copyWith( + color: AppColors.highEmphasisPrimary, + ), ), ), ], diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_small.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_small.dart index b24591925..3df99a1de 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_small.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/post_small.dart @@ -89,9 +89,7 @@ class PostSmallContent extends StatelessWidget { ), ), const SizedBox(height: AppSpacing.xs), - PostFooter( - publishedAt: publishedAt, - ), + PostFooter(publishedAt: publishedAt), ], ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/slideshow.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/slideshow.dart index 4b86f6960..bc7ea52e6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/slideshow.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/slideshow.dart @@ -39,9 +39,7 @@ class _SlideshowState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ - _SlideshowCategoryTitle( - categoryTitle: widget.categoryTitle, - ), + _SlideshowCategoryTitle(categoryTitle: widget.categoryTitle), _SlideshowHeaderTitle(title: widget.block.title), _SlideshowPageView( slides: widget.block.slides, @@ -66,9 +64,7 @@ class _SlideshowState extends State { } class _SlideshowCategoryTitle extends StatelessWidget { - const _SlideshowCategoryTitle({ - required this.categoryTitle, - }); + const _SlideshowCategoryTitle({required this.categoryTitle}); final String categoryTitle; @@ -86,9 +82,7 @@ class _SlideshowCategoryTitle extends StatelessWidget { } class _SlideshowHeaderTitle extends StatelessWidget { - const _SlideshowHeaderTitle({ - required this.title, - }); + const _SlideshowHeaderTitle({required this.title}); final String title; @override Widget build(BuildContext context) { @@ -111,10 +105,7 @@ class _SlideshowHeaderTitle extends StatelessWidget { } class _SlideshowPageView extends StatelessWidget { - const _SlideshowPageView({ - required this.slides, - required this.controller, - }); + const _SlideshowPageView({required this.slides, required this.controller}); final List slides; @@ -128,9 +119,7 @@ class _SlideshowPageView extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), controller: controller, itemCount: slides.length, - itemBuilder: (context, index) => SlideshowItem( - slide: slides[index], - ), + itemBuilder: (context, index) => SlideshowItem(slide: slides[index]), ), ); } @@ -166,9 +155,7 @@ class SlideshowItem extends StatelessWidget { ), child: Text( slide.caption, - style: theme.titleLarge?.apply( - color: AppColors.white, - ), + style: theme.titleLarge?.apply(color: AppColors.white), ), ), Padding( @@ -235,17 +222,15 @@ class _SlideshowButtonsState extends State<_SlideshowButtons> { '${_currentPage + 1} ${widget.navigationLabel} ${widget.totalPages}'; return Padding( - padding: const EdgeInsets.symmetric( - horizontal: AppSpacing.lg, - ), + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( key: const Key('slideshow_slideshowButtonsLeft'), - onPressed: () { + onPressed: () async { if (_currentPage >= 1) { - widget.controller.previousPage( + await widget.controller.previousPage( duration: _pageAnimationDuration, curve: Curves.easeInOut, ); @@ -261,9 +246,9 @@ class _SlideshowButtonsState extends State<_SlideshowButtons> { ), IconButton( key: const Key('slideshow_slideshowButtonsRight'), - onPressed: () { + onPressed: () async { if (_currentPage < widget.totalPages - 1) { - widget.controller.nextPage( + await widget.controller.nextPage( duration: _pageAnimationDuration, curve: Curves.easeInOut, ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/slideshow_introduction.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/slideshow_introduction.dart index ad15d3b24..ad822f041 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/slideshow_introduction.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/slideshow_introduction.dart @@ -51,9 +51,7 @@ class SlideshowIntroduction extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ SlideshowCategory(slideshowText: slideshowText), - const SizedBox( - height: AppSpacing.xs, - ), + const SizedBox(height: AppSpacing.xs), Text( block.title, style: textTheme.displayMedium?.copyWith( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/sliver_grid_custom_delegate.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/sliver_grid_custom_delegate.dart index 902105d23..d93cd12f8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/sliver_grid_custom_delegate.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/sliver_grid_custom_delegate.dart @@ -97,7 +97,8 @@ class HeaderGridTileLayout extends SliverGridRegularTileLayout { ); } else { return SliverGridGeometry( - scrollOffset: (((index + 1) ~/ crossAxisCount) * mainAxisStride) + + scrollOffset: + (((index + 1) ~/ crossAxisCount) * mainAxisStride) + childMainAxisExtent, crossAxisOffset: ((index + 1) % crossAxisCount) * crossAxisStride, mainAxisExtent: mainAxisStride, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/spacer.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/spacer.dart index 88c14f6b7..07ec25813 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/spacer.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/spacer.dart @@ -27,9 +27,6 @@ class Spacer extends StatelessWidget { ? _spacingValues[block.spacing] : 0.0; - return SizedBox( - width: double.infinity, - height: spacing, - ); + return SizedBox(width: double.infinity, height: spacing); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_headline.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_headline.dart index 90eb40750..74448d827 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_headline.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_headline.dart @@ -16,10 +16,7 @@ class TextHeadline extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), - child: Text( - block.text, - style: Theme.of(context).textTheme.displayMedium, - ), + child: Text(block.text, style: Theme.of(context).textTheme.displayMedium), ); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_lead_paragraph.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_lead_paragraph.dart index 83dd63e53..c1e8dac93 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_lead_paragraph.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_lead_paragraph.dart @@ -16,10 +16,7 @@ class TextLeadParagraph extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), - child: Text( - block.text, - style: Theme.of(context).textTheme.titleLarge, - ), + child: Text(block.text, style: Theme.of(context).textTheme.titleLarge), ); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_paragraph.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_paragraph.dart index 90275a9a5..cc24ac837 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_paragraph.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/text_paragraph.dart @@ -16,10 +16,7 @@ class TextParagraph extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), - child: Text( - block.text, - style: Theme.of(context).textTheme.bodyLarge, - ), + child: Text(block.text, style: Theme.of(context).textTheme.bodyLarge), ); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/video_introduction.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/video_introduction.dart index 27d9f753a..f4c9f659b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/video_introduction.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/video_introduction.dart @@ -8,11 +8,18 @@ import 'package:news_blocks_ui/src/widgets/widgets.dart'; /// {@endtemplate} class VideoIntroduction extends StatelessWidget { /// {@macro video_introduction} - const VideoIntroduction({required this.block, super.key}); + const VideoIntroduction({ + required this.block, + required this.categoryName, + super.key, + }); /// The associated [VideoIntroductionBlock] instance. final VideoIntroductionBlock block; + /// The name of the category of the associated article. + final String? categoryName; + @override Widget build(BuildContext context) { return Column( @@ -29,7 +36,7 @@ class VideoIntroduction extends StatelessWidget { AppSpacing.lg, ), child: PostContent( - categoryName: block.category.name, + categoryName: categoryName, title: block.title, isVideoContent: true, ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/banner_ad_content.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/banner_ad_content.dart index ca9ad46dc..1df4e914c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/banner_ad_content.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/banner_ad_content.dart @@ -27,19 +27,20 @@ class BannerAdFailedToGetSizeException implements Exception { } /// Signature for [BannerAd] builder. -typedef BannerAdBuilder = BannerAd Function({ - required AdSize size, - required String adUnitId, - required BannerAdListener listener, - required AdRequest request, -}); +typedef BannerAdBuilder = + BannerAd Function({ + required AdSize size, + required String adUnitId, + required BannerAdListener listener, + required AdRequest request, + }); /// Signature for [AnchoredAdaptiveBannerAdSize] provider. -typedef AnchoredAdaptiveAdSizeProvider = Future - Function( - Orientation orientation, - int width, -); +typedef AnchoredAdaptiveAdSizeProvider = + Future Function( + Orientation orientation, + int width, + ); /// {@template banner_ad_content} /// A reusable content of a banner ad. @@ -137,7 +138,7 @@ class _BannerAdContentState extends State @override void dispose() { - _ad?.dispose(); + unawaited(_ad?.dispose()); super.dispose(); } @@ -153,10 +154,10 @@ class _BannerAdContentState extends State child: _adLoaded ? AdWidget(ad: _ad!) : _adFailedToLoad && adFailedToLoadTitle != null - ? Text(adFailedToLoadTitle) - : widget.showProgressIndicator - ? const ProgressIndicator(color: AppColors.transparent) - : const SizedBox(), + ? Text(adFailedToLoadTitle) + : widget.showProgressIndicator + ? const ProgressIndicator(color: AppColors.transparent) + : const SizedBox(), ), ); } @@ -189,9 +190,10 @@ class _BannerAdContentState extends State try { final adCompleter = Completer(); - setState( - () => _ad = widget.adBuilder( - adUnitId: widget.adUnitId ?? + setState(() { + _ad = widget.adBuilder( + adUnitId: + widget.adUnitId ?? (widget.currentPlatform.isAndroid ? BannerAdContent.androidTestUnitId : BannerAdContent.iosTestUnitAd), @@ -203,11 +205,12 @@ class _BannerAdContentState extends State adCompleter.completeError(error); }, ), - )..load(), - ); + ); + unawaited(_ad!.load()); + }); _onAdLoaded(await adCompleter.future); - } catch (error, stackTrace) { + } on Object catch (error, stackTrace) { _reportError(BannerAdFailedToLoadException(error), stackTrace); if (retry < widget.adsRetryPolicy.maxRetryCount) { @@ -236,19 +239,14 @@ class _BannerAdContentState extends State /// /// Only supports the portrait mode. Future _getAnchoredAdaptiveAdSize() async { - final adWidth = widget.anchoredAdaptiveWidth ?? + final adWidth = + widget.anchoredAdaptiveWidth ?? MediaQuery.of(context).size.width.truncate(); - return widget.anchoredAdaptiveAdSizeProvider( - Orientation.portrait, - adWidth, - ); + return widget.anchoredAdaptiveAdSizeProvider(Orientation.portrait, adWidth); } void _reportError(Object exception, StackTrace stackTrace) => FlutterError.reportError( - FlutterErrorDetails( - exception: exception, - stack: stackTrace, - ), + FlutterErrorDetails(exception: exception, stack: stackTrace), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/inline_image.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/inline_image.dart index 32f1ef977..0dba41986 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/inline_image.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/inline_image.dart @@ -13,7 +13,7 @@ class InlineImage extends StatelessWidget { }); /// The aspect ratio of this image. - static const _aspectRatio = 3 / 2; + static const double _aspectRatio = 3 / 2; /// The url of this image. final String imageUrl; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/inline_video.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/inline_video.dart index 21028ef11..40d6758b6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/inline_video.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/inline_video.dart @@ -1,11 +1,12 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:news_blocks_ui/src/generated/generated.dart'; import 'package:video_player/video_player.dart'; /// Signature for [VideoPlayerController] builder. -typedef VideoPlayerControllerBuilder = VideoPlayerController Function( - Uri videoUrl, -); +typedef VideoPlayerControllerBuilder = + VideoPlayerController Function(Uri videoUrl); /// {@template inline_video} /// A reusable video widget displayed inline with the content. @@ -20,7 +21,7 @@ class InlineVideo extends StatefulWidget { }); /// The aspect ratio of this video. - static const _aspectRatio = 3 / 2; + static const double _aspectRatio = 3 / 2; /// The url of this video. final String videoUrl; @@ -44,21 +45,22 @@ class _InlineVideoState extends State { super.initState(); _controller = widget.videoPlayerControllerBuilder( Uri.parse(widget.videoUrl), - ) - ..addListener(_onVideoUpdated) - ..initialize().then((_) { + ); + _controller.addListener(_onVideoUpdated); + unawaited( + _controller.initialize().then((_) { // Ensure the first frame of the video is shown // after the video is initialized. if (mounted) setState(() {}); - }); + }), + ); } @override - void dispose() { + Future dispose() async { super.dispose(); - _controller - ..removeListener(_onVideoUpdated) - ..dispose(); + _controller.removeListener(_onVideoUpdated); + await _controller.dispose(); } void _onVideoUpdated() { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/lock_icon.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/lock_icon.dart index a007545ce..31360b86e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/lock_icon.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/lock_icon.dart @@ -20,12 +20,12 @@ class LockIcon extends StatelessWidget { color: AppColors.white, shadows: [ Shadow( - color: Colors.black.withOpacity(0.3), + color: Colors.black.withValues(alpha: 0.3), offset: const Offset(0, 1), blurRadius: 2, ), Shadow( - color: Colors.black.withOpacity(0.15), + color: Colors.black.withValues(alpha: 0.15), offset: const Offset(0, 1), blurRadius: 3, ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/overlaid_image.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/overlaid_image.dart index 298f73ca0..431955671 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/overlaid_image.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/overlaid_image.dart @@ -14,7 +14,7 @@ class OverlaidImage extends StatelessWidget { }); /// The aspect ratio of this image. - static const aspectRatio = 3 / 2; + static const double aspectRatio = 3 / 2; /// The url of this image. final String imageUrl; @@ -42,7 +42,7 @@ class OverlaidImage extends StatelessWidget { end: Alignment.bottomCenter, colors: [ AppColors.transparent, - gradientColor.withOpacity(0.7), + gradientColor.withValues(alpha: 0.7), ], ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_content_category.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_content_category.dart index b4bcc9e87..3a4e4dc3b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_content_category.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_content_category.dart @@ -25,16 +25,17 @@ class PostContentCategory extends StatelessWidget { @override Widget build(BuildContext context) { // Category label hierarchy - final backgroundColor = - isContentOverlaid ? AppColors.secondary : AppColors.transparent; + final backgroundColor = isContentOverlaid + ? AppColors.secondary + : AppColors.transparent; final isCategoryBackgroundDark = isContentOverlaid; final textColor = isVideoContent ? AppColors.orange : isCategoryBackgroundDark - ? AppColors.white - : AppColors.secondary; + ? AppColors.white + : AppColors.secondary; final horizontalSpacing = isCategoryBackgroundDark ? AppSpacing.xs : 0.0; @@ -53,10 +54,9 @@ class PostContentCategory extends StatelessWidget { ), child: Text( categoryName.toUpperCase(), - style: Theme.of(context) - .textTheme - .labelSmall - ?.copyWith(color: textColor), + style: Theme.of( + context, + ).textTheme.labelSmall?.copyWith(color: textColor), ), ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_content_premium_category.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_content_premium_category.dart index c17758a7b..f3061401d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_content_premium_category.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_content_premium_category.dart @@ -39,10 +39,9 @@ class PostContentPremiumCategory extends StatelessWidget { ), child: Text( premiumText.toUpperCase(), - style: Theme.of(context) - .textTheme - .labelSmall - ?.copyWith(color: textColor), + style: Theme.of( + context, + ).textTheme.labelSmall?.copyWith(color: textColor), ), ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_footer.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_footer.dart index 749112f73..848250621 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_footer.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/post_footer.dart @@ -41,30 +41,19 @@ class PostFooter extends StatelessWidget { text: TextSpan( style: textTheme.bodySmall?.copyWith(color: textColor), children: [ - if (author != null) - TextSpan( - text: author, - ), + if (author != null) TextSpan(text: author), if (author != null && publishedAt != null) ...[ const WidgetSpan(child: SizedBox(width: AppSpacing.sm)), - const TextSpan( - text: '•', - ), + const TextSpan(text: '•'), const WidgetSpan(child: SizedBox(width: AppSpacing.sm)), ], - if (publishedAt != null) - TextSpan( - text: publishedAt!.mDY, - ), + if (publishedAt != null) TextSpan(text: publishedAt!.mDY), ], ), ), if (onShare != null) IconButton( - icon: Icon( - Icons.share, - color: textColor, - ), + icon: Icon(Icons.share, color: textColor), onPressed: onShare, ), ], diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/progress_indicator.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/progress_indicator.dart index dfc2ffd18..3f57a4fa9 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/progress_indicator.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/progress_indicator.dart @@ -24,9 +24,7 @@ class ProgressIndicator extends StatelessWidget { Widget build(BuildContext context) { return ColoredBox( color: color, - child: Center( - child: CircularProgressIndicator(value: progress), - ), + child: Center(child: CircularProgressIndicator(value: progress)), ); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/share_button.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/share_button.dart index 65ac60c61..76cf45a2a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/share_button.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/share_button.dart @@ -28,16 +28,11 @@ class ShareButton extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); return TextButton.icon( - icon: Icon( - Icons.share, - color: _color, - ), + icon: Icon(Icons.share, color: _color), onPressed: onPressed, label: Text( shareText, - style: theme.textTheme.labelLarge?.copyWith( - color: _color, - ), + style: theme.textTheme.labelLarge?.copyWith(color: _color), ), ); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/slideshow_category.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/slideshow_category.dart index 0c79ed591..1fdaf9ee3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/slideshow_category.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/lib/src/widgets/slideshow_category.dart @@ -22,8 +22,9 @@ class SlideshowCategory extends StatelessWidget { @override Widget build(BuildContext context) { - final backgroundColor = - isIntroduction ? AppColors.secondary : AppColors.transparent; + final backgroundColor = isIntroduction + ? AppColors.secondary + : AppColors.transparent; final textColor = isIntroduction ? AppColors.white : AppColors.orange; const horizontalSpacing = AppSpacing.xs; @@ -42,10 +43,9 @@ class SlideshowCategory extends StatelessWidget { ), child: Text( slideshowText.toUpperCase(), - style: Theme.of(context) - .textTheme - .labelSmall - ?.copyWith(color: textColor), + style: Theme.of( + context, + ).textTheme.labelSmall?.copyWith(color: textColor), ), ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/pubspec.yaml index 8f6c85fed..1c298710f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=2.19.3 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: app_ui: @@ -13,28 +13,28 @@ dependencies: flutter: sdk: flutter flutter_html: 3.0.0-beta.2 - google_mobile_ads: ^5.0.0 + google_mobile_ads: ^5.3.1 news_blocks: path: ../../api/packages/news_blocks path_provider_platform_interface: ^2.0.5 - platform: ^3.1.0 + platform: ^3.1.6 plugin_platform_interface: ^2.1.3 - url_launcher: ^6.1.7 + url_launcher: ^6.3.2 url_launcher_platform_interface: ^2.1.1 video_player: ^2.7.0 - video_player_platform_interface: ^6.0.1 + video_player_platform_interface: ^6.5.0 dev_dependencies: - build_runner: ^2.0.3 + build_runner: ^2.4.15 fake_async: ^1.3.0 flutter_gen_runner: ^5.2.0 flutter_test: sdk: flutter html: ^0.15.4 mocktail: ^1.0.2 - mocktail_image_network: ^1.0.0 + mocktail_image_network: ^1.2.0 sqflite_common_ffi: ^2.3.3 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 flutter: uses-material-design: true diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/helpers/pump_app.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/helpers/pump_app.dart index 601472480..602b66a41 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/helpers/pump_app.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/helpers/pump_app.dart @@ -2,16 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; extension AppTester on WidgetTester { - Future pumpApp( - Widget widgetUnderTest, { - ThemeData? theme, - }) async { + Future pumpApp(Widget widgetUnderTest, {ThemeData? theme}) async { await pumpWidget( MaterialApp( theme: theme, - home: Scaffold( - body: widgetUnderTest, - ), + home: Scaffold(body: widgetUnderTest), ), ); await pump(); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/helpers/tolerant_comparator.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/helpers/tolerant_comparator.dart index 890310d73..db1acbc22 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/helpers/tolerant_comparator.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/helpers/tolerant_comparator.dart @@ -2,9 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; class _TolerantGoldenFileComparator extends LocalFileComparator { - _TolerantGoldenFileComparator( - super.testFile, - ); + _TolerantGoldenFileComparator(super.testFile); /// How much the golden image can differ from the test image. /// diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/article_introduction_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/article_introduction_test.dart index 0c7c87de0..dc97676be 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/article_introduction_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/article_introduction_test.dart @@ -7,14 +7,15 @@ import 'package:news_blocks_ui/news_blocks_ui.dart'; import '../helpers/helpers.dart'; void main() { - const category = PostCategory.technology; + const category = Category(id: 'technology', name: 'Technology'); const author = 'Sean Hollister'; final publishedAt = DateTime(2022, 3, 9); const imageUrl = 'https://cdn.vox-cdn.com/thumbor/OTpmptgr7XcTVAJ27UBvIxl0vrg=' '/0x146:2040x1214/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset' '/file/22049166/shollister_201117_4303_0003.0.jpg'; - const title = 'Nvidia and AMD GPUs are returning to shelves ' + const title = + 'Nvidia and AMD GPUs are returning to shelves ' 'and prices are finally falling'; const premiumText = 'Subscriber Exclusive'; @@ -25,7 +26,7 @@ void main() { ); final technologyArticleIntroduction = ArticleIntroductionBlock( - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -40,6 +41,7 @@ void main() { children: [ ArticleIntroduction( block: technologyArticleIntroduction, + categoryName: category.name, premiumText: premiumText, ), ], diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/banner_ad_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/banner_ad_test.dart index 5eecc7f85..b937b8a38 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/banner_ad_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/banner_ad_test.dart @@ -13,10 +13,7 @@ void main() { const block = BannerAdBlock(size: BannerAdSize.normal); await tester.pumpApp( - BannerAd( - block: block, - adFailedToLoadTitle: 'adFailedToLoadTitle', - ), + BannerAd(block: block, adFailedToLoadTitle: 'adFailedToLoadTitle'), ); expect( @@ -31,10 +28,7 @@ void main() { const block = BannerAdBlock(size: BannerAdSize.normal); await tester.pumpApp( - BannerAd( - block: block, - adFailedToLoadTitle: 'adFailedToLoadTitle', - ), + BannerAd(block: block, adFailedToLoadTitle: 'adFailedToLoadTitle'), ); expect( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/divider_horizontal_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/divider_horizontal_test.dart index e27650698..0fae157dc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/divider_horizontal_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/divider_horizontal_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -15,9 +15,7 @@ void main() { testWidgets('renders correctly', (tester) async { final widget = Center( - child: DividerHorizontal( - block: DividerHorizontalBlock(), - ), + child: DividerHorizontal(block: DividerHorizontalBlock()), ); await tester.pumpApp(widget); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/header_grid_tile_layout_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/header_grid_tile_layout_test.dart index b3d89f0df..71d277bb7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/header_grid_tile_layout_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/header_grid_tile_layout_test.dart @@ -6,8 +6,7 @@ import 'package:news_blocks_ui/src/sliver_grid_custom_delegate.dart'; void main() { group('HeaderGridTileLayout', () { - testWidgets( - 'getMinChildIndexForScrollOffset when ' + testWidgets('getMinChildIndexForScrollOffset when ' 'super.getMinChildIndexForScrollOffset is 0', (tester) async { final headerTileLayout = HeaderGridTileLayout( crossAxisCount: 1, @@ -23,10 +22,10 @@ void main() { expect(result, 0); }); - testWidgets( - 'getMinChildIndexForScrollOffset when ' - 'super.getMinChildIndexForScrollOffset is greater than 0', - (tester) async { + testWidgets('getMinChildIndexForScrollOffset when ' + 'super.getMinChildIndexForScrollOffset is greater than 0', ( + tester, + ) async { final headerTileLayout = HeaderGridTileLayout( crossAxisCount: 1, mainAxisStride: 14, @@ -42,34 +41,36 @@ void main() { }); testWidgets( - 'getGeometryForChildIndex when index is equal to 0 (first element) ', - (tester) async { - final headerTileLayout = HeaderGridTileLayout( - crossAxisCount: 1, - mainAxisStride: 14, - crossAxisStride: 15, - childMainAxisExtent: 2, - childCrossAxisExtent: 3, - reverseCrossAxis: false, - ); - - final geometry = headerTileLayout.getGeometryForChildIndex(0); - - final expectedGeometry = SliverGridGeometry( - scrollOffset: 0, - crossAxisOffset: 0, - mainAxisExtent: 16, - crossAxisExtent: 18, - ); - - expect(geometry.crossAxisExtent, expectedGeometry.crossAxisExtent); - expect(geometry.scrollOffset, expectedGeometry.scrollOffset); - expect(geometry.mainAxisExtent, expectedGeometry.mainAxisExtent); - expect(geometry.crossAxisExtent, expectedGeometry.crossAxisExtent); - }); - - testWidgets('getGeometryForChildIndex when index is not equal to 0', - (tester) async { + 'getGeometryForChildIndex when index is equal to 0 (first element) ', + (tester) async { + final headerTileLayout = HeaderGridTileLayout( + crossAxisCount: 1, + mainAxisStride: 14, + crossAxisStride: 15, + childMainAxisExtent: 2, + childCrossAxisExtent: 3, + reverseCrossAxis: false, + ); + + final geometry = headerTileLayout.getGeometryForChildIndex(0); + + final expectedGeometry = SliverGridGeometry( + scrollOffset: 0, + crossAxisOffset: 0, + mainAxisExtent: 16, + crossAxisExtent: 18, + ); + + expect(geometry.crossAxisExtent, expectedGeometry.crossAxisExtent); + expect(geometry.scrollOffset, expectedGeometry.scrollOffset); + expect(geometry.mainAxisExtent, expectedGeometry.mainAxisExtent); + expect(geometry.crossAxisExtent, expectedGeometry.crossAxisExtent); + }, + ); + + testWidgets('getGeometryForChildIndex when index is not equal to 0', ( + tester, + ) async { final headerTileLayout = HeaderGridTileLayout( crossAxisCount: 1, mainAxisStride: 14, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/html_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/html_test.dart index 08c39ffdf..64c788f59 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/html_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/html_test.dart @@ -52,9 +52,7 @@ void main() { group('hyperlinks', () { testWidgets('does not launch the url when it is null', (tester) async { const String? link = null; - const block = HtmlBlock( - content: 'flutter.dev', - ); + const block = HtmlBlock(content: 'flutter.dev'); await tester.pumpApp(Html(block: block)); @@ -69,9 +67,7 @@ void main() { testWidgets('does not launch the url when it is invalid', (tester) async { const link = '::Not valid URI::'; - const block = HtmlBlock( - content: 'flutter.dev', - ); + const block = HtmlBlock(content: 'flutter.dev'); await tester.pumpApp(Html(block: block)); @@ -86,9 +82,7 @@ void main() { testWidgets('launches the url when it is a valid url', (tester) async { const link = 'https://flutter.dev'; - const block = HtmlBlock( - content: 'flutter.dev', - ); + const block = HtmlBlock(content: 'flutter.dev'); await tester.pumpApp(Html(block: block)); @@ -122,8 +116,9 @@ void main() { (widget) => widget is flutter_html.Html && widget.style['p']!.generateTextStyle() == - flutter_html.Style.fromTextStyle(theme.textTheme.bodyLarge!) - .generateTextStyle(), + flutter_html.Style.fromTextStyle( + theme.textTheme.bodyLarge!, + ).generateTextStyle(), ), findsOneWidget, ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/image_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/image_test.dart index 332442dc2..4a43e2792 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/image_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/image_test.dart @@ -12,9 +12,7 @@ void main() { testWidgets('renders InlineImage with correct image', (tester) async { const block = ImageBlock(imageUrl: 'imageUrl'); - await tester.pumpApp( - Image(block: block), - ); + await tester.pumpApp(Image(block: block)); expect( find.byWidgetPredicate( @@ -28,9 +26,7 @@ void main() { testWidgets('renders ProgressIndicator when loading', (tester) async { const block = ImageBlock(imageUrl: 'imageUrl'); - await tester.pumpApp( - Image(block: block), - ); + await tester.pumpApp(Image(block: block)); expect(find.byType(ProgressIndicator), findsOneWidget); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_grid_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_grid_test.dart index 5d47572de..b3e333fff 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_grid_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_grid_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -10,14 +10,16 @@ import '../helpers/helpers.dart'; void main() { group('PostGrid', () { + const category = Category(id: 'science', name: 'Science'); final postGridTileBlock = PostGridTileBlock( id: '842e3193-86d2-4069-a7e6-f769faa6f970', - category: PostCategory.science, + categoryId: category.id, author: 'SciTechDaily', publishedAt: DateTime(2022, 5, 5), imageUrl: 'https://scitechdaily.com/images/Qubit-Platform-Single-Electron-on-Solid-Neon.jpg', - title: 'The Quest for an Ideal Quantum Bit: New Qubit Breakthrough Could ' + title: + 'The Quest for an Ideal Quantum Bit: New Qubit Breakthrough Could ' 'Revolutionize Quantum Computing', action: NavigateToArticleAction( articleId: '842e3193-86d2-4069-a7e6-f769faa6f970', @@ -26,7 +28,7 @@ void main() { testWidgets('renders correctly 5 PostGridTiles', (tester) async { final gridGroupBlock = PostGridGroupBlock( - category: PostCategory.science, + categoryId: category.id, tiles: List.generate(5, (index) => postGridTileBlock), ); @@ -36,6 +38,7 @@ void main() { slivers: [ PostGrid( gridGroupBlock: gridGroupBlock, + categoryName: category.name, premiumText: 'Premium', ), ], @@ -53,7 +56,7 @@ void main() { testWidgets('renders correctly 1 PostGridTile', (tester) async { final gridGroupBlock = PostGridGroupBlock( - category: PostCategory.science, + categoryId: category.id, tiles: [postGridTileBlock], ); @@ -61,7 +64,11 @@ void main() { () async => tester.pumpContentThemedApp( CustomScrollView( slivers: [ - PostGrid(gridGroupBlock: gridGroupBlock, premiumText: 'Premium'), + PostGrid( + gridGroupBlock: gridGroupBlock, + categoryName: category.name, + premiumText: 'Premium', + ), ], ), ), @@ -74,7 +81,7 @@ void main() { testWidgets('handles empty tiles list', (tester) async { final gridGroupBlock = PostGridGroupBlock( - category: PostCategory.science, + categoryId: category.id, tiles: [], ); @@ -82,7 +89,11 @@ void main() { () async => tester.pumpContentThemedApp( CustomScrollView( slivers: [ - PostGrid(gridGroupBlock: gridGroupBlock, premiumText: 'Premium'), + PostGrid( + gridGroupBlock: gridGroupBlock, + categoryName: category.name, + premiumText: 'Premium', + ), ], ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_large/post_large_image_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_large/post_large_image_test.dart index a9feda820..c8ba2860d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_large/post_large_image_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_large/post_large_image_test.dart @@ -9,8 +9,7 @@ import '../../helpers/helpers.dart'; void main() { group('PostLargeImage', () { - testWidgets( - 'renders InlineImage ' + testWidgets('renders InlineImage ' 'when isContentOverlaid is false', (tester) async { final postLargeImage = PostLargeImage( imageUrl: 'url', @@ -25,8 +24,7 @@ void main() { expect(find.byType(InlineImage), findsOneWidget); }); - testWidgets( - 'renders OverlaidImage ' + testWidgets('renders OverlaidImage ' 'when isContentOverlaid is true', (tester) async { final postLargeImage = PostLargeImage( imageUrl: 'url', @@ -41,8 +39,7 @@ void main() { expect(find.byType(OverlaidImage), findsOneWidget); }); - testWidgets( - 'renders LockIcon ' + testWidgets('renders LockIcon ' 'when isLocked is true and ' 'when isContentOverlaid is true', (tester) async { final postLargeImage = PostLargeImage( @@ -58,8 +55,7 @@ void main() { expect(find.byType(LockIcon), findsOneWidget); }); - testWidgets( - 'renders LockIcon ' + testWidgets('renders LockIcon ' 'when isLocked is true and ' 'when isContentOverlaid is false', (tester) async { final postLargeImage = PostLargeImage( @@ -75,8 +71,7 @@ void main() { expect(find.byType(LockIcon), findsOneWidget); }); - testWidgets( - 'does not render LockIcon ' + testWidgets('does not render LockIcon ' 'when isLocked is false and ' 'when isContentOverlaid is true', (tester) async { final postLargeImage = PostLargeImage( @@ -92,8 +87,7 @@ void main() { expect(find.byType(LockIcon), findsNothing); }); - testWidgets( - 'does not render LockIcon ' + testWidgets('does not render LockIcon ' 'when isLocked is false and ' 'when isContentOverlaid is false', (tester) async { final postLargeImage = PostLargeImage( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_large/post_large_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_large/post_large_test.dart index 1d23c9fc6..6227d3ea7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_large/post_large_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_large/post_large_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -11,14 +11,15 @@ import '../../helpers/helpers.dart'; void main() { const id = '499305f6-5096-4051-afda-824dcfc7df23'; - const category = PostCategory.technology; + const category = Category(id: 'technology', name: 'Technology'); const author = 'Sean Hollister'; final publishedAt = DateTime(2022, 3, 9); const imageUrl = 'https://cdn.vox-cdn.com/thumbor/OTpmptgr7XcTVAJ27UBvIxl0vrg=' '/0x146:2040x1214/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset' '/file/22049166/shollister_201117_4303_0003.0.jpg'; - const title = 'Nvidia and AMD GPUs are returning to shelves ' + const title = + 'Nvidia and AMD GPUs are returning to shelves ' 'and prices are finally falling'; group('PostLarge', () { @@ -27,12 +28,11 @@ void main() { ); group('renders correctly overlaid ', () { - testWidgets( - 'showing LockIcon ' + testWidgets('showing LockIcon ' 'when isLocked is true', (tester) async { final technologyPostLarge = PostLargeBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -46,6 +46,7 @@ void main() { children: [ PostLarge( block: technologyPostLarge, + categoryName: category.name, premiumText: 'Premium', isLocked: true, ), @@ -59,12 +60,11 @@ void main() { expect(find.byType(LockIcon), findsOneWidget); }); - testWidgets( - 'not showing LockIcon ' + testWidgets('not showing LockIcon ' 'when isLocked is false', (tester) async { final technologyPostLarge = PostLargeBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -78,6 +78,7 @@ void main() { children: [ PostLarge( block: technologyPostLarge, + categoryName: category.name, premiumText: 'Premium', isLocked: false, ), @@ -93,12 +94,11 @@ void main() { }); group('renders correctly in column ', () { - testWidgets( - 'showing LockIcon ' + testWidgets('showing LockIcon ' 'when isLocked is true', (tester) async { final technologyPostLarge = PostLargeBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -112,6 +112,7 @@ void main() { children: [ PostLarge( block: technologyPostLarge, + categoryName: category.name, premiumText: 'Premium', isLocked: true, ), @@ -125,12 +126,11 @@ void main() { expect(find.byType(LockIcon), findsOneWidget); }); - testWidgets( - 'not showing LockIcon ' + testWidgets('not showing LockIcon ' 'when isLocked is false', (tester) async { final technologyPostLarge = PostLargeBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -144,6 +144,7 @@ void main() { children: [ PostLarge( block: technologyPostLarge, + categoryName: category.name, premiumText: 'Premium', isLocked: false, ), @@ -165,7 +166,7 @@ void main() { final technologyPostLarge = PostLargeBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -180,6 +181,7 @@ void main() { children: [ PostLarge( block: technologyPostLarge, + categoryName: category.name, premiumText: 'Premium', onPressed: actions.add, isLocked: false, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_medium/post_medium_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_medium/post_medium_test.dart index f045ce0c1..ee79e781a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_medium/post_medium_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_medium/post_medium_test.dart @@ -16,20 +16,19 @@ void main() { // Change the default factory databaseFactory = databaseFactoryFfi; - setUpTolerantComparator( - 'test/src/post_medium/post_medium_test.dart', - ); + setUpTolerantComparator('test/src/post_medium/post_medium_test.dart'); setUpMockPathProvider(); }); group('PostMedium', () { const id = '82c49bf1-946d-4920-a801-302291f367b5'; - const category = PostCategory.sports; + const category = Category(id: 'sports', name: 'Sports'); const author = 'Tom Dierberger'; final publishedAt = DateTime(2022, 3, 10); const imageUrl = 'https://www.nbcsports.com/sites/rsnunited/files/styles/metatags_opengraph/public/article/hero/pat-bev-ja-morant-USA.jpg'; - const title = 'No Man’s Sky’s new Outlaws update ' + const title = + 'No Man’s Sky’s new Outlaws update ' 'lets players go full space pirate'; const description = 'No Man’s Sky’s newest update, Outlaws, is now live, and it lets ' @@ -39,7 +38,7 @@ void main() { testWidgets('renders correctly overlaid layout', (tester) async { final postMediumBlock = PostMediumBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -63,7 +62,7 @@ void main() { testWidgets('renders correctly description layout', (tester) async { final postMediumBlock = PostMediumBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -88,7 +87,7 @@ void main() { final actions = []; final postMediumBlock = PostMediumBlock( id: id, - category: category, + categoryId: category.id, author: author, publishedAt: publishedAt, imageUrl: imageUrl, @@ -99,10 +98,7 @@ void main() { await mockNetworkImages( () async => tester.pumpContentThemedApp( - PostMedium( - block: postMediumBlock, - onPressed: actions.add, - ), + PostMedium(block: postMediumBlock, onPressed: actions.add), ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_small_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_small_test.dart index b09c2654a..35c4537d1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_small_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/post_small_test.dart @@ -22,6 +22,7 @@ void main() { }); testWidgets('renders correctly without image', (tester) async { + const category = Category(id: 'technology', name: 'Technology'); await mockNetworkImages(() async { await tester.pumpApp( Column( @@ -29,9 +30,10 @@ void main() { PostSmall( block: PostSmallBlock( id: 'id', - category: PostCategory.technology, + categoryId: category.id, publishedAt: DateTime(2022, 03, 12), - title: 'Nvidia and AMD GPUs are ' + title: + 'Nvidia and AMD GPUs are ' 'returning to shelves and prices ' 'are finally falling', author: 'Sean Hollister', @@ -48,6 +50,7 @@ void main() { }); testWidgets('renders correctly with image', (tester) async { + const category = Category(id: 'technology', name: 'Technology'); await mockNetworkImages(() async { await tester.pumpApp( Column( @@ -55,9 +58,10 @@ void main() { PostSmall( block: PostSmallBlock( id: 'id', - category: PostCategory.technology, + categoryId: category.id, publishedAt: DateTime(2022, 03, 12), - title: 'Nvidia and AMD GPUs are ' + title: + 'Nvidia and AMD GPUs are ' 'returning to shelves and prices ' 'are finally falling', author: 'Sean Hollister', @@ -78,6 +82,7 @@ void main() { testWidgets('onPressed is called with action when tapped', (tester) async { final action = NavigateToArticleAction(articleId: 'id'); final actions = []; + const category = Category(id: 'technology', name: 'Technology'); await mockNetworkImages(() async { await tester.pumpApp( @@ -86,7 +91,7 @@ void main() { PostSmall( block: PostSmallBlock( id: 'id', - category: PostCategory.technology, + categoryId: category.id, publishedAt: DateTime(2022, 03, 12), title: 'Nvidia and AMD GPUs are returning to shelves and prices ' diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/section_header_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/section_header_test.dart index 15d200603..c270caf4e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/section_header_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/section_header_test.dart @@ -13,9 +13,7 @@ void main() { testWidgets('renders correctly without action', (tester) async { const widget = Center( - child: SectionHeader( - block: SectionHeaderBlock(title: 'example'), - ), + child: SectionHeader(block: SectionHeaderBlock(title: 'example')), ); await tester.pumpApp(widget); @@ -27,11 +25,12 @@ void main() { }); testWidgets('renders correctly with action', (tester) async { + const category = Category(id: 'sports', name: 'Sports'); const widget = Center( child: SectionHeader( block: SectionHeaderBlock( title: 'example', - action: NavigateToFeedCategoryAction(category: Category.top), + action: NavigateToFeedCategoryAction(category: category), ), ), ); @@ -45,15 +44,13 @@ void main() { }); testWidgets('onPressed is called with action on tap', (tester) async { + const category = Category(id: 'sports', name: 'Sports'); final actions = []; - const action = NavigateToFeedCategoryAction(category: Category.top); + const action = NavigateToFeedCategoryAction(category: category); final widget = Center( child: SectionHeader( - block: const SectionHeaderBlock( - title: 'example', - action: action, - ), + block: const SectionHeaderBlock(title: 'example', action: action), onPressed: actions.add, ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/slideshow_introduction_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/slideshow_introduction_test.dart index 6984fba0a..881e411dd 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/slideshow_introduction_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/slideshow_introduction_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -63,8 +63,7 @@ void main() { expect( find.byWidgetPredicate( - (widget) => - widget is SlideshowCategory && widget.isIntroduction == true, + (widget) => widget is SlideshowCategory && widget.isIntroduction, ), findsOneWidget, ); @@ -99,40 +98,37 @@ void main() { ); }); - testWidgets( - 'onPressed is called with action when tapped', - (tester) async { - final action = NavigateToArticleAction(articleId: 'articleId'); - final actions = []; - - final block = SlideshowIntroductionBlock( - title: 'title', - coverImageUrl: imageUrl, - action: action, - ); - - await mockNetworkImages( - () async => tester.pumpContentThemedApp( - SingleChildScrollView( - child: Column( - children: [ - SlideshowIntroduction( - block: block, - slideshowText: 'slideshowText', - onPressed: actions.add, - ), - ], - ), + testWidgets('onPressed is called with action when tapped', (tester) async { + final action = NavigateToArticleAction(articleId: 'articleId'); + final actions = []; + + final block = SlideshowIntroductionBlock( + title: 'title', + coverImageUrl: imageUrl, + action: action, + ); + + await mockNetworkImages( + () async => tester.pumpContentThemedApp( + SingleChildScrollView( + child: Column( + children: [ + SlideshowIntroduction( + block: block, + slideshowText: 'slideshowText', + onPressed: actions.add, + ), + ], ), ), - ); + ), + ); - await tester.ensureVisible(find.byType(SlideshowIntroduction)); - await tester.tap(find.byType(SlideshowIntroduction)); - await tester.pump(); + await tester.ensureVisible(find.byType(SlideshowIntroduction)); + await tester.tap(find.byType(SlideshowIntroduction)); + await tester.pump(); - expect(actions, equals([action])); - }, - ); + expect(actions, equals([action])); + }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/slideshow_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/slideshow_test.dart index 398648eb3..99d251c7f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/slideshow_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/slideshow_test.dart @@ -26,7 +26,8 @@ void main() { 3, (index) => SlideBlock( caption: 'Oink, Oink', - description: 'Domestic pigs come in different colors, ' + description: + 'Domestic pigs come in different colors, ' 'shapes and sizes. They are usually pink, but little pigs kept as' ' pets (pot-bellied pigs) are sometimes other colors. ' ' Pigs roll in mud to protect themselves from sunlight. ' diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/spacer_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/spacer_test.dart index b3f505eb8..c8dd9d0ec 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/spacer_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/spacer_test.dart @@ -9,16 +9,12 @@ import '../helpers/helpers.dart'; void main() { group('Spacer', () { - setUpAll( - () => setUpTolerantComparator('test/src/spacer_test.dart'), - ); + setUpAll(() => setUpTolerantComparator('test/src/spacer_test.dart')); testWidgets('renders correctly for extraSmall spacing', (tester) async { final widget = ColoredBox( color: Colors.black, - child: Spacer( - block: SpacerBlock(spacing: Spacing.extraSmall), - ), + child: Spacer(block: SpacerBlock(spacing: Spacing.extraSmall)), ); await tester.pumpApp(widget); @@ -32,9 +28,7 @@ void main() { testWidgets('renders correctly for small spacing', (tester) async { final widget = ColoredBox( color: Colors.black, - child: Spacer( - block: SpacerBlock(spacing: Spacing.small), - ), + child: Spacer(block: SpacerBlock(spacing: Spacing.small)), ); await tester.pumpApp(widget); @@ -48,9 +42,7 @@ void main() { testWidgets('renders correctly for medium spacing', (tester) async { final widget = ColoredBox( color: Colors.black, - child: Spacer( - block: SpacerBlock(spacing: Spacing.medium), - ), + child: Spacer(block: SpacerBlock(spacing: Spacing.medium)), ); await tester.pumpApp(widget); @@ -64,9 +56,7 @@ void main() { testWidgets('renders correctly for large spacing', (tester) async { final widget = ColoredBox( color: Colors.black, - child: Spacer( - block: SpacerBlock(spacing: Spacing.large), - ), + child: Spacer(block: SpacerBlock(spacing: Spacing.large)), ); await tester.pumpApp(widget); @@ -80,9 +70,7 @@ void main() { testWidgets('renders correctly for veryLarge spacing', (tester) async { final widget = ColoredBox( color: Colors.black, - child: Spacer( - block: SpacerBlock(spacing: Spacing.veryLarge), - ), + child: Spacer(block: SpacerBlock(spacing: Spacing.veryLarge)), ); await tester.pumpApp(widget); @@ -96,9 +84,7 @@ void main() { testWidgets('renders correctly for extraLarge spacing', (tester) async { final widget = ColoredBox( color: Colors.black, - child: Spacer( - block: SpacerBlock(spacing: Spacing.extraLarge), - ), + child: Spacer(block: SpacerBlock(spacing: Spacing.extraLarge)), ); await tester.pumpApp(widget); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_caption_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_caption_test.dart index aabb906f4..5a80f38a0 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_caption_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_caption_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -9,12 +9,9 @@ import '../helpers/helpers.dart'; void main() { group('TextCaption', () { - setUpAll( - () => setUpTolerantComparator('test/src/text_caption_test.dart'), - ); + setUpAll(() => setUpTolerantComparator('test/src/text_caption_test.dart')); - testWidgets( - 'renders correctly ' + testWidgets('renders correctly ' 'with default normal color', (tester) async { final widget = Center( child: TextCaption( @@ -33,8 +30,7 @@ void main() { ); }); - testWidgets( - 'renders correctly ' + testWidgets('renders correctly ' 'with default light color', (tester) async { final widget = Center( child: TextCaption( @@ -53,8 +49,7 @@ void main() { ); }); - testWidgets( - 'renders correctly ' + testWidgets('renders correctly ' 'with provided normal color', (tester) async { final widget = Center( child: TextCaption( @@ -62,9 +57,7 @@ void main() { text: 'Text caption', color: TextCaptionColor.normal, ), - colorValues: const { - TextCaptionColor.normal: Colors.green, - }, + colorValues: const {TextCaptionColor.normal: Colors.green}, ), ); @@ -76,8 +69,7 @@ void main() { ); }); - testWidgets( - 'renders correctly ' + testWidgets('renders correctly ' 'with provided light color', (tester) async { final widget = Center( child: TextCaption( @@ -85,9 +77,7 @@ void main() { text: 'Text caption', color: TextCaptionColor.light, ), - colorValues: const { - TextCaptionColor.light: Colors.green, - }, + colorValues: const {TextCaptionColor.light: Colors.green}, ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_headline_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_headline_test.dart index 97be6b2e2..1b88bdfee 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_headline_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_headline_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -9,15 +9,11 @@ import '../helpers/helpers.dart'; void main() { group('TextHeadline', () { - setUpAll( - () => setUpTolerantComparator('test/src/text_headline_test.dart'), - ); + setUpAll(() => setUpTolerantComparator('test/src/text_headline_test.dart')); testWidgets('renders correctly', (tester) async { final widget = Center( - child: TextHeadline( - block: TextHeadlineBlock(text: 'Title text'), - ), + child: TextHeadline(block: TextHeadlineBlock(text: 'Title text')), ); await tester.pumpApp(widget); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_lead_paragraph_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_lead_paragraph_test.dart index 6b6507a8f..448db1ad3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_lead_paragraph_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_lead_paragraph_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_paragraph_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_paragraph_test.dart index 59aa59169..0c6fc5e90 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_paragraph_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/text_paragraph_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -14,9 +14,7 @@ void main() { ); testWidgets('renders correctly', (tester) async { final widget = Center( - child: TextParagraph( - block: TextParagraphBlock(text: 'text Paragraph'), - ), + child: TextParagraph(block: TextParagraphBlock(text: 'text Paragraph')), ); await tester.pumpApp(widget); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/trending_story_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/trending_story_test.dart index 166fff79f..3538b902c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/trending_story_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/trending_story_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors - import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail_image_network/mocktail_image_network.dart'; import 'package:news_blocks/news_blocks.dart'; @@ -21,13 +19,14 @@ void main() { }); testWidgets('renders correctly', (tester) async { + const category = Category(id: 'technology', name: 'Technology'); await mockNetworkImages(() async { final widget = TrendingStory( title: 'TRENDING', block: TrendingStoryBlock( content: PostSmallBlock( id: 'id', - category: PostCategory.technology, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/video_introduction_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/video_introduction_test.dart index 87439714b..a263120ba 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/video_introduction_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/video_introduction_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors - import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail_image_network/mocktail_image_network.dart'; @@ -11,28 +9,25 @@ import 'package:video_player_platform_interface/video_player_platform_interface. import '../helpers/helpers.dart'; void main() { - const category = PostCategory.technology; + const category = Category(id: 'technology', name: 'Technology'); const videoUrl = 'https://cdn.vox-cdn.com/thumbor/OTpmptgr7XcTVAJ27UBvIxl0vrg=' '/0x146:2040x1214/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset' '/file/22049166/shollister_201117_4303_0003.0.jpg'; - const title = 'Nvidia and AMD GPUs are returning to shelves ' + const title = + 'Nvidia and AMD GPUs are returning to shelves ' 'and prices are finally falling'; group('VideoIntroduction', () { - setUpAll( - () { - final fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); - VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; - setUpTolerantComparator( - 'test/src/video_introduction_test.dart', - ); - }, - ); + setUpAll(() { + final fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); + VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; + setUpTolerantComparator('test/src/video_introduction_test.dart'); + }); testWidgets('renders correctly', (tester) async { final technologyVideoIntroduction = VideoIntroductionBlock( - category: category, + categoryId: category.id, videoUrl: videoUrl, title: title, ); @@ -42,7 +37,10 @@ void main() { SingleChildScrollView( child: Column( children: [ - VideoIntroduction(block: technologyVideoIntroduction), + VideoIntroduction( + block: technologyVideoIntroduction, + categoryName: category.name, + ), ], ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/video_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/video_test.dart index a1779b899..0ab7bd7b2 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/video_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/video_test.dart @@ -10,19 +10,15 @@ import '../helpers/helpers.dart'; void main() { group('Video', () { - setUp( - () { - final fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); - VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; - }, - ); + setUp(() { + final fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); + VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; + }); testWidgets('renders InlineVideo with correct video', (tester) async { const block = VideoBlock(videoUrl: 'videoUrl'); - await tester.pumpApp( - Video(block: block), - ); + await tester.pumpApp(Video(block: block)); expect( find.byWidgetPredicate( @@ -36,9 +32,7 @@ void main() { testWidgets('renders ProgressIndicator when loading', (tester) async { const block = VideoBlock(videoUrl: 'videoUrl'); - await tester.pumpWidget( - Video(block: block), - ); + await tester.pumpWidget(Video(block: block)); expect( find.byType(ProgressIndicator, skipOffstage: false), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/ads_retry_policy_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/ads_retry_policy_test.dart index c083b12e2..dcf4c601f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/ads_retry_policy_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/ads_retry_policy_test.dart @@ -21,10 +21,7 @@ void main() { group('getIntervalForRetry', () { test('returns correct values', () { final adsRetryPolicy = AdsRetryPolicy(); - expect( - adsRetryPolicy.getIntervalForRetry(0), - equals(Duration.zero), - ); + expect(adsRetryPolicy.getIntervalForRetry(0), equals(Duration.zero)); for (var i = 1; i <= adsRetryPolicy.maxRetryCount; i++) { expect( adsRetryPolicy.getIntervalForRetry(i), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/banner_ad_container_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/banner_ad_container_test.dart index 41977eae7..7e25f8b17 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/banner_ad_container_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/banner_ad_container_test.dart @@ -11,10 +11,7 @@ void main() { group('BannerAdContainer', () { testWidgets('renders ColoredBox', (tester) async { await tester.pumpApp( - BannerAdContainer( - size: BannerAdSize.normal, - child: SizedBox(), - ), + BannerAdContainer(size: BannerAdSize.normal, child: SizedBox()), ); expect(find.byKey(Key('bannerAdContainer_coloredBox')), findsOneWidget); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/banner_ad_content_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/banner_ad_content_test.dart index 339cbb751..26e1d9b0c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/banner_ad_content_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/banner_ad_content_test.dart @@ -17,8 +17,6 @@ class MockBannerAd extends Mock implements BannerAd {} class MockPlatform extends Mock implements Platform {} -class MockLoadAdError extends Mock implements LoadAdError {} - void main() { group('BannerAdContent', () { late AdSize capturedSize; @@ -39,21 +37,21 @@ void main() { when(() => ad.size).thenReturn(AdSize.banner); when(ad.dispose).thenAnswer((_) async {}); - adBuilder = ({ - required AdSize size, - required String adUnitId, - required BannerAdListener listener, - required AdRequest request, - }) { - capturedSize = size; - capturedAdUnitId = adUnitId; - capturedListener = listener; - return ad; - }; + adBuilder = + ({ + required AdSize size, + required String adUnitId, + required BannerAdListener listener, + required AdRequest request, + }) { + capturedSize = size; + capturedAdUnitId = adUnitId; + capturedListener = listener; + return ad; + }; }); - testWidgets( - 'loads ad object correctly ' + testWidgets('loads ad object correctly ' 'on Android', (tester) async { await tester.pumpApp( BannerAdContent( @@ -68,8 +66,7 @@ void main() { verify(ad.load).called(1); }); - testWidgets( - 'loads ad object correctly ' + testWidgets('loads ad object correctly ' 'on iOS', (tester) async { when(() => platform.isIOS).thenReturn(true); when(() => platform.isAndroid).thenReturn(false); @@ -81,10 +78,10 @@ void main() { currentPlatform: platform, anchoredAdaptiveAdSizeProvider: (orientation, width) async => AnchoredAdaptiveBannerAdSize( - Orientation.portrait, - width: 100, - height: 100, - ), + Orientation.portrait, + width: 100, + height: 100, + ), ), ); @@ -93,8 +90,7 @@ void main() { verify(ad.load).called(1); }); - testWidgets( - 'loads ad object correctly ' + testWidgets('loads ad object correctly ' 'with provided adUnitId', (tester) async { const adUnitId = 'adUnitId'; @@ -106,10 +102,10 @@ void main() { currentPlatform: platform, anchoredAdaptiveAdSizeProvider: (orientation, width) async => AnchoredAdaptiveBannerAdSize( - Orientation.portrait, - width: 100, - height: 100, - ), + Orientation.portrait, + width: 100, + height: 100, + ), ), ); @@ -118,8 +114,7 @@ void main() { verify(ad.load).called(1); }); - testWidgets( - 'renders ProgressIndicator ' + testWidgets('renders ProgressIndicator ' 'when ad is loading ' 'and showProgressIndicator is true', (tester) async { await tester.pumpApp( @@ -134,8 +129,7 @@ void main() { expect(find.byType(AdWidget), findsNothing); }); - testWidgets( - 'does not render ProgressIndicator ' + testWidgets('does not render ProgressIndicator ' 'when ad is loading ' 'and showProgressIndicator is false', (tester) async { await tester.pumpApp( @@ -199,8 +193,9 @@ void main() { ); }); - testWidgets('uses AdSize.mediumRectangle for BannerAdSize.large', - (tester) async { + testWidgets('uses AdSize.mediumRectangle for BannerAdSize.large', ( + tester, + ) async { const expectedSize = AdSize.mediumRectangle; when(() => ad.size).thenReturn(expectedSize); @@ -225,8 +220,9 @@ void main() { ); }); - testWidgets('uses AdSize(300, 600) for BannerAdSize.extraLarge', - (tester) async { + testWidgets('uses AdSize(300, 600) for BannerAdSize.extraLarge', ( + tester, + ) async { const expectedSize = AdSize(width: 300, height: 600); when(() => ad.size).thenReturn(expectedSize); @@ -266,28 +262,30 @@ void main() { verify(ad.dispose).called(1); }); - testWidgets( - 'retries loading ad based on AdsRetryPolicy ' + testWidgets('retries loading ad based on AdsRetryPolicy ' 'and renders placeholder ' 'when ad fails to load', (tester) async { final fakeAsync = FakeAsync(); const adFailedToLoadTitle = 'adFailedToLoadTitle'; final adsRetryPolicy = AdsRetryPolicy(); - adBuilder = ({ - required AdSize size, - required String adUnitId, - required BannerAdListener listener, - required AdRequest request, - }) { - Future.microtask( - () => listener.onAdFailedToLoad!( - ad, - LoadAdError(0, 'domain', 'message', null), - ), - ); - return ad; - }; + adBuilder = + ({ + required AdSize size, + required String adUnitId, + required BannerAdListener listener, + required AdRequest request, + }) { + unawaited( + Future.microtask( + () => listener.onAdFailedToLoad!( + ad, + LoadAdError(0, 'domain', 'message', null), + ), + ), + ); + return ad; + }; final errors = []; FlutterError.onError = (error) => errors.add(error.exception); @@ -333,8 +331,7 @@ void main() { ); }); - testWidgets( - 'throws BannerAdFailedToGetSizeException ' + testWidgets('throws BannerAdFailedToGetSizeException ' 'for BannerAdSize.anchoredAdaptive ' 'when ad size fails to load', (tester) async { await tester.pumpApp( @@ -346,10 +343,7 @@ void main() { ), ); - expect( - tester.takeException(), - isA(), - ); + expect(tester.takeException(), isA()); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/inline_image_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/inline_image_test.dart index a996b1ad2..5163afafd 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/inline_image_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/inline_image_test.dart @@ -17,8 +17,7 @@ void main() { BuildContext context, String url, DownloadProgress progress, - ) => - ProgressIndicator(); + ) => ProgressIndicator(); await mockNetworkImages( () async => tester.pumpApp( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/inline_video_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/inline_video_test.dart index c293ed6b1..7a038b4d2 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/inline_video_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/inline_video_test.dart @@ -10,12 +10,10 @@ import '../../helpers/helpers.dart'; void main() { group('InlineVideo', () { - setUp( - () { - final fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); - VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; - }, - ); + setUp(() { + final fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); + VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; + }); testWidgets('renders progressIndicator when loading', (tester) async { const progressIndicatorKey = Key('__progress_indicator__'); @@ -70,8 +68,7 @@ void main() { expect(find.byType(VideoPlayer), findsOneWidget); }); - testWidgets( - 'plays video when tapped ' + testWidgets('plays video when tapped ' 'and video is not playing', (tester) async { final controller = FakeVideoPlayerController(); @@ -94,8 +91,7 @@ void main() { expect(controller.pauseCalled, equals(0)); }); - testWidgets( - 'pauses video when tapped ' + testWidgets('pauses video when tapped ' 'and video is playing', (tester) async { final controller = FakeVideoPlayerController(); @@ -112,9 +108,7 @@ void main() { ), ); - controller - ..textureId = 123 - ..value = controller.value.copyWith(isPlaying: true); + controller.value = controller.value.copyWith(isPlaying: true); await tester.pump(); @@ -147,7 +141,7 @@ void main() { class FakeVideoPlayerController extends ValueNotifier implements VideoPlayerController { FakeVideoPlayerController() - : super(VideoPlayerValue(duration: Duration.zero)); + : super(VideoPlayerValue(duration: Duration.zero)); int playCalled = 0; int pauseCalled = 0; @@ -157,9 +151,6 @@ class FakeVideoPlayerController extends ValueNotifier super.dispose(); } - @override - int textureId = VideoPlayerController.kUninitializedTextureId; - @override String get dataSource => ''; @@ -212,6 +203,12 @@ class FakeVideoPlayerController extends ValueNotifier Future setClosedCaptionFile( Future? closedCaptionFile, ) async {} + + @override + int get playerId => 1; + + @override + VideoViewType get viewType => VideoViewType.textureView; } Future _loadClosedCaption() async => diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/overlaid_image_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/overlaid_image_test.dart index a9bf5ee24..b1387b9f8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/overlaid_image_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/overlaid_image_test.dart @@ -19,10 +19,7 @@ void main() { () async => tester.pumpContentThemedApp(overlaidImage), ); - expect( - find.byKey(const Key('overlaidImage_stack')), - findsOneWidget, - ); + expect(find.byKey(const Key('overlaidImage_stack')), findsOneWidget); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_category_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_category_test.dart index 15acd26a1..a6a07d334 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_category_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_category_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter_test/flutter_test.dart'; import 'package:news_blocks_ui/src/widgets/widgets.dart'; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_premium_category_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_premium_category_test.dart index d68ea8b60..1f1d800ab 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_premium_category_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_premium_category_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter_test/flutter_test.dart'; import 'package:news_blocks_ui/src/widgets/widgets.dart'; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_test.dart index c6f3cbd46..ce2d12000 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_content_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'dart:async'; @@ -18,10 +18,7 @@ void main() { await tester.pumpContentThemedApp(testPostContent); - expect( - find.text('title'), - findsOneWidget, - ); + expect(find.text('title'), findsOneWidget); }); testWidgets('renders category when isPremium is false', (tester) async { @@ -33,18 +30,11 @@ void main() { await tester.pumpContentThemedApp(testPostContent); - expect( - find.byType(PostContentCategory), - findsOneWidget, - ); - expect( - find.byType(PostContentPremiumCategory), - findsNothing, - ); + expect(find.byType(PostContentCategory), findsOneWidget); + expect(find.byType(PostContentPremiumCategory), findsNothing); }); - testWidgets( - 'renders category and premium ' + testWidgets('renders category and premium ' 'when isPremium is true', (tester) async { final testPostContent = PostContent( title: 'title', @@ -55,18 +45,11 @@ void main() { await tester.pumpContentThemedApp(testPostContent); - expect( - find.byType(PostContentCategory), - findsOneWidget, - ); - expect( - find.byType(PostContentPremiumCategory), - findsOneWidget, - ); + expect(find.byType(PostContentCategory), findsOneWidget); + expect(find.byType(PostContentPremiumCategory), findsOneWidget); }); - testWidgets( - 'renders premium without category ' + testWidgets('renders premium without category ' 'when isPremium is true and categoryName is empty', (tester) async { final testPostContent = PostContent( title: 'title', @@ -77,14 +60,8 @@ void main() { await tester.pumpContentThemedApp(testPostContent); - expect( - find.byType(PostContentCategory), - findsNothing, - ); - expect( - find.byType(PostContentPremiumCategory), - findsOneWidget, - ); + expect(find.byType(PostContentCategory), findsNothing); + expect(find.byType(PostContentPremiumCategory), findsOneWidget); }); group('renders PostFooter', () { @@ -97,10 +74,7 @@ void main() { await tester.pumpContentThemedApp(testPostContent); - expect( - find.byType(PostFooter), - findsOneWidget, - ); + expect(find.byType(PostFooter), findsOneWidget); }); testWidgets('when publishedAt provided', (tester) async { @@ -112,10 +86,7 @@ void main() { await tester.pumpContentThemedApp(testPostContent); - expect( - find.byType(PostFooter), - findsOneWidget, - ); + expect(find.byType(PostFooter), findsOneWidget); }); testWidgets('when onShare provided', (tester) async { @@ -128,10 +99,7 @@ void main() { await tester.pumpContentThemedApp(testPostContent); - expect( - find.byType(PostFooter), - findsOneWidget, - ); + expect(find.byType(PostFooter), findsOneWidget); }); testWidgets('calls onShare when clicked on share icon', (tester) async { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_footer_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_footer_test.dart index 55b594297..9a9187e57 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_footer_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/post_footer_test.dart @@ -13,9 +13,7 @@ void main() { ); testWidgets('renders correctly', (tester) async { - await tester.pumpApp( - PostFooter(), - ); + await tester.pumpApp(PostFooter()); await expectLater( find.byType(PostFooter), @@ -24,9 +22,7 @@ void main() { }); testWidgets('renders correctly with author', (tester) async { - await tester.pumpApp( - PostFooter(author: 'Author'), - ); + await tester.pumpApp(PostFooter(author: 'Author')); await expectLater( find.byType(PostFooter), @@ -35,9 +31,7 @@ void main() { }); testWidgets('renders correctly with publishedAt date', (tester) async { - await tester.pumpApp( - PostFooter(publishedAt: DateTime(2022, 5, 9)), - ); + await tester.pumpApp(PostFooter(publishedAt: DateTime(2022, 5, 9))); await expectLater( find.byType(PostFooter), @@ -45,8 +39,9 @@ void main() { ); }); - testWidgets('renders correctly with author and publishedAt date', - (tester) async { + testWidgets('renders correctly with author and publishedAt date', ( + tester, + ) async { await tester.pumpApp( PostFooter(author: 'Author', publishedAt: DateTime(2022, 5, 9)), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/progress_indicator_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/progress_indicator_test.dart index 46bd8e76e..f52435f03 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/progress_indicator_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/progress_indicator_test.dart @@ -9,12 +9,9 @@ import '../../helpers/helpers.dart'; void main() { group('ProgressIndicator', () { - testWidgets( - 'renders ColoredBox ' + testWidgets('renders ColoredBox ' 'with gainsboro color as default', (tester) async { - await tester.pumpApp( - ProgressIndicator(progress: 0.5), - ); + await tester.pumpApp(ProgressIndicator(progress: 0.5)); expect( find.byWidgetPredicate( @@ -25,14 +22,11 @@ void main() { ); }); - testWidgets( - 'renders ColoredBox ' + testWidgets('renders ColoredBox ' 'with provided color', (tester) async { const color = Colors.orange; - await tester.pumpApp( - ProgressIndicator(progress: 0.5, color: color), - ); + await tester.pumpApp(ProgressIndicator(progress: 0.5, color: color)); expect( find.byWidgetPredicate( @@ -45,9 +39,7 @@ void main() { testWidgets('renders CircularProgressIndicator', (tester) async { const progress = 0.5; - await tester.pumpApp( - ProgressIndicator(progress: progress), - ); + await tester.pumpApp(ProgressIndicator(progress: progress)); expect( find.descendant( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/share_button_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/share_button_test.dart index 70b9a602c..fc499485b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/share_button_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/share_button_test.dart @@ -10,10 +10,7 @@ void main() { testWidgets('calls onPress when tapped', (tester) async { final completer = Completer(); await tester.pumpContentThemedApp( - ShareButton( - shareText: 'shareText', - onPressed: completer.complete, - ), + ShareButton(shareText: 'shareText', onPressed: completer.complete), ); await tester.tap(find.byType(ShareButton)); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/slideshow_category_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/slideshow_category_test.dart index 1e321b72a..aa2329351 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/slideshow_category_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_blocks_ui/test/src/widgets/slideshow_category_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: unnecessary_const, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:flutter_test/flutter_test.dart'; import 'package:news_blocks_ui/src/widgets/widgets.dart'; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/lib/src/news_repository.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/lib/src/news_repository.dart index b9a6dbd37..ca73b9fb7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/lib/src/news_repository.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/lib/src/news_repository.dart @@ -52,27 +52,26 @@ class RelevantSearchFailure extends NewsFailure { /// {@endtemplate} class NewsRepository { /// {@macro news_repository} - const NewsRepository({ - required {{project_name.pascalCase()}}ApiClient apiClient, - }) : _apiClient = apiClient; + const NewsRepository({required {{project_name.pascalCase()}}ApiClient apiClient}) + : _apiClient = apiClient; final {{project_name.pascalCase()}}ApiClient _apiClient; /// Requests news feed metadata. /// /// Supported parameters: - /// * [category] - the desired news [Category]. + /// * [categoryId] - the desired news category. /// * [limit] - The number of results to return. /// * [offset] - The (zero-based) offset of the first item /// in the collection to return. Future getFeed({ - Category? category, + String? categoryId, int? limit, int? offset, }) async { try { return await _apiClient.getFeed( - category: category, + categoryId: categoryId, limit: limit, offset: offset, ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/pubspec.yaml index b464adc23..83a687c70 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: equatable: ^2.0.3 @@ -15,4 +15,4 @@ dev_dependencies: coverage: ^1.2.0 mocktail: ^1.0.2 test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/test/src/news_repository_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/test/src/news_repository_test.dart index 89c5d2c7f..bf0d4a6d8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/test/src/news_repository_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/news_repository/test/src/news_repository_test.dart @@ -18,22 +18,19 @@ void main() { }); group('getFeed', () { - test( - 'returns FeedResponse ' + test('returns FeedResponse ' 'from ApiClient.getFeed', () { + const category = Category(id: 'sports', name: 'Sports'); final feed = [ SpacerBlock(spacing: Spacing.extraLarge), DividerHorizontalBlock(), ]; - final feedResponse = FeedResponse( - feed: feed, - totalCount: feed.length, - ); + final feedResponse = FeedResponse(feed: feed, totalCount: feed.length); when( () => apiClient.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), offset: any(named: 'offset'), limit: any(named: 'limit'), ), @@ -41,7 +38,7 @@ void main() { expect( newsRepository.getFeed( - category: Category.entertainment, + categoryId: category.id, offset: 10, limit: 20, ), @@ -49,45 +46,38 @@ void main() { ); verify( - () => apiClient.getFeed( - category: Category.entertainment, - offset: 10, - limit: 20, - ), + () => + apiClient.getFeed(categoryId: category.id, offset: 10, limit: 20), ).called(1); }); - test( - 'throws GetFeedFailure ' + test('throws GetFeedFailure ' 'if ApiClient.getFeed fails', () async { when( () => apiClient.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), offset: any(named: 'offset'), limit: any(named: 'limit'), ), ).thenThrow(Exception); - expect( - newsRepository.getFeed, - throwsA(isA()), - ); + expect(newsRepository.getFeed, throwsA(isA())); }); }); group('getCategories', () { - test( - 'returns CategoriesResponse ' + test('returns CategoriesResponse ' 'from ApiClient.getCategories', () { + const sportsCategory = Category(id: 'sports', name: 'Sports'); + const topCategory = Category(id: 'top', name: 'Top'); + const categoriesResponse = CategoriesResponse( - categories: [ - Category.top, - Category.health, - ], + categories: [topCategory, sportsCategory], ); - when(apiClient.getCategories) - .thenAnswer((_) async => categoriesResponse); + when( + apiClient.getCategories, + ).thenAnswer((_) async => categoriesResponse); expect( newsRepository.getCategories(), @@ -97,8 +87,7 @@ void main() { verify(apiClient.getCategories).called(1); }); - test( - 'throws GetCategoriesFailure ' + test('throws GetCategoriesFailure ' 'if ApiClient.getCategories fails', () async { when(apiClient.getCategories).thenThrow(Exception); @@ -112,27 +101,19 @@ void main() { group('subscribeToNewsletter', () { test('completes from ApiClient.subscribeToNewsletter', () { when( - () => apiClient.subscribeToNewsletter( - email: any(named: 'email'), - ), + () => apiClient.subscribeToNewsletter(email: any(named: 'email')), ).thenAnswer((_) async {}); final response = newsRepository.subscribeToNewsletter(email: 'email'); expect(response, completes); - verify( - () => apiClient.subscribeToNewsletter( - email: 'email', - ), - ).called(1); + verify(() => apiClient.subscribeToNewsletter(email: 'email')).called(1); }); test('throws GetFeedFailure if ApiClient.subscribeToNewsletter', () { when( - () => apiClient.subscribeToNewsletter( - email: any(named: 'email'), - ), + () => apiClient.subscribeToNewsletter(email: any(named: 'email')), ).thenThrow(Exception); final response = newsRepository.subscribeToNewsletter(email: 'email'); @@ -142,8 +123,7 @@ void main() { }); group('popularSearch', () { - test( - 'returns PopularSearchResponse ' + test('returns PopularSearchResponse ' 'from ApiClient.popularSearch', () { const popularResponse = PopularSearchResponse( articles: [ @@ -163,8 +143,7 @@ void main() { verify(apiClient.popularSearch).called(1); }); - test( - 'throws PopularSearchFailure ' + test('throws PopularSearchFailure ' 'if ApiClient.popularSearch fails', () async { when(apiClient.popularSearch).thenThrow(Exception); @@ -176,8 +155,7 @@ void main() { }); group('relevantSearch', () { - test( - 'returns RelevantSearchResponse ' + test('returns RelevantSearchResponse ' 'from ApiClient.relevantSearch', () { const relevantResponse = RelevantSearchResponse( articles: [ @@ -187,23 +165,25 @@ void main() { topics: ['Topic'], ); - when(() => apiClient.relevantSearch(term: '')) - .thenAnswer((_) async => relevantResponse); + when( + () => apiClient.relevantSearch(term: ''), + ).thenAnswer((_) async => relevantResponse); expect( newsRepository.relevantSearch(term: ''), completion(equals(relevantResponse)), ); - verify(() => apiClient.relevantSearch(term: any(named: 'term'))) - .called(1); + verify( + () => apiClient.relevantSearch(term: any(named: 'term')), + ).called(1); }); - test( - 'throws RelevantSearchFailure ' + test('throws RelevantSearchFailure ' 'if ApiClient.relevantSearch fails', () async { - when(() => apiClient.relevantSearch(term: any(named: 'term'))) - .thenThrow(Exception); + when( + () => apiClient.relevantSearch(term: any(named: 'term')), + ).thenThrow(Exception); expect( newsRepository.relevantSearch(term: 'term'), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/lib/src/firebase_notifications_client.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/lib/src/firebase_notifications_client.dart index 81420a49f..c3bae2ea7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/lib/src/firebase_notifications_client.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/lib/src/firebase_notifications_client.dart @@ -18,10 +18,7 @@ class FirebaseNotificationsClient implements NotificationsClient { try { await _firebaseMessaging.subscribeToTopic(category); } catch (error, stackTrace) { - Error.throwWithStackTrace( - SubscribeToCategoryFailure(error), - stackTrace, - ); + Error.throwWithStackTrace(SubscribeToCategoryFailure(error), stackTrace); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/pubspec.yaml index 63deab839..f52e356f1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/pubspec.yaml @@ -4,10 +4,10 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: - firebase_messaging: ^15.1.1 + firebase_messaging: ^15.2.8 flutter: sdk: flutter notifications_client: @@ -17,4 +17,4 @@ dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/test/src/firebase_notifications_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/test/src/firebase_notifications_client_test.dart index 4857ddca1..14461c22d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/test/src/firebase_notifications_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/firebase_notifications_client/test/src/firebase_notifications_client_test.dart @@ -1,4 +1,3 @@ -// ignore_for_file: prefer_const_constructors import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_notifications_client/firebase_notifications_client.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -32,8 +31,7 @@ void main() { verify(() => firebaseMessaging.subscribeToTopic(category)).called(1); }); - test( - 'throws SubscribeToCategoryFailure ' + test('throws SubscribeToCategoryFailure ' 'when FirebaseMessaging.subscribeToTopic fails', () async { when( () => firebaseMessaging.subscribeToTopic(category), @@ -54,12 +52,12 @@ void main() { await firebaseNotificationsClient.unsubscribeFromCategory(category); - verify(() => firebaseMessaging.unsubscribeFromTopic(category)) - .called(1); + verify( + () => firebaseMessaging.unsubscribeFromTopic(category), + ).called(1); }); - test( - 'throws UnsubscribeFromCategoryFailure ' + test('throws UnsubscribeFromCategoryFailure ' 'when FirebaseMessaging.unsubscribeFromTopic fails', () async { when( () => firebaseMessaging.unsubscribeFromTopic(category), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/pubspec.yaml index 5e882d5fa..60e678a5e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/pubspec.yaml @@ -4,8 +4,8 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dev_dependencies: test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/test/src/notifications_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/test/src/notifications_client_test.dart index 28a64c413..43383222b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/test/src/notifications_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/notifications_client/test/src/notifications_client_test.dart @@ -12,16 +12,10 @@ void main() { }); test('exports SubscribeToCategoryFailure', () { - expect( - () => SubscribeToCategoryFailure('oops'), - returnsNormally, - ); + expect(() => SubscribeToCategoryFailure('oops'), returnsNormally); }); test('exports UnsubscribeFromCategoryFailure', () { - expect( - () => UnsubscribeFromCategoryFailure('oops'), - returnsNormally, - ); + expect(() => UnsubscribeFromCategoryFailure('oops'), returnsNormally); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/lib/src/one_signal_notifications_client.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/lib/src/one_signal_notifications_client.dart index 4ea51666f..25c459c39 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/lib/src/one_signal_notifications_client.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/lib/src/one_signal_notifications_client.dart @@ -6,9 +6,8 @@ import 'package:onesignal_flutter/onesignal_flutter.dart'; /// {@endtemplate} class OneSignalNotificationsClient implements NotificationsClient { /// {@macro one_signal_notifications_client} - const OneSignalNotificationsClient({ - required OneSignal oneSignal, - }) : _oneSignal = oneSignal; + const OneSignalNotificationsClient({required OneSignal oneSignal}) + : _oneSignal = oneSignal; /// OneSignal instance. final OneSignal _oneSignal; @@ -18,10 +17,7 @@ class OneSignalNotificationsClient implements NotificationsClient { try { await _oneSignal.sendTag(category, true); } catch (error, stackTrace) { - Error.throwWithStackTrace( - SubscribeToCategoryFailure(error), - stackTrace, - ); + Error.throwWithStackTrace(SubscribeToCategoryFailure(error), stackTrace); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/pubspec.yaml index 818168206..cb55ed2c4 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: flutter: @@ -17,4 +17,4 @@ dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/test/src/one_signal_notifications_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/test/src/one_signal_notifications_client_test.dart index 89fe0b472..772beaa9a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/test/src/one_signal_notifications_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_client/one_signal_notifications_client/test/src/one_signal_notifications_client_test.dart @@ -1,4 +1,3 @@ -// ignore_for_file: prefer_const_constructors import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:notifications_client/notifications_client.dart'; @@ -32,8 +31,7 @@ void main() { verify(() => oneSignal.sendTag(category, true)).called(1); }); - test( - 'throws SubscribeToCategoryFailure ' + test('throws SubscribeToCategoryFailure ' 'when OneSignal.deleteTag fails', () async { when( () => oneSignal.sendTag(category, true), @@ -46,28 +44,30 @@ void main() { }); }); - group('when OneSignalNotificationsClient.unsubscribeFromCategory called', - () { - test('calls OneSignal.deleteTag', () async { - when(() => oneSignal.deleteTag(category)).thenAnswer((_) async => {}); + group( + 'when OneSignalNotificationsClient.unsubscribeFromCategory called', + () { + test('calls OneSignal.deleteTag', () async { + when(() => oneSignal.deleteTag(category)).thenAnswer((_) async => {}); - await oneSignalNotificationsClient.unsubscribeFromCategory(category); + await oneSignalNotificationsClient.unsubscribeFromCategory(category); - verify(() => oneSignal.deleteTag(category)).called(1); - }); + verify(() => oneSignal.deleteTag(category)).called(1); + }); - test( - 'throws UnsubscribeFromCategoryFailure ' - 'when OneSignal.deleteTag fails', () async { - when( - () => oneSignal.deleteTag(category), - ).thenAnswer((_) async => throw Exception()); + test('throws UnsubscribeFromCategoryFailure ' + 'when OneSignal.deleteTag fails', () async { + when( + () => oneSignal.deleteTag(category), + ).thenAnswer((_) async => throw Exception()); - expect( - () => oneSignalNotificationsClient.unsubscribeFromCategory(category), - throwsA(isA()), - ); - }); - }); + expect( + () => + oneSignalNotificationsClient.unsubscribeFromCategory(category), + throwsA(isA()), + ); + }); + }, + ); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/lib/src/notifications_repository.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/lib/src/notifications_repository.dart index b364d6c36..b90631ba0 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/lib/src/notifications_repository.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/lib/src/notifications_repository.dart @@ -80,10 +80,10 @@ class NotificationsRepository { required NotificationsStorage storage, required NotificationsClient notificationsClient, required {{project_name.pascalCase()}}ApiClient apiClient, - }) : _permissionClient = permissionClient, - _storage = storage, - _notificationsClient = notificationsClient, - _apiClient = apiClient { + }) : _permissionClient = permissionClient, + _storage = storage, + _notificationsClient = notificationsClient, + _apiClient = apiClient { unawaited(_initializeCategoriesPreferences()); } @@ -118,8 +118,8 @@ class NotificationsRepository { // Request the permission if the permission status is denied. if (permissionStatus.isDenied) { - final updatedPermissionStatus = - await _permissionClient.requestNotifications(); + final updatedPermissionStatus = await _permissionClient + .requestNotifications(); if (!updatedPermissionStatus.isGranted) { return; } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/lib/src/notifications_storage.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/lib/src/notifications_storage.dart index 756b6bd9e..25425ab06 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/lib/src/notifications_storage.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/lib/src/notifications_storage.dart @@ -14,9 +14,7 @@ abstract class NotificationsStorageKeys { /// {@endtemplate} class NotificationsStorage { /// {@macro notifications_storage} - const NotificationsStorage({ - required Storage storage, - }) : _storage = storage; + const NotificationsStorage({required Storage storage}) : _storage = storage; final Storage _storage; @@ -29,8 +27,9 @@ class NotificationsStorage { /// Fetches the notifications enabled value from Storage. Future fetchNotificationsEnabled() async => - (await _storage.read(key: NotificationsStorageKeys.notificationsEnabled)) - ?.parseBool() ?? + (await _storage.read( + key: NotificationsStorageKeys.notificationsEnabled, + ))?.parseBool() ?? false; /// Sets the categories preferences to [categories] in Storage. @@ -38,7 +37,7 @@ class NotificationsStorage { required Set categories, }) async { final categoriesEncoded = json.encode( - categories.map((category) => category.name).toList(), + categories.map((category) => category.toJson()).toList(), ); await _storage.write( key: NotificationsStorageKeys.categoriesPreferences, @@ -54,9 +53,12 @@ class NotificationsStorage { if (categories == null) { return null; } - return List.from(json.decode(categories) as List) - .map(Category.fromString) - .toSet(); + + return List.from(json.decode(categories) as List).map(( + value, + ) { + return Category.fromJson(value as Map); + }).toSet(); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/pubspec.yaml index 1a6add1d1..a7cfee7ec 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: equatable: ^2.0.3 @@ -24,4 +24,4 @@ dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/test/src/notifications_repository_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/test/src/notifications_repository_test.dart index 5edc71f6b..1ce4154c6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/test/src/notifications_repository_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/test/src/notifications_repository_test.dart @@ -35,13 +35,12 @@ void main() { notificationsClient = MockNotificationsClient(); apiClient = Mock{{project_name.pascalCase()}}ApiClient(); - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.denied); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.denied); when( - () => storage.setNotificationsEnabled( - enabled: any(named: 'enabled'), - ), + () => storage.setNotificationsEnabled(enabled: any(named: 'enabled')), ).thenAnswer((_) async {}); when( @@ -52,31 +51,36 @@ void main() { when(storage.fetchNotificationsEnabled).thenAnswer((_) async => false); - when(storage.fetchCategoriesPreferences) - .thenAnswer((_) async => {Category.top}); + when( + storage.fetchCategoriesPreferences, + ).thenAnswer((_) async => {Category(id: 'top', name: 'Top')}); - when(() => notificationsClient.subscribeToCategory(any())) - .thenAnswer((_) async {}); - when(() => notificationsClient.unsubscribeFromCategory(any())) - .thenAnswer((_) async {}); + when( + () => notificationsClient.subscribeToCategory(any()), + ).thenAnswer((_) async {}); + when( + () => notificationsClient.unsubscribeFromCategory(any()), + ).thenAnswer((_) async {}); - when(apiClient.getCategories).thenAnswer( - (_) async => CategoriesResponse(categories: []), - ); + when( + apiClient.getCategories, + ).thenAnswer((_) async => CategoriesResponse(categories: [])); }); group('constructor', () { - test( - 'initializes categories preferences ' + test('initializes categories preferences ' 'from {{project_name.pascalCase()}}ApiClient.getCategories', () async { when(storage.fetchCategoriesPreferences).thenAnswer((_) async => null); final completer = Completer(); - const categories = [Category.top, Category.technology]; + const categories = [ + Category(id: 'top', name: 'Top'), + Category(id: 'technology', name: 'Technology'), + ]; - when(apiClient.getCategories).thenAnswer( - (_) async => CategoriesResponse(categories: categories), - ); + when( + apiClient.getCategories, + ).thenAnswer((_) async => CategoriesResponse(categories: categories)); when( () => storage.setCategoriesPreferences( @@ -94,46 +98,45 @@ void main() { await expectLater(completer.future, completes); verify( - () => storage.setCategoriesPreferences( - categories: categories.toSet(), - ), + () => + storage.setCategoriesPreferences(categories: categories.toSet()), ).called(1); }); - test( - 'throws an InitializeCategoriesPreferencesFailure ' + test('throws an InitializeCategoriesPreferencesFailure ' 'when initialization fails', () async { Object? caughtError; - await runZonedGuarded(() async { - when(storage.fetchCategoriesPreferences).thenThrow(Exception()); - - final _ = NotificationsRepository( - permissionClient: permissionClient, - storage: storage, - notificationsClient: notificationsClient, - apiClient: apiClient, - ); - }, (error, stackTrace) { - caughtError = error; - }); - - expect( - caughtError, - isA(), + await runZonedGuarded( + () async { + when(storage.fetchCategoriesPreferences).thenThrow(Exception()); + + final _ = NotificationsRepository( + permissionClient: permissionClient, + storage: storage, + notificationsClient: notificationsClient, + apiClient: apiClient, + ); + }, + (error, stackTrace) { + caughtError = error; + }, ); + + expect(caughtError, isA()); }); }); group('toggleNotifications', () { group('when enable is true', () { - test( - 'calls openPermissionSettings on PermissionClient ' + test('calls openPermissionSettings on PermissionClient ' 'when PermissionStatus is permanentlyDenied', () async { - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.permanentlyDenied); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.permanentlyDenied); - when(permissionClient.openPermissionSettings) - .thenAnswer((_) async => true); + when( + permissionClient.openPermissionSettings, + ).thenAnswer((_) async => true); await NotificationsRepository( permissionClient: permissionClient, @@ -145,14 +148,15 @@ void main() { verify(permissionClient.openPermissionSettings).called(1); }); - test( - 'calls openPermissionSettings on PermissionClient ' + test('calls openPermissionSettings on PermissionClient ' 'when PermissionStatus is restricted', () async { - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.restricted); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.restricted); - when(permissionClient.openPermissionSettings) - .thenAnswer((_) async => true); + when( + permissionClient.openPermissionSettings, + ).thenAnswer((_) async => true); await NotificationsRepository( permissionClient: permissionClient, @@ -164,14 +168,15 @@ void main() { verify(permissionClient.openPermissionSettings).called(1); }); - test( - 'calls requestNotifications on PermissionClient ' + test('calls requestNotifications on PermissionClient ' 'when PermissionStatus is denied', () async { - when(permissionClient.requestNotifications) - .thenAnswer((_) async => PermissionStatus.granted); + when( + permissionClient.requestNotifications, + ).thenAnswer((_) async => PermissionStatus.granted); - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.denied); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.denied); await NotificationsRepository( permissionClient: permissionClient, @@ -184,16 +189,18 @@ void main() { }); test('subscribes to categories preferences', () async { - const categoriesPreferences = { - Category.top, - Category.technology, + final categoriesPreferences = { + Category(id: 'top', name: 'Top'), + Category(id: 'technology', name: 'Technology'), }; - when(storage.fetchCategoriesPreferences) - .thenAnswer((_) async => categoriesPreferences); + when( + storage.fetchCategoriesPreferences, + ).thenAnswer((_) async => categoriesPreferences); - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.granted); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.granted); await NotificationsRepository( permissionClient: permissionClient, @@ -203,16 +210,17 @@ void main() { ).toggleNotifications(enable: true); for (final category in categoriesPreferences) { - verify(() => notificationsClient.subscribeToCategory(category.name)) - .called(1); + verify( + () => notificationsClient.subscribeToCategory(category.name), + ).called(1); } }); - test( - 'calls setNotificationsEnabled with true ' + test('calls setNotificationsEnabled with true ' 'on NotificationsStorage', () async { - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.granted); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.granted); await NotificationsRepository( permissionClient: permissionClient, @@ -229,13 +237,14 @@ void main() { group('when enabled is false', () { test('unsubscribes from categories preferences', () async { - const categoriesPreferences = { - Category.top, - Category.technology, + final categoriesPreferences = { + Category(id: 'top', name: 'Top'), + Category(id: 'technology', name: 'Technology'), }; - when(storage.fetchCategoriesPreferences) - .thenAnswer((_) async => categoriesPreferences); + when( + storage.fetchCategoriesPreferences, + ).thenAnswer((_) async => categoriesPreferences); await NotificationsRepository( permissionClient: permissionClient, @@ -251,8 +260,7 @@ void main() { } }); - test( - 'calls setNotificationsEnabled with false ' + test('calls setNotificationsEnabled with false ' 'on NotificationsStorage', () async { await NotificationsRepository( permissionClient: permissionClient, @@ -267,8 +275,7 @@ void main() { }); }); - test( - 'throws a ToggleNotificationsFailure ' + test('throws a ToggleNotificationsFailure ' 'when toggling notifications fails', () async { when(permissionClient.notificationsStatus).thenThrow(Exception()); @@ -285,12 +292,12 @@ void main() { }); group('fetchNotificationsEnabled', () { - test( - 'returns true ' + test('returns true ' 'when the notification permission is granted ' 'and the notification setting is enabled', () async { - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.granted); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.granted); when(storage.fetchNotificationsEnabled).thenAnswer((_) async => true); @@ -304,12 +311,12 @@ void main() { expect(result, isTrue); }); - test( - 'returns false ' + test('returns false ' 'when the notification permission is not granted ' 'and the notification setting is enabled', () async { - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.denied); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.denied); when(storage.fetchNotificationsEnabled).thenAnswer((_) async => true); @@ -323,12 +330,12 @@ void main() { expect(result, isFalse); }); - test( - 'returns false ' + test('returns false ' 'when the notification permission is not granted ' 'and the notification setting is disabled', () async { - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.denied); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.denied); when(storage.fetchNotificationsEnabled).thenAnswer((_) async => false); @@ -342,12 +349,12 @@ void main() { expect(result, isFalse); }); - test( - 'returns false ' + test('returns false ' 'when the notification permission is granted ' 'and the notification setting is disabled', () async { - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.granted); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.granted); when(storage.fetchNotificationsEnabled).thenAnswer((_) async => false); @@ -361,8 +368,7 @@ void main() { expect(result, isFalse); }); - test( - 'throws a FetchNotificationsEnabledFailure ' + test('throws a FetchNotificationsEnabledFailure ' 'when fetching notifications enabled fails', () async { when(permissionClient.notificationsStatus).thenThrow(Exception()); @@ -379,9 +385,9 @@ void main() { }); group('setCategoriesPreferences', () { - const categoriesPreferences = { - Category.top, - Category.technology, + final categoriesPreferences = { + Category(id: 'top', name: 'Top'), + Category(id: 'technology', name: 'Technology'), }; test('calls setCategoriesPreferences on NotificationsStorage', () async { @@ -409,13 +415,14 @@ void main() { }); test('unsubscribes from previous categories preferences', () async { - const previousCategoriesPreferences = { - Category.health, - Category.entertainment, + final previousCategoriesPreferences = { + Category(id: 'top', name: 'Top'), + Category(id: 'technology', name: 'Technology'), }; - when(storage.fetchCategoriesPreferences) - .thenAnswer((_) async => previousCategoriesPreferences); + when( + storage.fetchCategoriesPreferences, + ).thenAnswer((_) async => previousCategoriesPreferences); await NotificationsRepository( permissionClient: permissionClient, @@ -431,16 +438,17 @@ void main() { } }); - test( - 'subscribes to categories preferences ' + test('subscribes to categories preferences ' 'when notifications are enabled', () async { - when(storage.fetchCategoriesPreferences) - .thenAnswer((_) async => categoriesPreferences); + when( + storage.fetchCategoriesPreferences, + ).thenAnswer((_) async => categoriesPreferences); when(storage.fetchNotificationsEnabled).thenAnswer((_) async => true); - when(permissionClient.notificationsStatus) - .thenAnswer((_) async => PermissionStatus.granted); + when( + permissionClient.notificationsStatus, + ).thenAnswer((_) async => PermissionStatus.granted); await NotificationsRepository( permissionClient: permissionClient, @@ -450,13 +458,13 @@ void main() { ).setCategoriesPreferences(categoriesPreferences); for (final category in categoriesPreferences) { - verify(() => notificationsClient.subscribeToCategory(category.name)) - .called(1); + verify( + () => notificationsClient.subscribeToCategory(category.name), + ).called(1); } }); - test( - 'throws a SetCategoriesPreferencesFailure ' + test('throws a SetCategoriesPreferencesFailure ' 'when setting categories preferences fails', () async { when( () => storage.setCategoriesPreferences( @@ -477,44 +485,49 @@ void main() { }); group('fetchCategoriesPreferences', () { - const categoriesPreferences = { - Category.top, - Category.technology, + final categoriesPreferences = { + Category(id: 'top', name: 'Top'), + Category(id: 'technology', name: 'Technology'), }; - test('returns categories preferences from NotificationsStorage', - () async { - when(storage.fetchCategoriesPreferences) - .thenAnswer((_) async => categoriesPreferences); + test( + 'returns categories preferences from NotificationsStorage', + () async { + when( + storage.fetchCategoriesPreferences, + ).thenAnswer((_) async => categoriesPreferences); - final actualPreferences = await NotificationsRepository( - permissionClient: permissionClient, - storage: storage, - notificationsClient: notificationsClient, - apiClient: apiClient, - ).fetchCategoriesPreferences(); + final actualPreferences = await NotificationsRepository( + permissionClient: permissionClient, + storage: storage, + notificationsClient: notificationsClient, + apiClient: apiClient, + ).fetchCategoriesPreferences(); - expect(actualPreferences, equals(categoriesPreferences)); - }); + expect(actualPreferences, equals(categoriesPreferences)); + }, + ); test( - 'returns null ' - 'when categories preferences do not exist in NotificationsStorage', - () async { - when(storage.fetchCategoriesPreferences).thenAnswer((_) async => null); - - final preferences = await NotificationsRepository( - permissionClient: permissionClient, - storage: storage, - notificationsClient: notificationsClient, - apiClient: apiClient, - ).fetchCategoriesPreferences(); + 'returns null ' + 'when categories preferences do not exist in NotificationsStorage', + () async { + when( + storage.fetchCategoriesPreferences, + ).thenAnswer((_) async => null); + + final preferences = await NotificationsRepository( + permissionClient: permissionClient, + storage: storage, + notificationsClient: notificationsClient, + apiClient: apiClient, + ).fetchCategoriesPreferences(); - expect(preferences, isNull); - }); + expect(preferences, isNull); + }, + ); - test( - 'throws a FetchCategoriesPreferencesFailure ' + test('throws a FetchCategoriesPreferencesFailure ' 'when read fails', () async { final notificationsRepository = NotificationsRepository( permissionClient: permissionClient, @@ -523,8 +536,9 @@ void main() { apiClient: apiClient, ); - when(storage.fetchCategoriesPreferences) - .thenThrow(StorageException(Error())); + when( + storage.fetchCategoriesPreferences, + ).thenThrow(StorageException(Error())); expect( notificationsRepository.fetchCategoriesPreferences, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/test/src/notifications_storage_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/test/src/notifications_storage_test.dart index 339377a77..6d8264048 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/test/src/notifications_storage_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/notifications_repository/test/src/notifications_storage_test.dart @@ -27,8 +27,9 @@ void main() { test('saves the value in Storage', () async { const enabled = true; - await NotificationsStorage(storage: storage) - .setNotificationsEnabled(enabled: enabled); + await NotificationsStorage( + storage: storage, + ).setNotificationsEnabled(enabled: enabled); verify( () => storage.write( @@ -46,13 +47,13 @@ void main() { storage.read(key: NotificationsStorageKeys.notificationsEnabled), ).thenAnswer((_) async => 'true'); - final result = await NotificationsStorage(storage: storage) - .fetchNotificationsEnabled(); + final result = await NotificationsStorage( + storage: storage, + ).fetchNotificationsEnabled(); verify( - () => storage.read( - key: NotificationsStorageKeys.notificationsEnabled, - ), + () => + storage.read(key: NotificationsStorageKeys.notificationsEnabled), ).called(1); expect(result, isTrue); @@ -64,13 +65,13 @@ void main() { storage.read(key: NotificationsStorageKeys.notificationsEnabled), ).thenAnswer((_) async => null); - final result = await NotificationsStorage(storage: storage) - .fetchNotificationsEnabled(); + final result = await NotificationsStorage( + storage: storage, + ).fetchNotificationsEnabled(); verify( - () => storage.read( - key: NotificationsStorageKeys.notificationsEnabled, - ), + () => + storage.read(key: NotificationsStorageKeys.notificationsEnabled), ).called(1); expect(result, isFalse); @@ -79,20 +80,20 @@ void main() { group('setCategoriesPreferences', () { test('saves the value in Storage', () async { - const preferences = { - Category.top, - Category.health, + final preferences = { + const Category(id: 'top', name: 'Top'), + const Category(id: 'technology', name: 'Technology'), }; - await NotificationsStorage(storage: storage).setCategoriesPreferences( - categories: preferences, - ); + await NotificationsStorage( + storage: storage, + ).setCategoriesPreferences(categories: preferences); verify( () => storage.write( key: NotificationsStorageKeys.categoriesPreferences, value: json.encode( - preferences.map((category) => category.name).toList(), + preferences.map((category) => category.toJson()).toList(), ), ), ).called(1); @@ -101,9 +102,9 @@ void main() { group('fetchCategoriesPreferences', () { test('returns the value from Storage', () async { - const preferences = { - Category.health, - Category.entertainment, + final preferences = { + const Category(id: 'top', name: 'Top'), + const Category(id: 'technology', name: 'Technology'), }; when( @@ -111,17 +112,17 @@ void main() { storage.read(key: NotificationsStorageKeys.categoriesPreferences), ).thenAnswer( (_) async => json.encode( - preferences.map((preference) => preference.name).toList(), + preferences.map((preference) => preference.toJson()).toList(), ), ); - final result = await NotificationsStorage(storage: storage) - .fetchCategoriesPreferences(); + final result = await NotificationsStorage( + storage: storage, + ).fetchCategoriesPreferences(); verify( - () => storage.read( - key: NotificationsStorageKeys.categoriesPreferences, - ), + () => + storage.read(key: NotificationsStorageKeys.categoriesPreferences), ).called(1); expect(result, equals(preferences)); @@ -133,13 +134,13 @@ void main() { storage.read(key: NotificationsStorageKeys.categoriesPreferences), ).thenAnswer((_) async => null); - final result = await NotificationsStorage(storage: storage) - .fetchCategoriesPreferences(); + final result = await NotificationsStorage( + storage: storage, + ).fetchCategoriesPreferences(); verify( - () => storage.read( - key: NotificationsStorageKeys.categoriesPreferences, - ), + () => + storage.read(key: NotificationsStorageKeys.categoriesPreferences), ).called(1); expect(result, isNull); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/pubspec.yaml index e9ca38d53..d92c7c6c7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/pubspec.yaml @@ -4,11 +4,11 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: equatable: ^2.0.3 dev_dependencies: test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/test/src/package_info_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/test/src/package_info_client_test.dart index 40c44e48e..e235d66b3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/test/src/package_info_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/package_info_client/test/src/package_info_client_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: prefer_const_constructors - import 'package:package_info_client/package_info_client.dart'; import 'package:test/test.dart'; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/pubspec.yaml index 4e8209266..fb0a250e9 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: flutter: @@ -15,4 +15,4 @@ dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/test/src/permission_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/test/src/permission_client_test.dart index d57610a3d..bc9cd9037 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/test/src/permission_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/permission_client/test/src/permission_client_test.dart @@ -46,32 +46,16 @@ void main() { }); Matcher permissionWasRequested(Permission permission) => contains( - isA() - .having( - (c) => c.method, - 'method', - 'requestPermissions', - ) - .having( - (c) => c.arguments, - 'arguments', - contains(permission.value), - ), - ); + isA() + .having((c) => c.method, 'method', 'requestPermissions') + .having((c) => c.arguments, 'arguments', contains(permission.value)), + ); Matcher permissionWasChecked(Permission permission) => contains( - isA() - .having( - (c) => c.method, - 'method', - 'checkPermissionStatus', - ) - .having( - (c) => c.arguments, - 'arguments', - equals(permission.value), - ), - ); + isA() + .having((c) => c.method, 'method', 'checkPermissionStatus') + .having((c) => c.arguments, 'arguments', equals(permission.value)), + ); group('requestNotifications', () { test('calls correct method', () async { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/lib/src/purchase_client.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/lib/src/purchase_client.dart index 35800a873..fceeca9ac 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/lib/src/purchase_client.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/lib/src/purchase_client.dart @@ -19,13 +19,14 @@ extension PurchaseDetailsCopyWith on PurchaseDetails { bool? pendingCompletePurchase, }) => PurchaseDetails( - purchaseID: purchaseID ?? this.purchaseID, - productID: productID ?? this.productID, - verificationData: verificationData ?? this.verificationData, - transactionDate: transactionDate ?? this.transactionDate, - status: status ?? this.status, - )..pendingCompletePurchase = - pendingCompletePurchase ?? this.pendingCompletePurchase; + purchaseID: purchaseID ?? this.purchaseID, + productID: productID ?? this.productID, + verificationData: verificationData ?? this.verificationData, + transactionDate: transactionDate ?? this.transactionDate, + status: status ?? this.status, + ) + ..pendingCompletePurchase = + pendingCompletePurchase ?? this.pendingCompletePurchase; } /// {@template purchase_client} @@ -52,8 +53,7 @@ class PurchaseClient implements InAppPurchase { Future buyConsumable({ required PurchaseParam purchaseParam, bool autoConsume = true, - }) => - throw UnimplementedError(); + }) => throw UnimplementedError(); @override Future buyNonConsumable({required PurchaseParam purchaseParam}) async { @@ -120,7 +120,6 @@ class PurchaseClient implements InAppPurchase { } @override - /// This method is not implemented as the scope of this template /// is limited. Future countryCode() { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/pubspec.yaml index 2cce28df8..99d25f769 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/pubspec.yaml @@ -4,10 +4,10 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: - clock: ^1.1.0 + clock: ^1.1.1 equatable: ^2.0.3 flutter: sdk: flutter @@ -17,4 +17,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/test/src/purchase_client_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/test/src/purchase_client_test.dart index bd820acb7..bbb12ba8b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/test/src/purchase_client_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/purchase_client/test/src/purchase_client_test.dart @@ -33,14 +33,12 @@ void main() { ), ); - test( - 'returns the same object ' + test('returns the same object ' 'when no parameters are passed', () { expect(purchaseDetails.copyWith().equals(purchaseDetails), isTrue); }); - test( - 'sets purchaseID ' + test('sets purchaseID ' 'when purchaseID is passed', () { expect( purchaseDetails.copyWith(purchaseID: 'newId').purchaseID, @@ -48,22 +46,19 @@ void main() { ); }); - test( - 'sets productID ' + test('sets productID ' 'when productID is passed', () { expect(purchaseDetails.copyWith(productID: 'newId').productID, 'newId'); }); - test( - 'sets status ' + test('sets status ' 'when status is passed', () { expect( purchaseDetails.copyWith(status: PurchaseStatus.purchased).status, PurchaseStatus.purchased, ); }); - test( - 'sets transactionDate ' + test('sets transactionDate ' 'when transactionDate is passed', () { expect( purchaseDetails.copyWith(transactionDate: 'newDate').transactionDate, @@ -71,8 +66,7 @@ void main() { ); }); - test( - 'sets verificationData ' + test('sets verificationData ' 'when verificationData is passed', () { final verificationData = PurchaseVerificationData( localVerificationData: 'newLocal', @@ -87,8 +81,7 @@ void main() { ); }); - test( - 'sets pendingCompletePurchase ' + test('sets pendingCompletePurchase ' 'when pendingCompletePurchase is passed', () { expect( purchaseDetails @@ -125,8 +118,7 @@ void main() { }); group('queryProductDetails', () { - test( - 'returns empty productDetails ' + test('returns empty productDetails ' 'with an empty notFoundIDs ' 'when no identifiers are provided', () async { final response = await purchaseClient.queryProductDetails({}); @@ -134,29 +126,28 @@ void main() { expect(response.productDetails, isEmpty); }); - test( - 'returns found productDetails ' + test('returns found productDetails ' 'with an empty notFoundIDs', () async { - final response = await purchaseClient - .queryProductDetails({productDetails.id}); + final response = await purchaseClient.queryProductDetails({ + productDetails.id, + }); expect(response.notFoundIDs, isEmpty); expect(response.productDetails.length, equals(1)); expect(response.productDetails.first.id, equals(productDetails.id)); }); - test( - 'returns an empty productDetails ' + test('returns an empty productDetails ' 'with provided id in notFoundIDs', () async { - final response = - await purchaseClient.queryProductDetails({'unknownId'}); + final response = await purchaseClient.queryProductDetails({ + 'unknownId', + }); expect(response.notFoundIDs, equals(['unknownId'])); expect(response.productDetails, equals([])); }); }); - test( - 'buyNonConsumable returns true ' + test('buyNonConsumable returns true ' 'and adds purchase ' 'with purchase status PurchaseStatus.pending ' 'to purchaseStream', () async { @@ -175,17 +166,14 @@ void main() { expect(result, true); }); - test( - 'completePurchase adds purchaseDetails to purchaseStream ' + test('completePurchase adds purchaseDetails to purchaseStream ' 'with pendingCompletePurchase set to false', () async { await Future.wait([ expectLater( purchaseClient.purchaseStream, emitsThrough( (List purchases) => purchases.first.equals( - purchaseDetails.copyWith( - pendingCompletePurchase: false, - ), + purchaseDetails.copyWith(pendingCompletePurchase: false), ), ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/lib/src/share_launcher.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/lib/src/share_launcher.dart index 82760db50..b78e4e7a7 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/lib/src/share_launcher.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/lib/src/share_launcher.dart @@ -16,7 +16,7 @@ class ShareFailure with EquatableMixin implements Exception { } /// ShareProvider is a function type that provides the ability to share content. -typedef ShareProvider = Future Function(String); +typedef ShareProvider = Future Function(ShareParams); /// {@template share_launcher} /// A class allowing opening native share bottom sheet. @@ -24,16 +24,16 @@ typedef ShareProvider = Future Function(String); class ShareLauncher { /// {@macro share_launcher} - const ShareLauncher({ShareProvider? shareProvider}) - : _shareProvider = shareProvider ?? Share.share; + ShareLauncher({ShareProvider? shareProvider}) + : _shareProvider = shareProvider ?? SharePlus.instance.share; final ShareProvider _shareProvider; /// Method for opening native share bottom sheet. Future share({required String text}) async { try { - return _shareProvider(text); - } catch (error, stackTrace) { + await _shareProvider(ShareParams(text: text)); + } on Exception catch (error, stackTrace) { Error.throwWithStackTrace(ShareFailure(error), stackTrace); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/pubspec.yaml index 8fbc2249f..08dff78dc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/pubspec.yaml @@ -4,14 +4,14 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: equatable: ^2.0.3 flutter: sdk: flutter - share_plus: ^10.0.2 - share_plus_platform_interface: ^5.0.0 + share_plus: ^12.0.1 + share_plus_platform_interface: ^6.1.0 dev_dependencies: flutter_test: @@ -19,4 +19,4 @@ dev_dependencies: mocktail: ^1.0.2 plugin_platform_interface: ^2.1.2 url_launcher_platform_interface: ^2.0.5 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/test/src/share_launcher_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/test/src/share_launcher_test.dart index a87f85540..b2c9dca91 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/test/src/share_launcher_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/share_launcher/test/src/share_launcher_test.dart @@ -10,6 +10,9 @@ class MockSharePlatform extends Mock implements SharePlatform {} void main() { + setUpAll(() { + registerFallbackValue(ShareParams(text: 'fallback')); + }); group('ShareFailure', () { test('supports value comparison', () { final shareFailure1 = ShareFailure('error'); @@ -25,9 +28,10 @@ void main() { var called = false; final shareLauncher = ShareLauncher( - shareProvider: (String text) async { + shareProvider: (params) async { called = true; - expect(text, equals('text')); + expect(params.text, equals('text')); + return ShareResult('raw', ShareResultStatus.success); }, ); @@ -38,25 +42,23 @@ void main() { test('throws ShareFailure when shareLauncher throws', () async { final shareLauncher = ShareLauncher( - shareProvider: (String text) => throw Exception(), + shareProvider: (params) => throw Exception(), ); expect(shareLauncher.share(text: 'text'), throwsA(isA())); }); - test( - 'calls default ShareProvider with text ' + test('calls default ShareProvider with text ' 'when shareProvider not provided ', () async { final mock = MockSharePlatform(); SharePlatform.instance = mock; - when(() => SharePlatform.instance.share(any(that: isA()))) - .thenAnswer( - (_) async => ShareResult('raw', ShareResultStatus.success), - ); + when( + () => SharePlatform.instance.share(any(that: isA())), + ).thenAnswer((_) async => ShareResult('raw', ShareResultStatus.success)); await ShareLauncher().share(text: 'text'); - verify(() => mock.share('text')).called(1); + verify(() => mock.share(any(that: isA()))).called(1); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/lib/src/persistent_storage.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/lib/src/persistent_storage.dart index dc840bef3..5ed146db1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/lib/src/persistent_storage.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/lib/src/persistent_storage.dart @@ -6,9 +6,8 @@ import 'package:storage/storage.dart'; /// {@endtemplate} class PersistentStorage implements Storage { /// {@macro persistent_storage} - const PersistentStorage({ - required SharedPreferences sharedPreferences, - }) : _sharedPreferences = sharedPreferences; + const PersistentStorage({required SharedPreferences sharedPreferences}) + : _sharedPreferences = sharedPreferences; final SharedPreferences _sharedPreferences; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/pubspec.yaml index 2ad65c943..1ebba055f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/pubspec.yaml @@ -3,12 +3,12 @@ description: Storage that saves data in the device's persistent memory. publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: flutter: sdk: flutter - shared_preferences: ^2.0.15 + shared_preferences: ^2.5.3 storage: path: ../storage @@ -16,4 +16,4 @@ dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/test/src/persistent_storage_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/test/src/persistent_storage_test.dart index 01bf45016..38f470d3f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/test/src/persistent_storage_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/persistent_storage/test/src/persistent_storage_test.dart @@ -23,24 +23,21 @@ void main() { }); group('read', () { - test( - 'returns value ' + test('returns value ' 'when SharedPreferences.getString returns successfully', () async { when(() => sharedPreferences.getString(any())).thenReturn(mockValue); final actualValue = await persistentStorage.read(key: mockKey); expect(actualValue, mockValue); }); - test( - 'returns null ' + test('returns null ' 'when sharedPreferences.getString returns null', () async { when(() => sharedPreferences.getString(any())).thenReturn(null); final actualValue = await persistentStorage.read(key: mockKey); expect(actualValue, isNull); }); - test( - 'throws a StorageException ' + test('throws a StorageException ' 'when sharedPreferences.getString fails', () async { when(() => sharedPreferences.getString(any())).thenThrow(mockException); @@ -58,22 +55,22 @@ void main() { }); group('write', () { - test( - 'completes ' + test('completes ' 'when sharedPreferences.setString completes', () async { - when(() => sharedPreferences.setString(any(), any())) - .thenAnswer((_) async => true); + when( + () => sharedPreferences.setString(any(), any()), + ).thenAnswer((_) async => true); expect( persistentStorage.write(key: mockKey, value: mockValue), completes, ); }); - test( - 'throws a StorageException ' + test('throws a StorageException ' 'when sharedPreferences.setString fails', () async { - when(() => sharedPreferences.setString(any(), any())) - .thenThrow(mockException); + when( + () => sharedPreferences.setString(any(), any()), + ).thenThrow(mockException); expect( () => persistentStorage.write(key: mockKey, value: mockValue), throwsA( @@ -88,19 +85,15 @@ void main() { }); group('delete', () { - test( - 'completes ' + test('completes ' 'when sharedPreferences.remove completes', () async { - when(() => sharedPreferences.remove(any())) - .thenAnswer((_) async => true); - expect( - persistentStorage.delete(key: mockKey), - completes, - ); + when( + () => sharedPreferences.remove(any()), + ).thenAnswer((_) async => true); + expect(persistentStorage.delete(key: mockKey), completes); }); - test( - 'throws a StorageException ' + test('throws a StorageException ' 'when sharedPreferences.remove fails', () async { when(() => sharedPreferences.remove(any())).thenThrow(mockException); expect( @@ -117,15 +110,13 @@ void main() { }); group('clear', () { - test( - 'completes ' + test('completes ' 'when sharedPreferences.clear completes', () async { when(sharedPreferences.clear).thenAnswer((_) async => true); expect(persistentStorage.clear(), completes); }); - test( - 'throws a StorageException ' + test('throws a StorageException ' 'when sharedPreferences.clear fails', () async { when(sharedPreferences.clear).thenThrow(mockException); expect( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/lib/src/secure_storage.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/lib/src/secure_storage.dart index 41b436eb5..b25eb54fd 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/lib/src/secure_storage.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/lib/src/secure_storage.dart @@ -19,7 +19,7 @@ import 'package:storage/storage.dart'; class SecureStorage implements Storage { /// {@macro secure_storage} const SecureStorage([FlutterSecureStorage? secureStorage]) - : _secureStorage = secureStorage ?? const FlutterSecureStorage(); + : _secureStorage = secureStorage ?? const FlutterSecureStorage(); final FlutterSecureStorage _secureStorage; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/pubspec.yaml index 77dbb0e20..94f28330a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/pubspec.yaml @@ -3,7 +3,7 @@ description: A Key/Value Secure Storage Client publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: flutter: @@ -16,4 +16,4 @@ dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.2 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/test/secure_storage_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/test/secure_storage_test.dart index bd333f2cd..566270287 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/test/secure_storage_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/secure_storage/test/secure_storage_test.dart @@ -27,24 +27,29 @@ void main() { }); group('read', () { - test('returns value when FlutterSecureStorage returns successfully', - () async { - when(() => flutterSecureStorage.read(key: any(named: 'key'))) - .thenAnswer((_) => Future.value(mockValue)); - final actual = await secureStorage.read(key: mockKey); - expect(actual, mockValue); - }); + test( + 'returns value when FlutterSecureStorage returns successfully', + () async { + when( + () => flutterSecureStorage.read(key: any(named: 'key')), + ).thenAnswer((_) => Future.value(mockValue)); + final actual = await secureStorage.read(key: mockKey); + expect(actual, mockValue); + }, + ); test('returns null when FlutterSecureStorage returns null', () async { - when(() => flutterSecureStorage.read(key: any(named: 'key'))) - .thenAnswer((_) => Future.value()); + when( + () => flutterSecureStorage.read(key: any(named: 'key')), + ).thenAnswer((_) => Future.value()); final actual = await secureStorage.read(key: mockKey); expect(actual, isNull); }); test('throws StorageException when FlutterSecureStorage fails', () async { - when(() => flutterSecureStorage.read(key: any(named: 'key'))) - .thenThrow(mockException); + when( + () => flutterSecureStorage.read(key: any(named: 'key')), + ).thenThrow(mockException); try { await secureStorage.read(key: mockKey); @@ -62,10 +67,7 @@ void main() { value: any(named: 'value'), ), ).thenAnswer((_) => Future.value()); - expect( - secureStorage.write(key: mockKey, value: mockValue), - completes, - ); + expect(secureStorage.write(key: mockKey, value: mockValue), completes); }); test('throws StorageException when FlutterSecureStorage fails', () async { @@ -85,17 +87,16 @@ void main() { group('delete', () { test('completes when FlutterSecureStorage completes', () async { - when(() => flutterSecureStorage.delete(key: any(named: 'key'))) - .thenAnswer((_) => Future.value()); - expect( - secureStorage.delete(key: mockKey), - completes, - ); + when( + () => flutterSecureStorage.delete(key: any(named: 'key')), + ).thenAnswer((_) => Future.value()); + expect(secureStorage.delete(key: mockKey), completes); }); test('throws StorageException when FlutterSecureStorage fails', () async { - when(() => flutterSecureStorage.delete(key: any(named: 'key'))) - .thenThrow(mockException); + when( + () => flutterSecureStorage.delete(key: any(named: 'key')), + ).thenThrow(mockException); try { await secureStorage.delete(key: mockKey); } on StorageException catch (e) { @@ -106,8 +107,9 @@ void main() { group('clear', () { test('completes when FlutterSecureStorage completes', () async { - when(() => flutterSecureStorage.deleteAll()) - .thenAnswer((_) => Future.value()); + when( + () => flutterSecureStorage.deleteAll(), + ).thenAnswer((_) => Future.value()); expect(secureStorage.clear(), completes); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/storage/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/storage/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/storage/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/storage/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/storage/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/storage/pubspec.yaml index bf3802d4b..814e851fb 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/storage/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/storage/storage/pubspec.yaml @@ -3,8 +3,8 @@ description: A Key/Value Storage Client for Dart. publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dev_dependencies: test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/analysis_options.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/analysis_options.yaml index bb7209144..5709d8cdc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/analysis_options.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/analysis_options.yaml @@ -1 +1,5 @@ -include: package:very_good_analysis/analysis_options.6.0.0.yaml +include: package:very_good_analysis/analysis_options.10.0.0.yaml + +analyzer: + errors: + document_ignores: ignore \ No newline at end of file diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/models/user.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/models/user.dart index ed4c35031..872e1d726 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/models/user.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/models/user.dart @@ -19,15 +19,14 @@ class User extends AuthenticationUser { factory User.fromAuthenticationUser({ required AuthenticationUser authenticationUser, required SubscriptionPlan subscriptionPlan, - }) => - User( - email: authenticationUser.email, - id: authenticationUser.id, - name: authenticationUser.name, - photo: authenticationUser.photo, - isNewUser: authenticationUser.isNewUser, - subscriptionPlan: subscriptionPlan, - ); + }) => User( + email: authenticationUser.email, + id: authenticationUser.id, + name: authenticationUser.name, + photo: authenticationUser.photo, + isNewUser: authenticationUser.isNewUser, + subscriptionPlan: subscriptionPlan, + ); /// Whether the current user is anonymous. @override diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/user_repository.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/user_repository.dart index e083a23c8..89158d6b8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/user_repository.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/user_repository.dart @@ -60,11 +60,11 @@ class UserRepository { required PackageInfoClient packageInfoClient, required DeepLinkService deepLinkService, required UserStorage storage, - }) : _apiClient = apiClient, - _authenticationClient = authenticationClient, - _packageInfoClient = packageInfoClient, - _deepLinkService = deepLinkService, - _storage = storage; + }) : _apiClient = apiClient, + _authenticationClient = authenticationClient, + _packageInfoClient = packageInfoClient, + _deepLinkService = deepLinkService, + _storage = storage; final {{project_name.pascalCase()}}ApiClient _apiClient; final AuthenticationClient _authenticationClient; @@ -95,10 +95,10 @@ class UserRepository { /// Emits when a new email link is emitted on [DeepLinkClient.deepLinkStream], /// which is validated using [AuthenticationClient.isLogInWithEmailLink]. Stream get incomingEmailLinks => _deepLinkService.deepLinkStream.where( - (deepLink) => _authenticationClient.isLogInWithEmailLink( - emailLink: deepLink.toString(), - ), - ); + (deepLink) => _authenticationClient.isLogInWithEmailLink( + emailLink: deepLink.toString(), + ), + ); /// Starts the Sign In with Apple Flow. /// @@ -164,9 +164,7 @@ class UserRepository { /// Sends an authentication link to the provided [email]. /// /// Throws a [SendLoginEmailLinkFailure] if an exception occurs. - Future sendLoginEmailLink({ - required String email, - }) async { + Future sendLoginEmailLink({required String email}) async { try { await _authenticationClient.sendLoginEmailLink( email: email, @@ -228,10 +226,7 @@ class UserRepository { try { return await _storage.fetchAppOpenedCount(); } catch (error, stackTrace) { - Error.throwWithStackTrace( - FetchAppOpenedCountFailure(error), - stackTrace, - ); + Error.throwWithStackTrace(FetchAppOpenedCountFailure(error), stackTrace); } } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/user_storage.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/user_storage.dart index 24eb5308b..547ba5d5d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/user_storage.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/lib/src/user_storage.dart @@ -11,17 +11,15 @@ abstract class UserStorageKeys { /// {@endtemplate} class UserStorage { /// {@macro user_storage} - const UserStorage({ - required Storage storage, - }) : _storage = storage; + const UserStorage({required Storage storage}) : _storage = storage; final Storage _storage; /// Sets the number of times the app was opened. Future setAppOpenedCount({required int count}) => _storage.write( - key: UserStorageKeys.appOpenedCount, - value: count.toString(), - ); + key: UserStorageKeys.appOpenedCount, + value: count.toString(), + ); /// Fetches the number of times the app was opened value from Storage. Future fetchAppOpenedCount() async { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/pubspec.yaml index 5475734fe..6b8fdfc7b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/pubspec.yaml @@ -3,7 +3,7 @@ description: Dart package which manages the user domain. publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: authentication_client: @@ -22,4 +22,4 @@ dependencies: dev_dependencies: mocktail: ^1.0.2 test: ^1.21.4 - very_good_analysis: ^6.0.0 + very_good_analysis: ^10.0.0 diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/models/user_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/models/user_test.dart index e0dcf3b78..375bf6620 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/models/user_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/models/user_test.dart @@ -17,12 +17,7 @@ void main() { authenticationUser: authenticationUser, subscriptionPlan: subscriptionPlan, ), - equals( - User( - id: 'id', - subscriptionPlan: subscriptionPlan, - ), - ), + equals(User(id: 'id', subscriptionPlan: subscriptionPlan)), ); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/user_repository_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/user_repository_test.dart index 1203396cb..f5d0652ad 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/user_repository_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/user_repository_test.dart @@ -18,8 +18,6 @@ class MockDeepLinkService extends Mock implements DeepLinkService {} class MockUserStorage extends Mock implements UserStorage {} -class MockUser extends Mock implements AuthenticationUser {} - class Mock{{project_name.pascalCase()}}ApiClient extends Mock implements api.{{project_name.pascalCase()}}ApiClient {} @@ -71,8 +69,9 @@ void main() { deepLinkClientController = StreamController.broadcast(); apiClient = Mock{{project_name.pascalCase()}}ApiClient(); - when(() => deepLinkService.deepLinkStream) - .thenAnswer((_) => deepLinkClientController.stream); + when( + () => deepLinkService.deepLinkStream, + ).thenAnswer((_) => deepLinkClientController.stream); userRepository = UserRepository( apiClient: apiClient, @@ -84,24 +83,22 @@ void main() { }); test( - 'currentSubscriptionPlan emits none ' - 'when initialized and authenticationClient.user is anonymous', - () async { - when(() => authenticationClient.user).thenAnswer( - (invocation) => Stream.value(AuthenticationUser.anonymous), - ); - final response = await userRepository.user.first; - expect( - response.subscriptionPlan, - equals(api.SubscriptionPlan.none), - ); - }); + 'currentSubscriptionPlan emits none ' + 'when initialized and authenticationClient.user is anonymous', + () async { + when(() => authenticationClient.user).thenAnswer( + (invocation) => Stream.value(AuthenticationUser.anonymous), + ); + final response = await userRepository.user.first; + expect(response.subscriptionPlan, equals(api.SubscriptionPlan.none)); + }, + ); group('user', () { test('calls user on AuthenticationClient', () { - when(() => authenticationClient.user).thenAnswer( - (_) => const Stream.empty(), - ); + when( + () => authenticationClient.user, + ).thenAnswer((_) => const Stream.empty()); userRepository.user; verify(() => authenticationClient.user).called(1); }); @@ -112,8 +109,7 @@ void main() { final validEmailLink2 = Uri.https('valid.email.link'); final invalidEmailLink = Uri.https('invalid.email.link'); - test( - 'emits a new email link ' + test('emits a new email link ' 'for every valid email link from DeepLinkClient.deepLinkStream', () { when( () => authenticationClient.isLogInWithEmailLink( @@ -133,12 +129,11 @@ void main() { ), ).thenReturn(false); - expectLater( - userRepository.incomingEmailLinks, - emitsInOrder([ - validEmailLink, - validEmailLink2, - ]), + unawaited( + expectLater( + userRepository.incomingEmailLinks, + emitsInOrder([validEmailLink, validEmailLink2]), + ), ); deepLinkClientController @@ -159,13 +154,8 @@ void main() { test('rethrows LogInWithAppleFailure', () async { final exception = FakeLogInWithAppleFailure(); - when( - () => authenticationClient.logInWithApple(), - ).thenThrow(exception); - expect( - () => userRepository.logInWithApple(), - throwsA(exception), - ); + when(() => authenticationClient.logInWithApple()).thenThrow(exception); + expect(() => userRepository.logInWithApple(), throwsA(exception)); }); test('throws LogInWithAppleFailure on generic exception', () async { @@ -222,15 +212,17 @@ void main() { test('rethrows LogInWithTwitterFailure', () async { final exception = FakeLogInWithTwitterFailure(); - when(() => authenticationClient.logInWithTwitter()) - .thenThrow(exception); + when( + () => authenticationClient.logInWithTwitter(), + ).thenThrow(exception); expect(() => userRepository.logInWithTwitter(), throwsA(exception)); }); test('rethrows LogInWithTwitterCanceled', () async { final exception = FakeLogInWithTwitterCanceled(); - when(() => authenticationClient.logInWithTwitter()) - .thenThrow(exception); + when( + () => authenticationClient.logInWithTwitter(), + ).thenThrow(exception); expect(userRepository.logInWithTwitter(), throwsA(exception)); }); @@ -256,15 +248,17 @@ void main() { test('rethrows LogInWithFacebookFailure', () async { final exception = FakeLogInWithFacebookFailure(); - when(() => authenticationClient.logInWithFacebook()) - .thenThrow(exception); + when( + () => authenticationClient.logInWithFacebook(), + ).thenThrow(exception); expect(() => userRepository.logInWithFacebook(), throwsA(exception)); }); test('rethrows LogInWithFacebookCanceled', () async { final exception = FakeLogInWithFacebookCanceled(); - when(() => authenticationClient.logInWithFacebook()) - .thenThrow(exception); + when( + () => authenticationClient.logInWithFacebook(), + ).thenThrow(exception); expect(userRepository.logInWithFacebook(), throwsA(exception)); }); @@ -283,9 +277,7 @@ void main() { const packageName = 'appPackageName'; setUp(() { - when( - () => packageInfoClient.packageName, - ).thenReturn(packageName); + when(() => packageInfoClient.packageName).thenReturn(packageName); when( () => authenticationClient.sendLoginEmailLink( email: any(named: 'email'), @@ -294,8 +286,7 @@ void main() { ).thenAnswer((_) async {}); }); - test( - 'calls sendLoginEmailLink on AuthenticationClient ' + test('calls sendLoginEmailLink on AuthenticationClient ' 'with email and app package name from PackageInfoClient', () async { await userRepository.sendLoginEmailLink( email: 'ben_franklin@upenn.edu', @@ -325,8 +316,7 @@ void main() { ); }); - test( - 'throws FakeSendLoginEmailLinkFailure ' + test('throws FakeSendLoginEmailLinkFailure ' 'on generic exception', () async { when( () => authenticationClient.sendLoginEmailLink( @@ -423,8 +413,9 @@ void main() { group('deleteAccount', () { test('calls logOut on AuthenticationClient', () async { - when(() => authenticationClient.deleteAccount()) - .thenAnswer((_) async {}); + when( + () => authenticationClient.deleteAccount(), + ).thenAnswer((_) async {}); await userRepository.deleteAccount(); verify(() => authenticationClient.deleteAccount()).called(1); }); @@ -474,8 +465,7 @@ void main() { expect(result, 1); }); - test( - 'throws a FetchAppOpenedCountFailure ' + test('throws a FetchAppOpenedCountFailure ' 'when fetching app opened count fails', () async { when(() => storage.fetchAppOpenedCount()).thenThrow(Exception()); @@ -512,8 +502,7 @@ void main() { ); }); - test( - 'throws a IncrementAppOpenedCountFailure ' + test('throws a IncrementAppOpenedCountFailure ' 'when setting app opened count fails', () async { when( () => storage.setAppOpenedCount(count: any(named: 'count')), @@ -536,10 +525,7 @@ void main() { test('calls getCurrentUser on ApiClient', () async { when(() => apiClient.getCurrentUser()).thenAnswer( (_) async => api.CurrentUserResponse( - user: api.User( - id: 'id', - subscription: api.SubscriptionPlan.none, - ), + user: api.User(id: 'id', subscription: api.SubscriptionPlan.none), ), ); await userRepository.updateSubscriptionPlan(); @@ -547,9 +533,7 @@ void main() { }); test('throws FetchCurrentSubscriptionFailure on failure', () async { - when( - () => apiClient.getCurrentUser(), - ).thenThrow(Exception()); + when(() => apiClient.getCurrentUser()).thenThrow(Exception()); expect( () => userRepository.updateSubscriptionPlan(), throwsA(isA()), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/user_storage_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/user_storage_test.dart index 56c732ef0..6467219fa 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/user_storage_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/packages/user_repository/test/src/user_storage_test.dart @@ -42,13 +42,12 @@ void main() { () => storage.read(key: UserStorageKeys.appOpenedCount), ).thenAnswer((_) async => 1.toString()); - final result = - await UserStorage(storage: storage).fetchAppOpenedCount(); + final result = await UserStorage( + storage: storage, + ).fetchAppOpenedCount(); verify( - () => storage.read( - key: UserStorageKeys.appOpenedCount, - ), + () => storage.read(key: UserStorageKeys.appOpenedCount), ).called(1); expect(result, value); @@ -59,13 +58,12 @@ void main() { () => storage.read(key: UserStorageKeys.appOpenedCount), ).thenAnswer((_) async => null); - final result = - await UserStorage(storage: storage).fetchAppOpenedCount(); + final result = await UserStorage( + storage: storage, + ).fetchAppOpenedCount(); verify( - () => storage.read( - key: UserStorageKeys.appOpenedCount, - ), + () => storage.read(key: UserStorageKeys.appOpenedCount), ).called(1); expect(result, 0); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/pubspec.lock b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/pubspec.lock index c99272afb..158bf8b83 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/pubspec.lock +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "85.0.0" _flutterfire_internals: dependency: transitive description: name: _flutterfire_internals - sha256: ddc6f775260b89176d329dee26f88b9469ef46aa3228ff6a0b91caf2b2989692 + sha256: "50e24b769bd1e725732f0aff18b806b8731c1fbcf4e8018ab98e7c4805a2a52f" url: "https://pub.dev" source: hosted - version: "1.3.42" + version: "1.3.57" ads_consent_client: dependency: "direct main" description: @@ -35,10 +35,10 @@ packages: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "7.7.1" android_intent_plus: dependency: transitive description: @@ -88,26 +88,26 @@ packages: dependency: "direct main" description: name: bloc - sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" + sha256: a2cebb899f91d36eeeaa55c7b20b5915db5a9df1b8fd4a3c9c825e22e474537d url: "https://pub.dev" source: hosted - version: "8.1.2" + version: "9.1.0" bloc_concurrency: dependency: "direct main" description: name: bloc_concurrency - sha256: "44535c9f429cd7e91d548cf89fde1c23a8b4b3637decdb1865bb583091a00d4e" + sha256: "86b7b17a0a78f77fca0d7c030632b59b593b22acea2d96972588f40d4ef53a94" url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.3.0" bloc_test: dependency: "direct dev" description: name: bloc_test - sha256: af0de1a1e16a7536e95dcd7491e0a6d6078e11d2d691988e862280b74f5c7968 + sha256: "1dd549e58be35148bc22a9135962106aa29334bc1e3f285994946a1057b29d7b" url: "https://pub.dev" source: hosted - version: "9.1.4" + version: "10.0.0" boolean_selector: dependency: transitive description: @@ -120,10 +120,10 @@ packages: dependency: transitive description: name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_config: dependency: transitive description: @@ -144,34 +144,34 @@ packages: dependency: transitive description: name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" + sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.4" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.15" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 + sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" url: "https://pub.dev" source: hosted - version: "7.2.11" + version: "8.0.0" build_verify: dependency: "direct dev" description: name: build_verify - sha256: abbb9b9eda076854ac1678d284c053a5ec608e64da741d0801f56d4bbea27e23 + sha256: "3b17b59b6d66f9d3e6014996f089902d56cec5760e051c353cc387b9da577652" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" build_version: dependency: "direct dev" description: @@ -224,10 +224,10 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -240,10 +240,10 @@ packages: dependency: "direct main" description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" code_builder: dependency: transitive description: @@ -256,10 +256,10 @@ packages: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" convert: dependency: transitive description: @@ -304,18 +304,18 @@ packages: dependency: transitive description: name: dart_frog - sha256: a5da81b1ea4f3e8f03dfcd0bc9b44ba83a6576ce6dae1bb138a6a8f7b70fe727 + sha256: "4ab2323ad74f935f5f76cb2de7e699d0319245e8029753878b0ddc35984f9cfe" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.6" dart_style: dependency: transitive description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "3.1.1" deep_link_client: dependency: "direct main" description: @@ -342,10 +342,10 @@ packages: dependency: "direct main" description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" facebook_auth_desktop: dependency: transitive description: @@ -358,10 +358,10 @@ packages: dependency: "direct dev" description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: @@ -382,50 +382,50 @@ packages: dependency: "direct main" description: name: firebase_analytics - sha256: "7b5ae39d853ead76f9d030dc23389bfec4ea826d7cccb4eea4873dcb0cdd172b" + sha256: "366140abb55418ea23060b779893fa997c2d8e1974a4d1cc4d9590933b65c5fd" url: "https://pub.dev" source: hosted - version: "11.3.1" + version: "11.3.6" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: "0205e05bb37abd29d5dec5cd89aeb04f3f58bf849aad21dd938be0507d52a40c" + sha256: "8e987cf977c0c8f4ad02d9950a9b25b1a9606899f37b66a322a43af05be0246b" url: "https://pub.dev" source: hosted - version: "4.2.3" + version: "4.2.8" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "434807f8b30526e21cc062410c28ee5c6680a13626c4443b5ffede29f84b0c74" + sha256: "0b64ef9060d394bba3d3b4777f49ee098efeeea7b0afb04663c956de6a3da170" url: "https://pub.dev" source: hosted - version: "0.5.10" + version: "0.5.10+5" firebase_auth: dependency: transitive description: name: firebase_auth - sha256: f03a6cdbee1006f65cc6e64dc8f93a0179b10a4b54e6287430057dd9b8556ee4 + sha256: "03483af6e67b7c4b696ca9386989a6cd5593569e1ac5af6907ea5f7fd9c16d8b" url: "https://pub.dev" source: hosted - version: "5.2.1" + version: "5.3.4" firebase_auth_platform_interface: dependency: "direct main" description: name: firebase_auth_platform_interface - sha256: "48ed1841dbe617082d3b3b1db5a86dbce41503c4021d43982cfdcec598bb403e" + sha256: "4e8dbb33b0539563c7b528e5f2c6a502fb543206f768358c8dfc9e85d600d7a0" url: "https://pub.dev" source: hosted - version: "7.4.5" + version: "7.7.1" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "7d4a0f8a9234eda0622aaf8344c74d57adf9eb36bf714f37df4114492d0e34bc" + sha256: d83fe95c44d73c9c29b006ac7df3aa5e1b8ce92b62edc44e8f86250951fe2cd0 url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "5.13.5" firebase_authentication_client: dependency: "direct main" description: @@ -437,42 +437,42 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "40921de9795fbf5887ed5c0adfdf4972d5a8d7ae7e1b2bb98dea39bc02626a88" + sha256: "5bba5924139e91d26446fd2601c18a6aa62c1161c768a989bb5e245dcdc20644" url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.15.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: f7d7180c7f99babd4b4c517754d41a09a4943a0f7a69b65c894ca5c68ba66315 + sha256: "8bcfad6d7033f5ea951d15b867622a824b13812178bfec0c779b9d81de011bbb" url: "https://pub.dev" source: hosted - version: "5.2.1" + version: "5.4.2" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: f4ee170441ca141c5f9ee5ad8737daba3ee9c8e7efb6902aee90b4fbd178ce25 + sha256: eb3afccfc452b2b2075acbe0c4b27de62dd596802b4e5e19869c1e926cbb20b3 url: "https://pub.dev" source: hosted - version: "2.18.0" + version: "2.24.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: c4fdbb14ba6f36794f89dc27fb5c759c9cc67ecbaeb079edc4dba515bbf9f555 + sha256: "6f1aeb2862e09bf03f7c2d95e0e044e7d52cb242f2e48d6114262c0686134a0f" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.3.8" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "891d6f7ba4b93672d0e1265f27b6a9dccd56ba2cc30ce6496586b32d1d8770ac" + sha256: "39b9022eb190f63780a6441ab9e4dc1898cc29372ca457f94271796c3b6a416a" url: "https://pub.dev" source: hosted - version: "3.6.42" + version: "3.8.8" firebase_deep_link_client: dependency: "direct main" description: @@ -484,42 +484,42 @@ packages: dependency: "direct main" description: name: firebase_dynamic_links - sha256: c18edf7df09da698c0ddff6f625f3159d97909627c991cf165aefa718b827f7c + sha256: "2ae34cfa21395ee02fd6f1587a8096996073ec7fce507f6aa7d50b0b26fafae5" url: "https://pub.dev" source: hosted - version: "6.0.6" + version: "6.1.8" firebase_dynamic_links_platform_interface: dependency: transitive description: name: firebase_dynamic_links_platform_interface - sha256: "394e9e11bd31f9bab142f7aabdcca4041432f2fa281477f27a25742d74dbec25" + sha256: "81948a2259a39a29ea54c935a518c277eb0829854cf3fb7732e1ed98982ece0e" url: "https://pub.dev" source: hosted - version: "0.2.6+42" + version: "0.2.7+8" firebase_messaging: dependency: "direct main" description: name: firebase_messaging - sha256: cc02c4afd6510cd84586020670140c4a23fbe52af16cd260ccf8ede101bb8d1b + sha256: c6711cf2f455532b84a94022c7aaf85088849763af2f01b775ca79d82d10a01a url: "https://pub.dev" source: hosted - version: "15.1.1" + version: "15.2.8" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface - sha256: d8a4984635f09213302243ea670fe5c42f3261d7d8c7c0a5f7dcd5d6c84be459 + sha256: "1c9dacccb1aee1bf17ba519dda5563a16fdd2ec1e79b5f2e421cb4bf75a166f7" url: "https://pub.dev" source: hosted - version: "4.5.44" + version: "4.6.8" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web - sha256: "258b9d637965db7855299b123533609ed95e52350746a723dfd1d8d6f3fac678" + sha256: "54317c26fa92f0d90a2017977ac791cb0504eca29fcf397f06adf727d4a7a2d5" url: "https://pub.dev" source: hosted - version: "3.9.0" + version: "3.10.8" firebase_notifications_client: dependency: "direct main" description: @@ -539,10 +539,10 @@ packages: dependency: "direct main" description: name: flow_builder - sha256: "212a5c6575e87bec2003fa3f31dd7d80f0d89f6152db54f43fb0b6f45943e955" + sha256: "9e66aa7209e9a31c347bb316abf191d2300db41cd6b7254f89abd4e76d9b3f8d" url: "https://pub.dev" source: hosted - version: "0.0.9" + version: "0.1.0" flutter: dependency: "direct main" description: flutter @@ -552,10 +552,10 @@ packages: dependency: "direct main" description: name: flutter_bloc - sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae + sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38 url: "https://pub.dev" source: hosted - version: "8.1.3" + version: "9.1.1" flutter_cache_manager: dependency: transitive description: @@ -660,10 +660,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" + sha256: b9c2ad5872518a27507ab432d1fb97e8813b05f0fc693f9d40fad06d073e0678 url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.2.1" flutter_test: dependency: "direct dev" description: flutter @@ -678,10 +678,10 @@ packages: dependency: "direct main" description: name: font_awesome_flutter - sha256: "52671aea66da73b58d42ec6d0912b727a42248dd9a7c76d6c20f275783c48c08" + sha256: ef8e9591f6de2bf671c3b6f506f5ff85f03d34403084fccced62d3628fb086b9 url: "https://pub.dev" source: hosted - version: "10.6.0" + version: "10.11.0" form_inputs: dependency: "direct main" description: @@ -693,10 +693,10 @@ packages: dependency: transitive description: name: formz - sha256: a58eb48d84685b7ffafac1d143bf47d585bf54c7db89fe81c175dfd6e53201c7 + sha256: "382c7be452ff76833f9efa0b2333fec3a576393f6d2c7801725bed502f3d40c3" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.8.0" frontend_server_client: dependency: transitive description: @@ -725,10 +725,10 @@ packages: dependency: "direct main" description: name: google_mobile_ads - sha256: f07d4e2ebf56181e8dd18ee810607e84aeac8add5251bb44c1c886af47ff021a + sha256: "0d4a3744b5e8ed1b8be6a1b452d309f811688855a497c6113fc4400f922db603" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.3.1" google_sign_in: dependency: transitive description: @@ -777,14 +777,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" - hive: + hive_ce: dependency: transitive description: - name: hive - sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + name: hive_ce + sha256: e3a9d2608350ca5ae1fcaa594bbc24c443cf8e67cd16b89ec258fb0bcccb0847 url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.15.0" hotreloader: dependency: transitive description: @@ -805,10 +805,10 @@ packages: dependency: transitive description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.6.0" http_methods: dependency: transitive description: @@ -837,10 +837,10 @@ packages: dependency: "direct main" description: name: hydrated_bloc - sha256: "24994e61f64904d911683cce1a31dc4ef611619da5253f1de2b7b8fc6f79a118" + sha256: f359a1135cc2eaddb53eeb21c8f915a149b9bdb80d01fda27c6f4e333db0cf2f url: "https://pub.dev" source: hosted - version: "9.1.2" + version: "10.1.1" in_app_purchase: dependency: transitive description: @@ -884,10 +884,10 @@ packages: dependency: "direct main" description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.2" io: dependency: transitive description: @@ -896,6 +896,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + isolate_channel: + dependency: transitive + description: + name: isolate_channel + sha256: f3d36f783b301e6b312c3450eeb2656b0e7d1db81331af2a151d9083a3f6b18d + url: "https://pub.dev" + source: hosted + version: "0.2.2+1" js: dependency: transitive description: @@ -916,34 +924,34 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b + sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c url: "https://pub.dev" source: hosted - version: "6.8.0" + version: "6.9.5" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" list_counter: dependency: transitive description: @@ -964,10 +972,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -980,10 +988,10 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.0" mime: dependency: transitive description: @@ -996,10 +1004,10 @@ packages: dependency: "direct main" description: name: mockingjay - sha256: cea2f3d38d1343beb6908f0ade5724c12304ee74014fbd274b4065a4b7db6536 + sha256: "734257c0c68dd7344067c7bcb5f568b29dda1ed523b012d70507d9ace1142110" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "2.1.0" mocktail: dependency: "direct dev" description: @@ -1012,10 +1020,10 @@ packages: dependency: "direct dev" description: name: mocktail_image_network - sha256: "83e6b9fa3c6b0952636719f0444752d94c0ef7caa4172ca467d9c1b682aeddbb" + sha256: a1fccbba780343517cfc552e0af2b3834d8bdb8f9f55a746c4d495ed1a8d50d6 url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.2.0" nested: dependency: transitive description: @@ -1094,10 +1102,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_parsing: dependency: transitive description: @@ -1110,18 +1118,18 @@ packages: dependency: "direct main" description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.17" path_provider_foundation: dependency: transitive description: @@ -1220,10 +1228,10 @@ packages: dependency: "direct main" description: name: platform - sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -1298,74 +1306,74 @@ packages: dependency: transitive description: name: share_plus - sha256: "468c43f285207c84bcabf5737f33b914ceb8eb38398b91e5e3ad1698d1b72a52" + sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840" url: "https://pub.dev" source: hosted - version: "10.0.2" + version: "12.0.1" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "6ababf341050edff57da8b6990f11f4e99eaba837865e2e6defe16d039619db5" + sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "6.1.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.5.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.4.11" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.5.4" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.4.3" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shelf: dependency: transitive description: @@ -1434,7 +1442,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" sliver_tools: dependency: "direct main" description: @@ -1447,18 +1455,18 @@ packages: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "2.0.0" source_helper: dependency: transitive description: name: source_helper - sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" url: "https://pub.dev" source: hosted - version: "1.3.4" + version: "1.3.5" source_map_stack_trace: dependency: transitive description: @@ -1511,10 +1519,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" storage: dependency: transitive description: @@ -1526,26 +1534,26 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: dependency: "direct main" description: name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" synchronized: dependency: transitive description: @@ -1566,26 +1574,26 @@ packages: dependency: "direct main" description: name: test - sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" + sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" url: "https://pub.dev" source: hosted - version: "1.25.7" + version: "1.26.3" test_api: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.7" test_core: dependency: transitive description: name: test_core - sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" + sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "0.6.12" timing: dependency: transitive description: @@ -1621,26 +1629,26 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 url: "https://pub.dev" source: hosted - version: "6.1.14" + version: "6.3.2" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 + sha256: "0aedad096a85b49df2e4725fa32118f9fa580f3b14af7a2d2221896a02cd5656" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.3.17" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.3.3" url_launcher_linux: dependency: transitive description: @@ -1653,10 +1661,10 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.2.2" url_launcher_platform_interface: dependency: transitive description: @@ -1700,42 +1708,42 @@ packages: dependency: transitive description: name: vector_graphics - sha256: b16dadf7eb610e20da044c141b4a0199a5e8082ca21daba68322756f953ce714 + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 url: "https://pub.dev" source: hosted - version: "1.1.9" + version: "1.1.19" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: a4b01403d5c613db115e30e71eca33f7e9e09f2d3c52c3fb84e16333ecddc539 + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" url: "https://pub.dev" source: hosted - version: "1.1.9" + version: "1.1.13" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: d26c0e2f237476426523eb25512e4c09fa27c6d33ed659a0e69d79e20b5dc47f + sha256: ca81fdfaf62a5ab45d7296614aea108d2c7d0efca8393e96174bf4d51e6725b0 url: "https://pub.dev" source: hosted - version: "1.1.9" + version: "1.1.18" vector_math: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" very_good_analysis: dependency: "direct main" description: name: very_good_analysis - sha256: "1fb637c0022034b1f19ea2acb42a3603cbd8314a470646a59a2fb01f5f3a8629" + sha256: "96245839dbcc45dfab1af5fa551603b5c7a282028a64746c19c547d21a7f1e3a" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "10.0.0" video_player: dependency: transitive description: @@ -1764,10 +1772,10 @@ packages: dependency: "direct main" description: name: video_player_platform_interface - sha256: be72301bf2c0150ab35a8c34d66e5a99de525f6de1e8d27c0672b836fe48f73a + sha256: "9e372520573311055cb353b9a0da1c9d72b094b7ba01b8ecc66f28473553793b" url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.5.0" video_player_web: dependency: transitive description: @@ -1788,10 +1796,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.0" watcher: dependency: transitive description: @@ -1804,10 +1812,10 @@ packages: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.1" web_socket_channel: dependency: transitive description: @@ -1828,34 +1836,34 @@ packages: dependency: transitive description: name: webview_flutter - sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932" + sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.13.0" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934 + sha256: "0a42444056b24ed832bdf3442d65c5194f6416f7e782152384944053c2ecc9a3" url: "https://pub.dev" source: hosted - version: "3.16.0" + version: "4.10.0" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d + sha256: "63d26ee3aca7256a83ccb576a50272edd7cfc80573a4305caa98985feb493ee0" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.14.0" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7 + sha256: fb46db8216131a3e55bcf44040ca808423539bc6732e7ed34fb6d8044e3d512f url: "https://pub.dev" source: hosted - version: "3.13.0" + version: "3.23.0" win32: dependency: "direct overridden" description: @@ -1889,5 +1897,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.5.0 <4.0.0" - flutter: ">=3.24.0" + dart: ">=3.10.0 <4.0.0" + flutter: ">=3.38.3" diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/pubspec.yaml b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/pubspec.yaml index b0d49c249..e6d10e201 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/pubspec.yaml +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1+1 publish_to: none environment: - sdk: ">=3.5.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" flutter: {{flutter_version}} dependencies: @@ -17,46 +17,46 @@ dependencies: path: packages/article_repository authentication_client: path: packages/authentication_client/authentication_client - bloc: ^8.1.0 - bloc_concurrency: ^0.2.0 - clock: ^1.1.0 - collection: ^1.16.0 + bloc: ^9.1.0 + bloc_concurrency: ^0.3.0 + clock: ^1.1.1 + collection: ^1.19.0 deep_link_client: path: packages/deep_link_client/deep_link_client email_launcher: path: packages/email_launcher equatable: ^2.0.3 - firebase_analytics: ^11.3.1 - firebase_auth_platform_interface: ^7.4.5 + firebase_analytics: ^11.3.6 + firebase_auth_platform_interface: ^7.7.1 firebase_authentication_client: path: packages/authentication_client/firebase_authentication_client - firebase_core: ^3.4.1 - firebase_crashlytics: ^4.1.1 + firebase_core: ^3.15.0 + firebase_crashlytics: ^4.3.8 firebase_deep_link_client: path: packages/deep_link_client/firebase_deep_link_client - firebase_dynamic_links: ^6.0.6 - firebase_messaging: ^15.1.1 + firebase_dynamic_links: ^6.1.8 + firebase_messaging: ^15.2.8 firebase_notifications_client: path: packages/notifications_client/firebase_notifications_client - flow_builder: ^0.0.7 + flow_builder: ^0.1.0 flutter: sdk: flutter - flutter_bloc: ^8.0.1 + flutter_bloc: ^9.1.1 flutter_localizations: sdk: flutter {{project_name.snakeCase()}}_api: path: api - flutter_svg: ^2.0.5 - font_awesome_flutter: ^10.1.0 + flutter_svg: ^2.2.1 + font_awesome_flutter: ^10.11.0 form_inputs: path: packages/form_inputs - google_mobile_ads: ^5.0.0 - hydrated_bloc: ^9.0.0 + google_mobile_ads: ^5.3.1 + hydrated_bloc: ^10.1.1 in_app_purchase_repository: path: packages/in_app_purchase_repository - intl: ^0.19.0 + intl: ^0.20.2 json_annotation: ^4.9.0 - mockingjay: ^0.6.0 + mockingjay: ^2.1.0 news_blocks: path: api/packages/news_blocks news_blocks_ui: @@ -67,43 +67,42 @@ dependencies: path: packages/notifications_repository package_info_client: path: packages/package_info_client - path_provider: ^2.1.4 + path_provider: ^2.1.5 permission_client: path: packages/permission_client persistent_storage: path: packages/storage/persistent_storage - platform: ^3.0.2 + platform: ^3.1.6 purchase_client: path: packages/purchase_client share_launcher: path: packages/share_launcher - shared_preferences: ^2.0.15 + shared_preferences: ^2.5.3 sliver_tools: ^0.2.9 - stream_transform: ^2.0.0 + stream_transform: ^2.1.1 test: ^1.21.4 token_storage: path: packages/authentication_client/token_storage - url_launcher: ^6.0.9 + url_launcher: ^6.3.2 user_repository: path: packages/user_repository - very_good_analysis: ^6.0.0 - video_player_platform_interface: ^6.0.1 + very_good_analysis: ^10.0.0 + video_player_platform_interface: ^6.5.0 visibility_detector: ^0.4.0+2 dev_dependencies: - bloc_test: ^9.0.3 - build_runner: ^2.0.3 - build_verify: ^3.0.0 - build_version: ^2.0.3 + bloc_test: ^10.0.0 + build_runner: ^2.4.15 + build_verify: ^3.1.1 + build_version: ^2.1.1 fake_async: ^1.3.0 flutter_test: sdk: flutter - json_serializable: ^6.8.0 + json_serializable: ^6.9.5 mocktail: ^1.0.2 - mocktail_image_network: ^1.0.0 + mocktail_image_network: ^1.2.0 dependency_overrides: - intl: ^0.19.0 win32: ^5.5.4 flutter: diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/bloc/full_screen_ads_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/bloc/full_screen_ads_bloc_test.dart index e3c960120..d3fb254c5 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/bloc/full_screen_ads_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/bloc/full_screen_ads_bloc_test.dart @@ -26,14 +26,14 @@ class FakeInterstitialAd extends Fake implements ads.InterstitialAd { @override set fullScreenContentCallback( ads.FullScreenContentCallback? - fullScreenContentCallback, + fullScreenContentCallback, ) { contentCallback = fullScreenContentCallback; } @override ads.FullScreenContentCallback? - get fullScreenContentCallback => contentCallback; + get fullScreenContentCallback => contentCallback; } class MockRewardedAd extends Mock implements ads.RewardedAd {} @@ -59,7 +59,7 @@ class FakeRewardedAd extends Fake implements ads.RewardedAd { @override ads.FullScreenContentCallback? - get fullScreenContentCallback => contentCallback; + get fullScreenContentCallback => contentCallback; } class MockRewardItem extends Mock implements ads.RewardItem {} @@ -87,13 +87,14 @@ void main() { when(interstitialAd.show).thenAnswer((_) async {}); when(interstitialAd.dispose).thenAnswer((_) async {}); - interstitialAdLoader = ({ - required String adUnitId, - required ads.InterstitialAdLoadCallback adLoadCallback, - required ads.AdRequest request, - }) async { - capturedAdUnitId = adUnitId; - }; + interstitialAdLoader = + ({ + required String adUnitId, + required ads.InterstitialAdLoadCallback adLoadCallback, + required ads.AdRequest request, + }) async { + capturedAdUnitId = adUnitId; + }; rewardedAd = MockRewardedAd(); when( @@ -103,13 +104,14 @@ void main() { ).thenAnswer((_) async {}); when(rewardedAd.dispose).thenAnswer((_) async {}); - rewardedAdLoader = ({ - required String adUnitId, - required ads.RewardedAdLoadCallback rewardedAdLoadCallback, - required ads.AdRequest request, - }) async { - capturedAdUnitId = adUnitId; - }; + rewardedAdLoader = + ({ + required String adUnitId, + required ads.RewardedAdLoadCallback rewardedAdLoadCallback, + required ads.AdRequest request, + }) async { + capturedAdUnitId = adUnitId; + }; }); group('LoadInterstitialAdRequested', () { @@ -162,8 +164,9 @@ void main() { localPlatform: localPlatform, interstitialAdLoader: interstitialAdLoader, rewardedAdLoader: rewardedAdLoader, - fullScreenAdsConfig: - FullScreenAdsConfig(interstitialAdUnitId: 'interstitialAdUnitId'), + fullScreenAdsConfig: FullScreenAdsConfig( + interstitialAdUnitId: 'interstitialAdUnitId', + ), ), act: (bloc) => bloc.add(LoadInterstitialAdRequested()), verify: (bloc) { @@ -174,15 +177,16 @@ void main() { blocTest( 'emits ad when ad is loaded', setUp: () { - interstitialAdLoader = ({ - required String adUnitId, - required ads.InterstitialAdLoadCallback adLoadCallback, - required ads.AdRequest request, - }) async { - await Future.microtask( - () => adLoadCallback.onAdLoaded(interstitialAd), - ); - }; + interstitialAdLoader = + ({ + required String adUnitId, + required ads.InterstitialAdLoadCallback adLoadCallback, + required ads.AdRequest request, + }) async { + await Future.microtask( + () => adLoadCallback.onAdLoaded(interstitialAd), + ); + }; }, build: () => FullScreenAdsBloc( adsRetryPolicy: AdsRetryPolicy(), @@ -203,17 +207,18 @@ void main() { blocTest( 'adds error when ad fails to load', setUp: () { - interstitialAdLoader = ({ - required String adUnitId, - required ads.InterstitialAdLoadCallback adLoadCallback, - required ads.AdRequest request, - }) async { - await Future.microtask( - () => adLoadCallback.onAdFailedToLoad( - ads.LoadAdError(0, 'domain', 'message', null), - ), - ); - }; + interstitialAdLoader = + ({ + required String adUnitId, + required ads.InterstitialAdLoadCallback adLoadCallback, + required ads.AdRequest request, + }) async { + await Future.microtask( + () => adLoadCallback.onAdFailedToLoad( + ads.LoadAdError(0, 'domain', 'message', null), + ), + ); + }; }, build: () => FullScreenAdsBloc( adsRetryPolicy: AdsRetryPolicy(), @@ -238,17 +243,18 @@ void main() { fakeAsync.run((async) async { final adsRetryPolicy = AdsRetryPolicy(); - interstitialAdLoader = ({ - required String adUnitId, - required ads.InterstitialAdLoadCallback adLoadCallback, - required ads.AdRequest request, - }) async { - await Future.microtask( - () => adLoadCallback.onAdFailedToLoad( - ads.LoadAdError(0, 'domain', 'message', null), - ), - ); - }; + interstitialAdLoader = + ({ + required String adUnitId, + required ads.InterstitialAdLoadCallback adLoadCallback, + required ads.AdRequest request, + }) async { + await Future.microtask( + () => adLoadCallback.onAdFailedToLoad( + ads.LoadAdError(0, 'domain', 'message', null), + ), + ); + }; final bloc = FullScreenAdsBloc( adsRetryPolicy: adsRetryPolicy, @@ -270,16 +276,14 @@ void main() { for (var i = 1; i <= adsRetryPolicy.maxRetryCount; i++) { expect( bloc.stream, - emitsInOrder( - [ - FullScreenAdsState( - status: FullScreenAdsStatus.loadingInterstitialAd, - ), - FullScreenAdsState( - status: FullScreenAdsStatus.loadingInterstitialAdFailed, - ), - ], - ), + emitsInOrder([ + FullScreenAdsState( + status: FullScreenAdsStatus.loadingInterstitialAd, + ), + FullScreenAdsState( + status: FullScreenAdsStatus.loadingInterstitialAdFailed, + ), + ]), ); async.elapse(adsRetryPolicy.getIntervalForRetry(i)); } @@ -326,19 +330,20 @@ void main() { ); test('disposes ad on ad dismissed', () async { - final bloc = FullScreenAdsBloc( - adsRetryPolicy: AdsRetryPolicy(), - localPlatform: localPlatform, - interstitialAdLoader: interstitialAdLoader, - rewardedAdLoader: rewardedAdLoader, - ) - ..emit( - FullScreenAdsState( - status: FullScreenAdsStatus.loadingInterstitialAdSucceeded, - interstitialAd: ad, - ), - ) - ..add(ShowInterstitialAdRequested()); + final bloc = + FullScreenAdsBloc( + adsRetryPolicy: AdsRetryPolicy(), + localPlatform: localPlatform, + interstitialAdLoader: interstitialAdLoader, + rewardedAdLoader: rewardedAdLoader, + ) + ..emit( + FullScreenAdsState( + status: FullScreenAdsStatus.loadingInterstitialAdSucceeded, + interstitialAd: ad, + ), + ) + ..add(ShowInterstitialAdRequested()); await expectLater( bloc.stream, @@ -360,19 +365,20 @@ void main() { }); test('disposes ad when ads fails to show', () async { - final bloc = FullScreenAdsBloc( - adsRetryPolicy: AdsRetryPolicy(), - localPlatform: localPlatform, - interstitialAdLoader: interstitialAdLoader, - rewardedAdLoader: rewardedAdLoader, - ) - ..emit( - FullScreenAdsState( - status: FullScreenAdsStatus.loadingInterstitialAdSucceeded, - interstitialAd: ad, - ), - ) - ..add(ShowInterstitialAdRequested()); + final bloc = + FullScreenAdsBloc( + adsRetryPolicy: AdsRetryPolicy(), + localPlatform: localPlatform, + interstitialAdLoader: interstitialAdLoader, + rewardedAdLoader: rewardedAdLoader, + ) + ..emit( + FullScreenAdsState( + status: FullScreenAdsStatus.loadingInterstitialAdSucceeded, + interstitialAd: ad, + ), + ) + ..add(ShowInterstitialAdRequested()); await expectLater( bloc.stream, @@ -477,8 +483,9 @@ void main() { localPlatform: localPlatform, interstitialAdLoader: interstitialAdLoader, rewardedAdLoader: rewardedAdLoader, - fullScreenAdsConfig: - FullScreenAdsConfig(rewardedAdUnitId: 'rewardedAdUnitId'), + fullScreenAdsConfig: FullScreenAdsConfig( + rewardedAdUnitId: 'rewardedAdUnitId', + ), ), act: (bloc) => bloc.add(LoadRewardedAdRequested()), verify: (bloc) { @@ -489,15 +496,16 @@ void main() { blocTest( 'emits ad when ad is loaded', setUp: () { - rewardedAdLoader = ({ - required String adUnitId, - required ads.RewardedAdLoadCallback rewardedAdLoadCallback, - required ads.AdRequest request, - }) async { - await Future.microtask( - () => rewardedAdLoadCallback.onAdLoaded(rewardedAd), - ); - }; + rewardedAdLoader = + ({ + required String adUnitId, + required ads.RewardedAdLoadCallback rewardedAdLoadCallback, + required ads.AdRequest request, + }) async { + await Future.microtask( + () => rewardedAdLoadCallback.onAdLoaded(rewardedAd), + ); + }; }, build: () => FullScreenAdsBloc( adsRetryPolicy: AdsRetryPolicy(), @@ -518,17 +526,18 @@ void main() { blocTest( 'adds error when ad fails to load', setUp: () { - rewardedAdLoader = ({ - required String adUnitId, - required ads.RewardedAdLoadCallback rewardedAdLoadCallback, - required ads.AdRequest request, - }) async { - await Future.microtask( - () => rewardedAdLoadCallback.onAdFailedToLoad( - ads.LoadAdError(0, 'domain', 'message', null), - ), - ); - }; + rewardedAdLoader = + ({ + required String adUnitId, + required ads.RewardedAdLoadCallback rewardedAdLoadCallback, + required ads.AdRequest request, + }) async { + await Future.microtask( + () => rewardedAdLoadCallback.onAdFailedToLoad( + ads.LoadAdError(0, 'domain', 'message', null), + ), + ); + }; }, build: () => FullScreenAdsBloc( adsRetryPolicy: AdsRetryPolicy(), @@ -553,17 +562,18 @@ void main() { fakeAsync.run((async) async { final adsRetryPolicy = AdsRetryPolicy(); - rewardedAdLoader = ({ - required String adUnitId, - required ads.RewardedAdLoadCallback rewardedAdLoadCallback, - required ads.AdRequest request, - }) async { - await Future.microtask( - () => rewardedAdLoadCallback.onAdFailedToLoad( - ads.LoadAdError(0, 'domain', 'message', null), - ), - ); - }; + rewardedAdLoader = + ({ + required String adUnitId, + required ads.RewardedAdLoadCallback rewardedAdLoadCallback, + required ads.AdRequest request, + }) async { + await Future.microtask( + () => rewardedAdLoadCallback.onAdFailedToLoad( + ads.LoadAdError(0, 'domain', 'message', null), + ), + ); + }; final bloc = FullScreenAdsBloc( adsRetryPolicy: adsRetryPolicy, @@ -585,16 +595,14 @@ void main() { for (var i = 1; i <= adsRetryPolicy.maxRetryCount; i++) { expect( bloc.stream, - emitsInOrder( - [ - FullScreenAdsState( - status: FullScreenAdsStatus.loadingRewardedAd, - ), - FullScreenAdsState( - status: FullScreenAdsStatus.loadingRewardedAdFailed, - ), - ], - ), + emitsInOrder([ + FullScreenAdsState( + status: FullScreenAdsStatus.loadingRewardedAd, + ), + FullScreenAdsState( + status: FullScreenAdsStatus.loadingRewardedAdFailed, + ), + ]), ); async.elapse(adsRetryPolicy.getIntervalForRetry(i)); } @@ -682,19 +690,20 @@ void main() { ); test('disposes ad on ad dismissed', () async { - final bloc = FullScreenAdsBloc( - adsRetryPolicy: AdsRetryPolicy(), - localPlatform: localPlatform, - interstitialAdLoader: interstitialAdLoader, - rewardedAdLoader: rewardedAdLoader, - ) - ..emit( - FullScreenAdsState( - status: FullScreenAdsStatus.loadingRewardedAdSucceeded, - rewardedAd: ad, - ), - ) - ..add(ShowRewardedAdRequested()); + final bloc = + FullScreenAdsBloc( + adsRetryPolicy: AdsRetryPolicy(), + localPlatform: localPlatform, + interstitialAdLoader: interstitialAdLoader, + rewardedAdLoader: rewardedAdLoader, + ) + ..emit( + FullScreenAdsState( + status: FullScreenAdsStatus.loadingRewardedAdSucceeded, + rewardedAd: ad, + ), + ) + ..add(ShowRewardedAdRequested()); await expectLater( bloc.stream, @@ -716,19 +725,20 @@ void main() { }); test('disposes ad when ads fails to show', () async { - final bloc = FullScreenAdsBloc( - adsRetryPolicy: AdsRetryPolicy(), - localPlatform: localPlatform, - interstitialAdLoader: interstitialAdLoader, - rewardedAdLoader: rewardedAdLoader, - ) - ..emit( - FullScreenAdsState( - status: FullScreenAdsStatus.loadingRewardedAdSucceeded, - rewardedAd: ad, - ), - ) - ..add(ShowRewardedAdRequested()); + final bloc = + FullScreenAdsBloc( + adsRetryPolicy: AdsRetryPolicy(), + localPlatform: localPlatform, + interstitialAdLoader: interstitialAdLoader, + rewardedAdLoader: rewardedAdLoader, + ) + ..emit( + FullScreenAdsState( + status: FullScreenAdsStatus.loadingRewardedAdSucceeded, + rewardedAd: ad, + ), + ) + ..add(ShowRewardedAdRequested()); await expectLater( bloc.stream, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/bloc/full_screen_ads_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/bloc/full_screen_ads_state_test.dart index a1c33b6fa..112ef4825 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/bloc/full_screen_ads_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/bloc/full_screen_ads_state_test.dart @@ -36,8 +36,7 @@ void main() { }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no properties are passed', () { expect( FullScreenAdsState.initial().copyWith(), @@ -45,8 +44,7 @@ void main() { ); }); - test( - 'returns object with updated interstitialAd ' + test('returns object with updated interstitialAd ' 'when interstitialAd is passed', () { final interstitialAd = FakeInterstitialAd(); expect( @@ -60,8 +58,7 @@ void main() { ); }); - test( - 'returns object with updated rewardedAd ' + test('returns object with updated rewardedAd ' 'when rewardedAd is passed', () { final rewardedAd = FakeRewardedAd(); expect( @@ -75,8 +72,7 @@ void main() { ); }); - test( - 'returns object with updated earnedReward ' + test('returns object with updated earnedReward ' 'when earnedReward is passed', () { final earnedReward = FakeRewardItem(); expect( @@ -90,15 +86,12 @@ void main() { ); }); - test( - 'returns object with updated status ' + test('returns object with updated status ' 'when status is passed', () { const status = FullScreenAdsStatus.showingInterstitialAd; expect( FullScreenAdsState.initial().copyWith(status: status), - equals( - FullScreenAdsState(status: status), - ), + equals(FullScreenAdsState(status: status)), ); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/widgets/sticky_ad_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/widgets/sticky_ad_test.dart index 59877ed63..b9a162d61 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/widgets/sticky_ad_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ads/widgets/sticky_ad_test.dart @@ -9,8 +9,7 @@ import '../../helpers/helpers.dart'; void main() { group('StickyAd', () { - testWidgets( - 'renders StickyAdContainer ' + testWidgets('renders StickyAdContainer ' 'with anchoredAdaptive BannerAdContent', (tester) async { await tester.pumpApp(StickyAd()); @@ -21,23 +20,23 @@ void main() { (widget) => widget is BannerAdContent && widget.size == BannerAdSize.anchoredAdaptive && - widget.showProgressIndicator == false, + !widget.showProgressIndicator, ), ), findsOneWidget, ); }); - testWidgets( - 'renders StickyAdCloseIconBackground and StickyAdCloseIcon ' + testWidgets('renders StickyAdCloseIconBackground and StickyAdCloseIcon ' 'when ad is loaded', (tester) async { await tester.pumpApp(StickyAd()); expect(find.byType(StickyAdCloseIconBackground), findsNothing); expect(find.byType(StickyAdCloseIcon), findsNothing); - final bannerAdContent = - tester.widget(find.byType(BannerAdContent)); + final bannerAdContent = tester.widget( + find.byType(BannerAdContent), + ); bannerAdContent.onAdLoaded?.call(); await tester.pump(); @@ -48,8 +47,9 @@ void main() { testWidgets('hides ad when closed icon is tapped', (tester) async { await tester.pumpApp(StickyAd()); - final bannerAdContent = - tester.widget(find.byType(BannerAdContent)); + final bannerAdContent = tester.widget( + find.byType(BannerAdContent), + ); bannerAdContent.onAdLoaded?.call(); await tester.pump(); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/analytics/bloc/analytics_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/analytics/bloc/analytics_bloc_test.dart index b195f232e..5873b840b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/analytics/bloc/analytics_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/analytics/bloc/analytics_bloc_test.dart @@ -36,15 +36,17 @@ void main() { setUp(() { user = MockUser(); when(() => user.id).thenReturn('id'); - when(() => analyticsRepository.setUserId(any())) - .thenAnswer((_) async {}); + when( + () => analyticsRepository.setUserId(any()), + ).thenAnswer((_) async {}); }); blocTest( 'calls AnalyticsRepository.setUserId ' 'with null when user is anonymous', - setUp: () => when(() => userRepository.user) - .thenAnswer((_) => Stream.value(User.anonymous)), + setUp: () => when( + () => userRepository.user, + ).thenAnswer((_) => Stream.value(User.anonymous)), build: () => AnalyticsBloc( analyticsRepository: analyticsRepository, userRepository: userRepository, @@ -57,8 +59,9 @@ void main() { blocTest( 'calls AnalyticsRepository.setUserId ' 'with user id when user is not anonymous', - setUp: () => when(() => userRepository.user) - .thenAnswer((_) => Stream.value(user)), + setUp: () => when( + () => userRepository.user, + ).thenAnswer((_) => Stream.value(user)), build: () => AnalyticsBloc( analyticsRepository: analyticsRepository, userRepository: userRepository, @@ -73,8 +76,9 @@ void main() { 'when AnalyticsRepository.setUserId throws', setUp: () { when(() => userRepository.user).thenAnswer((_) => Stream.value(user)); - when(() => analyticsRepository.setUserId(any())) - .thenThrow(Exception()); + when( + () => analyticsRepository.setUserId(any()), + ).thenThrow(Exception()); }, build: () => AnalyticsBloc( analyticsRepository: analyticsRepository, @@ -126,8 +130,9 @@ void main() { setUp(() { userController = StreamController(); - when(() => userRepository.user) - .thenAnswer((_) => userController.stream); + when( + () => userRepository.user, + ).thenAnswer((_) => userController.stream); }); blocTest( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/analytics/bloc/analytics_event_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/analytics/bloc/analytics_event_test.dart index c378a47d9..a41e8fbb3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/analytics/bloc/analytics_event_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/analytics/bloc/analytics_event_test.dart @@ -2,9 +2,6 @@ import 'package:analytics_repository/analytics_repository.dart' as analytics; import 'package:{{project_name.snakeCase()}}/analytics/analytics.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:user_repository/user_repository.dart'; - -class MockUser extends Mock implements User {} class MockAnalyticsEvent extends Mock implements analytics.AnalyticsEvent {} diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_bloc_test.dart index f5195c7ed..87380f6ba 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_bloc_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: prefer_const_constructors, must_be_immutable +// ignore_for_file: prefer_const_constructors import 'dart:async'; import 'package:bloc_test/bloc_test.dart'; @@ -30,8 +30,9 @@ void main() { when(() => userRepository.user).thenAnswer((_) => Stream.empty()); when(() => user.isNewUser).thenReturn(User.anonymous.isNewUser); when(() => user.id).thenReturn(User.anonymous.id); - when(() => user.subscriptionPlan) - .thenReturn(User.anonymous.subscriptionPlan); + when( + () => user.subscriptionPlan, + ).thenReturn(User.anonymous.subscriptionPlan); }); test('initial state is unauthenticated when user is anonymous', () { @@ -54,8 +55,9 @@ void main() { newUser = MockUser(); when(() => returningUser.isNewUser).thenReturn(false); when(() => returningUser.id).thenReturn('id'); - when(() => returningUser.subscriptionPlan) - .thenReturn(SubscriptionPlan.none); + when( + () => returningUser.subscriptionPlan, + ).thenReturn(SubscriptionPlan.none); when(() => newUser.isNewUser).thenReturn(true); when(() => newUser.id).thenReturn('id'); when(() => newUser.subscriptionPlan).thenReturn(SubscriptionPlan.none); @@ -65,9 +67,9 @@ void main() { 'emits nothing when ' 'state is unauthenticated and user is anonymous', setUp: () { - when(() => userRepository.user).thenAnswer( - (_) => Stream.value(User.anonymous), - ); + when( + () => userRepository.user, + ).thenAnswer((_) => Stream.value(User.anonymous)); }, build: () => AppBloc( userRepository: userRepository, @@ -82,9 +84,9 @@ void main() { 'emits unauthenticated when ' 'state is onboardingRequired and user is anonymous', setUp: () { - when(() => userRepository.user).thenAnswer( - (_) => Stream.value(User.anonymous), - ); + when( + () => userRepository.user, + ).thenAnswer((_) => Stream.value(User.anonymous)); }, build: () => AppBloc( userRepository: userRepository, @@ -98,9 +100,9 @@ void main() { blocTest( 'emits onboardingRequired when user is new and not anonymous', setUp: () { - when(() => userRepository.user).thenAnswer( - (_) => Stream.value(newUser), - ); + when( + () => userRepository.user, + ).thenAnswer((_) => Stream.value(newUser)); }, build: () => AppBloc( userRepository: userRepository, @@ -113,9 +115,9 @@ void main() { blocTest( 'emits authenticated when user is returning and not anonymous', setUp: () { - when(() => userRepository.user).thenAnswer( - (_) => Stream.value(returningUser), - ); + when( + () => userRepository.user, + ).thenAnswer((_) => Stream.value(returningUser)); }, build: () => AppBloc( userRepository: userRepository, @@ -129,9 +131,9 @@ void main() { 'emits authenticated ' 'when authenticated user changes', setUp: () { - when(() => userRepository.user).thenAnswer( - (_) => Stream.value(returningUser), - ); + when( + () => userRepository.user, + ).thenAnswer((_) => Stream.value(returningUser)); }, seed: () => AppState.authenticated(user), build: () => AppBloc( @@ -171,9 +173,9 @@ void main() { blocTest( 'emits unauthenticated when user is anonymous', setUp: () { - when(() => userRepository.user).thenAnswer( - (_) => Stream.value(User.anonymous), - ); + when( + () => userRepository.user, + ).thenAnswer((_) => Stream.value(User.anonymous)); }, build: () => AppBloc( userRepository: userRepository, @@ -187,9 +189,9 @@ void main() { 'emits nothing when ' 'state is unauthenticated and user is anonymous', setUp: () { - when(() => userRepository.user).thenAnswer( - (_) => Stream.value(User.anonymous), - ); + when( + () => userRepository.user, + ).thenAnswer((_) => Stream.value(User.anonymous)); }, build: () => AppBloc( userRepository: userRepository, @@ -305,8 +307,9 @@ void main() { setUp(() { userController = StreamController(); - when(() => userRepository.user) - .thenAnswer((_) => userController.stream); + when( + () => userRepository.user, + ).thenAnswer((_) => userController.stream); }); blocTest( @@ -327,10 +330,12 @@ void main() { 'when fetchAppOpenedCount returns a count value of 4 ' 'and user is anonymous', setUp: () { - when(() => userRepository.fetchAppOpenedCount()) - .thenAnswer((_) async => 4); - when(() => userRepository.incrementAppOpenedCount()) - .thenAnswer((_) async {}); + when( + () => userRepository.fetchAppOpenedCount(), + ).thenAnswer((_) async => 4); + when( + () => userRepository.incrementAppOpenedCount(), + ).thenAnswer((_) async {}); }, build: () => AppBloc( userRepository: userRepository, @@ -340,15 +345,10 @@ void main() { act: (bloc) => bloc.add(AppOpened()), seed: AppState.unauthenticated, expect: () => [ - AppState( - showLoginOverlay: true, - status: AppStatus.unauthenticated, - ), + AppState(showLoginOverlay: true, status: AppStatus.unauthenticated), ], verify: (_) { - verify( - () => userRepository.incrementAppOpenedCount(), - ).called(1); + verify(() => userRepository.incrementAppOpenedCount()).called(1); }, ); @@ -357,10 +357,12 @@ void main() { 'when fetchAppOpenedCount returns a count value of 3 ' 'and user is anonymous', setUp: () { - when(() => userRepository.fetchAppOpenedCount()) - .thenAnswer((_) async => 3); - when(() => userRepository.incrementAppOpenedCount()) - .thenAnswer((_) async {}); + when( + () => userRepository.fetchAppOpenedCount(), + ).thenAnswer((_) async => 3); + when( + () => userRepository.incrementAppOpenedCount(), + ).thenAnswer((_) async {}); }, build: () => AppBloc( userRepository: userRepository, @@ -371,9 +373,7 @@ void main() { seed: AppState.unauthenticated, expect: () => [], verify: (_) { - verify( - () => userRepository.incrementAppOpenedCount(), - ).called(1); + verify(() => userRepository.incrementAppOpenedCount()).called(1); }, ); @@ -382,8 +382,9 @@ void main() { 'when fetchAppOpenedCount returns a count value of 6 ' 'and user is anonymous', setUp: () { - when(() => userRepository.fetchAppOpenedCount()) - .thenAnswer((_) async => 6); + when( + () => userRepository.fetchAppOpenedCount(), + ).thenAnswer((_) async => 6); }, build: () => AppBloc( userRepository: userRepository, @@ -394,9 +395,7 @@ void main() { seed: AppState.unauthenticated, expect: () => [], verify: (_) { - verifyNever( - () => userRepository.incrementAppOpenedCount(), - ); + verifyNever(() => userRepository.incrementAppOpenedCount()); }, ); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_event_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_event_test.dart index 76ed06ba4..abfe4f5f9 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_event_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_event_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: prefer_const_constructors, must_be_immutable +// ignore_for_file: prefer_const_constructors import 'package:{{project_name.snakeCase()}}/app/app.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -12,46 +12,31 @@ void main() { final user = MockUser(); test('supports value comparisons', () { - expect( - AppUserChanged(user), - AppUserChanged(user), - ); + expect(AppUserChanged(user), AppUserChanged(user)); }); }); group('AppOnboardingCompleted', () { test('supports value comparisons', () { - expect( - AppOnboardingCompleted(), - AppOnboardingCompleted(), - ); + expect(AppOnboardingCompleted(), AppOnboardingCompleted()); }); }); group('AppLogoutRequested', () { test('supports value comparisons', () { - expect( - AppLogoutRequested(), - AppLogoutRequested(), - ); + expect(AppLogoutRequested(), AppLogoutRequested()); }); }); group('AppDeleteAccountRequested', () { test('supports value comparisons', () { - expect( - AppDeleteAccountRequested(), - AppDeleteAccountRequested(), - ); + expect(AppDeleteAccountRequested(), AppDeleteAccountRequested()); }); }); group('AppOpened', () { test('supports value comparisons', () { - expect( - AppOpened(), - AppOpened(), - ); + expect(AppOpened(), AppOpened()); }); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_state_test.dart index a2c6aeb15..6aaa33297 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/bloc/app_state_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: must_be_immutable, prefer_const_constructors +// ignore_for_file: prefer_const_constructors import 'package:{{project_name.snakeCase()}}/app/app.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_repository/in_app_purchase_repository.dart'; @@ -41,42 +41,33 @@ void main() { }); group('isUserSubscribed', () { - test('returns true when userSubscriptionPlan is not null and not none', - () { - when(() => user.subscriptionPlan).thenReturn(SubscriptionPlan.premium); - expect( - AppState.authenticated(user).isUserSubscribed, - isTrue, - ); - }); + test( + 'returns true when userSubscriptionPlan is not null and not none', + () { + when( + () => user.subscriptionPlan, + ).thenReturn(SubscriptionPlan.premium); + expect(AppState.authenticated(user).isUserSubscribed, isTrue); + }, + ); test('returns false when userSubscriptionPlan is none', () { - expect( - AppState.authenticated(user).isUserSubscribed, - isFalse, - ); + expect(AppState.authenticated(user).isUserSubscribed, isFalse); }); }); group('AppStatus', () { - test( - 'authenticated and onboardingRequired are the only statuses ' + test('authenticated and onboardingRequired are the only statuses ' 'where loggedIn is true', () { expect( AppStatus.values.where((e) => e.isLoggedIn).toList(), - equals( - [ - AppStatus.onboardingRequired, - AppStatus.authenticated, - ], - ), + equals([AppStatus.onboardingRequired, AppStatus.authenticated]), ); }); }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no properties are passed', () { expect( AppState.unauthenticated().copyWith(), @@ -84,34 +75,21 @@ void main() { ); }); - test( - 'returns object with updated status ' + test('returns object with updated status ' 'when status is passed', () { expect( AppState.unauthenticated().copyWith( status: AppStatus.onboardingRequired, ), - equals( - AppState( - status: AppStatus.onboardingRequired, - ), - ), + equals(AppState(status: AppStatus.onboardingRequired)), ); }); - test( - 'returns object with updated user ' + test('returns object with updated user ' 'when user is passed', () { expect( - AppState.unauthenticated().copyWith( - user: user, - ), - equals( - AppState( - status: AppStatus.unauthenticated, - user: user, - ), - ), + AppState.unauthenticated().copyWith(user: user), + equals(AppState(status: AppStatus.unauthenticated, user: user)), ); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/routes/routes_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/routes/routes_test.dart index 075c88896..96b1783da 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/routes/routes_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/routes/routes_test.dart @@ -7,42 +7,33 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('onGenerateAppViewPages', () { test('returns [OnboardingPage] when onboardingRequired', () { - expect( - onGenerateAppViewPages(AppStatus.onboardingRequired, []), - [ - isA>().having( - (p) => p.child, - 'child', - isA(), - ), - ], - ); + expect(onGenerateAppViewPages(AppStatus.onboardingRequired, []), [ + isA>().having( + (p) => p.child, + 'child', + isA(), + ), + ]); }); test('returns [HomePage] when authenticated', () { - expect( - onGenerateAppViewPages(AppStatus.authenticated, []), - [ - isA>().having( - (p) => p.child, - 'child', - isA(), - ), - ], - ); + expect(onGenerateAppViewPages(AppStatus.authenticated, []), [ + isA>().having( + (p) => p.child, + 'child', + isA(), + ), + ]); }); test('returns [HomePage] when unauthenticated', () { - expect( - onGenerateAppViewPages(AppStatus.unauthenticated, []), - [ - isA>().having( - (p) => p.child, - 'child', - isA(), - ), - ], - ); + expect(onGenerateAppViewPages(AppStatus.unauthenticated, []), [ + isA>().having( + (p) => p.child, + 'child', + isA(), + ), + ]); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/view/app_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/view/app_test.dart index 2c986fe74..43b0154f8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/view/app_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/app/view/app_test.dart @@ -6,6 +6,7 @@ import 'package:article_repository/article_repository.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:{{project_name.snakeCase()}}/analytics/analytics.dart' as analytics; import 'package:{{project_name.snakeCase()}}/app/app.dart'; +import 'package:{{project_name.snakeCase()}}/categories/categories.dart'; import 'package:{{project_name.snakeCase()}}/home/home.dart'; import 'package:{{project_name.snakeCase()}}/onboarding/onboarding.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -17,7 +18,6 @@ import 'package:user_repository/user_repository.dart'; import '../../helpers/helpers.dart'; -// ignore: must_be_immutable class MockUser extends Mock implements User {} class MockUserRepository extends Mock implements UserRepository {} @@ -38,6 +38,9 @@ class MockAdsConsentClient extends Mock implements AdsConsentClient {} class MockAppBloc extends MockBloc implements AppBloc {} +class MockCategoriesBloc extends MockBloc + implements CategoriesBloc {} + class MockAnalyticsBloc extends MockBloc implements analytics.AnalyticsBloc {} @@ -66,12 +69,18 @@ void main() { adsConsentClient = MockAdsConsentClient(); when(() => userRepository.user).thenAnswer((_) => const Stream.empty()); - when(() => userRepository.incomingEmailLinks) - .thenAnswer((_) => const Stream.empty()); - when(() => userRepository.fetchAppOpenedCount()) - .thenAnswer((_) async => 2); - when(() => userRepository.incrementAppOpenedCount()) - .thenAnswer((_) async {}); + when( + () => userRepository.incomingEmailLinks, + ).thenAnswer((_) => const Stream.empty()); + when( + () => userRepository.fetchAppOpenedCount(), + ).thenAnswer((_) async => 2); + when( + () => userRepository.incrementAppOpenedCount(), + ).thenAnswer((_) async {}); + when( + () => newsRepository.getCategories(), + ).thenAnswer((_) async => const CategoriesResponse(categories: [])); }); testWidgets('renders AppView', (tester) async { @@ -103,8 +112,9 @@ void main() { userRepository = MockUserRepository(); }); - testWidgets('navigates to OnboardingPage when onboardingRequired', - (tester) async { + testWidgets('navigates to OnboardingPage when onboardingRequired', ( + tester, + ) async { final user = MockUser(); when(() => appBloc.state).thenReturn(AppState.onboardingRequired(user)); await tester.pumpApp( @@ -117,11 +127,16 @@ void main() { }); testWidgets('navigates to HomePage when unauthenticated', (tester) async { + final categoriesBloc = MockCategoriesBloc(); when(() => appBloc.state).thenReturn(AppState.unauthenticated()); + when( + () => categoriesBloc.state, + ).thenReturn(const CategoriesState(status: CategoriesStatus.initial)); await tester.pumpApp( const AppView(), appBloc: appBloc, userRepository: userRepository, + categoriesBloc: categoriesBloc, ); await tester.pumpAndSettle(); expect(find.byType(HomePage), findsOneWidget); @@ -129,20 +144,24 @@ void main() { testWidgets('navigates to HomePage when authenticated', (tester) async { final user = MockUser(); + final categoriesBloc = MockCategoriesBloc(); when(() => user.isAnonymous).thenReturn(false); when(() => appBloc.state).thenReturn(AppState.authenticated(user)); + when( + () => categoriesBloc.state, + ).thenReturn(const CategoriesState(status: CategoriesStatus.initial)); await tester.pumpApp( const AppView(), appBloc: appBloc, userRepository: userRepository, + categoriesBloc: categoriesBloc, ); await tester.pumpAndSettle(); expect(find.byType(HomePage), findsOneWidget); }); group('adds TrackAnalyticsEvent to AnalyticsBloc', () { - testWidgets( - 'with RegistrationEvent ' + testWidgets('with RegistrationEvent ' 'when user is authenticated and new', (tester) async { final user = MockUser(); when(() => user.isAnonymous).thenReturn(false); @@ -150,12 +169,10 @@ void main() { whenListen( appBloc, - Stream.fromIterable( - [ - AppState.unauthenticated(), - AppState.authenticated(user), - ], - ), + Stream.fromIterable([ + AppState.unauthenticated(), + AppState.authenticated(user), + ]), initialState: AppState.unauthenticated(), ); @@ -173,8 +190,7 @@ void main() { ).called(1); }); - testWidgets( - 'with LoginEvent ' + testWidgets('with LoginEvent ' 'when user is authenticated and not new', (tester) async { final user = MockUser(); when(() => user.isAnonymous).thenReturn(false); @@ -182,12 +198,10 @@ void main() { whenListen( appBloc, - Stream.fromIterable( - [ - AppState.unauthenticated(), - AppState.authenticated(user), - ], - ), + Stream.fromIterable([ + AppState.unauthenticated(), + AppState.authenticated(user), + ]), initialState: AppState.unauthenticated(), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/bloc/article_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/bloc/article_bloc_test.dart index f886bd76c..fb415cd0c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/bloc/article_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/bloc/article_bloc_test.dart @@ -87,12 +87,15 @@ void main() { setUp(() { when(articleRepository.incrementArticleViews).thenAnswer((_) async {}); when(articleRepository.resetArticleViews).thenAnswer((_) async {}); - when(articleRepository.fetchArticleViews) - .thenAnswer((_) async => ArticleViews(3, DateTime(2022, 6, 7))); - when(() => articleRepository.incrementTotalArticleViews()) - .thenAnswer((_) async => {}); - when(() => articleRepository.fetchTotalArticleViews()) - .thenAnswer((_) async => 0); + when( + articleRepository.fetchArticleViews, + ).thenAnswer((_) async => ArticleViews(3, DateTime(2022, 6, 7))); + when( + () => articleRepository.incrementTotalArticleViews(), + ).thenAnswer((_) async => {}); + when( + () => articleRepository.fetchTotalArticleViews(), + ).thenAnswer((_) async => 0); when( () => articleRepository.getArticle( @@ -200,8 +203,9 @@ void main() { 'and ArticleRepository.incrementArticleViews ' 'and emits hasReachedArticleViewsLimit as false ' 'when the number of article views was never reset', - setUp: () => when(articleRepository.fetchArticleViews) - .thenAnswer((_) async => ArticleViews(0, null)), + setUp: () => when( + articleRepository.fetchArticleViews, + ).thenAnswer((_) async => ArticleViews(0, null)), build: () => articleBloc, act: (bloc) => bloc.add(ArticleRequested()), expect: () => [ @@ -259,8 +263,7 @@ void main() { ], ); - test( - 'calls ArticleRepository.resetArticleViews ' + test('calls ArticleRepository.resetArticleViews ' 'and ArticleRepository.incrementArticleViews ' 'and emits hasReachedArticleViewsLimit as false ' 'when the number of article views was last reset ' @@ -269,8 +272,9 @@ void main() { final now = DateTime(2022, 6, 8, 0, 0, 1); await withClock(Clock.fixed(now), () async { await testBloc( - setUp: () => when(articleRepository.fetchArticleViews) - .thenAnswer((_) async => ArticleViews(3, resetAt)), + setUp: () => when( + articleRepository.fetchArticleViews, + ).thenAnswer((_) async => ArticleViews(3, resetAt)), build: () => ArticleBloc( articleId: articleId, articleRepository: articleRepository, @@ -307,8 +311,7 @@ void main() { }); }); - test( - 'calls ArticleRepository.incrementArticleViews ' + test('calls ArticleRepository.incrementArticleViews ' 'and emits hasReachedArticleViewsLimit as false ' 'when the article views limit of 4 was not reached ' 'and the the number of article views was last reset ' @@ -317,8 +320,9 @@ void main() { final now = DateTime(2022, 6, 7, 12, 0, 0); await withClock(Clock.fixed(now), () async { await testBloc( - setUp: () => when(articleRepository.fetchArticleViews) - .thenAnswer((_) async => ArticleViews(2, resetAt)), + setUp: () => when( + articleRepository.fetchArticleViews, + ).thenAnswer((_) async => ArticleViews(2, resetAt)), build: () => ArticleBloc( articleId: articleId, shareLauncher: shareLauncher, @@ -355,8 +359,7 @@ void main() { }); }); - test( - 'does not call ArticleRepository.incrementArticleViews ' + test('does not call ArticleRepository.incrementArticleViews ' 'and emits hasReachedArticleViewsLimit as true ' 'when the article views limit of 4 was reached ' 'and the the number of article views was last reset ' @@ -364,11 +367,12 @@ void main() { final resetAt = DateTime(2022, 6, 7); final now = DateTime(2022, 6, 7, 12, 0, 0); - withClock(Clock.fixed(now), () { - testBloc( + await withClock(Clock.fixed(now), () async { + await testBloc( seed: () => ArticleState(status: ArticleStatus.populated), - setUp: () => when(articleRepository.fetchArticleViews) - .thenAnswer((_) async => ArticleViews(4, resetAt)), + setUp: () => when( + articleRepository.fetchArticleViews, + ).thenAnswer((_) async => ArticleViews(4, resetAt)), build: () => ArticleBloc( articleId: articleId, articleRepository: articleRepository, @@ -409,8 +413,9 @@ void main() { 'emits showInterstitialAd true ' 'when fetchTotalArticleViews returns 4 ', setUp: () { - when(() => articleRepository.fetchTotalArticleViews()) - .thenAnswer((_) async => 4); + when( + () => articleRepository.fetchTotalArticleViews(), + ).thenAnswer((_) async => 4); }, build: () => articleBloc, act: (bloc) => bloc.add(ArticleRequested()), @@ -439,13 +444,9 @@ void main() { 'when new count (contentIndex + 1) is higher than current', build: () => articleBloc, act: (bloc) => bloc.add(ArticleContentSeen(contentIndex: 15)), - seed: () => articleStatePopulated.copyWith( - contentSeenCount: 10, - ), + seed: () => articleStatePopulated.copyWith(contentSeenCount: 10), expect: () => [ - articleStatePopulated.copyWith( - contentSeenCount: 16, - ), + articleStatePopulated.copyWith(contentSeenCount: 16), ], ); @@ -455,9 +456,7 @@ void main() { 'or equal to current', build: () => articleBloc, act: (bloc) => bloc.add(ArticleContentSeen(contentIndex: 9)), - seed: () => articleStatePopulated.copyWith( - contentSeenCount: 10, - ), + seed: () => articleStatePopulated.copyWith(contentSeenCount: 10), expect: () => [], ); }); @@ -472,17 +471,15 @@ void main() { 'and emits hasReachedArticleViewsLimit as false ' 'when the number of article views is less than ' 'the article views limit of 4', - setUp: () => when(articleRepository.fetchArticleViews) - .thenAnswer((_) async => ArticleViews(3, null)), + setUp: () => when( + articleRepository.fetchArticleViews, + ).thenAnswer((_) async => ArticleViews(3, null)), build: () => articleBloc, act: (bloc) => bloc.add(ArticleRewardedAdWatched()), - seed: () => articleStatePopulated.copyWith( - hasReachedArticleViewsLimit: true, - ), + seed: () => + articleStatePopulated.copyWith(hasReachedArticleViewsLimit: true), expect: () => [ - articleStatePopulated.copyWith( - hasReachedArticleViewsLimit: false, - ), + articleStatePopulated.copyWith(hasReachedArticleViewsLimit: false), ], verify: (bloc) => verify(articleRepository.decrementArticleViews).called(1), @@ -493,17 +490,15 @@ void main() { 'and emits hasReachedArticleViewsLimit as true ' 'when the number of article views is equal to ' 'the article views limit of 4', - setUp: () => when(articleRepository.fetchArticleViews) - .thenAnswer((_) async => ArticleViews(4, null)), + setUp: () => when( + articleRepository.fetchArticleViews, + ).thenAnswer((_) async => ArticleViews(4, null)), build: () => articleBloc, act: (bloc) => bloc.add(ArticleRewardedAdWatched()), - seed: () => articleStatePopulated.copyWith( - hasReachedArticleViewsLimit: false, - ), + seed: () => + articleStatePopulated.copyWith(hasReachedArticleViewsLimit: false), expect: () => [ - articleStatePopulated.copyWith( - hasReachedArticleViewsLimit: true, - ), + articleStatePopulated.copyWith(hasReachedArticleViewsLimit: true), ], verify: (bloc) => verify(articleRepository.decrementArticleViews).called(1), @@ -512,13 +507,15 @@ void main() { blocTest( 'emits [rewardedAdWatchedFailure] ' 'when decrementArticleViews throws', - setUp: () => when(articleRepository.decrementArticleViews) - .thenThrow(Exception()), + setUp: () => when( + articleRepository.decrementArticleViews, + ).thenThrow(Exception()), build: () => articleBloc, act: (bloc) => bloc.add(ArticleRewardedAdWatched()), expect: () => [ - ArticleState.initial() - .copyWith(status: ArticleStatus.rewardedAdWatchedFailure), + ArticleState.initial().copyWith( + status: ArticleStatus.rewardedAdWatchedFailure, + ), ], ); @@ -530,8 +527,9 @@ void main() { build: () => articleBloc, act: (bloc) => bloc.add(ArticleRewardedAdWatched()), expect: () => [ - ArticleState.initial() - .copyWith(status: ArticleStatus.rewardedAdWatchedFailure), + ArticleState.initial().copyWith( + status: ArticleStatus.rewardedAdWatchedFailure, + ), ], ); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/bloc/article_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/bloc/article_state_test.dart index a96b65982..237605dab 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/bloc/article_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/bloc/article_state_test.dart @@ -7,22 +7,15 @@ import 'package:news_blocks/news_blocks.dart'; void main() { group('ArticleState', () { test('initial has correct status', () { - expect( - ArticleState.initial().status, - equals(ArticleStatus.initial), - ); + expect(ArticleState.initial().status, equals(ArticleStatus.initial)); }); test('supports value comparisons', () { - expect( - ArticleState.initial(), - equals(ArticleState.initial()), - ); + expect(ArticleState.initial(), equals(ArticleState.initial())); }); group('contentMilestone', () { - test( - 'returns 0 ' + test('returns 0 ' 'when contentTotalCount is null', () { expect( ArticleState.initial().copyWith(contentSeenCount: 5).contentMilestone, @@ -30,8 +23,7 @@ void main() { ); }); - test( - 'returns 0 ' + test('returns 0 ' 'when isPreview is true', () { expect( ArticleState.initial() @@ -41,110 +33,78 @@ void main() { ); }); - test( - 'returns 0 ' + test('returns 0 ' 'when user has seen less than 25% of content', () { expect( ArticleState.initial() - .copyWith( - contentSeenCount: 0, - contentTotalCount: 100, - ) + .copyWith(contentSeenCount: 0, contentTotalCount: 100) .contentMilestone, isZero, ); expect( ArticleState.initial() - .copyWith( - contentSeenCount: 24, - contentTotalCount: 100, - ) + .copyWith(contentSeenCount: 24, contentTotalCount: 100) .contentMilestone, isZero, ); }); - test( - 'returns 25 ' + test('returns 25 ' 'when user has seen at least 25% of content ' 'and less than 50%', () { expect( ArticleState.initial() - .copyWith( - contentSeenCount: 25, - contentTotalCount: 100, - ) + .copyWith(contentSeenCount: 25, contentTotalCount: 100) .contentMilestone, equals(25), ); expect( ArticleState.initial() - .copyWith( - contentSeenCount: 49, - contentTotalCount: 100, - ) + .copyWith(contentSeenCount: 49, contentTotalCount: 100) .contentMilestone, equals(25), ); }); - test( - 'returns 50 ' + test('returns 50 ' 'when user has seen at least 50% of content ' 'and less than 75%', () { expect( ArticleState.initial() - .copyWith( - contentSeenCount: 50, - contentTotalCount: 100, - ) + .copyWith(contentSeenCount: 50, contentTotalCount: 100) .contentMilestone, equals(50), ); expect( ArticleState.initial() - .copyWith( - contentSeenCount: 74, - contentTotalCount: 100, - ) + .copyWith(contentSeenCount: 74, contentTotalCount: 100) .contentMilestone, equals(50), ); }); - test( - 'returns 75 ' + test('returns 75 ' 'when user has seen at least 75% of content ' 'and less than 100%', () { expect( ArticleState.initial() - .copyWith( - contentSeenCount: 75, - contentTotalCount: 100, - ) + .copyWith(contentSeenCount: 75, contentTotalCount: 100) .contentMilestone, equals(75), ); expect( ArticleState.initial() - .copyWith( - contentSeenCount: 99, - contentTotalCount: 100, - ) + .copyWith(contentSeenCount: 99, contentTotalCount: 100) .contentMilestone, equals(75), ); }); - test( - 'returns 100 ' + test('returns 100 ' 'when user has seen 100% of content', () { expect( ArticleState.initial() - .copyWith( - contentSeenCount: 100, - contentTotalCount: 100, - ) + .copyWith(contentSeenCount: 100, contentTotalCount: 100) .contentMilestone, equals(100), ); @@ -152,8 +112,7 @@ void main() { }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no properties are passed', () { expect( ArticleState.initial().copyWith(), @@ -161,39 +120,25 @@ void main() { ); }); - test( - 'returns object with updated title ' + test('returns object with updated title ' 'when title is passed', () { expect( ArticleState( status: ArticleStatus.populated, ).copyWith(title: 'title'), - equals( - ArticleState( - status: ArticleStatus.populated, - title: 'title', - ), - ), + equals(ArticleState(status: ArticleStatus.populated, title: 'title')), ); }); - test( - 'returns object with updated status ' + test('returns object with updated status ' 'when status is passed', () { expect( - ArticleState.initial().copyWith( - status: ArticleStatus.loading, - ), - equals( - ArticleState( - status: ArticleStatus.loading, - ), - ), + ArticleState.initial().copyWith(status: ArticleStatus.loading), + equals(ArticleState(status: ArticleStatus.loading)), ); }); - test( - 'returns object with updated content ' + test('returns object with updated content ' 'when content is passed', () { final content = [ TextCaptionBlock(text: 'text', color: TextCaptionColor.normal), @@ -201,26 +146,22 @@ void main() { ]; expect( - ArticleState(status: ArticleStatus.populated).copyWith( - content: content, - ), + ArticleState( + status: ArticleStatus.populated, + ).copyWith(content: content), equals( - ArticleState( - status: ArticleStatus.populated, - content: content, - ), + ArticleState(status: ArticleStatus.populated, content: content), ), ); }); - test( - 'returns object with updated contentTotalCount ' + test('returns object with updated contentTotalCount ' 'when contentTotalCount is passed', () { const contentTotalCount = 10; expect( - ArticleState(status: ArticleStatus.populated).copyWith( - contentTotalCount: contentTotalCount, - ), + ArticleState( + status: ArticleStatus.populated, + ).copyWith(contentTotalCount: contentTotalCount), equals( ArticleState( status: ArticleStatus.populated, @@ -230,14 +171,13 @@ void main() { ); }); - test( - 'returns object with updated contentSeenCount ' + test('returns object with updated contentSeenCount ' 'when contentSeenCount is passed', () { const contentSeenCount = 10; expect( - ArticleState(status: ArticleStatus.populated).copyWith( - contentSeenCount: contentSeenCount, - ), + ArticleState( + status: ArticleStatus.populated, + ).copyWith(contentSeenCount: contentSeenCount), equals( ArticleState( status: ArticleStatus.populated, @@ -247,8 +187,7 @@ void main() { ); }); - test( - 'returns object with updated hasMoreContent ' + test('returns object with updated hasMoreContent ' 'when hasMoreContent is passed', () { const hasMoreContent = false; @@ -266,8 +205,7 @@ void main() { ); }); - test( - 'returns object with updated relatedArticles ' + test('returns object with updated relatedArticles ' 'when relatedArticles is passed', () { const relatedArticles = [DividerHorizontalBlock()]; @@ -284,8 +222,7 @@ void main() { ); }); - test( - 'returns object with updated hasReachedArticleViewsLimit ' + test('returns object with updated hasReachedArticleViewsLimit ' 'when hasReachedArticleViewsLimit is passed', () { const hasReachedArticleViewsLimit = true; @@ -302,8 +239,7 @@ void main() { ); }); - test( - 'returns object with updated isPreview ' + test('returns object with updated isPreview ' 'when isPreview is passed', () { const isPreview = true; @@ -312,16 +248,12 @@ void main() { status: ArticleStatus.populated, ).copyWith(isPreview: isPreview), equals( - ArticleState( - status: ArticleStatus.populated, - isPreview: isPreview, - ), + ArticleState(status: ArticleStatus.populated, isPreview: isPreview), ), ); }); - test( - 'returns object with updated isPremium ' + test('returns object with updated isPremium ' 'when isPremium is passed', () { const isPremium = true; @@ -330,10 +262,7 @@ void main() { status: ArticleStatus.populated, ).copyWith(isPremium: isPremium), equals( - ArticleState( - status: ArticleStatus.populated, - isPremium: isPremium, - ), + ArticleState(status: ArticleStatus.populated, isPremium: isPremium), ), ); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/view/article_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/view/article_page_test.dart index 3cfd1addf..960c08502 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/view/article_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/view/article_page_test.dart @@ -115,12 +115,11 @@ void main() { expect(find.byType(ShareButton), findsOneWidget); }); - testWidgets('that adds ShareRequested on ShareButton tap', - (tester) async { + testWidgets('that adds ShareRequested on ShareButton tap', ( + tester, + ) async { when(() => articleBloc.state).thenReturn( - ArticleState.initial().copyWith( - uri: Uri(path: 'notEmptyUrl'), - ), + ArticleState.initial().copyWith(uri: Uri(path: 'notEmptyUrl')), ); await tester.pumpApp( fullScreenAdsBloc: fullScreenAdsBloc, @@ -136,11 +135,8 @@ void main() { await tester.tap(find.byType(ShareButton)); verify( - () => articleBloc.add( - ShareRequested( - uri: Uri(path: 'notEmptyUrl'), - ), - ), + () => + articleBloc.add(ShareRequested(uri: Uri(path: 'notEmptyUrl'))), ).called(1); }); }); @@ -177,11 +173,7 @@ void main() { group('ArticleSubscribeButton', () { testWidgets('renders AppButton', (tester) async { - await tester.pumpApp( - Row( - children: [ArticleSubscribeButton()], - ), - ); + await tester.pumpApp(Row(children: [ArticleSubscribeButton()])); expect(find.byType(AppButton), findsOneWidget); }); @@ -191,13 +183,13 @@ void main() { setUp(() { inAppPurchaseRepository = MockInAppPurchaseRepository(); - when(() => inAppPurchaseRepository.purchaseUpdate).thenAnswer( - (_) => const Stream.empty(), - ); + when( + () => inAppPurchaseRepository.purchaseUpdate, + ).thenAnswer((_) => const Stream.empty()); - when(inAppPurchaseRepository.fetchSubscriptions).thenAnswer( - (_) async => [], - ); + when( + inAppPurchaseRepository.fetchSubscriptions, + ).thenAnswer((_) async => []); }); testWidgets('when tapped', (tester) async { @@ -212,17 +204,14 @@ void main() { }); testWidgets('does nothing when tapped', (tester) async { - await tester.pumpApp( - Row( - children: [ArticleSubscribeButton()], - ), - ); + await tester.pumpApp(Row(children: [ArticleSubscribeButton()])); await tester.tap(find.byType(ArticleSubscribeButton)); }); }); - testWidgets('renders ArticleContent in ArticleThemeOverride', - (tester) async { + testWidgets('renders ArticleContent in ArticleThemeOverride', ( + tester, + ) async { await tester.pumpApp( fullScreenAdsBloc: fullScreenAdsBloc, BlocProvider.value( @@ -242,15 +231,12 @@ void main() { ); }); - testWidgets( - 'renders AppBar with ShareButton and ' + testWidgets('renders AppBar with ShareButton and ' 'ArticleSubscribeButton action ' 'when user is not a subscriber ' 'and uri is provided', (tester) async { when(() => articleBloc.state).thenReturn( - ArticleState.initial().copyWith( - uri: Uri(path: 'notEmptyUrl'), - ), + ArticleState.initial().copyWith(uri: Uri(path: 'notEmptyUrl')), ); await tester.pumpApp( fullScreenAdsBloc: fullScreenAdsBloc, @@ -273,17 +259,14 @@ void main() { expect( tester.widget(appBar).actions, - containsAll( - [ - tester.widget(articleSubscribeButton), - tester.widget(shareButton), - ], - ), + containsAll([ + tester.widget(articleSubscribeButton), + tester.widget(shareButton), + ]), ); }); - testWidgets( - 'renders AppBar without ShareButton ' + testWidgets('renders AppBar without ShareButton ' 'when user is not a subscriber ' 'and uri is not provided', (tester) async { await tester.pumpApp( @@ -313,8 +296,7 @@ void main() { expect(find.byType(ShareButton), findsNothing); }); - testWidgets( - 'renders AppBar with ShareButton ' + testWidgets('renders AppBar with ShareButton ' 'action when user is a subscriber ' 'and url is not empty', (tester) async { when(() => appBloc.state).thenReturn( @@ -352,11 +334,7 @@ void main() { expect( tester.widget(appBar).actions, - containsAll( - [ - tester.widget(shareButton), - ], - ), + containsAll([tester.widget(shareButton)]), ); }); @@ -382,19 +360,14 @@ void main() { }); }); - testWidgets( - 'adds ArticleRequested to ArticleBloc ' + testWidgets('adds ArticleRequested to ArticleBloc ' 'when hasReachedArticleViewsLimit changes to false', (tester) async { whenListen( articleBloc, - Stream.fromIterable( - [ - ArticleState.initial() - .copyWith(hasReachedArticleViewsLimit: true), - ArticleState.initial() - .copyWith(hasReachedArticleViewsLimit: false), - ], - ), + Stream.fromIterable([ + ArticleState.initial().copyWith(hasReachedArticleViewsLimit: true), + ArticleState.initial().copyWith(hasReachedArticleViewsLimit: false), + ]), ); await tester.pumpApp( fullScreenAdsBloc: fullScreenAdsBloc, @@ -409,18 +382,15 @@ void main() { verify(() => articleBloc.add(ArticleRequested())); }); - testWidgets( - 'adds ShowInterstitialAdRequested to FullScreenAdsBloc ' + testWidgets('adds ShowInterstitialAdRequested to FullScreenAdsBloc ' 'when interstitialAdBehavior is onOpen and ' 'showInterstitialAd is true', (tester) async { whenListen( articleBloc, - Stream.fromIterable( - [ - ArticleState.initial().copyWith(showInterstitialAd: false), - ArticleState.initial().copyWith(showInterstitialAd: true), - ], - ), + Stream.fromIterable([ + ArticleState.initial().copyWith(showInterstitialAd: false), + ArticleState.initial().copyWith(showInterstitialAd: true), + ]), ); await tester.pumpApp( @@ -433,21 +403,19 @@ void main() { ), ), ); - verify(() => fullScreenAdsBloc.add(ShowInterstitialAdRequested())) - .called(1); + verify( + () => fullScreenAdsBloc.add(ShowInterstitialAdRequested()), + ).called(1); }); - testWidgets( - 'verify ShowInterstitialAdRequested is not ' + testWidgets('verify ShowInterstitialAdRequested is not ' 'added to FullScreenAdsBloc when interstitialAdBehavior is onOpen ' 'and showInterstitialAd is false', (tester) async { whenListen( articleBloc, - Stream.fromIterable( - [ - ArticleState.initial().copyWith(showInterstitialAd: false), - ], - ), + Stream.fromIterable([ + ArticleState.initial().copyWith(showInterstitialAd: false), + ]), ); await tester.pumpApp( @@ -464,13 +432,12 @@ void main() { verifyNever(() => fullScreenAdsBloc.add(ShowInterstitialAdRequested())); }); - testWidgets( - 'adds ShowInterstitialAdRequested to FullScreenAdsBloc ' + testWidgets('adds ShowInterstitialAdRequested to FullScreenAdsBloc ' 'when interstitialAdBehavior in onClose and ' 'showInterstitialAd is true', (tester) async { - when(() => articleBloc.state).thenReturn( - ArticleState.initial().copyWith(showInterstitialAd: true), - ); + when( + () => articleBloc.state, + ).thenReturn(ArticleState.initial().copyWith(showInterstitialAd: true)); await tester.pumpApp( fullScreenAdsBloc: fullScreenAdsBloc, @@ -485,12 +452,12 @@ void main() { await tester.tap(find.byType(AppBackButton)); - verify(() => fullScreenAdsBloc.add(ShowInterstitialAdRequested())) - .called(1); + verify( + () => fullScreenAdsBloc.add(ShowInterstitialAdRequested()), + ).called(1); }); - testWidgets( - 'verify ShowInterstitialAdRequested is not ' + testWidgets('verify ShowInterstitialAdRequested is not ' 'added to FullScreenAdsBloc when interstitialAdBehavior is onClose ' 'showInterstitialAd is false ', (tester) async { when(() => articleBloc.state).thenReturn(ArticleState.initial()); @@ -511,14 +478,13 @@ void main() { verifyNever(() => fullScreenAdsBloc.add(ShowInterstitialAdRequested())); }); - testWidgets( - 'adds ShowInterstitialAdRequested to FullScreenAdsBloc ' + testWidgets('adds ShowInterstitialAdRequested to FullScreenAdsBloc ' 'with video article ' 'when interstitialAdBehavior in onClose and ' 'showInterstitialAd is true', (tester) async { - when(() => articleBloc.state).thenReturn( - ArticleState.initial().copyWith(showInterstitialAd: true), - ); + when( + () => articleBloc.state, + ).thenReturn(ArticleState.initial().copyWith(showInterstitialAd: true)); await tester.pumpApp( fullScreenAdsBloc: fullScreenAdsBloc, @@ -533,12 +499,12 @@ void main() { await tester.tap(find.byType(AppBackButton)); - verify(() => fullScreenAdsBloc.add(ShowInterstitialAdRequested())) - .called(1); + verify( + () => fullScreenAdsBloc.add(ShowInterstitialAdRequested()), + ).called(1); }); - testWidgets( - 'adds ArticleRewardedAdWatched to ArticleBloc ' + testWidgets('adds ArticleRewardedAdWatched to ArticleBloc ' 'when earnedReward is not null', (tester) async { whenListen( fullScreenAdsBloc, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_comments_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_comments_test.dart index 272f501b2..ce9083bdd 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_comments_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_comments_test.dart @@ -1,5 +1,4 @@ // ignore_for_file: prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables import 'package:app_ui/app_ui.dart'; import 'package:bloc_test/bloc_test.dart'; @@ -23,24 +22,17 @@ void main() { find.byKey(Key('articleComments_discussionTitle')), findsOneWidget, ); - expect( - find.byType(AppTextField), - findsOneWidget, - ); + expect(find.byType(AppTextField), findsOneWidget); }); - testWidgets( - 'adds ArticleCommented with article title to ArticleBloc ' + testWidgets('adds ArticleCommented with article title to ArticleBloc ' 'when comment is submitted', (tester) async { final ArticleBloc articleBloc = MockArticleBloc(); final articleState = ArticleState.initial().copyWith(title: 'title'); when(() => articleBloc.state).thenReturn(articleState); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleComments(), - ), + BlocProvider.value(value: articleBloc, child: ArticleComments()), ); final textField = tester.widget(find.byType(AppTextField)); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_content_item_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_content_item_test.dart index 9df20193b..16442fc0d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_content_item_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_content_item_test.dart @@ -22,8 +22,7 @@ void main() { } group('ArticleContentItem', () { - testWidgets( - 'renders DividerHorizontal ' + testWidgets('renders DividerHorizontal ' 'for DividerHorizontalBlock', (tester) async { const block = DividerHorizontalBlock(); await tester.pumpApp(ArticleContentItem(block: block)); @@ -35,8 +34,7 @@ void main() { ); }); - testWidgets( - 'renders Spacer ' + testWidgets('renders Spacer ' 'for SpacerBlock', (tester) async { const block = SpacerBlock(spacing: Spacing.large); await tester.pumpApp(ArticleContentItem(block: block)); @@ -48,8 +46,7 @@ void main() { ); }); - testWidgets( - 'renders Image ' + testWidgets('renders Image ' 'for ImageBlock', (tester) async { const block = ImageBlock(imageUrl: 'imageUrl'); await tester.pumpApp(ArticleContentItem(block: block)); @@ -61,8 +58,7 @@ void main() { ); }); - testWidgets( - 'renders TextCaption ' + testWidgets('renders TextCaption ' 'with colorValues from ArticleThemeColors ' 'for TextCaptionBlock', (tester) async { const block = TextCaptionBlock( @@ -96,8 +92,7 @@ void main() { ); }); - testWidgets( - 'renders TextHeadline ' + testWidgets('renders TextHeadline ' 'for TextHeadlineBlock', (tester) async { const block = TextHeadlineBlock(text: 'text'); await tester.pumpApp(ArticleContentItem(block: block)); @@ -109,8 +104,7 @@ void main() { ); }); - testWidgets( - 'renders TextLeadParagraph ' + testWidgets('renders TextLeadParagraph ' 'for TextLeadParagraphBlock', (tester) async { const block = TextLeadParagraphBlock(text: 'text'); await tester.pumpApp(ArticleContentItem(block: block)); @@ -122,8 +116,7 @@ void main() { ); }); - testWidgets( - 'renders TextParagraph ' + testWidgets('renders TextParagraph ' 'for TextParagraphBlock', (tester) async { const block = TextParagraphBlock(text: 'text'); await tester.pumpApp(ArticleContentItem(block: block)); @@ -135,8 +128,7 @@ void main() { ); }); - testWidgets( - 'renders Video ' + testWidgets('renders Video ' 'for VideoBlock', (tester) async { setUpVideoPlayerPlatform(); const block = VideoBlock(videoUrl: 'videoUrl'); @@ -149,22 +141,18 @@ void main() { ); }); - testWidgets( - 'renders ArticleIntroduction ' + testWidgets('renders ArticleIntroduction ' 'for ArticleIntroductionBlock', (tester) async { + const category = Category(id: 'technology', name: 'Technology'); final block = ArticleIntroductionBlock( - category: PostCategory.technology, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 9), imageUrl: 'imageUrl', title: 'title', ); await tester.pumpApp( - ListView( - children: [ - ArticleContentItem(block: block), - ], - ), + ListView(children: [ArticleContentItem(block: block)]), ); expect( find.byWidgetPredicate( @@ -174,22 +162,17 @@ void main() { ); }); - testWidgets( - 'renders VideoIntroduction ' + testWidgets('renders VideoIntroduction ' 'for VideoIntroductionBlock', (tester) async { setUpVideoPlayerPlatform(); - + const category = Category(id: 'technology', name: 'Technology'); final block = VideoIntroductionBlock( - category: PostCategory.technology, + categoryId: category.id, title: 'title', videoUrl: 'videoUrl', ); await tester.pumpApp( - ListView( - children: [ - ArticleContentItem(block: block), - ], - ), + ListView(children: [ArticleContentItem(block: block)]), ); expect( find.byWidgetPredicate( @@ -199,16 +182,14 @@ void main() { ); }); - testWidgets( - 'renders BannerAd ' + testWidgets('renders BannerAd ' 'for BannerAdBlock', (tester) async { final block = BannerAdBlock(size: BannerAdSize.normal); await tester.pumpApp(ArticleContentItem(block: block)); expect(find.byType(BannerAd), findsOneWidget); }); - testWidgets( - 'renders Newsletter ' + testWidgets('renders Newsletter ' 'for NewsletterBlock', (tester) async { VisibilityDetectorController.instance.updateInterval = Duration.zero; final block = NewsletterBlock(); @@ -216,60 +197,47 @@ void main() { expect(find.byType(Newsletter), findsOneWidget); }); - testWidgets( - 'renders Html ' + testWidgets('renders Html ' 'for HtmlBlock', (tester) async { final block = HtmlBlock(content: '

Lorem

'); await tester.pumpApp(ArticleContentItem(block: block)); expect(find.byType(Html), findsOneWidget); }); - testWidgets( - 'renders SlideshowIntroduction ' + testWidgets('renders SlideshowIntroduction ' 'for SlideshowIntroductionBlock', (tester) async { final block = SlideshowIntroductionBlock( title: 'title', coverImageUrl: 'coverImageUrl', action: NavigateToSlideshowAction( - slideshow: SlideshowBlock( - slides: [], - title: 'title', - ), + slideshow: SlideshowBlock(slides: [], title: 'title'), articleId: 'articleId', ), ); await tester.pumpApp( - ListView( - children: [ - ArticleContentItem(block: block), - ], - ), + ListView(children: [ArticleContentItem(block: block)]), ); await tester.ensureVisible(find.byType(SlideshowIntroduction)); await tester.tap(find.byType(SlideshowIntroduction)); await tester.pumpAndSettle(); - expect( - find.byType(SlideshowPage), - findsOneWidget, - ); + expect(find.byType(SlideshowPage), findsOneWidget); }); }); - testWidgets( - 'renders SizedBox ' + testWidgets('renders SizedBox ' 'for unsupported block', (tester) async { final block = UnknownBlock(); await tester.pumpApp(ArticleContentItem(block: block)); expect(find.byType(SizedBox), findsOneWidget); }); - testWidgets( - 'renders TrendingStory ' + testWidgets('renders TrendingStory ' 'for TrendingStoryBlock', (tester) async { + const category = Category(id: 'health', name: 'Health'); final content = PostSmallBlock( id: 'id', - category: PostCategory.health, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_content_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_content_test.dart index fb1ce07e6..9b3230064 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_content_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_content_test.dart @@ -45,10 +45,7 @@ void main() { group('ArticleContent', () { testWidgets('renders a SelectionArea', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); expect(find.byType(SelectionArea), findsOneWidget); @@ -56,10 +53,7 @@ void main() { testWidgets('renders StickyAd', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); expect(find.byType(StickyAd), findsOneWidget); @@ -78,25 +72,17 @@ void main() { testWidgets('shows NetworkErrorAlert', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); - expect( - find.byType(NetworkError), - findsOneWidget, - ); + expect(find.byType(NetworkError), findsOneWidget); }); - testWidgets('shows ArticleContentItem for each content block', - (tester) async { + testWidgets('shows ArticleContentItem for each content block', ( + tester, + ) async { await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); for (final block in content) { @@ -109,19 +95,14 @@ void main() { } }); - testWidgets('NetworkErrorAlert requests article on press', - (tester) async { + testWidgets('NetworkErrorAlert requests article on press', ( + tester, + ) async { await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); - expect( - find.text(networkErrorButtonText), - findsOneWidget, - ); + expect(find.text(networkErrorButtonText), findsOneWidget); await tester.ensureVisible(find.textContaining(networkErrorButtonText)); verify(() => articleBloc.add(ArticleRequested())).called(1); @@ -152,10 +133,7 @@ void main() { final navigatorObserver = MockNavigatorObserver(); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), navigatorObserver: navigatorObserver, ); @@ -170,24 +148,19 @@ void main() { ); }); - testWidgets('NetworkErrorAlert requests article on press', - (tester) async { + testWidgets('NetworkErrorAlert requests article on press', ( + tester, + ) async { final navigatorObserver = MockNavigatorObserver(); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), navigatorObserver: navigatorObserver, ); verify(() => navigatorObserver.didPush(any(), any())); - expect( - find.text(networkErrorButtonText), - findsOneWidget, - ); + expect(find.text(networkErrorButtonText), findsOneWidget); await tester.ensureVisible(find.textContaining(networkErrorButtonText)); verify(() => articleBloc.add(ArticleRequested())).called(1); @@ -214,10 +187,7 @@ void main() { testWidgets('shows SnackBar with error message', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); expect( @@ -243,13 +213,11 @@ void main() { ); }); - testWidgets('shows ArticleContentItem for each content block', - (tester) async { + testWidgets('shows ArticleContentItem for each content block', ( + tester, + ) async { await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); for (final block in content) { @@ -262,14 +230,10 @@ void main() { } }); - testWidgets( - 'adds ShareRequested to ArticleBloc ' + testWidgets('adds ShareRequested to ArticleBloc ' 'when ArticleContentItem onSharePressed is called', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); final articleItem = find.byType(ArticleContentItem).first; @@ -278,8 +242,7 @@ void main() { verify(() => articleBloc.add(ShareRequested(uri: uri))).called(1); }); - testWidgets( - 'adds ArticleContentSeen to ArticleBloc ' + testWidgets('adds ArticleContentSeen to ArticleBloc ' 'for every visible content block', (tester) async { final longContent = [ DividerHorizontalBlock(), @@ -298,27 +261,23 @@ void main() { TextLeadParagraphBlock(text: 'text'), ]; - final state = - ArticleState(content: longContent, status: ArticleStatus.populated); - - whenListen( - articleBloc, - Stream.value(state), - initialState: state, + final state = ArticleState( + content: longContent, + status: ArticleStatus.populated, ); + whenListen(articleBloc, Stream.value(state), initialState: state); + await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); await tester.pump(); verifyNever( - () => articleBloc - .add(ArticleContentSeen(contentIndex: longContent.length - 1)), + () => articleBloc.add( + ArticleContentSeen(contentIndex: longContent.length - 1), + ), ); await tester.dragUntilVisible( @@ -341,8 +300,7 @@ void main() { } }); - testWidgets( - 'adds TrackAnalyticsEvent to AnalyticsBloc ' + testWidgets('adds TrackAnalyticsEvent to AnalyticsBloc ' 'with ArticleMilestoneEvent ' 'when contentMilestone changes', (tester) async { final analyticsBloc = MockAnalyticsBloc(); @@ -353,16 +311,10 @@ void main() { initialState.copyWith(contentSeenCount: 10, contentTotalCount: 10), ]; - whenListen( - articleBloc, - Stream.fromIterable(states), - ); + whenListen(articleBloc, Stream.fromIterable(states)); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), analyticsBloc: analyticsBloc, ); @@ -386,16 +338,11 @@ void main() { testWidgets('when ArticleStatus is initial', (tester) async { whenListen( articleBloc, - Stream.fromIterable([ - ArticleState.initial(), - ]), + Stream.fromIterable([ArticleState.initial()]), ); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); expect( @@ -407,16 +354,11 @@ void main() { testWidgets('when ArticleStatus is loading', (tester) async { whenListen( articleBloc, - Stream.fromIterable([ - ArticleState(status: ArticleStatus.loading), - ]), + Stream.fromIterable([ArticleState(status: ArticleStatus.loading)]), ); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); expect( @@ -425,8 +367,7 @@ void main() { ); }); - testWidgets( - 'when ArticleStatus is populated ' + testWidgets('when ArticleStatus is populated ' 'and hasMoreContent is true', (tester) async { whenListen( articleBloc, @@ -441,10 +382,7 @@ void main() { ); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); expect( @@ -454,8 +392,7 @@ void main() { }); }); - testWidgets( - 'is not shown and ArticleTrailingContent is shown ' + testWidgets('is not shown and ArticleTrailingContent is shown ' 'when ArticleStatus is populated ' 'and hasMoreContent is false', (tester) async { whenListen( @@ -471,10 +408,7 @@ void main() { ); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); expect(find.byType(ArticleContentLoaderItem), findsNothing); @@ -496,17 +430,13 @@ void main() { ); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); verify(() => articleBloc.add(ArticleRequested())).called(1); }); - testWidgets( - 'does not add ArticleRequested to ArticleBloc ' + testWidgets('does not add ArticleRequested to ArticleBloc ' 'when ArticleStatus is loading', (tester) async { whenListen( articleBloc, @@ -522,10 +452,7 @@ void main() { ); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: ArticleContent(), - ), + BlocProvider.value(value: articleBloc, child: ArticleContent()), ); verifyNever(() => articleBloc.add(ArticleRequested())); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_theme_override_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_theme_override_test.dart index 6d370630d..c437c1f7e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_theme_override_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_theme_override_test.dart @@ -11,10 +11,7 @@ void main() { group('ArticleThemeOverride', () { testWidgets('renders ContentThemeOverrideBuilder', (tester) async { await tester.pumpApp( - ArticleThemeOverride( - isVideoArticle: false, - child: SizedBox(), - ), + ArticleThemeOverride(isVideoArticle: false, child: SizedBox()), ); expect(find.byType(ContentThemeOverrideBuilder), findsOneWidget); @@ -68,8 +65,7 @@ void main() { }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no properties are passed', () { expect( ArticleThemeColors( @@ -85,8 +81,7 @@ void main() { ); }); - test( - 'returns object with updated captionNormal ' + test('returns object with updated captionNormal ' 'when captionNormal is passed', () { expect( ArticleThemeColors( @@ -102,8 +97,7 @@ void main() { ); }); - test( - 'returns object with updated captionLight ' + test('returns object with updated captionLight ' 'when captionLight is passed', () { expect( ArticleThemeColors( @@ -121,8 +115,7 @@ void main() { }); group('lerp', () { - test( - 'returns same object ' + test('returns same object ' 'when other is null', () { expect( ArticleThemeColors( @@ -138,8 +131,7 @@ void main() { ); }); - test( - 'returns object ' + test('returns object ' 'with interpolated colors ' 'when other is passed', () { const colorA = Colors.black; @@ -147,14 +139,8 @@ void main() { const t = 0.5; expect( - ArticleThemeColors( - captionNormal: colorA, - captionLight: colorA, - ).lerp( - ArticleThemeColors( - captionNormal: colorB, - captionLight: colorB, - ), + ArticleThemeColors(captionNormal: colorA, captionLight: colorA).lerp( + ArticleThemeColors(captionNormal: colorB, captionLight: colorB), t, ), equals( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_trailing_content_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_trailing_content_test.dart index 4ea014e78..9462dd012 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_trailing_content_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/article/widgets/article_trailing_content_test.dart @@ -28,18 +28,21 @@ void main() { group('ArticleTrailingContent', () { late ArticleBloc articleBloc; late AppBloc appBloc; + const category = Category(id: 'technology', name: 'Technology'); final postSmallBlock = PostSmallBlock( id: '36f4a017-d099-4fce-8727-1d9ca6a0398c', - category: PostCategory.technology, + categoryId: category.id, author: 'Tom Phillips', publishedAt: DateTime(2022, 6, 2), imageUrl: 'https://assets.reedpopcdn.com/stray_XlfRQmc.jpg/BROK/thumbnail/' '1600x900/format/jpg/quality/80/stray_XlfRQmc.jpg', - title: 'Stray launches next month, included in pricier PlayStation ' + title: + 'Stray launches next month, included in pricier PlayStation ' 'Plus tiers on day one', - description: "Stray, everyone's favorite upcoming cyberpunk cat game, " + description: + "Stray, everyone's favorite upcoming cyberpunk cat game, " 'launches for PC, PlayStation 4 and PS5 on 19th July.', action: const NavigateToArticleAction( articleId: '36f4a017-d099-4fce-8727-1d9ca6a0398c', @@ -56,8 +59,7 @@ void main() { appBloc = MockAppBloc(); }); - testWidgets( - 'renders relatedArticles ' + testWidgets('renders relatedArticles ' 'when article is not preview', (tester) async { final user = MockUser(); when(() => user.subscriptionPlan).thenReturn(SubscriptionPlan.premium); @@ -74,9 +76,7 @@ void main() { ), ); - when(() => appBloc.state).thenReturn( - AppState.authenticated(user), - ); + when(() => appBloc.state).thenReturn(AppState.authenticated(user)); await tester.pumpApp( MultiBlocProvider( @@ -84,9 +84,7 @@ void main() { BlocProvider.value(value: articleBloc), BlocProvider.value(value: appBloc), ], - child: CustomScrollView( - slivers: [ArticleTrailingContent()], - ), + child: CustomScrollView(slivers: [ArticleTrailingContent()]), ), ); @@ -100,8 +98,7 @@ void main() { } }); - testWidgets( - 'renders only ArticleComments when ' + testWidgets('renders only ArticleComments when ' 'relatedArticles is empty', (tester) async { when(() => articleBloc.state).thenAnswer( (invocation) => ArticleState( @@ -154,9 +151,7 @@ void main() { BlocProvider.value(value: articleBloc), BlocProvider.value(value: appBloc), ], - child: CustomScrollView( - slivers: [ArticleTrailingContent()], - ), + child: CustomScrollView(slivers: [ArticleTrailingContent()]), ), ); @@ -170,19 +165,17 @@ void main() { BlocProvider.value(value: articleBloc), BlocProvider.value(value: appBloc), ], - child: CustomScrollView( - slivers: [ArticleTrailingContent()], - ), + child: CustomScrollView(slivers: [ArticleTrailingContent()]), ), ); expect(find.byType(ArticleComments), findsNothing); }); - testWidgets( - 'renders SubscribeModal ' - 'when article is premium and user is not a subscriber', - (tester) async { + testWidgets('renders SubscribeModal ' + 'when article is premium and user is not a subscriber', ( + tester, + ) async { when(() => articleBloc.state).thenReturn( ArticleState( content: const [ @@ -204,9 +197,7 @@ void main() { BlocProvider.value(value: articleBloc), BlocProvider.value(value: appBloc), ], - child: CustomScrollView( - slivers: [ArticleTrailingContent()], - ), + child: CustomScrollView(slivers: [ArticleTrailingContent()]), ), ); @@ -214,8 +205,7 @@ void main() { expect(find.byType(SubscribeWithArticleLimitModal), findsNothing); }); - testWidgets( - 'does not render SubscribeModal ' + testWidgets('does not render SubscribeModal ' 'when article is premium and user is a subscriber', (tester) async { final user = MockUser(); when(() => user.subscriptionPlan).thenReturn(SubscriptionPlan.premium); @@ -233,11 +223,7 @@ void main() { ), ); - when(() => appBloc.state).thenReturn( - AppState.authenticated( - user, - ), - ); + when(() => appBloc.state).thenReturn(AppState.authenticated(user)); await tester.pumpApp( MultiBlocProvider( @@ -245,9 +231,7 @@ void main() { BlocProvider.value(value: articleBloc), BlocProvider.value(value: appBloc), ], - child: CustomScrollView( - slivers: [ArticleTrailingContent()], - ), + child: CustomScrollView(slivers: [ArticleTrailingContent()]), ), ); @@ -255,8 +239,7 @@ void main() { expect(find.byType(SubscribeWithArticleLimitModal), findsNothing); }); - testWidgets( - 'renders SubscribeWithArticleLimitModal ' + testWidgets('renders SubscribeWithArticleLimitModal ' 'when article is not premium ' 'and user has reached article views limit ' 'and user is not a subscriber', (tester) async { @@ -281,9 +264,7 @@ void main() { BlocProvider.value(value: articleBloc), BlocProvider.value(value: appBloc), ], - child: CustomScrollView( - slivers: [ArticleTrailingContent()], - ), + child: CustomScrollView(slivers: [ArticleTrailingContent()]), ), ); }); @@ -292,8 +273,7 @@ void main() { expect(find.byType(SubscribeModal), findsNothing); }); - testWidgets( - 'does not render SubscribeWithArticleLimitModal ' + testWidgets('does not render SubscribeWithArticleLimitModal ' 'when article is not premium ' 'and user has reached article views limit ' 'and user is a subscriber', (tester) async { @@ -313,9 +293,7 @@ void main() { ), ); - when(() => appBloc.state).thenReturn( - AppState.authenticated(user), - ); + when(() => appBloc.state).thenReturn(AppState.authenticated(user)); await tester.pumpApp( MultiBlocProvider( @@ -323,9 +301,7 @@ void main() { BlocProvider.value(value: articleBloc), BlocProvider.value(value: appBloc), ], - child: CustomScrollView( - slivers: [ArticleTrailingContent()], - ), + child: CustomScrollView(slivers: [ArticleTrailingContent()]), ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_bloc_test.dart index 5fca86e97..664487cb3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_bloc_test.dart @@ -1,5 +1,4 @@ // ignore_for_file: prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables import 'package:bloc_test/bloc_test.dart'; import 'package:{{project_name.snakeCase()}}/categories/categories.dart'; @@ -18,8 +17,11 @@ void main() { late NewsRepository newsRepository; late CategoriesBloc categoriesBloc; + final sportsCategory = Category(id: 'sports', name: 'Sports'); + final healthCategory = Category(id: 'health', name: 'Health'); + final categoriesResponse = CategoriesResponse( - categories: [Category.top, Category.health], + categories: [sportsCategory, healthCategory], ); setUp(() async { @@ -27,25 +29,13 @@ void main() { categoriesBloc = CategoriesBloc(newsRepository: newsRepository); }); - test('can be (de)serialized', () { - final categoriesState = CategoriesState( - status: CategoriesStatus.populated, - categories: categoriesResponse.categories, - selectedCategory: categoriesResponse.categories.first, - ); - - final serialized = categoriesBloc.toJson(categoriesState); - final deserialized = categoriesBloc.fromJson(serialized!); - - expect(deserialized, categoriesState); - }); - group('CategoriesRequested', () { blocTest( 'emits [loading, populated] ' 'when getCategories succeeds', - setUp: () => when(newsRepository.getCategories) - .thenAnswer((_) async => categoriesResponse), + setUp: () => when( + newsRepository.getCategories, + ).thenAnswer((_) async => categoriesResponse), build: () => categoriesBloc, act: (bloc) => bloc.add(CategoriesRequested()), expect: () => [ @@ -75,11 +65,9 @@ void main() { blocTest( 'emits selectedCategory', build: () => categoriesBloc, - act: (bloc) => bloc.add(CategorySelected(category: Category.top)), + act: (bloc) => bloc.add(CategorySelected(category: sportsCategory)), expect: () => [ - CategoriesState.initial().copyWith( - selectedCategory: Category.top, - ), + CategoriesState.initial().copyWith(selectedCategory: sportsCategory), ], ); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_event_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_event_test.dart index e8de94e89..cc771a4ab 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_event_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_event_test.dart @@ -17,8 +17,10 @@ void main() { group('CategorySelected', () { test('supports value comparisons', () { - final event1 = CategorySelected(category: Category.top); - final event2 = CategorySelected(category: Category.top); + final sportsCategory = Category(id: 'sports', name: 'Sports'); + + final event1 = CategorySelected(category: sportsCategory); + final event2 = CategorySelected(category: sportsCategory); expect(event1, equals(event2)); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_state_test.dart index 2e1009e86..cec6bfa75 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/bloc/categories_state_test.dart @@ -14,15 +14,16 @@ void main() { }); test('supports value comparisons', () { - expect( - CategoriesState.initial(), - equals(CategoriesState.initial()), - ); + expect(CategoriesState.initial(), equals(CategoriesState.initial())); + }); + + test('getCategoryName returns null when not found', () { + final state = CategoriesState.initial(); + expect(state.getCategoryName('unknown'), isNull); }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no properties are passed', () { expect( CategoriesState.initial().copyWith(), @@ -30,29 +31,25 @@ void main() { ); }); - test( - 'returns object with updated status ' + test('returns object with updated status ' 'when status is passed', () { expect( - CategoriesState.initial().copyWith( - status: CategoriesStatus.loading, - ), - equals( - CategoriesState( - status: CategoriesStatus.loading, - ), - ), + CategoriesState.initial().copyWith(status: CategoriesStatus.loading), + equals(CategoriesState(status: CategoriesStatus.loading)), ); }); - test( - 'returns object with updated categories ' + test('returns object with updated categories ' 'when categories is passed', () { - final categories = [Category.top, Category.health]; + final sportsCategory = Category(id: 'sports', name: 'Sports'); + final healthCategory = Category(id: 'health', name: 'Health'); + + final categories = [sportsCategory, healthCategory]; expect( - CategoriesState(status: CategoriesStatus.populated) - .copyWith(categories: categories), + CategoriesState( + status: CategoriesStatus.populated, + ).copyWith(categories: categories), equals( CategoriesState( status: CategoriesStatus.populated, @@ -62,14 +59,14 @@ void main() { ); }); - test( - 'returns object with updated selectedCategory ' + test('returns object with updated selectedCategory ' 'when selectedCategory is passed', () { - const selectedCategory = Category.top; + final selectedCategory = Category(id: 'sports', name: 'Sports'); expect( - CategoriesState(status: CategoriesStatus.populated) - .copyWith(selectedCategory: selectedCategory), + CategoriesState( + status: CategoriesStatus.populated, + ).copyWith(selectedCategory: selectedCategory), equals( CategoriesState( status: CategoriesStatus.populated, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/widgets/categories_tab_bar_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/widgets/categories_tab_bar_test.dart index 709f6300d..954b28237 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/widgets/categories_tab_bar_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/categories/widgets/categories_tab_bar_test.dart @@ -20,24 +20,24 @@ void main() { }); testWidgets('renders TabBar with tabs', (tester) async { + final sportsCategory = Category(id: 'sports', name: 'Sports'); + final healthCategory = Category(id: 'health', name: 'Health'); + final tabs = [ CategoryTab( - key: ValueKey(Category.top), - categoryName: Category.top.name, + key: ValueKey(sportsCategory), + categoryName: sportsCategory.name, ), CategoryTab( - key: ValueKey(Category.technology), - categoryName: Category.technology.name, + key: ValueKey(healthCategory), + categoryName: healthCategory.name, ), ]; final tabController = TabController(length: tabs.length, vsync: tester); await tester.pumpApp( - CategoriesTabBar( - controller: tabController, - tabs: tabs, - ), + CategoriesTabBar(controller: tabController, tabs: tabs), ); expect( @@ -45,7 +45,7 @@ void main() { (widget) => widget is TabBar && widget.controller == tabController && - widget.isScrollable == true, + widget.isScrollable, ), findsOneWidget, ); @@ -58,13 +58,11 @@ void main() { group('CategoryTab', () { testWidgets('renders category name uppercased', (tester) async { - await tester.pumpApp( - CategoryTab( - categoryName: Category.top.name, - ), - ); + final sportsCategory = Category(id: 'sports', name: 'Sports'); + + await tester.pumpApp(CategoryTab(categoryName: sportsCategory.name)); - expect(find.text(Category.top.name.toUpperCase()), findsOneWidget); + expect(find.text(sportsCategory.name.toUpperCase()), findsOneWidget); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ensure_build_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ensure_build_test.dart index 5c505c659..ab955f437 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ensure_build_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/ensure_build_test.dart @@ -1,13 +1,14 @@ @Tags(['presubmit-only']) library; +import 'dart:async'; + import 'package:build_verify/build_verify.dart'; import 'package:test/test.dart'; void main() { - test( - 'ensure_build', - () { + test('ensure_build', () { + unawaited( expectBuildClean( customCommand: [ 'flutter', @@ -17,8 +18,7 @@ void main() { 'build', '--delete-conflicting-outputs', ], - ); - }, - tags: ['presubmit-only'], - ); + ), + ); + }, tags: ['presubmit-only']); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_bloc_test.dart index f39010998..e775a1e9d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_bloc_test.dart @@ -19,6 +19,12 @@ void main() { late NewsRepository newsRepository; late FeedBloc feedBloc; + final entertainmentCategory = Category( + id: 'entertainment', + name: 'Entertainment', + ); + final healthCategory = Category(id: 'health', name: 'Health'); + final feedResponse = FeedResponse( feed: [ SectionHeaderBlock(title: 'title'), @@ -30,18 +36,13 @@ void main() { final feedStatePopulated = FeedState( status: FeedStatus.populated, feed: { - Category.entertainment: [ + entertainmentCategory.id: [ SpacerBlock(spacing: Spacing.medium), DividerHorizontalBlock(), ], - Category.health: [ - DividerHorizontalBlock(), - ], - }, - hasMoreNews: { - Category.entertainment: true, - Category.health: false, + healthCategory.id: [DividerHorizontalBlock()], }, + hasMoreNews: {entertainmentCategory.id: true, healthCategory.id: false}, ); setUp(() async { @@ -63,25 +64,19 @@ void main() { 'and there are more news to fetch', setUp: () => when( () => newsRepository.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), offset: any(named: 'offset'), limit: any(named: 'limit'), ), ).thenAnswer((_) async => feedResponse), build: () => feedBloc, - act: (bloc) => bloc.add( - FeedRequested(category: Category.entertainment), - ), + act: (bloc) => bloc.add(FeedRequested(category: entertainmentCategory)), expect: () => [ FeedState(status: FeedStatus.loading), FeedState( status: FeedStatus.populated, - feed: { - Category.entertainment: feedResponse.feed, - }, - hasMoreNews: { - Category.entertainment: true, - }, + feed: {entertainmentCategory.id: feedResponse.feed}, + hasMoreNews: {entertainmentCategory.id: true}, ), ], ); @@ -94,30 +89,26 @@ void main() { seed: () => feedStatePopulated, setUp: () => when( () => newsRepository.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), offset: any(named: 'offset'), limit: any(named: 'limit'), ), ).thenAnswer((_) async => feedResponse), build: () => feedBloc, - act: (bloc) => bloc.add( - FeedRequested(category: Category.entertainment), - ), + act: (bloc) => bloc.add(FeedRequested(category: entertainmentCategory)), expect: () => [ feedStatePopulated.copyWith(status: FeedStatus.loading), feedStatePopulated.copyWith( status: FeedStatus.populated, feed: feedStatePopulated.feed ..addAll({ - Category.entertainment: [ - ...feedStatePopulated.feed[Category.entertainment]!, + entertainmentCategory.id: [ + ...feedStatePopulated.feed[entertainmentCategory.id]!, ...feedResponse.feed, ], }), hasMoreNews: feedStatePopulated.hasMoreNews - ..addAll({ - Category.entertainment: false, - }), + ..addAll({entertainmentCategory.id: false}), ), ], ); @@ -127,15 +118,13 @@ void main() { 'when getFeed fails', setUp: () => when( () => newsRepository.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), offset: any(named: 'offset'), limit: any(named: 'limit'), ), ).thenThrow(Exception()), build: () => feedBloc, - act: (bloc) => bloc.add( - FeedRequested(category: Category.entertainment), - ), + act: (bloc) => bloc.add(FeedRequested(category: entertainmentCategory)), expect: () => [ FeedState(status: FeedStatus.loading), FeedState(status: FeedStatus.failure), @@ -150,24 +139,19 @@ void main() { 'and there is more news to fetch', setUp: () => when( () => newsRepository.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), offset: any(named: 'offset'), ), ).thenAnswer((_) async => feedResponse), build: () => feedBloc, - act: (bloc) => bloc.add( - FeedRefreshRequested(category: Category.entertainment), - ), + act: (bloc) => + bloc.add(FeedRefreshRequested(category: entertainmentCategory)), expect: () => [ FeedState(status: FeedStatus.loading), FeedState( status: FeedStatus.populated, - feed: { - Category.entertainment: feedResponse.feed, - }, - hasMoreNews: { - Category.entertainment: true, - }, + feed: {entertainmentCategory.id: feedResponse.feed}, + hasMoreNews: {entertainmentCategory.id: true}, ), ], ); @@ -177,14 +161,13 @@ void main() { 'when getFeed fails', setUp: () => when( () => newsRepository.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), offset: any(named: 'offset'), ), ).thenThrow(Exception()), build: () => feedBloc, - act: (bloc) => bloc.add( - FeedRefreshRequested(category: Category.entertainment), - ), + act: (bloc) => + bloc.add(FeedRefreshRequested(category: entertainmentCategory)), expect: () => [ FeedState(status: FeedStatus.loading), FeedState(status: FeedStatus.failure), @@ -199,7 +182,7 @@ void main() { 'and there are more news to fetch for a single category', setUp: () => when( () => newsRepository.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), offset: any(named: 'offset'), limit: any(named: 'limit'), ), @@ -207,24 +190,20 @@ void main() { build: () => feedBloc, seed: () => FeedState( status: FeedStatus.populated, - feed: {Category.top: []}, + feed: {entertainmentCategory.id: []}, ), act: (bloc) => bloc.add(FeedResumed()), expect: () => [ FeedState( status: FeedStatus.populated, - feed: { - Category.top: feedResponse.feed, - }, - hasMoreNews: { - Category.top: true, - }, + feed: {entertainmentCategory.id: feedResponse.feed}, + hasMoreNews: {entertainmentCategory.id: true}, ), ], verify: (_) { verify( () => newsRepository.getFeed( - category: Category.top, + categoryId: entertainmentCategory.id, offset: 0, ), ).called(1); @@ -237,7 +216,7 @@ void main() { 'and there are more news to fetch for multiple category', setUp: () => when( () => newsRepository.getFeed( - category: any(named: 'category'), + categoryId: any(named: 'categoryId'), offset: any(named: 'offset'), limit: any(named: 'limit'), ), @@ -245,39 +224,40 @@ void main() { build: () => feedBloc, seed: () => FeedState( status: FeedStatus.populated, - feed: {Category.top: [], Category.technology: []}, + feed: {entertainmentCategory.id: [], healthCategory.id: []}, ), act: (bloc) => bloc.add(FeedResumed()), expect: () => [ FeedState( status: FeedStatus.populated, feed: { - Category.top: feedResponse.feed, - Category.technology: [], - }, - hasMoreNews: { - Category.top: true, + entertainmentCategory.id: feedResponse.feed, + healthCategory.id: [], }, + hasMoreNews: {entertainmentCategory.id: true}, ), FeedState( status: FeedStatus.populated, feed: { - Category.top: feedResponse.feed, - Category.technology: feedResponse.feed, + entertainmentCategory.id: feedResponse.feed, + healthCategory.id: feedResponse.feed, }, hasMoreNews: { - Category.top: true, - Category.technology: true, + entertainmentCategory.id: true, + healthCategory.id: true, }, ), ], verify: (_) { verify( - () => newsRepository.getFeed(category: Category.top, offset: 0), + () => newsRepository.getFeed( + categoryId: entertainmentCategory.id, + offset: 0, + ), ).called(1); verify( () => newsRepository.getFeed( - category: Category.technology, + categoryId: healthCategory.id, offset: 0, ), ).called(1); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_event_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_event_test.dart index b75a7472a..1ccec111a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_event_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_event_test.dart @@ -6,10 +6,16 @@ import 'package:news_repository/news_repository.dart'; void main() { group('FeedEvent', () { + final entertainmentCategory = Category( + id: 'entertainment', + name: 'Entertainment', + ); + final healthCategory = Category(id: 'health', name: 'Health'); + group('FeedRequested', () { test('supports value comparisons', () { - final event1 = FeedRequested(category: Category.health); - final event2 = FeedRequested(category: Category.health); + final event1 = FeedRequested(category: healthCategory); + final event2 = FeedRequested(category: healthCategory); expect(event1, equals(event2)); }); @@ -17,8 +23,8 @@ void main() { group('FeedRefreshRequested', () { test('supports value comparisons', () { - final event1 = FeedRefreshRequested(category: Category.science); - final event2 = FeedRefreshRequested(category: Category.science); + final event1 = FeedRefreshRequested(category: entertainmentCategory); + final event2 = FeedRefreshRequested(category: entertainmentCategory); expect(event1, equals(event2)); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_state_test.dart index 51f020f82..260be631e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/bloc/feed_state_test.dart @@ -6,78 +6,52 @@ import 'package:news_blocks/news_blocks.dart'; void main() { group('FeedState', () { + final healthCategory = Category(id: 'health', name: 'Health'); + test('initial has correct status', () { - expect( - FeedState.initial().status, - equals(FeedStatus.initial), - ); + expect(FeedState.initial().status, equals(FeedStatus.initial)); }); test('supports value comparisons', () { - expect( - FeedState.initial(), - equals(FeedState.initial()), - ); + expect(FeedState.initial(), equals(FeedState.initial())); }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no properties are passed', () { - expect( - FeedState.initial().copyWith(), - equals(FeedState.initial()), - ); + expect(FeedState.initial().copyWith(), equals(FeedState.initial())); }); - test( - 'returns object with updated status ' + test('returns object with updated status ' 'when status is passed', () { expect( - FeedState.initial().copyWith( - status: FeedStatus.loading, - ), - equals( - FeedState( - status: FeedStatus.loading, - ), - ), + FeedState.initial().copyWith(status: FeedStatus.loading), + equals(FeedState(status: FeedStatus.loading)), ); }); - test( - 'returns object with updated feed ' + test('returns object with updated feed ' 'when feed is passed', () { final feed = { - Category.health: [SectionHeaderBlock(title: 'Health')], + healthCategory.id: [SectionHeaderBlock(title: 'Health')], }; expect( FeedState(status: FeedStatus.populated).copyWith(feed: feed), - equals( - FeedState( - status: FeedStatus.populated, - feed: feed, - ), - ), + equals(FeedState(status: FeedStatus.populated, feed: feed)), ); }); - test( - 'returns object with updated hasMoreNews ' + test('returns object with updated hasMoreNews ' 'when hasMoreNews is passed', () { - final hasMoreNews = { - Category.health: false, - }; + final hasMoreNews = {healthCategory.id: false}; expect( - FeedState(status: FeedStatus.populated) - .copyWith(hasMoreNews: hasMoreNews), + FeedState( + status: FeedStatus.populated, + ).copyWith(hasMoreNews: hasMoreNews), equals( - FeedState( - status: FeedStatus.populated, - hasMoreNews: hasMoreNews, - ), + FeedState(status: FeedStatus.populated, hasMoreNews: hasMoreNews), ), ); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/view/feed_view_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/view/feed_view_test.dart index fe6b97029..d72726672 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/view/feed_view_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/view/feed_view_test.dart @@ -25,10 +25,13 @@ void main() { late CategoriesBloc categoriesBloc; late FeedBloc feedBloc; - const categories = [Category.top, Category.technology]; + final topCategory = Category(id: 'top', name: 'Top'); + final technologyCategory = Category(id: 'technology', name: 'Technology'); - final feed = >{ - Category.top: [ + final categories = [topCategory, technologyCategory]; + + final feed = >{ + topCategory.id: [ SectionHeaderBlock(title: 'Top'), SpacerBlock(spacing: Spacing.medium), SpacerBlock(spacing: Spacing.extraLarge), @@ -44,7 +47,7 @@ void main() { SpacerBlock(spacing: Spacing.extraLarge), DividerHorizontalBlock(), ], - Category.technology: [ + technologyCategory.id: [ SectionHeaderBlock(title: 'Technology'), DividerHorizontalBlock(), SpacerBlock(spacing: Spacing.medium), @@ -62,16 +65,12 @@ void main() { ), ); - when(() => feedBloc.state).thenReturn( - FeedState( - feed: feed, - status: FeedStatus.populated, - ), - ); + when( + () => feedBloc.state, + ).thenReturn(FeedState(feed: feed, status: FeedStatus.populated)); }); - testWidgets( - 'renders empty feed ' + testWidgets('renders empty feed ' 'when categories are empty', (tester) async { when(() => categoriesBloc.state).thenReturn( CategoriesState( @@ -94,8 +93,7 @@ void main() { expect(find.byType(FeedViewPopulated), findsNothing); }); - testWidgets( - 'renders FeedViewPopulated ' + testWidgets('renders FeedViewPopulated ' 'when categories are available', (tester) async { await tester.pumpApp( MultiBlocProvider( @@ -117,32 +115,26 @@ void main() { expect(find.byKey(Key('feedView_empty')), findsNothing); }); - testWidgets( - 'adds FeedResumed when the app is resumed', - (tester) async { - await tester.pumpApp( - MultiBlocProvider( - providers: [ - BlocProvider.value(value: categoriesBloc), - BlocProvider.value(value: feedBloc), - ], - child: FeedView(), - ), - ); + testWidgets('adds FeedResumed when the app is resumed', (tester) async { + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value(value: categoriesBloc), + BlocProvider.value(value: feedBloc), + ], + child: FeedView(), + ), + ); - tester.binding.handleAppLifecycleStateChanged( - AppLifecycleState.resumed, - ); + tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed); - verify( - () => feedBloc.add(FeedResumed()), - ).called(1); - }, - ); + verify(() => feedBloc.add(FeedResumed())).called(1); + }); group('FeedViewPopulated', () { - testWidgets('renders CategoryTabBar with CategoryTab for each category', - (tester) async { + testWidgets('renders CategoryTabBar with CategoryTab for each category', ( + tester, + ) async { await tester.pumpApp( MultiBlocProvider( providers: [ @@ -185,8 +177,7 @@ void main() { expect(find.byType(CategoryFeed), findsOneWidget); }); - testWidgets( - 'adds CategorySelected to CategoriesBloc ' + testWidgets('adds CategorySelected to CategoriesBloc ' 'when CategoryTab is tapped', (tester) async { final selectedCategory = categories[1]; @@ -211,14 +202,12 @@ void main() { await tester.pump(kTabScrollDuration); verify( - () => categoriesBloc.add( - CategorySelected(category: selectedCategory), - ), + () => + categoriesBloc.add(CategorySelected(category: selectedCategory)), ).called(1); }); - testWidgets( - 'animates to CategoryFeed in TabBarView ' + testWidgets('animates to CategoryFeed in TabBarView ' 'when selectedCategory changes', (tester) async { final categoriesStateController = StreamController.broadcast(); @@ -248,8 +237,8 @@ void main() { ); Finder findCategoryFeed(Category category) => find.byWidgetPredicate( - (widget) => widget is CategoryFeed && widget.category == category, - ); + (widget) => widget is CategoryFeed && widget.category == category, + ); expect(findCategoryFeed(defaultCategory), findsOneWidget); expect(findCategoryFeed(selectedCategory), findsNothing); @@ -278,14 +267,8 @@ void main() { ), ); - expect( - find.byType(DividerHorizontal), - findsNothing, - ); - expect( - find.text('Top'), - findsOneWidget, - ); + expect(find.byType(DividerHorizontal), findsNothing); + expect(find.text('Top'), findsOneWidget); await tester.dragUntilVisible( find.byType(DividerHorizontal), @@ -294,10 +277,7 @@ void main() { duration: Duration.zero, ); - expect( - find.byType(DividerHorizontal), - findsOneWidget, - ); + expect(find.byType(DividerHorizontal), findsOneWidget); final tab = find.widgetWithText( CategoryTab, @@ -311,14 +291,8 @@ void main() { await tester.pump(Duration(milliseconds: 300)); await tester.pump(Duration(milliseconds: 300)); - expect( - find.byType(DividerHorizontal), - findsNothing, - ); - expect( - find.text('Top'), - findsOneWidget, - ); + expect(find.byType(DividerHorizontal), findsNothing); + expect(find.text('Top'), findsOneWidget); }, ); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/widgets/category_feed_item_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/widgets/category_feed_item_test.dart index d9beedf98..72d04c324 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/widgets/category_feed_item_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/widgets/category_feed_item_test.dart @@ -32,10 +32,15 @@ void main() { setUp(() { articleRepository = MockArticleRepository(); + when(articleRepository.fetchTotalArticleViews).thenAnswer((_) async => 1); + when( + articleRepository.incrementTotalArticleViews, + ).thenAnswer((_) async {}); when(articleRepository.incrementArticleViews).thenAnswer((_) async {}); when(articleRepository.resetArticleViews).thenAnswer((_) async {}); - when(articleRepository.fetchArticleViews) - .thenAnswer((_) async => ArticleViews(0, null)); + when( + articleRepository.fetchArticleViews, + ).thenAnswer((_) async => ArticleViews(0, null)); when( () => articleRepository.getArticle( @@ -60,15 +65,12 @@ void main() { offset: any(named: 'offset'), ), ).thenAnswer( - (_) async => RelatedArticlesResponse( - relatedArticles: [], - totalCount: 0, - ), + (_) async => + RelatedArticlesResponse(relatedArticles: [], totalCount: 0), ); }); - testWidgets( - 'renders DividerHorizontal ' + testWidgets('renders DividerHorizontal ' 'for DividerHorizontalBlock', (tester) async { const block = DividerHorizontalBlock(); await tester.pumpApp( @@ -82,8 +84,7 @@ void main() { ); }); - testWidgets( - 'renders Spacer ' + testWidgets('renders Spacer ' 'for SpacerBlock', (tester) async { const block = SpacerBlock(spacing: Spacing.large); await tester.pumpApp( @@ -97,8 +98,7 @@ void main() { ); }); - testWidgets( - 'renders SectionHeader ' + testWidgets('renders SectionHeader ' 'for SectionHeaderBlock', (tester) async { const block = SectionHeaderBlock(title: 'title'); await tester.pumpApp( @@ -112,12 +112,12 @@ void main() { ); }); - testWidgets( - 'renders PostLarge ' + testWidgets('renders PostLarge ' 'for PostLargeBlock', (tester) async { + const category = Category(id: 'technology', name: 'Technology'); final block = PostLargeBlock( id: 'id', - category: PostCategory.technology, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 9), imageUrl: 'imageUrl', @@ -125,9 +125,7 @@ void main() { ); await mockNetworkImages(() async { await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), ); }); expect( @@ -138,12 +136,12 @@ void main() { ); }); - testWidgets( - 'renders PostMedium ' + testWidgets('renders PostMedium ' 'for PostMediumBlock', (tester) async { + const category = Category(id: 'sports', name: 'Sports'); final block = PostMediumBlock( id: 'id', - category: PostCategory.sports, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 10), imageUrl: 'imageUrl', @@ -162,12 +160,12 @@ void main() { ); }); - testWidgets( - 'renders PostSmall ' + testWidgets('renders PostSmall ' 'for PostSmallBlock', (tester) async { + const category = Category(id: 'health', name: 'Health'); final block = PostSmallBlock( id: 'id', - category: PostCategory.health, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', @@ -186,15 +184,15 @@ void main() { ); }); - testWidgets( - 'renders PostGrid ' + testWidgets('renders PostGrid ' 'for PostGridGroupBlock', (tester) async { + const category = Category(id: 'science', name: 'Science'); final block = PostGridGroupBlock( - category: PostCategory.science, + categoryId: category.id, tiles: [ PostGridTileBlock( id: 'id', - category: PostCategory.science, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 12), imageUrl: 'imageUrl', @@ -204,9 +202,7 @@ void main() { ); await mockNetworkImages(() async { await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), ); }); expect( @@ -217,8 +213,7 @@ void main() { ); }); - testWidgets( - 'renders Newsletter ' + testWidgets('renders Newsletter ' 'for NewsletterBlock', (tester) async { VisibilityDetectorController.instance.updateInterval = Duration.zero; final block = NewsletterBlock(); @@ -228,8 +223,7 @@ void main() { expect(find.byType(Newsletter), findsOneWidget); }); - testWidgets( - 'renders BannerAd ' + testWidgets('renders BannerAd ' 'for BannerAdBlock', (tester) async { final block = BannerAdBlock(size: BannerAdSize.normal); await tester.pumpApp( @@ -238,8 +232,7 @@ void main() { expect(find.byType(BannerAd), findsOneWidget); }); - testWidgets( - 'renders SizedBox ' + testWidgets('renders SizedBox ' 'for unsupported block', (tester) async { final block = UnknownBlock(); await tester.pumpApp( @@ -249,15 +242,15 @@ void main() { expect(find.byType(SizedBox), findsNothing); }); - group( - 'navigates to ArticlePage ' + group('navigates to ArticlePage ' 'on NavigateToArticleAction', () { const articleId = 'articleId'; testWidgets('from PostLarge', (tester) async { + const category = Category(id: 'technology', name: 'Technology'); final block = PostLargeBlock( id: articleId, - category: PostCategory.technology, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 9), imageUrl: 'imageUrl', @@ -267,9 +260,7 @@ void main() { ); await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), articleRepository: articleRepository, ); @@ -286,9 +277,10 @@ void main() { }); testWidgets('from PostMedium', (tester) async { + const category = Category(id: 'sports', name: 'Sports'); final block = PostMediumBlock( id: 'id', - category: PostCategory.sports, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 10), imageUrl: 'imageUrl', @@ -299,9 +291,7 @@ void main() { await mockNetworkImages(() async { await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), articleRepository: articleRepository, ); }); @@ -319,9 +309,10 @@ void main() { }); testWidgets('from PostSmall', (tester) async { + const category = Category(id: 'health', name: 'Health'); final block = PostSmallBlock( id: 'id', - category: PostCategory.health, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', @@ -330,9 +321,7 @@ void main() { ); await mockNetworkImages(() async { await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), articleRepository: articleRepository, ); }); @@ -350,12 +339,13 @@ void main() { }); testWidgets('from PostGrid', (tester) async { + const category = Category(id: 'science', name: 'Science'); final block = PostGridGroupBlock( - category: PostCategory.science, + categoryId: category.id, tiles: [ PostGridTileBlock( id: 'id', - category: PostCategory.science, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 12), imageUrl: 'imageUrl', @@ -367,9 +357,7 @@ void main() { await mockNetworkImages(() async { await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), articleRepository: articleRepository, ); }); @@ -389,15 +377,15 @@ void main() { }); }); - group( - 'navigates to video ArticlePage ' + group('navigates to video ArticlePage ' 'on NavigateToVideoArticleAction', () { const articleId = 'articleId'; testWidgets('from PostLarge', (tester) async { + const category = Category(id: 'technology', name: 'Technology'); final block = PostLargeBlock( id: articleId, - category: PostCategory.technology, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 9), imageUrl: 'imageUrl', @@ -408,9 +396,7 @@ void main() { await mockNetworkImages(() async { await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), articleRepository: articleRepository, ); }); @@ -424,16 +410,17 @@ void main() { (widget) => widget is ArticlePage && widget.id == articleId && - widget.isVideoArticle == true, + widget.isVideoArticle, ), findsOneWidget, ); }); testWidgets('from PostMedium', (tester) async { + const category = Category(id: 'sports', name: 'Sports'); final block = PostMediumBlock( id: 'id', - category: PostCategory.sports, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 10), imageUrl: 'imageUrl', @@ -444,9 +431,7 @@ void main() { await mockNetworkImages(() async { await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), articleRepository: articleRepository, ); }); @@ -460,16 +445,17 @@ void main() { (widget) => widget is ArticlePage && widget.id == articleId && - widget.isVideoArticle == true, + widget.isVideoArticle, ), findsOneWidget, ); }); testWidgets('from PostSmall', (tester) async { + const category = Category(id: 'health', name: 'Health'); final block = PostSmallBlock( id: 'id', - category: PostCategory.health, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 11), imageUrl: 'imageUrl', @@ -479,9 +465,7 @@ void main() { await mockNetworkImages(() async { await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), articleRepository: articleRepository, ); }); @@ -495,19 +479,20 @@ void main() { (widget) => widget is ArticlePage && widget.id == articleId && - widget.isVideoArticle == true, + widget.isVideoArticle, ), findsOneWidget, ); }); testWidgets('from PostGrid', (tester) async { + const category = Category(id: 'science', name: 'Science'); final block = PostGridGroupBlock( - category: PostCategory.science, + categoryId: category.id, tiles: [ PostGridTileBlock( id: 'id', - category: PostCategory.science, + categoryId: category.id, author: 'author', publishedAt: DateTime(2022, 3, 12), imageUrl: 'imageUrl', @@ -518,9 +503,8 @@ void main() { ); await tester.pumpApp( - CustomScrollView( - slivers: [CategoryFeedItem(block: block)], - ), + CustomScrollView(slivers: [CategoryFeedItem(block: block)]), + articleRepository: articleRepository, ); // We're tapping on a PostLarge as the first post of the PostGrid @@ -537,19 +521,18 @@ void main() { (widget) => widget is ArticlePage && widget.id == articleId && - widget.isVideoArticle == true, + widget.isVideoArticle, ), findsOneWidget, ); }); }); - testWidgets( - 'adds CategorySelected to CategoriesBloc ' + testWidgets('adds CategorySelected to CategoriesBloc ' 'on NavigateToFeedCategoryAction', (tester) async { final categoriesBloc = MockCategoriesBloc(); - const category = Category.top; + const category = Category(id: 'top', name: 'Top'); const block = SectionHeaderBlock( title: 'title', action: NavigateToFeedCategoryAction(category: category), @@ -566,8 +549,9 @@ void main() { await tester.tap(find.byType(IconButton)); await tester.pumpAndSettle(); - verify(() => categoriesBloc.add(CategorySelected(category: category))) - .called(1); + verify( + () => categoriesBloc.add(CategorySelected(category: category)), + ).called(1); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/widgets/category_feed_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/widgets/category_feed_test.dart index 39f8ee3f3..b56249019 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/widgets/category_feed_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/feed/widgets/category_feed_test.dart @@ -21,9 +21,9 @@ const networkErrorButtonText = 'Try Again'; void main() { late FeedBloc feedBloc; - const category = Category.top; - final feed = >{ - Category.top: [ + const category = Category(id: 'top', name: 'Top'); + final feed = >{ + category.id: [ DividerHorizontalBlock(), SpacerBlock(spacing: Spacing.medium), ], @@ -31,16 +31,16 @@ void main() { setUp(() { feedBloc = MockFeedBloc(); - when(() => feedBloc.state).thenReturn( - FeedState(feed: feed, status: FeedStatus.populated), - ); - registerFallbackValue(FeedRefreshRequested(category: Category.business)); + when( + () => feedBloc.state, + ).thenReturn(FeedState(feed: feed, status: FeedStatus.populated)); + registerFallbackValue(FeedRefreshRequested(category: category)); }); group('CategoryFeed', () { group('when FeedStatus is failure and feed is populated', () { setUpAll(() { - registerFallbackValue(Category.top); + registerFallbackValue(category); }); setUp(() { @@ -61,14 +61,12 @@ void main() { ), ); - expect( - find.byType(NetworkError), - findsOneWidget, - ); + expect(find.byType(NetworkError), findsOneWidget); }); - testWidgets('requests feed refresh on NetworkErrorAlert press', - (tester) async { + testWidgets('requests feed refresh on NetworkErrorAlert press', ( + tester, + ) async { await tester.pumpApp( BlocProvider.value( value: feedBloc, @@ -80,8 +78,9 @@ void main() { await tester.tap(find.textContaining(networkErrorButtonText)); - verify(() => feedBloc.add(any(that: isA()))) - .called(1); + verify( + () => feedBloc.add(any(that: isA())), + ).called(1); }); testWidgets('renders a SelectionArea', (tester) async { @@ -96,7 +95,7 @@ void main() { }); testWidgets('shows CategoryFeedItem for each feed block', (tester) async { - final categoryFeed = feed[category]!; + final categoryFeed = feed[category.id]!; await tester.pumpApp( BlocProvider.value( @@ -123,7 +122,7 @@ void main() { group('when FeedStatus is failure and feed is unpopulated', () { setUpAll(() { - registerFallbackValue(Category.top); + registerFallbackValue(category); registerFallbackValue(NetworkError.route()); }); @@ -159,8 +158,9 @@ void main() { ); }); - testWidgets('requests feed refresh on NetworkErrorAlert press', - (tester) async { + testWidgets('requests feed refresh on NetworkErrorAlert press', ( + tester, + ) async { final navigatorObserver = MockNavigatorObserver(); await tester.pumpApp( @@ -178,8 +178,9 @@ void main() { await tester.pump(Duration(seconds: 1)); await tester.tap(find.textContaining(networkErrorButtonText)); - verify(() => feedBloc.add(any(that: isA()))) - .called(1); + verify( + () => feedBloc.add(any(that: isA())), + ).called(1); verify(() => navigatorObserver.didPop(any(), any())); }); }); @@ -196,7 +197,7 @@ void main() { }); testWidgets('shows CategoryFeedItem for each feed block', (tester) async { - final categoryFeed = feed[category]!; + final categoryFeed = feed[category.id]!; await tester.pumpApp( BlocProvider.value( @@ -247,18 +248,11 @@ void main() { }); group('CategoryFeedLoaderItem', () { - final hasMoreNews = { - Category.top: true, - }; + final hasMoreNews = {category.id: true}; group('is shown', () { testWidgets('when FeedStatus is initial', (tester) async { - whenListen( - feedBloc, - Stream.fromIterable([ - FeedState.initial(), - ]), - ); + whenListen(feedBloc, Stream.fromIterable([FeedState.initial()])); await tester.pumpApp( BlocProvider.value( @@ -273,9 +267,7 @@ void main() { testWidgets('when FeedStatus is loading', (tester) async { whenListen( feedBloc, - Stream.fromIterable([ - FeedState(status: FeedStatus.loading), - ]), + Stream.fromIterable([FeedState(status: FeedStatus.loading)]), ); await tester.pumpApp( @@ -288,8 +280,7 @@ void main() { expect(find.byType(CategoryFeedLoaderItem), findsOneWidget); }); - testWidgets( - 'when FeedStatus is populated and ' + testWidgets('when FeedStatus is populated and ' 'hasMoreNews is true', (tester) async { whenListen( feedBloc, @@ -314,8 +305,7 @@ void main() { }); }); - testWidgets( - 'is not shown ' + testWidgets('is not shown ' 'when FeedStatus is populated and ' 'hasMoreNews is false', (tester) async { whenListen( @@ -325,9 +315,7 @@ void main() { FeedState( status: FeedStatus.populated, feed: feed, - hasMoreNews: { - category: false, - }, + hasMoreNews: {category.id: false}, ), ]), ); @@ -362,9 +350,7 @@ void main() { ), ); - verify( - () => feedBloc.add(FeedRequested(category: category)), - ).called(1); + verify(() => feedBloc.add(FeedRequested(category: category))).called(1); }); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/helpers/pump_app.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/helpers/pump_app.dart index b0d487178..9bfa08573 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/helpers/pump_app.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/helpers/pump_app.dart @@ -7,6 +7,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:{{project_name.snakeCase()}}/ads/ads.dart'; import 'package:{{project_name.snakeCase()}}/analytics/analytics.dart'; import 'package:{{project_name.snakeCase()}}/app/app.dart'; +import 'package:{{project_name.snakeCase()}}/categories/categories.dart'; import 'package:{{project_name.snakeCase()}}/l10n/l10n.dart'; import 'package:{{project_name.snakeCase()}}/theme_selector/theme_selector.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -39,6 +40,21 @@ class MockFullScreenAdsBloc FullScreenAdsState get state => const FullScreenAdsState.initial(); } +class MockCategoriesBloc extends MockBloc + implements CategoriesBloc { + @override + CategoriesState get state => const CategoriesState( + status: CategoriesStatus.populated, + categories: [ + Category(id: 'sports', name: 'Sports'), + Category(id: 'health', name: 'Health'), + Category(id: 'technology', name: 'Technology'), + Category(id: 'science', name: 'Science'), + ], + selectedCategory: Category(id: 'sports', name: 'Sports'), + ); +} + class MockUserRepository extends Mock implements UserRepository { @override Stream get incomingEmailLinks => const Stream.empty(); @@ -47,10 +63,29 @@ class MockUserRepository extends Mock implements UserRepository { Stream get user => const Stream.empty(); } -class MockNewsRepository extends Mock implements NewsRepository {} +class MockNewsRepository extends Mock implements NewsRepository { + @override + Future getCategories() async => + const CategoriesResponse(categories: []); + + @override + Future getFeed({ + String? categoryId, + int? limit, + int? offset, + }) async { + return const FeedResponse(feed: [], totalCount: 0); + } +} class MockNotificationsRepository extends Mock - implements NotificationsRepository {} + implements NotificationsRepository { + @override + Future?> fetchCategoriesPreferences() async => {}; + + @override + Future fetchNotificationsEnabled() async => false; +} class MockArticleRepository extends Mock implements ArticleRepository { @override @@ -61,6 +96,29 @@ class MockArticleRepository extends Mock implements ArticleRepository { @override Future resetArticleViews() async {} + + @override + Future incrementTotalArticleViews() async {} + + @override + Future fetchTotalArticleViews() async => 0; + + @override + Future getArticle({ + required String id, + int? limit, + int? offset, + bool preview = false, + }) async { + return ArticleResponse( + title: 'Test Article', + content: const [], + totalCount: 1, + url: Uri.https('example.org'), + isPremium: false, + isPreview: false, + ); + } } class MockInAppPurchaseRepository extends Mock @@ -74,6 +132,7 @@ extension AppTester on WidgetTester { AppBloc? appBloc, AnalyticsBloc? analyticsBloc, FullScreenAdsBloc? fullScreenAdsBloc, + CategoriesBloc? categoriesBloc, UserRepository? userRepository, NewsRepository? newsRepository, NotificationsRepository? notificationRepository, @@ -115,6 +174,7 @@ extension AppTester on WidgetTester { BlocProvider.value( value: fullScreenAdsBloc ?? MockFullScreenAdsBloc(), ), + BlocProvider.value(value: categoriesBloc ?? MockCategoriesBloc()), ], child: MaterialApp( title: '{{app_name}}', @@ -134,7 +194,7 @@ extension AppTester on WidgetTester { ), ), navigatorObservers: [ - if (navigatorObserver != null) navigatorObserver, + ?navigatorObserver, ], ), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/cubit/home_cubit_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/cubit/home_cubit_test.dart index f9cfb5281..9ca186d8a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/cubit/home_cubit_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/cubit/home_cubit_test.dart @@ -6,10 +6,7 @@ void main() { group('HomeCubit', () { group('constructor', () { test('has correct initial state', () async { - expect( - HomeCubit().state, - equals(HomeState.topStories), - ); + expect(HomeCubit().state, equals(HomeState.topStories)); }); }); @@ -18,27 +15,21 @@ void main() { 'sets tab on top stories', build: HomeCubit.new, act: (cubit) => cubit.setTab(0), - expect: () => [ - HomeState.topStories, - ], + expect: () => [HomeState.topStories], ); blocTest( 'sets tab on search', build: HomeCubit.new, act: (cubit) => cubit.setTab(1), - expect: () => [ - HomeState.search, - ], + expect: () => [HomeState.search], ); blocTest( 'sets tab on subscribe', build: HomeCubit.new, act: (cubit) => cubit.setTab(2), - expect: () => [ - HomeState.subscribe, - ], + expect: () => [HomeState.subscribe], ); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/view/home_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/view/home_page_test.dart index 1b3fa98ff..4f899bda3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/view/home_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/view/home_page_test.dart @@ -19,12 +19,18 @@ void main() { setUp(() { newsRepository = MockNewsRepository(); - - when(newsRepository.getCategories).thenAnswer( - (_) async => CategoriesResponse( - categories: [Category.top], + final healthCategory = Category(id: 'health', name: 'Health'); + + when( + newsRepository.getCategories, + ).thenAnswer((_) async => CategoriesResponse(categories: [healthCategory])); + when( + () => newsRepository.getFeed( + categoryId: any(named: 'categoryId'), + limit: any(named: 'limit'), + offset: any(named: 'offset'), ), - ); + ).thenAnswer((_) async => FeedResponse(feed: [], totalCount: 0)); }); test('has a page', () { @@ -32,16 +38,13 @@ void main() { }); testWidgets('renders a HomeView', (tester) async { - await tester.pumpApp(const HomePage()); + await tester.pumpApp(const HomePage(), newsRepository: newsRepository); expect(find.byType(HomeView), findsOneWidget); }); testWidgets('renders FeedView', (tester) async { - await tester.pumpApp( - const HomePage(), - newsRepository: newsRepository, - ); + await tester.pumpApp(const HomePage(), newsRepository: newsRepository); expect(find.byType(FeedView), findsOneWidget); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/view/home_view_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/view/home_view_test.dart index df5b6b1b4..4ad7882fd 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/view/home_view_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/home/view/home_view_test.dart @@ -1,5 +1,4 @@ // ignore_for_file: prefer_const_constructors, avoid_redundant_argument_values -// ignore_for_file: prefer_const_literals_to_create_immutables import 'dart:async'; @@ -42,15 +41,21 @@ void main() { late FeedBloc feedBloc; late AppBloc appBloc; - const categories = [Category.top, Category.technology]; + final entertainmentCategory = Category( + id: 'entertainment', + name: 'Entertainment', + ); + final healthCategory = Category(id: 'health', name: 'Health'); + + final categories = [entertainmentCategory, healthCategory]; - final feed = >{ - Category.top: [ + final feed = >{ + entertainmentCategory.id: [ SectionHeaderBlock(title: 'Top'), DividerHorizontalBlock(), SpacerBlock(spacing: Spacing.medium), ], - Category.technology: [ + healthCategory.id: [ SectionHeaderBlock(title: 'Technology'), DividerHorizontalBlock(), SpacerBlock(spacing: Spacing.medium), @@ -65,10 +70,7 @@ void main() { appBloc = MockAppBloc(); when(() => appBloc.state).thenReturn( - AppState( - showLoginOverlay: false, - status: AppStatus.unauthenticated, - ), + AppState(showLoginOverlay: false, status: AppStatus.unauthenticated), ); when(() => categoriesBloc.state).thenReturn( @@ -78,18 +80,13 @@ void main() { ), ); - when(() => feedBloc.state).thenReturn( - FeedState( - feed: feed, - status: FeedStatus.populated, - ), - ); + when( + () => feedBloc.state, + ).thenReturn(FeedState(feed: feed, status: FeedStatus.populated)); - when(newsRepository.getCategories).thenAnswer( - (_) async => CategoriesResponse( - categories: [Category.top], - ), - ); + when( + newsRepository.getCategories, + ).thenAnswer((_) async => CategoriesResponse(categories: [healthCategory])); when(() => cubit.state).thenReturn(HomeState.topStories); }); @@ -127,8 +124,7 @@ void main() { expect(find.byType(UserProfileButton), findsOneWidget); }); - testWidgets( - 'renders NavDrawer ' + testWidgets('renders NavDrawer ' 'when menu icon is tapped', (tester) async { when(() => cubit.state).thenReturn(HomeState.topStories); @@ -159,8 +155,9 @@ void main() { expect(find.byType(FeedView), findsOneWidget); }); - testWidgets('shows LoginOverlay when showLoginOverlay is true', - (tester) async { + testWidgets('shows LoginOverlay when showLoginOverlay is true', ( + tester, + ) async { whenListen( appBloc, Stream.fromIterable([ @@ -183,92 +180,80 @@ void main() { }); group('BottomNavigationBar', () { - testWidgets( - 'has selected index to 0 by default.', - (tester) async { - when(() => cubit.state).thenReturn(HomeState.topStories); - - await pumpHomeView( - tester: tester, - cubit: cubit, - categoriesBloc: categoriesBloc, - feedBloc: feedBloc, - newsRepository: newsRepository, - ); - - expect(find.byType(FeedView), findsOneWidget); - }, - ); + testWidgets('has selected index to 0 by default.', (tester) async { + when(() => cubit.state).thenReturn(HomeState.topStories); - testWidgets( - 'set tab to selected index 0 when top stories is tapped.', - (tester) async { - await pumpHomeView( - tester: tester, - cubit: cubit, - categoriesBloc: categoriesBloc, - feedBloc: feedBloc, - newsRepository: newsRepository, - ); - await tester.ensureVisible(find.byType(BottomNavBar)); - await tester.tap(find.byIcon(Icons.home_outlined)); - verify(() => cubit.setTab(0)).called(1); - }, - ); + await pumpHomeView( + tester: tester, + cubit: cubit, + categoriesBloc: categoriesBloc, + feedBloc: feedBloc, + newsRepository: newsRepository, + ); - testWidgets( - 'set tab to selected index 1 when search is tapped.', - (tester) async { - await pumpHomeView( - tester: tester, - cubit: cubit, - categoriesBloc: categoriesBloc, - feedBloc: feedBloc, - newsRepository: newsRepository, - ); - await tester.ensureVisible(find.byType(BottomNavBar)); - await tester.tap(find.byKey(Key('bottomNavBar_search'))); - verify(() => cubit.setTab(1)).called(1); - }, - ); + expect(find.byType(FeedView), findsOneWidget); + }); - testWidgets( - 'unfocuses keyboard when tab is changed.', - (tester) async { - final controller = StreamController(); - whenListen( - cubit, - controller.stream, - initialState: HomeState.topStories, - ); - await pumpHomeView( - tester: tester, - cubit: cubit, - categoriesBloc: categoriesBloc, - feedBloc: feedBloc, - newsRepository: newsRepository, - ); - - await tester.ensureVisible(find.byType(BottomNavBar)); - await tester.tap(find.byKey(Key('bottomNavBar_search'))); - verify(() => cubit.setTab(1)).called(1); - - controller.add(HomeState.search); - - await tester.pump(kThemeAnimationDuration); - await tester.showKeyboard(find.byType(SearchTextField)); - - final initialFocus = tester.binding.focusManager.primaryFocus; - - controller.add(HomeState.topStories); - await tester.pump(); - - expect( - tester.binding.focusManager.primaryFocus, - isNot(equals(initialFocus)), - ); - }, - ); + testWidgets('set tab to selected index 0 when top stories is tapped.', ( + tester, + ) async { + await pumpHomeView( + tester: tester, + cubit: cubit, + categoriesBloc: categoriesBloc, + feedBloc: feedBloc, + newsRepository: newsRepository, + ); + await tester.ensureVisible(find.byType(BottomNavBar)); + await tester.tap(find.byIcon(Icons.home_outlined)); + verify(() => cubit.setTab(0)).called(1); + }); + + testWidgets('set tab to selected index 1 when search is tapped.', ( + tester, + ) async { + await pumpHomeView( + tester: tester, + cubit: cubit, + categoriesBloc: categoriesBloc, + feedBloc: feedBloc, + newsRepository: newsRepository, + ); + await tester.ensureVisible(find.byType(BottomNavBar)); + await tester.tap(find.byKey(Key('bottomNavBar_search'))); + verify(() => cubit.setTab(1)).called(1); + }); + + testWidgets('unfocuses keyboard when tab is changed.', (tester) async { + final controller = StreamController(); + whenListen(cubit, controller.stream, initialState: HomeState.topStories); + await pumpHomeView( + tester: tester, + cubit: cubit, + categoriesBloc: categoriesBloc, + feedBloc: feedBloc, + newsRepository: newsRepository, + ); + + await tester.ensureVisible(find.byType(BottomNavBar)); + await tester.tap(find.byKey(Key('bottomNavBar_search'))); + verify(() => cubit.setTab(1)).called(1); + + controller.add(HomeState.search); + + await tester.pump(kThemeAnimationDuration); + await tester.showKeyboard(find.byType(SearchTextField)); + + final initialFocus = tester.binding.focusManager.primaryFocus; + + controller.add(HomeState.topStories); + await tester.pump(); + + expect( + tester.binding.focusManager.primaryFocus, + isNot(equals(initialFocus)), + ); + }); }); } @@ -283,15 +268,9 @@ Future pumpHomeView({ await tester.pumpApp( MultiBlocProvider( providers: [ - BlocProvider.value( - value: categoriesBloc, - ), - BlocProvider.value( - value: feedBloc, - ), - BlocProvider.value( - value: cubit, - ), + BlocProvider.value(value: categoriesBloc), + BlocProvider.value(value: feedBloc), + BlocProvider.value(value: cubit), ], child: HomeView(), ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/l10n/extensions/localizations_extension_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/l10n/extensions/localizations_extension_test.dart index 3192d0e27..7445599ad 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/l10n/extensions/localizations_extension_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/l10n/extensions/localizations_extension_test.dart @@ -8,9 +8,7 @@ void main() { group('LocalizationsX', () { testWidgets('performs localizations lookup', (tester) async { await tester.pumpApp( - Builder( - builder: (context) => Text(context.l10n.loginButtonText), - ), + Builder(builder: (context) => Text(context.l10n.loginButtonText)), ); expect(find.text('LOGIN'), findsOneWidget); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_bloc_test.dart index 4e37758a7..1e26f19a3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_bloc_test.dart @@ -9,8 +9,6 @@ import 'package:user_repository/user_repository.dart'; class MockUserRepository extends Mock implements UserRepository {} -class MockUser extends Mock implements User {} - void main() { const invalidEmailString = 'invalid'; const invalidEmail = Email.dirty(invalidEmailString); @@ -36,9 +34,7 @@ void main() { () => userRepository.logInWithApple(), ).thenAnswer((_) => Future.value()); when( - () => userRepository.sendLoginEmailLink( - email: any(named: 'email'), - ), + () => userRepository.sendLoginEmailLink(email: any(named: 'email')), ).thenAnswer((_) => Future.value()); }); @@ -51,9 +47,7 @@ void main() { 'emits [invalid] when email is invalid', build: () => LoginBloc(userRepository: userRepository), act: (bloc) => bloc.add(LoginEmailChanged(invalidEmailString)), - expect: () => const [ - LoginState(email: invalidEmail), - ], + expect: () => const [LoginState(email: invalidEmail)], ); blocTest( @@ -81,9 +75,7 @@ void main() { act: (bloc) => bloc.add(SendEmailLinkSubmitted()), verify: (_) { verify( - () => userRepository.sendLoginEmailLink( - email: validEmailString, - ), + () => userRepository.sendLoginEmailLink(email: validEmailString), ).called(1); }, ); @@ -113,9 +105,7 @@ void main() { 'when sendLoginEmailLink fails', setUp: () { when( - () => userRepository.sendLoginEmailLink( - email: any(named: 'email'), - ), + () => userRepository.sendLoginEmailLink(email: any(named: 'email')), ).thenThrow(Exception('oops')); }, build: () => LoginBloc(userRepository: userRepository), @@ -233,9 +223,7 @@ void main() { setUp: () { when( () => userRepository.logInWithTwitter(), - ).thenThrow( - LogInWithTwitterCanceled(Exception()), - ); + ).thenThrow(LogInWithTwitterCanceled(Exception())); }, build: () => LoginBloc(userRepository: userRepository), act: (bloc) => bloc.add(LoginTwitterSubmitted()), @@ -289,9 +277,7 @@ void main() { setUp: () { when( () => userRepository.logInWithFacebook(), - ).thenThrow( - LogInWithFacebookCanceled(Exception()), - ); + ).thenThrow(LogInWithFacebookCanceled(Exception())); }, build: () => LoginBloc(userRepository: userRepository), act: (bloc) => bloc.add(LoginFacebookSubmitted()), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_state_test.dart index 1f74fcdf4..728d578cd 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_state_test.dart @@ -23,17 +23,11 @@ void main() { }); test('returns object with updated email when email is passed', () { - expect( - LoginState().copyWith(email: email), - LoginState(email: email), - ); + expect(LoginState().copyWith(email: email), LoginState(email: email)); }); test('returns object with updated valid when valid is passed', () { - expect( - LoginState().copyWith(valid: true), - LoginState(valid: true), - ); + expect(LoginState().copyWith(valid: true), LoginState(valid: true)); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_bloc_test.dart index b1129b73c..ff23a9128 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_bloc_test.dart @@ -20,15 +20,14 @@ void main() { userRepository = MockUserRepository(); incomingEmailLinksController = StreamController(); - when(() => userRepository.incomingEmailLinks) - .thenAnswer((_) => incomingEmailLinksController.stream); + when( + () => userRepository.incomingEmailLinks, + ).thenAnswer((_) => incomingEmailLinksController.stream); }); test('initial state is LoginWithEmailLinkState', () { expect( - LoginWithEmailLinkBloc( - userRepository: userRepository, - ).state, + LoginWithEmailLinkBloc(userRepository: userRepository).state, LoginWithEmailLinkState(), ); }); @@ -37,8 +36,9 @@ void main() { const email = 'email@example.com'; final user = MockUser(); - final continueUrl = - Uri.https('continue.link', '', {'email': email}); + final continueUrl = Uri.https('continue.link', '', { + 'email': email, + }); final validEmailLink = Uri.https( 'email.link', @@ -58,8 +58,9 @@ void main() { ); setUp(() { - when(() => userRepository.user) - .thenAnswer((invocation) => Stream.value(user)); + when( + () => userRepository.user, + ).thenAnswer((invocation) => Stream.value(user)); when( () => userRepository.logInWithEmailLink( @@ -174,9 +175,7 @@ void main() { group('close', () { blocTest( 'cancels UserRepository.incomingEmailLinks subscription', - build: () => LoginWithEmailLinkBloc( - userRepository: userRepository, - ), + build: () => LoginWithEmailLinkBloc(userRepository: userRepository), tearDown: () { expect(incomingEmailLinksController.hasListener, isFalse); }, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_event_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_event_test.dart index 1713dcaa8..4f32bd569 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_event_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_event_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: prefer_const_constructors - import 'package:{{project_name.snakeCase()}}/login/login.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_state_test.dart index 94982e3b5..245dab905 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/bloc/login_with_email_link_state_test.dart @@ -15,11 +15,10 @@ void main() { test('returns object with updated status when status is passed', () { expect( - LoginWithEmailLinkState() - .copyWith(status: LoginWithEmailLinkStatus.success), - LoginWithEmailLinkState( + LoginWithEmailLinkState().copyWith( status: LoginWithEmailLinkStatus.success, ), + LoginWithEmailLinkState(status: LoginWithEmailLinkStatus.success), ); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/view/login_with_email_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/view/login_with_email_page_test.dart index a378122c2..24d024ea6 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/view/login_with_email_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/view/login_with_email_page_test.dart @@ -24,10 +24,7 @@ void main() { final navigator = MockNavigator(); when(navigator.canPop).thenAnswer((_) => true); when(navigator.pop).thenAnswer((_) async {}); - await tester.pumpApp( - const LoginWithEmailPage(), - navigator: navigator, - ); + await tester.pumpApp(const LoginWithEmailPage(), navigator: navigator); await tester.tap(find.byKey(closeIcon)); await tester.pumpAndSettle(); verify(navigator.pop).called(1); @@ -37,10 +34,7 @@ void main() { final navigator = MockNavigator(); when(navigator.canPop).thenAnswer((_) => true); when(navigator.pop).thenAnswer((_) async {}); - await tester.pumpApp( - const LoginWithEmailPage(), - navigator: navigator, - ); + await tester.pumpApp(const LoginWithEmailPage(), navigator: navigator); await tester.tap(find.byType(AppBackButton)); await tester.pumpAndSettle(); verify(navigator.pop).called(1); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/widgets/login_form_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/widgets/login_form_test.dart index 48c226762..0c7d1b5ae 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/widgets/login_form_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/widgets/login_form_test.dart @@ -44,8 +44,7 @@ void main() { }); group('adds', () { - testWidgets( - 'LoginGoogleSubmitted to LoginBloc ' + testWidgets('LoginGoogleSubmitted to LoginBloc ' 'when sign in with google button is pressed', (tester) async { await tester.pumpApp( BlocProvider.value(value: loginBloc, child: const LoginForm()), @@ -55,8 +54,7 @@ void main() { verify(() => loginBloc.add(LoginGoogleSubmitted())).called(1); }); - testWidgets( - 'LoginTwitterSubmitted to LoginBloc ' + testWidgets('LoginTwitterSubmitted to LoginBloc ' 'when sign in with Twitter button is pressed', (tester) async { await tester.pumpApp( BlocProvider.value(value: loginBloc, child: const LoginForm()), @@ -66,8 +64,7 @@ void main() { verify(() => loginBloc.add(LoginTwitterSubmitted())).called(1); }); - testWidgets( - 'LoginFacebookSubmitted to LoginBloc ' + testWidgets('LoginFacebookSubmitted to LoginBloc ' 'when sign in with Facebook button is pressed', (tester) async { await tester.pumpApp( BlocProvider.value(value: loginBloc, child: const LoginForm()), @@ -77,8 +74,7 @@ void main() { verify(() => loginBloc.add(LoginFacebookSubmitted())).called(1); }); - testWidgets( - 'LoginAppleSubmitted to LoginBloc ' + testWidgets('LoginAppleSubmitted to LoginBloc ' 'when sign in with apple button is pressed', (tester) async { await tester.pumpApp( BlocProvider.value(value: loginBloc, child: const LoginForm()), @@ -89,8 +85,9 @@ void main() { verify(() => loginBloc.add(LoginAppleSubmitted())).called(1); }); - testWidgets('AuthenticationFailure SnackBar when submission fails', - (tester) async { + testWidgets('AuthenticationFailure SnackBar when submission fails', ( + tester, + ) async { whenListen( loginBloc, Stream.fromIterable(const [ @@ -151,8 +148,9 @@ void main() { }); group('navigates', () { - testWidgets('to LoginWithEmailPage when Continue with email is pressed', - (tester) async { + testWidgets('to LoginWithEmailPage when Continue with email is pressed', ( + tester, + ) async { await tester.pumpApp( BlocProvider.value(value: loginBloc, child: const LoginForm()), ); @@ -209,10 +207,8 @@ void main() { child: Text(buttonText), onPressed: () => showAppModal( context: context, - builder: (context) => BlocProvider.value( - value: appBloc, - child: LoginModal(), - ), + builder: (context) => + BlocProvider.value(value: appBloc, child: LoginModal()), routeSettings: const RouteSettings(name: LoginModal.name), ), ); @@ -235,8 +231,9 @@ void main() { expect(find.byType(LoginForm), findsNothing); }); - testWidgets('when user is authenticated and onboarding is required', - (tester) async { + testWidgets('when user is authenticated and onboarding is required', ( + tester, + ) async { final appStateController = StreamController(); whenListen( @@ -252,10 +249,8 @@ void main() { child: Text(buttonText), onPressed: () => showAppModal( context: context, - builder: (context) => BlocProvider.value( - value: appBloc, - child: LoginModal(), - ), + builder: (context) => + BlocProvider.value(value: appBloc, child: LoginModal()), routeSettings: const RouteSettings(name: LoginModal.name), ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/widgets/login_with_email_form_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/widgets/login_with_email_form_test.dart index b25bc80cc..ffa1bcd0c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/widgets/login_with_email_form_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/login/widgets/login_with_email_form_test.dart @@ -11,26 +11,24 @@ import 'package:{{project_name.snakeCase()}}/terms_of_service/terms_of_service.d import 'package:flutter_test/flutter_test.dart'; import 'package:form_inputs/form_inputs.dart'; import 'package:mockingjay/mockingjay.dart'; -import 'package:user_repository/user_repository.dart'; import '../../helpers/helpers.dart'; -class MockUserRepository extends Mock implements UserRepository {} - class MockLoginBloc extends MockBloc implements LoginBloc {} -class MockEmail extends Mock implements Email {} - void main() { const nextButtonKey = Key('loginWithEmailForm_nextButton'); const emailInputKey = Key('loginWithEmailForm_emailInput_textField'); - const loginWithEmailFormHeaderTitleKey = - Key('loginWithEmailForm_header_title'); - const loginWithEmailFormTermsAndPrivacyPolicyKey = - Key('loginWithEmailForm_terms_and_privacy_policy'); - const loginWithEmailFormClearIconKey = - Key('loginWithEmailForm_clearIconButton'); + const loginWithEmailFormHeaderTitleKey = Key( + 'loginWithEmailForm_header_title', + ); + const loginWithEmailFormTermsAndPrivacyPolicyKey = Key( + 'loginWithEmailForm_terms_and_privacy_policy', + ); + const loginWithEmailFormClearIconKey = Key( + 'loginWithEmailForm_clearIconButton', + ); const testEmail = 'test@gmail.com'; const invalidTestEmail = 'test@g'; @@ -52,15 +50,15 @@ void main() { ), ); await tester.enterText(find.byKey(emailInputKey), testEmail); - verify(() => loginBloc.add(const LoginEmailChanged(testEmail))) - .called(1); + verify( + () => loginBloc.add(const LoginEmailChanged(testEmail)), + ).called(1); }); - testWidgets('SendEmailLinkSubmitted when next button is pressed', - (tester) async { - when(() => loginBloc.state).thenReturn( - const LoginState(valid: true), - ); + testWidgets('SendEmailLinkSubmitted when next button is pressed', ( + tester, + ) async { + when(() => loginBloc.state).thenReturn(const LoginState(valid: true)); await tester.pumpApp( BlocProvider.value( value: loginBloc, @@ -71,11 +69,12 @@ void main() { verify(() => loginBloc.add(SendEmailLinkSubmitted())).called(1); }); - testWidgets('LoginEmailChanged when pressed on suffixIcon', - (tester) async { - when(() => loginBloc.state).thenAnswer( - (_) => const LoginState(email: Email.dirty(testEmail)), - ); + testWidgets('LoginEmailChanged when pressed on suffixIcon', ( + tester, + ) async { + when( + () => loginBloc.state, + ).thenAnswer((_) => const LoginState(email: Email.dirty(testEmail))); await tester.pumpApp( BlocProvider.value( value: loginBloc, @@ -121,13 +120,15 @@ void main() { child: const LoginWithEmailForm(), ), ); - final termsAndPrivacyPolicyText = - find.byKey(loginWithEmailFormTermsAndPrivacyPolicyKey); + final termsAndPrivacyPolicyText = find.byKey( + loginWithEmailFormTermsAndPrivacyPolicyKey, + ); expect(termsAndPrivacyPolicyText, findsOneWidget); }); - testWidgets('Login with email failure SnackBar when submission fails', - (tester) async { + testWidgets('Login with email failure SnackBar when submission fails', ( + tester, + ) async { whenListen( loginBloc, Stream.fromIterable(const [ @@ -145,8 +146,7 @@ void main() { expect(find.byType(SnackBar), findsOneWidget); }); - testWidgets( - 'TermsOfServiceModal when tapped on ' + testWidgets('TermsOfServiceModal when tapped on ' 'Terms of Use and Privacy Policy text', (tester) async { await tester.pumpApp( BlocProvider.value( @@ -158,20 +158,18 @@ void main() { find.byKey(loginWithEmailFormTermsAndPrivacyPolicyKey), ); - tapTextSpan( - richText, - 'Terms of Use and Privacy Policy', - ); + tapTextSpan(richText, 'Terms of Use and Privacy Policy'); await tester.pumpAndSettle(); expect(find.byType(TermsOfServiceModal), findsOneWidget); }); - testWidgets('disabled next button when status is not validated', - (tester) async { - when(() => loginBloc.state).thenReturn( - const LoginState(valid: false), - ); + testWidgets('disabled next button when status is not validated', ( + tester, + ) async { + when( + () => loginBloc.state, + ).thenReturn(const LoginState(valid: false)); await tester.pumpApp( BlocProvider.value( value: loginBloc, @@ -184,8 +182,9 @@ void main() { expect(signUpButton.onPressed, null); }); - testWidgets('disabled next button when invalid email is added', - (tester) async { + testWidgets('disabled next button when invalid email is added', ( + tester, + ) async { await tester.pumpApp( BlocProvider.value( value: loginBloc, @@ -199,11 +198,10 @@ void main() { expect(signUpButton.onPressed, null); }); - testWidgets('enabled next button when status is validated', - (tester) async { - when(() => loginBloc.state).thenReturn( - const LoginState(valid: true), - ); + testWidgets('enabled next button when status is validated', ( + tester, + ) async { + when(() => loginBloc.state).thenReturn(const LoginState(valid: true)); await tester.pumpApp( BlocProvider.value( value: loginBloc, @@ -219,16 +217,15 @@ void main() { }); group('navigates', () { - testWidgets('to MagicLinkPromptPage when submission is success', - (tester) async { + testWidgets('to MagicLinkPromptPage when submission is success', ( + tester, + ) async { whenListen( loginBloc, - Stream.fromIterable( - [ - const LoginState(status: FormzSubmissionStatus.inProgress), - const LoginState(status: FormzSubmissionStatus.success), - ], - ), + Stream.fromIterable([ + const LoginState(status: FormzSubmissionStatus.inProgress), + const LoginState(status: FormzSubmissionStatus.success), + ]), initialState: const LoginState(), ); @@ -260,8 +257,9 @@ void main() { expect(emailTextField.readOnly, isTrue); }); - testWidgets('clear icon button when status is inProgress', - (tester) async { + testWidgets('clear icon button when status is inProgress', ( + tester, + ) async { when(() => loginBloc.state).thenAnswer( (_) => const LoginState(status: FormzSubmissionStatus.inProgress), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/magic_link_prompt/view/magic_link_prompt_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/magic_link_prompt/view/magic_link_prompt_page_test.dart index bac28b312..92a576542 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/magic_link_prompt/view/magic_link_prompt_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/magic_link_prompt/view/magic_link_prompt_page_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: prefer_const_constructors - import 'package:flutter/material.dart'; import 'package:{{project_name.snakeCase()}}/magic_link_prompt/magic_link_prompt.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -20,9 +18,7 @@ void main() { }); testWidgets('renders a MagicLinkPromptView', (tester) async { - await tester.pumpApp( - const MagicLinkPromptPage(email: testEmail), - ); + await tester.pumpApp(const MagicLinkPromptPage(email: testEmail)); expect(find.byType(MagicLinkPromptView), findsOneWidget); }); @@ -32,9 +28,10 @@ void main() { body: Builder( builder: (context) { return ElevatedButton( - onPressed: () { - Navigator.of(context) - .push(MagicLinkPromptPage.route(email: testEmail)); + onPressed: () async { + await Navigator.of( + context, + ).push(MagicLinkPromptPage.route(email: testEmail)); }, child: const Text('Tap me'), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/magic_link_prompt/view/magic_link_prompt_view_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/magic_link_prompt/view/magic_link_prompt_view_test.dart index 4f2099078..c9474d7b3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/magic_link_prompt/view/magic_link_prompt_view_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/magic_link_prompt/view/magic_link_prompt_view_test.dart @@ -34,21 +34,18 @@ void main() { }); group('opens default email app', () { - testWidgets( - 'when MagicLinkPromptOpenEmailButton is pressed', - (tester) async { - when(emailLauncher.launchEmailApp).thenAnswer((_) async {}); - await tester.pumpApp( - MagicLinkPromptOpenEmailButton( - emailLauncher: emailLauncher, - ), - ); - - await tester.tap(find.byType(MagicLinkPromptOpenEmailButton)); - await tester.pumpAndSettle(); - verify(emailLauncher.launchEmailApp).called(1); - }, - ); + testWidgets('when MagicLinkPromptOpenEmailButton is pressed', ( + tester, + ) async { + when(emailLauncher.launchEmailApp).thenAnswer((_) async {}); + await tester.pumpApp( + MagicLinkPromptOpenEmailButton(emailLauncher: emailLauncher), + ); + + await tester.tap(find.byType(MagicLinkPromptOpenEmailButton)); + await tester.pumpAndSettle(); + verify(emailLauncher.launchEmailApp).called(1); + }); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/view/bottom_nav_bar_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/view/bottom_nav_bar_test.dart index b9e965f02..8779991bc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/view/bottom_nav_bar_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/view/bottom_nav_bar_test.dart @@ -1,5 +1,3 @@ -// ignore_for_file: prefer_const_constructors - import 'dart:async'; import 'package:flutter/material.dart'; @@ -10,18 +8,12 @@ import '../../helpers/helpers.dart'; void main() { group('BottomNavBar', () { - testWidgets( - 'renders with currentIndex to 0 by default.', - (tester) async { - await tester.pumpApp( - BottomNavBar( - currentIndex: 0, - onTap: (selectedIndex) {}, - ), - ); - expect(find.byType(BottomNavBar), findsOneWidget); - }, - ); + testWidgets('renders with currentIndex to 0 by default.', (tester) async { + await tester.pumpApp( + BottomNavBar(currentIndex: 0, onTap: (selectedIndex) {}), + ); + expect(find.byType(BottomNavBar), findsOneWidget); + }); }); testWidgets('calls onTap when navigation bar item is tapped', (tester) async { @@ -41,20 +33,12 @@ void main() { expect(completer.isCompleted, isTrue); }); - testWidgets( - 'renders BottomNavigationBar with currentIndex', - (tester) async { - const currentIndex = 1; - await tester.pumpApp( - BottomNavBar( - currentIndex: currentIndex, - onTap: (selectedIndex) {}, - ), - ); - final bottomNavBar = tester.widget( - find.byType(BottomNavBar), - ); - expect(bottomNavBar.currentIndex, equals(currentIndex)); - }, - ); + testWidgets('renders BottomNavigationBar with currentIndex', (tester) async { + const currentIndex = 1; + await tester.pumpApp( + BottomNavBar(currentIndex: currentIndex, onTap: (selectedIndex) {}), + ); + final bottomNavBar = tester.widget(find.byType(BottomNavBar)); + expect(bottomNavBar.currentIndex, equals(currentIndex)); + }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/view/nav_drawer_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/view/nav_drawer_test.dart index 0a466a24f..49f0abc3c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/view/nav_drawer_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/view/nav_drawer_test.dart @@ -36,15 +36,9 @@ extension on WidgetTester { await pumpApp( MultiBlocProvider( providers: [ - BlocProvider.value( - value: categoriesBloc, - ), - BlocProvider.value( - value: appBloc, - ), - BlocProvider.value( - value: homeCubit, - ), + BlocProvider.value(value: categoriesBloc), + BlocProvider.value(value: appBloc), + BlocProvider.value(value: homeCubit), ], child: Scaffold( key: _scaffoldKey, @@ -66,7 +60,13 @@ void main() { late HomeCubit homeCubit; late User user; - const categories = [Category.top, Category.health]; + final entertainmentCategory = Category( + id: 'entertainment', + name: 'Entertainment', + ); + final healthCategory = Category(id: 'health', name: 'Health'); + + final categories = [entertainmentCategory, healthCategory]; setUp(() { categoriesBloc = MockCategoriesBloc(); @@ -74,9 +74,9 @@ void main() { homeCubit = MockHomeCubit(); user = MockUser(); - when(() => categoriesBloc.state).thenReturn( - CategoriesState.initial().copyWith(categories: categories), - ); + when( + () => categoriesBloc.state, + ).thenReturn(CategoriesState.initial().copyWith(categories: categories)); when(() => user.subscriptionPlan).thenReturn(SubscriptionPlan.none); when(() => appBloc.state).thenReturn(AppState.authenticated(user)); @@ -109,14 +109,11 @@ void main() { expect(find.byType(NavDrawerSections), findsOneWidget); }); - testWidgets( - 'renders NavDrawerSubscribe ' + testWidgets('renders NavDrawerSubscribe ' 'when user is not subscribed', (tester) async { final user = MockUser(); when(() => user.subscriptionPlan).thenReturn(SubscriptionPlan.none); - when(() => appBloc.state).thenReturn( - AppState.authenticated(user), - ); + when(() => appBloc.state).thenReturn(AppState.authenticated(user)); await tester.pumpDrawer( categoriesBloc: categoriesBloc, appBloc: appBloc, @@ -125,14 +122,11 @@ void main() { expect(find.byType(NavDrawerSubscribe), findsOneWidget); }); - testWidgets( - 'does not render NavDrawerSubscribe ' + testWidgets('does not render NavDrawerSubscribe ' 'when user is subscribed', (tester) async { final user = MockUser(); when(() => user.subscriptionPlan).thenReturn(SubscriptionPlan.premium); - when(() => appBloc.state).thenReturn( - AppState.authenticated(user), - ); + when(() => appBloc.state).thenReturn(AppState.authenticated(user)); await tester.pumpDrawer( categoriesBloc: categoriesBloc, appBloc: appBloc, @@ -159,8 +153,9 @@ void main() { await tester.pump(kThemeAnimationDuration); - final scaffoldState = - tester.firstState(find.byKey(_scaffoldKey)); + final scaffoldState = tester.firstState( + find.byKey(_scaffoldKey), + ); expect(scaffoldState.isDrawerOpen, isFalse); }); @@ -184,34 +179,34 @@ void main() { await tester.pump(kThemeAnimationDuration); - verify(() => categoriesBloc.add(CategorySelected(category: category))) - .called(1); + verify( + () => categoriesBloc.add(CategorySelected(category: category)), + ).called(1); }); - testWidgets( - 'sets tab to zero when NavDrawerSectionItem is tapped ', - (tester) async { - final category = categories.first; - - await tester.pumpDrawer( - categoriesBloc: categoriesBloc, - appBloc: appBloc, - homeCubit: homeCubit, - ); - - await tester.tap( - find.byWidgetPredicate( - (widget) => - widget is NavDrawerSectionItem && - widget.key == ValueKey(category), - ), - ); - - await tester.pump(kThemeAnimationDuration); - - verify(() => homeCubit.setTab(0)).called(1); - }, - ); + testWidgets('sets tab to zero when NavDrawerSectionItem is tapped ', ( + tester, + ) async { + final category = categories.first; + + await tester.pumpDrawer( + categoriesBloc: categoriesBloc, + appBloc: appBloc, + homeCubit: homeCubit, + ); + + await tester.tap( + find.byWidgetPredicate( + (widget) => + widget is NavDrawerSectionItem && + widget.key == ValueKey(category), + ), + ); + + await tester.pump(kThemeAnimationDuration); + + verify(() => homeCubit.setTab(0)).called(1); + }); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/widgets/nav_drawer_sections_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/widgets/nav_drawer_sections_test.dart index 2de52c499..65eec33fc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/widgets/nav_drawer_sections_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/widgets/nav_drawer_sections_test.dart @@ -19,7 +19,13 @@ void main() { group('NavDrawerSections', () { late CategoriesBloc categoriesBloc; - const categories = [Category.top, Category.health]; + final entertainmentCategory = Category( + id: 'entertainment', + name: 'Entertainment', + ); + final healthCategory = Category(id: 'health', name: 'Health'); + + final categories = [entertainmentCategory, healthCategory]; final selectedCategory = categories.first; setUp(() { @@ -34,22 +40,15 @@ void main() { testWidgets('renders NavDrawerSectionsTitle', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: categoriesBloc, - child: NavDrawerSections(), - ), + BlocProvider.value(value: categoriesBloc, child: NavDrawerSections()), ); expect(find.byType(NavDrawerSectionsTitle), findsOneWidget); }); - testWidgets( - 'renders NavDrawerSectionItem ' + testWidgets('renders NavDrawerSectionItem ' 'for each category', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: categoriesBloc, - child: NavDrawerSections(), - ), + BlocProvider.value(value: categoriesBloc, child: NavDrawerSections()), ); for (final category in categories) { @@ -68,21 +67,14 @@ void main() { group('NavDrawerSectionItem', () { testWidgets('renders ListTile with title', (tester) async { const title = 'title'; - await tester.pumpApp( - NavDrawerSectionItem( - title: title, - ), - ); + await tester.pumpApp(NavDrawerSectionItem(title: title)); expect(find.widgetWithText(ListTile, title), findsOneWidget); }); testWidgets('calls onTap when tapped', (tester) async { var tapped = false; await tester.pumpApp( - NavDrawerSectionItem( - title: 'title', - onTap: () => tapped = true, - ), + NavDrawerSectionItem(title: 'title', onTap: () => tapped = true), ); await tester.tap(find.byType(NavDrawerSectionItem)); @@ -92,18 +84,14 @@ void main() { testWidgets('has correct selected color', (tester) async { await tester.pumpApp( - NavDrawerSectionItem( - title: 'title', - selected: true, - onTap: () {}, - ), + NavDrawerSectionItem(title: 'title', selected: true, onTap: () {}), ); final tile = tester.widget(find.byType(ListTile)); expect( tile.selectedTileColor, - equals(AppColors.white.withOpacity(0.08)), + equals(AppColors.white.withValues(alpha: 0.08)), ); expect( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/widgets/nav_drawer_subscribe_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/widgets/nav_drawer_subscribe_test.dart index 5cee8175f..87c82a2d8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/widgets/nav_drawer_subscribe_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/navigation/widgets/nav_drawer_subscribe_test.dart @@ -31,8 +31,9 @@ void main() { expect(find.byType(AppButton), findsOneWidget); }); - testWidgets('opens PurchaseSubscriptionDialog when tapped', - (tester) async { + testWidgets('opens PurchaseSubscriptionDialog when tapped', ( + tester, + ) async { final inAppPurchaseRepository = MockInAppPurchaseRepository(); when( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/network_error/view/network_error_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/network_error/view/network_error_test.dart index 0a174ca23..c43a2ce72 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/network_error/view/network_error_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/network_error/view/network_error_test.dart @@ -22,8 +22,8 @@ void main() { body: Builder( builder: (context) { return ElevatedButton( - onPressed: () { - Navigator.of(context).push(NetworkError.route()); + onPressed: () async { + await Navigator.of(context).push(NetworkError.route()); }, child: const Text(tapMeText), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_bloc_test.dart index b79413e1d..9d85aab31 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_bloc_test.dart @@ -1,5 +1,4 @@ // ignore_for_file: prefer_const_constructors -// ignore_for_file: prefer_const_literals_to_create_immutables import 'package:bloc_test/bloc_test.dart'; import 'package:{{project_name.snakeCase()}}/newsletter/newsletter.dart'; @@ -24,9 +23,8 @@ void main() { 'emits [loading, success] ' 'when subscribeToNewsletter succeeds', setUp: () => when( - () => newsRepository.subscribeToNewsletter( - email: any(named: 'email'), - ), + () => + newsRepository.subscribeToNewsletter(email: any(named: 'email')), ).thenAnswer(Future.value), seed: () => NewsletterState(email: Email.dirty('test'), isValid: true), build: () => NewsletterBloc(newsRepository: newsRepository), @@ -49,22 +47,15 @@ void main() { 'emits [loading, failed] ' 'when subscribeToNewsletter throws', setUp: () => when( - () => newsRepository.subscribeToNewsletter( - email: any(named: 'email'), - ), + () => + newsRepository.subscribeToNewsletter(email: any(named: 'email')), ).thenThrow(Error.new), seed: () => NewsletterState(email: Email.dirty('test')), build: () => NewsletterBloc(newsRepository: newsRepository), act: (bloc) => bloc.add(NewsletterSubscribed()), expect: () => [ - NewsletterState( - status: NewsletterStatus.loading, - email: emailValid, - ), - NewsletterState( - status: NewsletterStatus.failure, - email: emailValid, - ), + NewsletterState(status: NewsletterStatus.loading, email: emailValid), + NewsletterState(status: NewsletterStatus.failure, email: emailValid), ], ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_event_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_event_test.dart index 7a4ed85dd..b1b89a311 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_event_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_event_test.dart @@ -7,14 +7,8 @@ void main() { const testEmail = 'test@test.com'; group('EmailChanged', () { test('supports value comparisons', () { - expect( - EmailChanged(email: testEmail), - EmailChanged(email: testEmail), - ); - expect( - EmailChanged(email: ''), - isNot(EmailChanged(email: testEmail)), - ); + expect(EmailChanged(email: testEmail), EmailChanged(email: testEmail)); + expect(EmailChanged(email: ''), isNot(EmailChanged(email: testEmail))); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_state_test.dart index 34ce4f2f9..03357226e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/bloc/newsletter_state_test.dart @@ -7,64 +7,36 @@ import 'package:form_inputs/form_inputs.dart'; void main() { group('NewsletterState', () { test('initial has correct status', () { - expect( - NewsletterState().status, - equals(NewsletterStatus.initial), - ); + expect(NewsletterState().status, equals(NewsletterStatus.initial)); }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no properties are passed', () { - expect( - NewsletterState().copyWith(), - equals(NewsletterState()), - ); + expect(NewsletterState().copyWith(), equals(NewsletterState())); }); - test( - 'returns object with updated status ' + test('returns object with updated status ' 'when status is passed', () { expect( - NewsletterState().copyWith( - status: NewsletterStatus.loading, - ), - equals( - NewsletterState( - status: NewsletterStatus.loading, - ), - ), + NewsletterState().copyWith(status: NewsletterStatus.loading), + equals(NewsletterState(status: NewsletterStatus.loading)), ); }); - test( - 'returns object with updated email ' + test('returns object with updated email ' 'when status is passed', () { expect( - NewsletterState().copyWith( - email: Email.dirty('email'), - ), - equals( - NewsletterState( - email: Email.dirty('email'), - ), - ), + NewsletterState().copyWith(email: Email.dirty('email')), + equals(NewsletterState(email: Email.dirty('email'))), ); }); - test( - 'returns object with updated isValid ' + test('returns object with updated isValid ' 'when status is passed', () { expect( - NewsletterState().copyWith( - isValid: true, - ), - equals( - NewsletterState( - isValid: true, - ), - ), + NewsletterState().copyWith(isValid: true), + equals(NewsletterState(isValid: true)), ); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/view/newsletter_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/view/newsletter_test.dart index 191e74090..052bf12f1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/view/newsletter_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/newsletter/view/newsletter_test.dart @@ -32,9 +32,7 @@ void main() { group('Newsletter', () { testWidgets('renders NewsletterView', (tester) async { - await tester.pumpApp( - Newsletter(), - ); + await tester.pumpApp(Newsletter()); await tester.pump(); @@ -60,8 +58,9 @@ void main() { expect(find.byType(NewsletterSignUp), findsOneWidget); }); - testWidgets('renders disabled button when status is not valid', - (tester) async { + testWidgets('renders disabled button when status is not valid', ( + tester, + ) async { whenListen( newsletterBloc, Stream.fromIterable([initialState]), @@ -102,8 +101,7 @@ void main() { verify(() => newsletterBloc.add(NewsletterSubscribed())).called(1); }); - testWidgets( - 'adds EmailChanged to NewsletterBloc ' + testWidgets('adds EmailChanged to NewsletterBloc ' 'on email text field filled', (tester) async { whenListen( newsletterBloc, @@ -121,12 +119,12 @@ void main() { const changedEmail = 'test@test.com'; await tester.enterText(find.byType(AppEmailTextField), changedEmail); - verify(() => newsletterBloc.add(EmailChanged(email: changedEmail))) - .called(1); + verify( + () => newsletterBloc.add(EmailChanged(email: changedEmail)), + ).called(1); }); - testWidgets( - 'adds TrackAnalyticsEvent to AnalyticsBloc ' + testWidgets('adds TrackAnalyticsEvent to AnalyticsBloc ' 'with NewsletterEvent.impression ' 'when shown', (tester) async { final AnalyticsBloc analyticsBloc = MockAnalyticsBloc(); @@ -154,20 +152,17 @@ void main() { ).called(1); }); - testWidgets( - 'adds TrackAnalyticsEvent to AnalyticsBloc ' + testWidgets('adds TrackAnalyticsEvent to AnalyticsBloc ' 'with NewsletterEvent.signUp ' 'when status is success', (tester) async { final AnalyticsBloc analyticsBloc = MockAnalyticsBloc(); whenListen( newsletterBloc, - Stream.fromIterable( - [ - NewsletterState(), - NewsletterState(status: NewsletterStatus.success), - ], - ), + Stream.fromIterable([ + NewsletterState(), + NewsletterState(status: NewsletterStatus.success), + ]), initialState: initialState, ); @@ -182,19 +177,18 @@ void main() { ); verify( - () => analyticsBloc.add( - TrackAnalyticsEvent(NewsletterEvent.signUp()), - ), + () => analyticsBloc.add(TrackAnalyticsEvent(NewsletterEvent.signUp())), ).called(1); }); - testWidgets('renders NewsletterSuccess when NewsletterStatus is success', - (tester) async { + testWidgets('renders NewsletterSuccess when NewsletterStatus is success', ( + tester, + ) async { whenListen( newsletterBloc, - Stream.fromIterable( - [NewsletterState(status: NewsletterStatus.success)], - ), + Stream.fromIterable([ + NewsletterState(status: NewsletterStatus.success), + ]), initialState: initialState, ); @@ -208,13 +202,14 @@ void main() { expect(find.byType(NewsletterSucceeded), findsOneWidget); }); - testWidgets('shows SnackBar when NewsletterStatus is failure', - (tester) async { + testWidgets('shows SnackBar when NewsletterStatus is failure', ( + tester, + ) async { whenListen( newsletterBloc, - Stream.fromIterable( - [NewsletterState(status: NewsletterStatus.failure)], - ), + Stream.fromIterable([ + NewsletterState(status: NewsletterStatus.failure), + ]), initialState: initialState, ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_bloc_test.dart index b4c79fbdd..2157d2a9e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_bloc_test.dart @@ -18,6 +18,12 @@ void main() { final notificationsRepository = MockNotificationsRepository(); final newsRepository = MockNewsRepository(); + final entertainmentCategory = Category( + id: 'entertainment', + name: 'Entertainment', + ); + final healthCategory = Category(id: 'health', name: 'Health'); + group('NotificationPreferencesBloc', () { group('on CategoriesPreferenceToggled ', () { blocTest( @@ -33,18 +39,16 @@ void main() { ), seed: () => initialState, act: (bloc) => bloc - ..add(CategoriesPreferenceToggled(category: Category.business)) - ..add(CategoriesPreferenceToggled(category: Category.business)), + ..add(CategoriesPreferenceToggled(category: entertainmentCategory)) + ..add(CategoriesPreferenceToggled(category: entertainmentCategory)), expect: () => [ + initialState.copyWith(status: NotificationPreferencesStatus.loading), initialState.copyWith( - status: NotificationPreferencesStatus.loading, - ), - initialState.copyWith( - selectedCategories: {Category.business}, + selectedCategories: {entertainmentCategory}, status: NotificationPreferencesStatus.success, ), initialState.copyWith( - selectedCategories: {Category.business}, + selectedCategories: {entertainmentCategory}, status: NotificationPreferencesStatus.loading, ), initialState.copyWith( @@ -66,15 +70,11 @@ void main() { notificationsRepository: notificationsRepository, ), seed: () => initialState, - act: (bloc) => - bloc..add(CategoriesPreferenceToggled(category: Category.business)), + act: (bloc) => bloc + ..add(CategoriesPreferenceToggled(category: entertainmentCategory)), expect: () => [ - initialState.copyWith( - status: NotificationPreferencesStatus.loading, - ), - initialState.copyWith( - status: NotificationPreferencesStatus.failure, - ), + initialState.copyWith(status: NotificationPreferencesStatus.loading), + initialState.copyWith(status: NotificationPreferencesStatus.failure), ], ); }); @@ -86,13 +86,10 @@ void main() { setUp: () { when( notificationsRepository.fetchCategoriesPreferences, - ).thenAnswer((_) async => {Category.business}); + ).thenAnswer((_) async => {entertainmentCategory}); when(newsRepository.getCategories).thenAnswer( (_) async => CategoriesResponse( - categories: const [ - Category.business, - Category.entertainment, - ], + categories: [entertainmentCategory, healthCategory], ), ); }, @@ -103,15 +100,10 @@ void main() { seed: () => initialState, act: (bloc) => bloc..add(InitialCategoriesPreferencesRequested()), expect: () => [ - initialState.copyWith( - status: NotificationPreferencesStatus.loading, - ), + initialState.copyWith(status: NotificationPreferencesStatus.loading), NotificationPreferencesState( - categories: const { - Category.business, - Category.entertainment, - }, - selectedCategories: const {Category.business}, + categories: {entertainmentCategory, healthCategory}, + selectedCategories: {entertainmentCategory}, status: NotificationPreferencesStatus.success, ), ], @@ -130,12 +122,8 @@ void main() { seed: () => initialState, act: (bloc) => bloc..add(InitialCategoriesPreferencesRequested()), expect: () => [ - initialState.copyWith( - status: NotificationPreferencesStatus.loading, - ), - initialState.copyWith( - status: NotificationPreferencesStatus.failure, - ), + initialState.copyWith(status: NotificationPreferencesStatus.loading), + initialState.copyWith(status: NotificationPreferencesStatus.failure), ], ); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_event_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_event_test.dart index 145f3d50e..eba9c3f23 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_event_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_event_test.dart @@ -5,11 +5,13 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:news_blocks/news_blocks.dart'; void main() { + final healthCategory = Category(id: 'health', name: 'Health'); + group('NotificationPreferencesEvent', () { group('CategoriesPreferenceToggled', () { test('supports value comparisons', () { - final event1 = CategoriesPreferenceToggled(category: Category.business); - final event2 = CategoriesPreferenceToggled(category: Category.business); + final event1 = CategoriesPreferenceToggled(category: healthCategory); + final event2 = CategoriesPreferenceToggled(category: healthCategory); expect(event1, equals(event2)); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_state_test.dart index 80b9c845c..c29fa3586 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/bloc/notification_preferences_state_test.dart @@ -6,6 +6,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:news_blocks/news_blocks.dart'; void main() { + final healthCategory = Category(id: 'health', name: 'Health'); + group('NotificationPreferencesState', () { test('initial has correct status', () { final initialState = NotificationPreferencesState.initial(); @@ -25,15 +27,12 @@ void main() { test('supports value comparison', () { expect( NotificationPreferencesState.initial(), - equals( - NotificationPreferencesState.initial(), - ), + equals(NotificationPreferencesState.initial()), ); }); group('copyWith ', () { - test( - 'returns same object ' + test('returns same object ' 'when no parameters changed', () { expect( NotificationPreferencesState.initial().copyWith(), @@ -41,24 +40,22 @@ void main() { ); }); - test( - 'returns object with updated categories ' + test('returns object with updated categories ' 'when categories changed', () { expect( NotificationPreferencesState.initial().copyWith( - categories: {Category.business}, + categories: {healthCategory}, ), equals( NotificationPreferencesState( - categories: {Category.business}, + categories: {healthCategory}, selectedCategories: {}, status: NotificationPreferencesStatus.initial, ), ), ); }); - test( - 'returns object with updated status ' + test('returns object with updated status ' 'when status changed', () { expect( NotificationPreferencesState.initial().copyWith( @@ -74,18 +71,17 @@ void main() { ); }); - test( - 'returns object with updated selectedCategories ' + test('returns object with updated selectedCategories ' 'when selectedCategories changed', () { expect( NotificationPreferencesState.initial().copyWith( - selectedCategories: {Category.business}, + selectedCategories: {healthCategory}, ), equals( NotificationPreferencesState( categories: {}, status: NotificationPreferencesStatus.initial, - selectedCategories: {Category.business}, + selectedCategories: {healthCategory}, ), ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/view/notification_preferences_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/view/notification_preferences_page_test.dart index 961e666f0..f3c071d8e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/view/notification_preferences_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/notification_preferences/view/notification_preferences_page_test.dart @@ -17,7 +17,10 @@ class MockNotificationPreferencesBloc extends Mock implements NotificationPreferencesBloc {} class MockNotificationPreferencesRepository extends Mock - implements NotificationsRepository {} + implements NotificationsRepository { + @override + Future?> fetchCategoriesPreferences() async => {}; +} class MockCategoriesBloc extends Mock implements CategoriesBloc {} @@ -27,10 +30,16 @@ void main() { MockNotificationPreferencesRepository(); final CategoriesBloc categoryBloc = MockCategoriesBloc(); + final entertainmentCategory = Category( + id: 'entertainment', + name: 'Entertainment', + ); + final healthCategory = Category(id: 'health', name: 'Health'); + group('NotificationPreferencesPage', () { - const populatedState = CategoriesState( + final populatedState = CategoriesState( status: CategoriesStatus.populated, - categories: [Category.business, Category.entertainment], + categories: [entertainmentCategory, healthCategory], ); test('has a route', () { @@ -63,13 +72,10 @@ void main() { group('NotificationPreferencesView', () { testWidgets('renders AppSwitch with state value', (tester) async { - const notificationState = NotificationPreferencesState( - selectedCategories: {Category.business}, + final notificationState = NotificationPreferencesState( + selectedCategories: {entertainmentCategory}, status: NotificationPreferencesStatus.success, - categories: { - Category.business, - Category.entertainment, - }, + categories: {entertainmentCategory, healthCategory}, ); whenListen( @@ -97,34 +103,36 @@ void main() { }); testWidgets( - 'adds CategoriesPreferenceToggled to NotificationPreferencesBloc ' - 'on AppSwitch toggled', (tester) async { - const notificationState = NotificationPreferencesState( - selectedCategories: {Category.business}, - status: NotificationPreferencesStatus.success, - categories: {Category.business}, - ); - - whenListen( - bloc, - Stream.value(notificationState), - initialState: notificationState, - ); - - await tester.pumpApp( - BlocProvider.value( - value: bloc, - child: const NotificationPreferencesView(), - ), - ); + 'adds CategoriesPreferenceToggled to NotificationPreferencesBloc ' + 'on AppSwitch toggled', + (tester) async { + final notificationState = NotificationPreferencesState( + selectedCategories: {entertainmentCategory}, + status: NotificationPreferencesStatus.success, + categories: {entertainmentCategory}, + ); + + whenListen( + bloc, + Stream.value(notificationState), + initialState: notificationState, + ); + + await tester.pumpApp( + BlocProvider.value( + value: bloc, + child: const NotificationPreferencesView(), + ), + ); - await tester.tap(find.byType(AppSwitch)); + await tester.tap(find.byType(AppSwitch)); - verify( - () => bloc.add( - CategoriesPreferenceToggled(category: Category.business), - ), - ).called(1); - }); + verify( + () => bloc.add( + CategoriesPreferenceToggled(category: entertainmentCategory), + ), + ).called(1); + }, + ); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/bloc/onboarding_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/bloc/onboarding_bloc_test.dart index 9a18b8bbf..6eaec6250 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/bloc/onboarding_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/bloc/onboarding_bloc_test.dart @@ -45,8 +45,9 @@ void main() { 'emits ' '[EnablingAdTracking, EnablingAdTrackingFailed] ' 'when AdsConsentClient.requestConsent returns false', - setUp: () => when(adsConsentClient.requestConsent) - .thenAnswer((_) async => false), + setUp: () => when( + adsConsentClient.requestConsent, + ).thenAnswer((_) async => false), build: () => OnboardingBloc( notificationsRepository: notificationsRepository, adsConsentClient: adsConsentClient, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/view/onboarding_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/view/onboarding_page_test.dart index 1422a3761..fde938e37 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/view/onboarding_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/view/onboarding_page_test.dart @@ -1,15 +1,11 @@ // ignore_for_file: prefer_const_constructors -import 'package:bloc_test/bloc_test.dart'; import 'package:flutter/material.dart'; -import 'package:{{project_name.snakeCase()}}/app/app.dart'; import 'package:{{project_name.snakeCase()}}/onboarding/onboarding.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../helpers/helpers.dart'; -class MockAppBloc extends MockBloc implements AppBloc {} - void main() { group('OnboardingPage', () { test('has a page', () { diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/view/onboarding_view_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/view/onboarding_view_test.dart index 791cf44a1..12c74a54a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/view/onboarding_view_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/onboarding/view/onboarding_view_test.dart @@ -23,15 +23,19 @@ void main() { const onboardingViewTitleKey = Key('onboardingView_onboardingTitle'); const onboardingViewSubtitleKey = Key('onboardingView_onboardingSubtitle'); const onboardingViewPageTwoKey = Key('onboarding_pageTwo'); - const onboardingViewPageOnePrimaryButtonKey = - Key('onboardingItem_primaryButton_pageOne'); - const onboardingViewPageOneSecondaryButtonKey = - Key('onboardingItem_secondaryButton_pageOne'); - - const onboardingViewPageTwoPrimaryButtonKey = - Key('onboardingItem_primaryButton_pageTwo'); - const onboardingViewPageTwoSecondaryButtonKey = - Key('onboardingItem_secondaryButton_pageTwo'); + const onboardingViewPageOnePrimaryButtonKey = Key( + 'onboardingItem_primaryButton_pageOne', + ); + const onboardingViewPageOneSecondaryButtonKey = Key( + 'onboardingItem_secondaryButton_pageOne', + ); + + const onboardingViewPageTwoPrimaryButtonKey = Key( + 'onboardingItem_primaryButton_pageTwo', + ); + const onboardingViewPageTwoSecondaryButtonKey = Key( + 'onboardingItem_secondaryButton_pageTwo', + ); setUp(() { appBloc = MockAppBloc(); @@ -47,43 +51,32 @@ void main() { group('renders', () { testWidgets('Onboarding title', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), ); expect(find.byKey(onboardingViewTitleKey), findsOneWidget); }); testWidgets('Onboarding subtitle', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), ); expect(find.byKey(onboardingViewSubtitleKey), findsOneWidget); }); testWidgets('Onboarding PageView', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), ); expect(find.byType(PageView), findsOneWidget); }); }); group('navigates', () { - testWidgets('to onboarding page two when button page one is tapped', - (tester) async { + testWidgets('to onboarding page two when button page one is tapped', ( + tester, + ) async { await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), ); final button = find.byKey(onboardingViewPageOneSecondaryButtonKey); @@ -101,8 +94,7 @@ void main() { expect(find.byKey(onboardingViewPageTwoKey), findsOneWidget); }); - testWidgets( - 'to onboarding page two ' + testWidgets('to onboarding page two ' 'when state is EnablingAdTrackingSucceeded', (tester) async { whenListen( onboardingBloc, @@ -114,10 +106,7 @@ void main() { ); await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), ); await tester.pumpAndSettle(); @@ -125,23 +114,16 @@ void main() { expect(find.byKey(onboardingViewPageTwoKey), findsOneWidget); }); - testWidgets( - 'to onboarding page two ' + testWidgets('to onboarding page two ' 'when state is EnablingAdTrackingFailed', (tester) async { whenListen( onboardingBloc, - Stream.fromIterable([ - OnboardingInitial(), - EnablingAdTrackingFailed(), - ]), + Stream.fromIterable([OnboardingInitial(), EnablingAdTrackingFailed()]), initialState: OnboardingInitial(), ); await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), ); await tester.pumpAndSettle(); @@ -151,10 +133,7 @@ void main() { testWidgets('to home when onboarding is complete', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), appBloc: appBloc, ); @@ -186,14 +165,10 @@ void main() { }); }); - testWidgets( - 'adds EnableNotificationsRequested to OnboardingBloc ' + testWidgets('adds EnableNotificationsRequested to OnboardingBloc ' 'when subscribe to notifications now button is pressed', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), ); final buttonOne = find.byKey(onboardingViewPageOneSecondaryButtonKey); @@ -222,16 +197,12 @@ void main() { await tester.tap(button); await tester.pumpAndSettle(); - expect( - find.byKey(onboardingViewPageTwoPrimaryButtonKey), - findsOneWidget, - ); + expect(find.byKey(onboardingViewPageTwoPrimaryButtonKey), findsOneWidget); verify(() => onboardingBloc.add(EnableNotificationsRequested())).called(1); }); - testWidgets( - 'adds AppOnboardingCompleted to AppBloc ' + testWidgets('adds AppOnboardingCompleted to AppBloc ' 'when OnboardingState is EnablingNotificationsSucceeded', (tester) async { final onboardingStateController = StreamController(); @@ -242,10 +213,7 @@ void main() { ); await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), appBloc: appBloc, ); @@ -260,10 +228,7 @@ void main() { group('does nothing', () { testWidgets('when personalized my ads button is pressed', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: onboardingBloc, - child: OnboardingView(), - ), + BlocProvider.value(value: onboardingBloc, child: OnboardingView()), ); final button = find.byKey(onboardingViewPageOnePrimaryButtonKey); @@ -278,10 +243,7 @@ void main() { await tester.tap(button); await tester.pumpAndSettle(); - expect( - find.byKey(onboardingViewPageOnePrimaryButtonKey), - findsOneWidget, - ); + expect(find.byKey(onboardingViewPageOnePrimaryButtonKey), findsOneWidget); }); }); } diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/bloc/search_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/bloc/search_bloc_test.dart index 7e672c64a..ef5549efb 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/bloc/search_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/bloc/search_bloc_test.dart @@ -32,9 +32,9 @@ void main() { 'emits [loading, populated] ' 'with articles and topics ' 'when popularSearch succeeds.', - setUp: () => when(() => newsRepository.popularSearch()).thenAnswer( - (_) async => popularResponseSuccess, - ), + setUp: () => when( + () => newsRepository.popularSearch(), + ).thenAnswer((_) async => popularResponseSuccess), build: () => SearchBloc(newsRepository: newsRepository), act: (bloc) => bloc.add(SearchTermChanged()), expect: () => [ @@ -50,8 +50,9 @@ void main() { blocTest( 'emits [loading, failure] ' 'when popularSearch throws.', - setUp: () => when(() => newsRepository.popularSearch()) - .thenThrow(PopularSearchFailure), + setUp: () => when( + () => newsRepository.popularSearch(), + ).thenThrow(PopularSearchFailure), build: () => SearchBloc(newsRepository: newsRepository), act: (bloc) => bloc.add(SearchTermChanged()), expect: () => [ @@ -74,12 +75,8 @@ void main() { 'and awaited debounce time ' 'when relevantSearch succeeds', setUp: () => when( - () => newsRepository.relevantSearch( - term: any(named: 'term'), - ), - ).thenAnswer( - (_) async => relevantResponseSuccess, - ), + () => newsRepository.relevantSearch(term: any(named: 'term')), + ).thenAnswer((_) async => relevantResponseSuccess), build: () => SearchBloc(newsRepository: newsRepository), act: (bloc) { bloc.add(SearchTermChanged(searchTerm: 'term')); @@ -105,9 +102,7 @@ void main() { 'emits [loading, failure] ' 'when relevantSearch throws.', setUp: () => when( - () => newsRepository.relevantSearch( - term: any(named: 'term'), - ), + () => newsRepository.relevantSearch(term: any(named: 'term')), ).thenThrow(RelevantSearchFailure), build: () => SearchBloc(newsRepository: newsRepository), act: (bloc) => bloc.add(SearchTermChanged(searchTerm: 'term')), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/bloc/search_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/bloc/search_state_test.dart index ba30417cd..07b64efa1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/bloc/search_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/bloc/search_state_test.dart @@ -7,29 +7,19 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('SearchState', () { test('has correct initial status', () { - expect( - const SearchState.initial().status, - equals(SearchStatus.initial), - ); + expect(const SearchState.initial().status, equals(SearchStatus.initial)); }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no properties are passed', () { - expect( - SearchState.initial().copyWith(), - equals(SearchState.initial()), - ); + expect(SearchState.initial().copyWith(), equals(SearchState.initial())); }); - test( - 'returns object with updated status ' + test('returns object with updated status ' 'when status is passed', () { expect( - SearchState.initial().copyWith( - status: SearchStatus.loading, - ), + SearchState.initial().copyWith(status: SearchStatus.loading), equals( SearchState( status: SearchStatus.loading, @@ -41,8 +31,7 @@ void main() { ); }); - test( - 'returns object with updated articles ' + test('returns object with updated articles ' 'when articles are passed', () { final articles = [DividerHorizontalBlock()]; @@ -59,8 +48,7 @@ void main() { ); }); - test( - 'returns object with updated topics ' + test('returns object with updated topics ' 'when topics are passed', () { final topics = ['Topic']; @@ -77,13 +65,10 @@ void main() { ); }); - test( - 'returns object with updated searchType ' + test('returns object with updated searchType ' 'when searchType is passed', () { expect( - SearchState.initial().copyWith( - searchType: SearchType.relevant, - ), + SearchState.initial().copyWith(searchType: SearchType.relevant), equals( SearchState( topics: const [], diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/view/search_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/view/search_page_test.dart index 384d7a00c..75cd21004 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/view/search_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/view/search_page_test.dart @@ -41,10 +41,7 @@ void main() { group('SearchView', () { testWidgets('renders filter chips', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: searchBloc, - child: const SearchView(), - ), + BlocProvider.value(value: searchBloc, child: const SearchView()), ); await tester.pump(); @@ -53,34 +50,31 @@ void main() { }); testWidgets( - 'when SearchFilterChip clicked adds SearchTermChanged to SearchBloc', - (tester) async { - await tester.pumpApp( - BlocProvider.value( - value: searchBloc, - child: const SearchView(), - ), - ); - - await tester.tap(find.byKey(Key('searchFilterChip_topic'))); - - verify(() => searchBloc.add(SearchTermChanged(searchTerm: 'topic'))) - .called(1); - }); + 'when SearchFilterChip clicked adds SearchTermChanged to SearchBloc', + (tester) async { + await tester.pumpApp( + BlocProvider.value(value: searchBloc, child: const SearchView()), + ); + + await tester.tap(find.byKey(Key('searchFilterChip_topic'))); + + verify( + () => searchBloc.add(SearchTermChanged(searchTerm: 'topic')), + ).called(1); + }, + ); testWidgets('renders articles', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: searchBloc, - child: const SearchView(), - ), + BlocProvider.value(value: searchBloc, child: const SearchView()), ); expect(find.byType(CategoryFeedItem), findsOneWidget); }); - testWidgets('in SearchType.relevant renders two headline titles', - (tester) async { + testWidgets('in SearchType.relevant renders two headline titles', ( + tester, + ) async { when(() => searchBloc.state).thenReturn( const SearchState( articles: [], @@ -91,23 +85,16 @@ void main() { ); await tester.pumpApp( - BlocProvider.value( - value: searchBloc, - child: const SearchView(), - ), + BlocProvider.value(value: searchBloc, child: const SearchView()), ); expect(find.byType(SearchHeadlineText), findsNWidgets(2)); }); - testWidgets( - 'when SearchTextField changes to non-empty value ' + testWidgets('when SearchTextField changes to non-empty value ' 'adds SearchTermChanged to SearchBloc', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: searchBloc, - child: const SearchView(), - ), + BlocProvider.value(value: searchBloc, child: const SearchView()), ); await tester.enterText( @@ -115,12 +102,12 @@ void main() { 'test', ); - verify(() => searchBloc.add(SearchTermChanged(searchTerm: 'test'))) - .called(1); + verify( + () => searchBloc.add(SearchTermChanged(searchTerm: 'test')), + ).called(1); }); - testWidgets( - 'when SearchTextField changes to an empty value ' + testWidgets('when SearchTextField changes to an empty value ' 'adds empty SearchTermChanged to SearchBloc', (tester) async { when(() => searchBloc.state).thenReturn( const SearchState( @@ -132,36 +119,26 @@ void main() { ); await tester.pumpApp( - BlocProvider.value( - value: searchBloc, - child: const SearchView(), - ), + BlocProvider.value(value: searchBloc, child: const SearchView()), ); - await tester.enterText( - find.byKey(Key('searchPage_searchTextField')), - '', - ); + await tester.enterText(find.byKey(Key('searchPage_searchTextField')), ''); verify(() => searchBloc.add(SearchTermChanged())).called(1); }); - testWidgets('shows snackbar when SearchBloc SearchStatus is failure', - (tester) async { + testWidgets('shows snackbar when SearchBloc SearchStatus is failure', ( + tester, + ) async { final expectedStates = [ SearchState.initial(), - SearchState.initial().copyWith( - status: SearchStatus.failure, - ), + SearchState.initial().copyWith(status: SearchStatus.failure), ]; whenListen(searchBloc, Stream.fromIterable(expectedStates)); await tester.pumpApp( - BlocProvider.value( - value: searchBloc, - child: const SearchView(), - ), + BlocProvider.value(value: searchBloc, child: const SearchView()), ); expect(find.byType(SnackBar), findsOneWidget); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_filter_chip_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_filter_chip_test.dart index 316de9bd5..51377734a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_filter_chip_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_filter_chip_test.dart @@ -9,10 +9,7 @@ void main() { group('SearchFilterChip', () { testWidgets('renders chipText', (tester) async { await tester.pumpApp( - SearchFilterChip( - chipText: 'text', - onSelected: (_) {}, - ), + SearchFilterChip(chipText: 'text', onSelected: (_) {}), ); expect(find.text('text'), findsOneWidget); @@ -22,10 +19,7 @@ void main() { final completer = Completer(); await tester.pumpApp( - SearchFilterChip( - chipText: 'text', - onSelected: completer.complete, - ), + SearchFilterChip(chipText: 'text', onSelected: completer.complete), ); await tester.tap(find.byType(SearchFilterChip)); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_headline_text_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_headline_text_test.dart index 2fa2a2854..f5b50800b 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_headline_text_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_headline_text_test.dart @@ -6,11 +6,7 @@ import '../../helpers/helpers.dart'; void main() { group('SearchHeadlineText', () { testWidgets('renders headerText uppercased', (tester) async { - await tester.pumpApp( - const SearchHeadlineText( - headerText: 'text', - ), - ); + await tester.pumpApp(const SearchHeadlineText(headerText: 'text')); expect(find.text('TEXT'), findsOneWidget); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_text_field_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_text_field_test.dart index d3a3f737f..84f90d7a1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_text_field_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/search/widgets/search_text_field_test.dart @@ -7,15 +7,12 @@ import '../../helpers/helpers.dart'; void main() { group('SearchTextField', () { - testWidgets('changes controller.value when text input changes', - (tester) async { + testWidgets('changes controller.value when text input changes', ( + tester, + ) async { final controller = TextEditingController(); - await tester.pumpApp( - SearchTextField( - controller: controller, - ), - ); + await tester.pumpApp(SearchTextField(controller: controller)); await tester.enterText(find.byType(AppTextField), 'text'); @@ -25,11 +22,7 @@ void main() { testWidgets('clears controller on IconButton pressed', (tester) async { final controller = TextEditingController(text: 'text'); - await tester.pumpApp( - SearchTextField( - controller: controller, - ), - ); + await tester.pumpApp(SearchTextField(controller: controller)); await tester.tap(find.byType(IconButton)); expect(controller.value.text, equals('')); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/slideshow/view/slideshow_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/slideshow/view/slideshow_page_test.dart index af796808d..1702ede62 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/slideshow/view/slideshow_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/slideshow/view/slideshow_page_test.dart @@ -28,10 +28,7 @@ void main() { test('has a route', () { expect( - SlideshowPage.route( - slideshow: slideshow, - articleId: articleId, - ), + SlideshowPage.route(slideshow: slideshow, articleId: articleId), isA>(), ); }); @@ -39,10 +36,7 @@ void main() { testWidgets('renders a SlideshowView', (tester) async { await mockNetworkImages( () => tester.pumpApp( - SlideshowPage( - slideshow: slideshow, - articleId: articleId, - ), + SlideshowPage(slideshow: slideshow, articleId: articleId), ), ); @@ -56,10 +50,7 @@ void main() { when(() => navigator.popUntil(any())).thenAnswer((_) async {}); await mockNetworkImages( () => tester.pumpApp( - SlideshowPage( - slideshow: slideshow, - articleId: articleId, - ), + SlideshowPage(slideshow: slideshow, articleId: articleId), navigator: navigator, ), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/slideshow/view/slideshow_view_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/slideshow/view/slideshow_view_test.dart index 6b52b62ab..9eefb7ed3 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/slideshow/view/slideshow_view_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/slideshow/view/slideshow_view_test.dart @@ -5,7 +5,6 @@ import 'package:app_ui/app_ui.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:{{project_name.snakeCase()}}/app/app.dart'; import 'package:{{project_name.snakeCase()}}/article/article.dart'; import 'package:{{project_name.snakeCase()}}/slideshow/slideshow.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -19,8 +18,6 @@ import '../../helpers/helpers.dart'; class MockArticleBloc extends MockBloc implements ArticleBloc {} -class MockAppBloc extends MockBloc implements AppBloc {} - void main() { late ArticleBloc articleBloc; final slides = List.generate( @@ -48,9 +45,7 @@ void main() { () async => tester.pumpApp( BlocProvider.value( value: articleBloc, - child: SlideshowView( - block: slideshow, - ), + child: SlideshowView(block: slideshow), ), ), ); @@ -59,17 +54,13 @@ void main() { testWidgets('that adds ShareRequested on ShareButton tap', (tester) async { when(() => articleBloc.state).thenReturn( - ArticleState.initial().copyWith( - uri: Uri(path: 'notEmptyUrl'), - ), + ArticleState.initial().copyWith(uri: Uri(path: 'notEmptyUrl')), ); await mockNetworkImages( () async => tester.pumpApp( BlocProvider.value( value: articleBloc, - child: SlideshowView( - block: slideshow, - ), + child: SlideshowView(block: slideshow), ), ), ); @@ -77,11 +68,7 @@ void main() { await tester.tap(find.byType(ShareButton)); verify( - () => articleBloc.add( - ShareRequested( - uri: Uri(path: 'notEmptyUrl'), - ), - ), + () => articleBloc.add(ShareRequested(uri: Uri(path: 'notEmptyUrl'))), ).called(1); }); }); @@ -92,9 +79,7 @@ void main() { () async => tester.pumpApp( BlocProvider.value( value: articleBloc, - child: SlideshowView( - block: slideshow, - ), + child: SlideshowView(block: slideshow), ), ), ); @@ -107,9 +92,7 @@ void main() { () async => tester.pumpApp( BlocProvider.value( value: articleBloc, - child: SlideshowView( - block: slideshow, - ), + child: SlideshowView(block: slideshow), ), ), ); @@ -118,11 +101,7 @@ void main() { group('ArticleSubscribeButton', () { testWidgets('renders AppButton', (tester) async { - await tester.pumpApp( - Row( - children: [ArticleSubscribeButton()], - ), - ); + await tester.pumpApp(Row(children: [ArticleSubscribeButton()])); expect(find.byType(AppButton), findsOneWidget); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_bloc_test.dart index 722ccc947..404bcf17c 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_bloc_test.dart @@ -22,14 +22,8 @@ void main() { final subscription = Subscription( id: 'dd339fda-33e9-49d0-9eb5-0ccb77eb760f', name: SubscriptionPlan.none, - cost: SubscriptionCost( - annual: 16200, - monthly: 1499, - ), - benefits: const [ - 'test benefits', - 'another test benefits', - ], + cost: SubscriptionCost(annual: 16200, monthly: 1499), + benefits: const ['test benefits', 'another test benefits'], ); setUp(() { @@ -47,18 +41,14 @@ void main() { 'and emits state with fetched subscriptions', setUp: () => when( inAppPurchaseRepository.fetchSubscriptions, - ).thenAnswer( - (_) async => [subscription], - ), + ).thenAnswer((_) async => [subscription]), build: () => SubscriptionsBloc( inAppPurchaseRepository: inAppPurchaseRepository, userRepository: userRepository, ), act: (bloc) => bloc.add(SubscriptionsRequested()), expect: () => [ - SubscriptionsState.initial().copyWith( - subscriptions: [subscription], - ), + SubscriptionsState.initial().copyWith(subscriptions: [subscription]), ], ); @@ -82,12 +72,8 @@ void main() { 'calls InAppPurchaseRepository.purchase ' 'and emits pending state', setUp: () => when( - () => inAppPurchaseRepository.purchase( - subscription: subscription, - ), - ).thenAnswer( - (_) async {}, - ), + () => inAppPurchaseRepository.purchase(subscription: subscription), + ).thenAnswer((_) async {}), build: () => SubscriptionsBloc( inAppPurchaseRepository: inAppPurchaseRepository, userRepository: userRepository, @@ -104,9 +90,7 @@ void main() { blocTest( 'adds error to state if fetchSubscriptions throws', setUp: () => when( - () => inAppPurchaseRepository.purchase( - subscription: subscription, - ), + () => inAppPurchaseRepository.purchase(subscription: subscription), ).thenThrow(Exception()), build: () => SubscriptionsBloc( inAppPurchaseRepository: inAppPurchaseRepository, @@ -130,15 +114,11 @@ void main() { seed: () => SubscriptionsState.initial().copyWith( purchaseStatus: PurchaseStatus.none, ), - setUp: () => when( - () => inAppPurchaseRepository.purchaseUpdate, - ).thenAnswer( - (_) => Stream.value( - PurchasePurchased( - subscription: subscription, + setUp: () => + when(() => inAppPurchaseRepository.purchaseUpdate).thenAnswer( + (_) => + Stream.value(PurchasePurchased(subscription: subscription)), ), - ), - ), build: () => SubscriptionsBloc( inAppPurchaseRepository: inAppPurchaseRepository, userRepository: userRepository, @@ -157,18 +137,10 @@ void main() { purchaseStatus: PurchaseStatus.pending, ), setUp: () { - when( - () => inAppPurchaseRepository.purchaseUpdate, - ).thenAnswer( - (_) => Stream.value( - PurchaseDelivered( - subscription: subscription, - ), - ), + when(() => inAppPurchaseRepository.purchaseUpdate).thenAnswer( + (_) => Stream.value(PurchaseDelivered(subscription: subscription)), ); - when( - userRepository.updateSubscriptionPlan, - ).thenAnswer((_) async {}); + when(userRepository.updateSubscriptionPlan).thenAnswer((_) async {}); }, build: () => SubscriptionsBloc( inAppPurchaseRepository: inAppPurchaseRepository, @@ -188,15 +160,14 @@ void main() { seed: () => SubscriptionsState.initial().copyWith( purchaseStatus: PurchaseStatus.pending, ), - setUp: () => when( - () => inAppPurchaseRepository.purchaseUpdate, - ).thenAnswer( - (_) => Stream.value( - PurchaseFailed( - failure: InternalInAppPurchaseFailure(subscription), + setUp: () => + when(() => inAppPurchaseRepository.purchaseUpdate).thenAnswer( + (_) => Stream.value( + PurchaseFailed( + failure: InternalInAppPurchaseFailure(subscription), + ), + ), ), - ), - ), build: () => SubscriptionsBloc( inAppPurchaseRepository: inAppPurchaseRepository, userRepository: userRepository, @@ -216,11 +187,7 @@ void main() { ), setUp: () => when( () => inAppPurchaseRepository.purchaseUpdate, - ).thenAnswer( - (_) => Stream.value( - PurchaseCanceled(), - ), - ), + ).thenAnswer((_) => Stream.value(PurchaseCanceled())), build: () => SubscriptionsBloc( inAppPurchaseRepository: inAppPurchaseRepository, userRepository: userRepository, @@ -236,7 +203,7 @@ void main() { group('close', () { late StreamController currentSubscriptionPlanController; late StreamController - subscriptionPurchaseUpdateController; + subscriptionPurchaseUpdateController; setUp(() { currentSubscriptionPlanController = @@ -244,8 +211,9 @@ void main() { subscriptionPurchaseUpdateController = StreamController(); - when(() => inAppPurchaseRepository.purchaseUpdate) - .thenAnswer((_) => subscriptionPurchaseUpdateController.stream); + when( + () => inAppPurchaseRepository.purchaseUpdate, + ).thenAnswer((_) => subscriptionPurchaseUpdateController.stream); }); blocTest( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_event_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_event_test.dart index e11e1c7be..6c59fc83f 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_event_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_event_test.dart @@ -17,10 +17,7 @@ void main() { final event1 = SubscriptionPurchaseRequested( subscription: const Subscription( benefits: [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -28,10 +25,7 @@ void main() { final event2 = SubscriptionPurchaseRequested( subscription: const Subscription( benefits: [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -46,10 +40,7 @@ void main() { final event1 = SubscriptionPurchaseCompleted( subscription: const Subscription( benefits: [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -57,10 +48,7 @@ void main() { final event2 = SubscriptionPurchaseCompleted( subscription: const Subscription( benefits: [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_state_test.dart index 7b53f1530..128d67a5a 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/bloc/subscriptions_state_test.dart @@ -24,15 +24,12 @@ void main() { test('supports value comparisons', () { expect( SubscriptionsState.initial(), - equals( - SubscriptionsState.initial(), - ), + equals(SubscriptionsState.initial()), ); }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no parameters changed', () { expect( SubscriptionsState.initial().copyWith(), @@ -40,8 +37,7 @@ void main() { ); }); - test( - 'returns object with updated purchaseStatus ' + test('returns object with updated purchaseStatus ' 'when status changed', () { expect( SubscriptionsState.initial().copyWith( @@ -56,18 +52,14 @@ void main() { ); }); - test( - 'returns object with updated subscriptions ' + test('returns object with updated subscriptions ' 'when subscriptions changed', () { expect( SubscriptionsState.initial().copyWith( subscriptions: [ Subscription( benefits: [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), @@ -78,10 +70,7 @@ void main() { subscriptions: [ Subscription( benefits: [], - cost: SubscriptionCost( - annual: 0, - monthly: 0, - ), + cost: SubscriptionCost(annual: 0, monthly: 0), id: '1', name: SubscriptionPlan.none, ), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/view/purchase_subscription_dialog_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/view/purchase_subscription_dialog_test.dart index 4c3eb1235..15f6b85fc 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/view/purchase_subscription_dialog_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/dialog/view/purchase_subscription_dialog_test.dart @@ -19,32 +19,23 @@ void main() { const subscription = Subscription( id: 'dd339fda-33e9-49d0-9eb5-0ccb77eb760f', name: SubscriptionPlan.premium, - cost: SubscriptionCost( - annual: 16200, - monthly: 1499, - ), - benefits: [ - 'first', - 'second', - 'third', - ], + cost: SubscriptionCost(annual: 16200, monthly: 1499), + benefits: ['first', 'second', 'third'], ); setUp(() { inAppPurchaseRepository = MockInAppPurchaseRepository(); userRepository = MockUserRepository(); - when(() => inAppPurchaseRepository.purchaseUpdate).thenAnswer( - (_) => const Stream.empty(), - ); - - when(inAppPurchaseRepository.fetchSubscriptions).thenAnswer( - (_) async => [], - ); + when( + () => inAppPurchaseRepository.purchaseUpdate, + ).thenAnswer((_) => const Stream.empty()); when( - userRepository.updateSubscriptionPlan, - ).thenAnswer((_) async {}); + inAppPurchaseRepository.fetchSubscriptions, + ).thenAnswer((_) async => []); + + when(userRepository.updateSubscriptionPlan).thenAnswer((_) async {}); }); group('showPurchaseSubscriptionDialog', () { @@ -71,16 +62,15 @@ void main() { }); group('PurchaseSubscriptionDialog', () { - testWidgets( - 'renders PurchaseSubscriptionDialogView', - (WidgetTester tester) async { - await tester.pumpApp( - const PurchaseSubscriptionDialog(), - inAppPurchaseRepository: inAppPurchaseRepository, - ); - expect(find.byType(PurchaseSubscriptionDialogView), findsOneWidget); - }, - ); + testWidgets('renders PurchaseSubscriptionDialogView', ( + WidgetTester tester, + ) async { + await tester.pumpApp( + const PurchaseSubscriptionDialog(), + inAppPurchaseRepository: inAppPurchaseRepository, + ); + expect(find.byType(PurchaseSubscriptionDialogView), findsOneWidget); + }); }); group('PurchaseSubscriptionDialogView', () { @@ -88,25 +78,15 @@ void main() { const otherSubscription = Subscription( id: 'other_subscription_id', name: SubscriptionPlan.premium, - cost: SubscriptionCost( - annual: 16200, - monthly: 1499, - ), - benefits: [ - 'first', - 'second', - 'third', - ], + cost: SubscriptionCost(annual: 16200, monthly: 1499), + benefits: ['first', 'second', 'third'], ); - final subscriptions = [ - subscription, - otherSubscription, - ]; + final subscriptions = [subscription, otherSubscription]; - when(inAppPurchaseRepository.fetchSubscriptions).thenAnswer( - (_) async => subscriptions, - ); + when( + inAppPurchaseRepository.fetchSubscriptions, + ).thenAnswer((_) async => subscriptions); await tester.pumpApp( const PurchaseSubscriptionDialog(), inAppPurchaseRepository: inAppPurchaseRepository, @@ -136,32 +116,28 @@ void main() { ); await tester.pump(); await tester.tap( - find.byKey( - const Key('purchaseSubscriptionDialog_closeIconButton'), - ), + find.byKey(const Key('purchaseSubscriptionDialog_closeIconButton')), ); await tester.pump(); verify(navigator.pop).called(1); }); - testWidgets( - 'shows PurchaseCompleted dialog ' + testWidgets('shows PurchaseCompleted dialog ' 'and adds UserSubscriptionConversionEvent to AnalyticsBloc ' - 'when SubscriptionsBloc emits purchaseStatus.completed', - (tester) async { + 'when SubscriptionsBloc emits purchaseStatus.completed', ( + tester, + ) async { final navigator = MockNavigator(); final analyticsBloc = MockAnalyticsBloc(); when(navigator.canPop).thenAnswer((_) => true); when(navigator.maybePop).thenAnswer((_) async => true); - when( - () => inAppPurchaseRepository.purchaseUpdate, - ).thenAnswer( - (_) => Stream.fromIterable( - [const PurchaseDelivered(subscription: subscription)], - ), + when(() => inAppPurchaseRepository.purchaseUpdate).thenAnswer( + (_) => Stream.fromIterable([ + const PurchaseDelivered(subscription: subscription), + ]), ); await tester.pumpApp( @@ -178,9 +154,7 @@ void main() { verify( () => analyticsBloc.add( - TrackAnalyticsEvent( - UserSubscriptionConversionEvent(), - ), + TrackAnalyticsEvent(UserSubscriptionConversionEvent()), ), ).called(1); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/view/manage_subscription_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/view/manage_subscription_page_test.dart index 291d2a9fb..137ba3c48 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/view/manage_subscription_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/view/manage_subscription_page_test.dart @@ -26,8 +26,7 @@ void main() { group('ManageSubscriptionView', () { final appBloc = MockAppBloc(); - testWidgets( - 'navigates back ' + testWidgets('navigates back ' 'when subscriptions ListTile tapped', (tester) async { final navigator = MockNavigator(); when(navigator.canPop).thenAnswer((_) => true); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscribe_modal_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscribe_modal_test.dart index 5d2b4d611..6412f5183 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscribe_modal_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscribe_modal_test.dart @@ -41,9 +41,9 @@ void main() { analyticsBloc = MockAnalyticsBloc(); articleBloc = MockArticleBloc(); - when(() => articleBloc.state).thenReturn( - ArticleState(status: ArticleStatus.initial, title: 'title'), - ); + when( + () => articleBloc.state, + ).thenReturn(ArticleState(status: ArticleStatus.initial, title: 'title')); when(() => appBloc.state).thenReturn(AppState.unauthenticated()); VisibilityDetectorController.instance.updateInterval = Duration.zero; @@ -51,28 +51,24 @@ void main() { group('SubscribeModal', () { group('renders', () { - testWidgets('subscribe button when user is authenticated', - (tester) async { + testWidgets('subscribe button when user is authenticated', ( + tester, + ) async { when(() => appBloc.state).thenReturn(AppState.authenticated(user)); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: SubscribeModal(), - ), + BlocProvider.value(value: articleBloc, child: SubscribeModal()), appBloc: appBloc, ); expect(find.byKey(subscribeButtonKey), findsOneWidget); expect(find.byKey(logInButtonKey), findsNothing); }); - testWidgets('subscribe and log in buttons when user is unauthenticated', - (tester) async { + testWidgets('subscribe and log in buttons when user is unauthenticated', ( + tester, + ) async { when(() => appBloc.state).thenReturn(AppState.unauthenticated()); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: SubscribeModal(), - ), + BlocProvider.value(value: articleBloc, child: SubscribeModal()), appBloc: appBloc, ); expect(find.byKey(subscribeButtonKey), findsOneWidget); @@ -88,29 +84,25 @@ void main() { inAppPurchaseRepository = MockInAppPurchaseRepository(); articleBloc = MockArticleBloc(); - when(() => inAppPurchaseRepository.purchaseUpdate).thenAnswer( - (_) => const Stream.empty(), - ); + when( + () => inAppPurchaseRepository.purchaseUpdate, + ).thenAnswer((_) => const Stream.empty()); - when(inAppPurchaseRepository.fetchSubscriptions).thenAnswer( - (_) async => [], - ); + when( + inAppPurchaseRepository.fetchSubscriptions, + ).thenAnswer((_) async => []); when(() => articleBloc.state).thenReturn( ArticleState(status: ArticleStatus.initial, title: 'title'), ); }); - testWidgets( - 'when tapped on subscribe button ' + testWidgets('when tapped on subscribe button ' 'adding PaywallPromptEvent.click to AnalyticsBloc', (tester) async { final analyticsBloc = MockAnalyticsBloc(); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: SubscribeModal(), - ), + BlocProvider.value(value: articleBloc, child: SubscribeModal()), inAppPurchaseRepository: inAppPurchaseRepository, analyticsBloc: analyticsBloc, ); @@ -128,8 +120,7 @@ void main() { }); }); - testWidgets( - 'shows LoginModal ' + testWidgets('shows LoginModal ' 'when tapped on log in button', (tester) async { whenListen( appBloc, @@ -138,10 +129,7 @@ void main() { ); await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: SubscribeModal(), - ), + BlocProvider.value(value: articleBloc, child: SubscribeModal()), appBloc: appBloc, ); @@ -151,15 +139,11 @@ void main() { expect(find.byType(LoginModal), findsOneWidget); }); - testWidgets( - 'adds TrackAnalyticsEvent to AnalyticsBloc ' + testWidgets('adds TrackAnalyticsEvent to AnalyticsBloc ' 'with PaywallPromptEvent.impression subscription ' 'when shown', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: articleBloc, - child: SubscribeModal(), - ), + BlocProvider.value(value: articleBloc, child: SubscribeModal()), analyticsBloc: analyticsBloc, appBloc: appBloc, ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscribe_with_article_limit_modal_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscribe_with_article_limit_modal_test.dart index e918edeb8..a9164bfd8 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscribe_with_article_limit_modal_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscribe_with_article_limit_modal_test.dart @@ -10,7 +10,6 @@ import 'package:{{project_name.snakeCase()}}/article/article.dart'; import 'package:{{project_name.snakeCase()}}/login/login.dart'; import 'package:{{project_name.snakeCase()}}/subscriptions/subscriptions.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:google_mobile_ads/google_mobile_ads.dart' as ads; import 'package:in_app_purchase_repository/in_app_purchase_repository.dart'; import 'package:mockingjay/mockingjay.dart'; import 'package:user_repository/user_repository.dart'; @@ -25,10 +24,6 @@ class MockArticleBloc extends MockBloc class MockUser extends Mock implements User {} -class MockAdWithoutView extends Mock implements ads.AdWithoutView {} - -class MockRewardItem extends Mock implements ads.RewardItem {} - class MockAnalyticsBloc extends MockBloc implements AnalyticsBloc {} @@ -42,11 +37,13 @@ void main() { late AnalyticsBloc analyticsBloc; late ArticleBloc articleBloc; - const subscribeButtonKey = - Key('subscribeWithArticleLimitModal_subscribeButton'); + const subscribeButtonKey = Key( + 'subscribeWithArticleLimitModal_subscribeButton', + ); const logInButtonKey = Key('subscribeWithArticleLimitModal_logInButton'); - const watchVideoButton = - Key('subscribeWithArticleLimitModal_watchVideoButton'); + const watchVideoButton = Key( + 'subscribeWithArticleLimitModal_watchVideoButton', + ); setUp(() { user = MockUser(); @@ -56,17 +53,16 @@ void main() { when(() => appBloc.state).thenReturn(AppState.unauthenticated()); - when(() => articleBloc.state).thenReturn( - ArticleState(status: ArticleStatus.initial, title: 'title'), - ); + when( + () => articleBloc.state, + ).thenReturn(ArticleState(status: ArticleStatus.initial, title: 'title')); VisibilityDetectorController.instance.updateInterval = Duration.zero; }); group('SubscribeWithArticleLimitModal', () { group('renders', () { - testWidgets( - 'subscribe and watch video buttons ' + testWidgets('subscribe and watch video buttons ' 'when user is authenticated', (tester) async { when(() => appBloc.state).thenReturn(AppState.authenticated(user)); await tester.pumpApp( @@ -82,8 +78,7 @@ void main() { expect(find.byKey(logInButtonKey), findsNothing); }); - testWidgets( - 'subscribe log in and watch video buttons ' + testWidgets('subscribe log in and watch video buttons ' 'when user is unauthenticated', (tester) async { when(() => appBloc.state).thenReturn(AppState.unauthenticated()); await tester.pumpApp( @@ -108,21 +103,20 @@ void main() { inAppPurchaseRepository = MockInAppPurchaseRepository(); analyticsBloc = MockAnalyticsBloc(); - when(() => inAppPurchaseRepository.purchaseUpdate).thenAnswer( - (_) => const Stream.empty(), - ); + when( + () => inAppPurchaseRepository.purchaseUpdate, + ).thenAnswer((_) => const Stream.empty()); - when(inAppPurchaseRepository.fetchSubscriptions).thenAnswer( - (_) async => [], - ); + when( + inAppPurchaseRepository.fetchSubscriptions, + ).thenAnswer((_) async => []); when(() => articleBloc.state).thenReturn( ArticleState(status: ArticleStatus.initial, title: 'title'), ); }); - testWidgets( - 'when tapped on subscribe button ' + testWidgets('when tapped on subscribe button ' 'adding PaywallPromptEvent.click to AnalyticsBloc', (tester) async { await tester.pumpApp( analyticsBloc: analyticsBloc, @@ -140,17 +134,14 @@ void main() { verify( () => analyticsBloc.add( TrackAnalyticsEvent( - PaywallPromptEvent.click( - articleTitle: 'title', - ), + PaywallPromptEvent.click(articleTitle: 'title'), ), ), ).called(1); }); }); - testWidgets( - 'shows LoginModal ' + testWidgets('shows LoginModal ' 'when tapped on log in button', (tester) async { whenListen( appBloc, @@ -173,8 +164,7 @@ void main() { expect(find.byType(LoginModal), findsOneWidget); }); - testWidgets( - 'adds ShowRewardedAdRequested to FullScreenAdsBloc ' + testWidgets('adds ShowRewardedAdRequested to FullScreenAdsBloc ' 'when tapped on watch video button', (tester) async { final fullScreenAdsBloc = MockFullScreenAdsBloc(); @@ -193,8 +183,7 @@ void main() { verify(() => fullScreenAdsBloc.add(ShowRewardedAdRequested())).called(1); }); - testWidgets( - 'adds TrackAnalyticsEvent to AnalyticsBloc ' + testWidgets('adds TrackAnalyticsEvent to AnalyticsBloc ' 'with PaywallPromptEvent.impression rewarded ' 'when shown', (tester) async { await tester.pumpApp( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscription_card_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscription_card_test.dart index b68caf381..edeaca29d 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscription_card_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/subscriptions/widgets/subscription_card_test.dart @@ -26,24 +26,14 @@ void main() { const subscription = Subscription( id: 'dd339fda-33e9-49d0-9eb5-0ccb77eb760f', name: SubscriptionPlan.premium, - cost: SubscriptionCost( - annual: 16200, - monthly: 1499, - ), - benefits: [ - 'first', - 'second', - 'third', - ], + cost: SubscriptionCost(annual: 16200, monthly: 1499), + benefits: ['first', 'second', 'third'], ); group('when isExpanded is true', () { testWidgets('renders correctly', (tester) async { await tester.pumpApp( - const SubscriptionCard( - isExpanded: true, - subscription: subscription, - ), + const SubscriptionCard(isExpanded: true, subscription: subscription), ); for (final benefit in subscription.benefits) { @@ -60,8 +50,7 @@ void main() { ); }); - testWidgets( - 'adds SubscriptionPurchaseRequested to SubscriptionsBloc ' + testWidgets('adds SubscriptionPurchaseRequested to SubscriptionsBloc ' 'on subscribeButton tap ' 'when user is logged in', (tester) async { final inAppPurchaseRepository = MockInAppPurchaseRepository(); @@ -70,13 +59,13 @@ void main() { final user = MockUser(); when(() => user.subscriptionPlan).thenReturn(SubscriptionPlan.premium); - when(() => appBloc.state).thenAnswer( - (_) => AppState.authenticated(user), - ); + when( + () => appBloc.state, + ).thenAnswer((_) => AppState.authenticated(user)); - when(() => subscriptionsBloc.state).thenAnswer( - (_) => SubscriptionsState.initial(), - ); + when( + () => subscriptionsBloc.state, + ).thenAnswer((_) => SubscriptionsState.initial()); await tester.pumpApp( BlocProvider.value( @@ -94,35 +83,31 @@ void main() { inAppPurchaseRepository: inAppPurchaseRepository, ); - await tester - .tap(find.byKey(const Key('subscriptionCard_subscribe_appButton'))); + await tester.tap( + find.byKey(const Key('subscriptionCard_subscribe_appButton')), + ); await tester.pumpAndSettle(); verify( () => subscriptionsBloc.add( - SubscriptionPurchaseRequested( - subscription: subscription, - ), + SubscriptionPurchaseRequested(subscription: subscription), ), ).called(1); }); - testWidgets( - 'shows LoginModal ' + testWidgets('shows LoginModal ' 'on subscribeButton tap ' 'when user is not logged in', (tester) async { final inAppPurchaseRepository = MockInAppPurchaseRepository(); final appBloc = MockAppBloc(); final SubscriptionsBloc subscriptionsBloc = MockSubscriptionsBloc(); - when(() => appBloc.state).thenAnswer( - (_) => AppState.unauthenticated(), - ); + when(() => appBloc.state).thenAnswer((_) => AppState.unauthenticated()); - when(() => subscriptionsBloc.state).thenAnswer( - (_) => SubscriptionsState.initial(), - ); + when( + () => subscriptionsBloc.state, + ).thenAnswer((_) => SubscriptionsState.initial()); await tester.pumpApp( BlocProvider.value( @@ -140,8 +125,9 @@ void main() { inAppPurchaseRepository: inAppPurchaseRepository, ); - await tester - .tap(find.byKey(const Key('subscriptionCard_subscribe_appButton'))); + await tester.tap( + find.byKey(const Key('subscriptionCard_subscribe_appButton')), + ); await tester.pumpAndSettle(); @@ -152,9 +138,7 @@ void main() { group('when isExpanded is false', () { testWidgets('renders correctly', (tester) async { await tester.pumpApp( - const SubscriptionCard( - subscription: subscription, - ), + const SubscriptionCard(subscription: subscription), ); for (final benefit in subscription.benefits) { @@ -173,15 +157,11 @@ void main() { testWidgets('shows SnackBar on viewDetails tap', (tester) async { await tester.pumpApp( - const SubscriptionCard( - subscription: subscription, - ), + const SubscriptionCard(subscription: subscription), ); final snackBarFinder = find.byKey( - const Key( - 'subscriptionCard_unimplemented_snackBar', - ), + const Key('subscriptionCard_unimplemented_snackBar'), ); expect(snackBarFinder, findsNothing); @@ -193,13 +173,11 @@ void main() { }); }); - testWidgets('renders bestValue Icon when isBestValue is true', - (tester) async { + testWidgets('renders bestValue Icon when isBestValue is true', ( + tester, + ) async { await tester.pumpApp( - const SubscriptionCard( - isBestValue: true, - subscription: subscription, - ), + const SubscriptionCard(isBestValue: true, subscription: subscription), ); expect( find.byKey(const Key('subscriptionCard_bestValueSvg')), diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/view/terms_of_service_modal_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/view/terms_of_service_modal_test.dart index 053aa517b..0915823e1 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/view/terms_of_service_modal_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/view/terms_of_service_modal_test.dart @@ -8,8 +8,9 @@ import 'package:mockingjay/mockingjay.dart'; import '../../helpers/helpers.dart'; void main() { - const termsOfServiceModalCloseButtonKey = - Key('termsOfServiceModal_closeModal_iconButton'); + const termsOfServiceModalCloseButtonKey = Key( + 'termsOfServiceModal_closeModal_iconButton', + ); group('TermsOfServiceModal', () { group('renders', () { @@ -35,10 +36,7 @@ void main() { final navigator = MockNavigator(); when(navigator.canPop).thenAnswer((_) => true); when(navigator.pop).thenAnswer((_) async {}); - await tester.pumpApp( - TermsOfServiceModal(), - navigator: navigator, - ); + await tester.pumpApp(TermsOfServiceModal(), navigator: navigator); await tester.tap(find.byKey(termsOfServiceModalCloseButtonKey)); await tester.pumpAndSettle(); verify(navigator.pop).called(1); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/view/terms_of_service_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/view/terms_of_service_page_test.dart index 5cd53cb85..32dea05a2 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/view/terms_of_service_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/view/terms_of_service_page_test.dart @@ -22,9 +22,10 @@ void main() { body: Builder( builder: (context) { return ElevatedButton( - onPressed: () { - Navigator.of(context) - .push(TermsOfServicePage.route()); + onPressed: () async { + await Navigator.of( + context, + ).push(TermsOfServicePage.route()); }, child: const Text(tapMeText), ); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/widgets/terms_of_service_body_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/widgets/terms_of_service_body_test.dart index 3f08cdabf..e343e4941 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/widgets/terms_of_service_body_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/terms_of_service/widgets/terms_of_service_body_test.dart @@ -12,24 +12,12 @@ void main() { group('TermsOfServiceBody', () { group('renders', () { testWidgets('SingleChildScrollView', (tester) async { - await tester.pumpApp( - Column( - children: const [ - TermsOfServiceBody(), - ], - ), - ); + await tester.pumpApp(Column(children: const [TermsOfServiceBody()])); expect(find.byType(SingleChildScrollView), findsOneWidget); }); testWidgets('terms of service body text', (tester) async { - await tester.pumpApp( - Column( - children: const [ - TermsOfServiceBody(), - ], - ), - ); + await tester.pumpApp(Column(children: const [TermsOfServiceBody()])); expect(find.byKey(termsOfServiceBodyTextKey), findsOneWidget); }); }); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/bloc/user_profile_bloc_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/bloc/user_profile_bloc_test.dart index f1ee5ccfd..02afdfd09 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/bloc/user_profile_bloc_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/bloc/user_profile_bloc_test.dart @@ -2,7 +2,6 @@ import 'dart:async'; -import 'package:authentication_client/authentication_client.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:{{project_name.snakeCase()}}/user_profile/user_profile.dart'; import 'package:{{project_name.snakeCase()}}_api/client.dart' hide User; @@ -16,8 +15,6 @@ class MockUserRepository extends Mock implements UserRepository {} class MockNotificationsRepository extends Mock implements NotificationsRepository {} -class MockAuthenticationClient extends Mock implements AuthenticationClient {} - void main() { group('UserProfileBloc', () { late UserRepository userRepository; @@ -107,8 +104,9 @@ void main() { 'emits ' '[fetchingNotificationsEnabled, fetchingNotificationsEnabledSucceeded] ' 'when fetchNotificationsEnabled succeeds', - setUp: () => when(notificationsRepository.fetchNotificationsEnabled) - .thenAnswer((_) async => true), + setUp: () => when( + notificationsRepository.fetchNotificationsEnabled, + ).thenAnswer((_) async => true), build: () => UserProfileBloc( userRepository: userRepository, notificationsRepository: notificationsRepository, @@ -129,8 +127,9 @@ void main() { 'emits ' '[fetchingNotificationsEnabled, fetchingNotificationsEnabledFailed] ' 'when fetchNotificationsEnabled fails', - setUp: () => when(notificationsRepository.fetchNotificationsEnabled) - .thenThrow(Exception()), + setUp: () => when( + notificationsRepository.fetchNotificationsEnabled, + ).thenThrow(Exception()), build: () => UserProfileBloc( userRepository: userRepository, notificationsRepository: notificationsRepository, @@ -161,9 +160,8 @@ void main() { '[togglingNotifications, togglingNotificationsSucceeded] ' 'when notifications are enabled ' 'and toggleNotifications succeeds', - seed: () => UserProfileState.initial().copyWith( - notificationsEnabled: true, - ), + seed: () => + UserProfileState.initial().copyWith(notificationsEnabled: true), build: () => UserProfileBloc( userRepository: userRepository, notificationsRepository: notificationsRepository, @@ -189,9 +187,8 @@ void main() { '[togglingNotifications, togglingNotificationsSucceeded] ' 'when notifications are disabled ' 'and toggleNotifications succeeds', - seed: () => UserProfileState.initial().copyWith( - notificationsEnabled: false, - ), + seed: () => + UserProfileState.initial().copyWith(notificationsEnabled: false), build: () => UserProfileBloc( userRepository: userRepository, notificationsRepository: notificationsRepository, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/bloc/user_profile_state_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/bloc/user_profile_state_test.dart index e965644ba..7f7334b96 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/bloc/user_profile_state_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/bloc/user_profile_state_test.dart @@ -14,15 +14,11 @@ void main() { }); test('supports value comparisons', () { - expect( - UserProfileState.initial(), - equals(UserProfileState.initial()), - ); + expect(UserProfileState.initial(), equals(UserProfileState.initial())); }); group('copyWith', () { - test( - 'returns same object ' + test('returns same object ' 'when no properties are passed', () { expect( UserProfileState.initial().copyWith(), @@ -30,8 +26,7 @@ void main() { ); }); - test( - 'returns object with updated status ' + test('returns object with updated status ' 'when status is passed', () { expect( UserProfileState.initial().copyWith( @@ -46,13 +41,10 @@ void main() { ); }); - test( - 'returns object with updated notificationsEnabled ' + test('returns object with updated notificationsEnabled ' 'when notificationsEnabled is passed', () { expect( - UserProfileState.initial().copyWith( - notificationsEnabled: true, - ), + UserProfileState.initial().copyWith(notificationsEnabled: true), equals( UserProfileState( user: User.anonymous, @@ -63,13 +55,10 @@ void main() { ); }); - test( - 'returns object with updated user ' + test('returns object with updated user ' 'when user is passed', () { expect( - UserProfileState.initial().copyWith( - user: User.anonymous, - ), + UserProfileState.initial().copyWith(user: User.anonymous), equals( UserProfileState( status: UserProfileStatus.initial, diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/view/user_profile_page_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/view/user_profile_page_test.dart index f3eecddca..bb27871ff 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/view/user_profile_page_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/view/user_profile_page_test.dart @@ -70,29 +70,24 @@ void main() { whenListen( appBloc, - Stream.fromIterable( - [AppState.unauthenticated()], - ), + Stream.fromIterable([AppState.unauthenticated()]), initialState: AppState.authenticated(user), ); }); - testWidgets( - 'adds FetchNotificationsEnabled to UserProfileBloc ' + testWidgets('adds FetchNotificationsEnabled to UserProfileBloc ' 'when initialized and each time the app is resumed', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), ); verify( () => userProfileBloc.add(FetchNotificationsEnabled()), ).called(1); - tester.binding - .handleAppLifecycleStateChanged(AppLifecycleState.resumed); + tester.binding.handleAppLifecycleStateChanged( + AppLifecycleState.resumed, + ); verify( () => userProfileBloc.add(FetchNotificationsEnabled()), @@ -107,14 +102,10 @@ void main() { ).called(1); }); - testWidgets( - 'navigates back ' + testWidgets('navigates back ' 'when app back button is pressed', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), ); await tester.tap(find.byType(AppBackButton)); @@ -123,14 +114,10 @@ void main() { expect(find.byType(UserProfileView), findsNothing); }); - testWidgets( - 'navigates back ' + testWidgets('navigates back ' 'when user is unauthenticated', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), appBloc: appBloc, ); @@ -139,8 +126,7 @@ void main() { expect(find.byType(UserProfileView), findsNothing); }); - testWidgets( - 'adds TrackAnalyticsEvent to AnalyticsBloc ' + testWidgets('adds TrackAnalyticsEvent to AnalyticsBloc ' 'with PushNotificationSubscriptionEvent ' 'when status is togglingNotificationsSucceeded ' 'and notificationsEnabled is true', (tester) async { @@ -175,24 +161,15 @@ void main() { testWidgets('renders UserProfileTitle', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), ); - expect( - find.byType(UserProfileTitle), - findsOneWidget, - ); + expect(find.byType(UserProfileTitle), findsOneWidget); }); testWidgets('renders user email', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), ); expect( @@ -203,14 +180,10 @@ void main() { ); }); - testWidgets( - 'renders notifications item ' + testWidgets('renders notifications item ' 'with trailing AppSwitch', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), ); expect( @@ -232,15 +205,12 @@ void main() { ); }); - group( - 'renders UserProfileSubscribeBox ' + group('renders UserProfileSubscribeBox ' 'when isUserSubscribed is false', () { testWidgets('correctly', (tester) async { whenListen( appBloc, - Stream.fromIterable([ - AppState.authenticated(user), - ]), + Stream.fromIterable([AppState.authenticated(user)]), ); await tester.pumpApp( @@ -254,22 +224,21 @@ void main() { expect(find.byType(UserProfileSubscribeBox), findsOneWidget); }); - testWidgets('opens PurchaseSubscriptionDialog when tapped', - (tester) async { + testWidgets('opens PurchaseSubscriptionDialog when tapped', ( + tester, + ) async { whenListen( appBloc, - Stream.fromIterable([ - AppState.authenticated(user), - ]), + Stream.fromIterable([AppState.authenticated(user)]), ); - when(() => inAppPurchaseRepository.purchaseUpdate).thenAnswer( - (_) => const Stream.empty(), - ); + when( + () => inAppPurchaseRepository.purchaseUpdate, + ).thenAnswer((_) => const Stream.empty()); - when(inAppPurchaseRepository.fetchSubscriptions).thenAnswer( - (invocation) async => [], - ); + when( + inAppPurchaseRepository.fetchSubscriptions, + ).thenAnswer((invocation) async => []); await tester.pumpApp( inAppPurchaseRepository: inAppPurchaseRepository, @@ -279,27 +248,21 @@ void main() { child: UserProfileView(), ), ); - final subscriptionButton = - find.byKey(Key('userProfileSubscribeBox_appButton')); + final subscriptionButton = find.byKey( + Key('userProfileSubscribeBox_appButton'), + ); await tester.scrollUntilVisible(subscriptionButton, -50); await tester.tap(subscriptionButton); await tester.pump(); - expect( - find.byType(PurchaseSubscriptionDialog), - findsOneWidget, - ); + expect(find.byType(PurchaseSubscriptionDialog), findsOneWidget); }); }); - testWidgets( - 'renders notification preferences item ' + testWidgets('renders notification preferences item ' 'with trailing Icon', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), ); expect( find.byWidgetPredicate( @@ -313,13 +276,11 @@ void main() { ); }); - testWidgets('renders terms of use and privacy policy item', - (tester) async { + testWidgets('renders terms of use and privacy policy item', ( + tester, + ) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), ); expect( find.byWidgetPredicate( @@ -333,10 +294,7 @@ void main() { testWidgets('renders about item', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), ); expect( find.byWidgetPredicate( @@ -348,14 +306,10 @@ void main() { ); }); - testWidgets( - 'adds ToggleNotifications to UserProfileBloc ' + testWidgets('adds ToggleNotifications to UserProfileBloc ' 'when notifications item trailing is tapped', (tester) async { await tester.pumpApp( - BlocProvider.value( - value: userProfileBloc, - child: UserProfileView(), - ), + BlocProvider.value(value: userProfileBloc, child: UserProfileView()), ); await tester.tap(find.byType(AppSwitch)); verify(() => userProfileBloc.add(ToggleNotifications())).called(1); @@ -363,11 +317,7 @@ void main() { group('UserProfileItem', () { testWidgets('renders ListTile', (tester) async { - await tester.pumpApp( - UserProfileItem( - title: 'title', - ), - ); + await tester.pumpApp(UserProfileItem(title: 'title')); expect(find.widgetWithText(ListTile, 'title'), findsOneWidget); }); @@ -401,10 +351,7 @@ void main() { testWidgets('calls onTap when tapped', (tester) async { var tapped = false; await tester.pumpApp( - UserProfileItem( - title: 'title', - onTap: () => tapped = true, - ), + UserProfileItem(title: 'title', onTap: () => tapped = true), ); await tester.tap(find.byType(UserProfileItem)); @@ -419,13 +366,9 @@ void main() { expect(find.byType(AppButton), findsOneWidget); }); - testWidgets( - 'adds AppLogoutRequested to AppBloc ' + testWidgets('adds AppLogoutRequested to AppBloc ' 'when tapped', (tester) async { - await tester.pumpApp( - UserProfileLogoutButton(), - appBloc: appBloc, - ); + await tester.pumpApp(UserProfileLogoutButton(), appBloc: appBloc); await tester.tap(find.byType(UserProfileLogoutButton)); @@ -434,8 +377,9 @@ void main() { }); group('navigates', () { - testWidgets('when tapped on Terms of User & Privacy Policy', - (tester) async { + testWidgets('when tapped on Terms of User & Privacy Policy', ( + tester, + ) async { await tester.pumpApp( BlocProvider.value( value: userProfileBloc, @@ -459,8 +403,7 @@ void main() { expect(find.byType(TermsOfServicePage), findsOneWidget); }); - testWidgets( - 'to ManageSubscriptionPage ' + testWidgets('to ManageSubscriptionPage ' 'when isUserSubscribed is true and ' 'tapped on Manage Subscription', (tester) async { final subscribedUser = User( @@ -481,8 +424,9 @@ void main() { ), ); - final subscriptionItem = - find.byKey(Key('userProfilePage_subscriptionItem')); + final subscriptionItem = find.byKey( + Key('userProfilePage_subscriptionItem'), + ); await tester.ensureVisible(subscriptionItem); await tester.tap(subscriptionItem); await tester.pumpAndSettle(); @@ -490,8 +434,7 @@ void main() { expect(find.byType(ManageSubscriptionPage), findsOneWidget); }); - testWidgets( - 'to NotificationPreferencesPage ' + testWidgets('to NotificationPreferencesPage ' 'when tapped on NotificationPreferences', (tester) async { await tester.pumpApp( BlocProvider.value( @@ -520,8 +463,7 @@ void main() { }); group('shows', () { - testWidgets( - 'UserProfileDeleteAccountDialog ' + testWidgets('UserProfileDeleteAccountDialog ' 'when tapped on Delete account', (tester) async { await tester.pumpApp( BlocProvider.value( diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_button_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_button_test.dart index eff88122c..d99ab9daa 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_button_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_button_test.dart @@ -14,10 +14,6 @@ import '../../helpers/helpers.dart'; class MockAppBloc extends MockBloc implements AppBloc {} -class MockUser extends Mock implements User {} - -class MockNavigatorObserver extends Mock implements NavigatorObserver {} - class MockRoute extends Mock implements Route {} void main() { @@ -34,8 +30,7 @@ void main() { registerFallbackValue(MockRoute()); }); - testWidgets( - 'renders LoginButton ' + testWidgets('renders LoginButton ' 'when user is unauthenticated', (tester) async { whenListen( appBloc, @@ -43,17 +38,13 @@ void main() { initialState: AppState.unauthenticated(), ); - await tester.pumpApp( - UserProfileButton(), - appBloc: appBloc, - ); + await tester.pumpApp(UserProfileButton(), appBloc: appBloc); expect(find.byType(LoginButton), findsOneWidget); expect(find.byType(OpenProfileButton), findsNothing); }); - testWidgets( - 'renders OpenProfileButton ' + testWidgets('renders OpenProfileButton ' 'when user is authenticated', (tester) async { whenListen( appBloc, @@ -61,17 +52,13 @@ void main() { initialState: AppState.authenticated(user), ); - await tester.pumpApp( - UserProfileButton(), - appBloc: appBloc, - ); + await tester.pumpApp(UserProfileButton(), appBloc: appBloc); expect(find.byType(OpenProfileButton), findsOneWidget); expect(find.byType(LoginButton), findsNothing); }); - testWidgets( - 'navigates to UserProfilePage ' + testWidgets('navigates to UserProfilePage ' 'when tapped on OpenProfileButton', (tester) async { whenListen( appBloc, @@ -79,10 +66,7 @@ void main() { initialState: AppState.authenticated(user), ); - await tester.pumpApp( - UserProfileButton(), - appBloc: appBloc, - ); + await tester.pumpApp(UserProfileButton(), appBloc: appBloc); await tester.tap(find.byType(OpenProfileButton)); await tester.pumpAndSettle(); @@ -90,8 +74,7 @@ void main() { expect(find.byType(UserProfilePage), findsOneWidget); }); - testWidgets( - 'renders LoginButton ' + testWidgets('renders LoginButton ' 'when user is unauthenticated', (tester) async { whenListen( appBloc, @@ -99,17 +82,13 @@ void main() { initialState: AppState.unauthenticated(), ); - await tester.pumpApp( - UserProfileButton(), - appBloc: appBloc, - ); + await tester.pumpApp(UserProfileButton(), appBloc: appBloc); expect(find.byType(LoginButton), findsOneWidget); expect(find.byType(OpenProfileButton), findsNothing); }); - testWidgets( - 'shows LoginModal ' + testWidgets('shows LoginModal ' 'when tapped on LoginButton', (tester) async { whenListen( appBloc, @@ -117,10 +96,7 @@ void main() { initialState: AppState.unauthenticated(), ); - await tester.pumpApp( - UserProfileButton(), - appBloc: appBloc, - ); + await tester.pumpApp(UserProfileButton(), appBloc: appBloc); await tester.tap(find.byType(LoginButton)); await tester.pumpAndSettle(); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_delete_account_dialog_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_delete_account_dialog_test.dart index de4538204..76637eb7e 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_delete_account_dialog_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_delete_account_dialog_test.dart @@ -23,10 +23,7 @@ void main() { }); testWidgets('renders cancel and delete account buttons', (tester) async { - await tester.pumpApp( - UserProfileDeleteAccountDialog(), - appBloc: appBloc, - ); + await tester.pumpApp(UserProfileDeleteAccountDialog(), appBloc: appBloc); expect(cancelButton, findsOneWidget); expect(deleteAccountButton, findsOneWidget); @@ -50,8 +47,7 @@ void main() { verify(navigator.pop).called(1); }); - testWidgets( - 'adds AppDeleteAccountRequested to AppBloc and closes dialog ' + testWidgets('adds AppDeleteAccountRequested to AppBloc and closes dialog ' 'when delete account button is pressed', (tester) async { final navigator = MockNavigator(); when(navigator.canPop).thenAnswer((_) => true); diff --git a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_subscribe_box_test.dart b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_subscribe_box_test.dart index 897dff42a..43e4fe6bb 100644 --- a/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_subscribe_box_test.dart +++ b/flutter_news_template/__brick__/{{project_name.snakeCase()}}/test/user_profile/widgets/user_profile_subscribe_box_test.dart @@ -10,8 +10,9 @@ import '../../helpers/helpers.dart'; void main() { group('UserProfileSubscribeBox', () { - testWidgets('calls onSubscribePressed when AppButton tapped', - (tester) async { + testWidgets('calls onSubscribePressed when AppButton tapped', ( + tester, + ) async { final completer = Completer(); await tester.pumpApp( UserProfileSubscribeBox(onSubscribePressed: completer.complete),