diff --git a/all_lint_rules.yaml b/all_lint_rules.yaml new file mode 100644 index 00000000..0fd01adc --- /dev/null +++ b/all_lint_rules.yaml @@ -0,0 +1,194 @@ +linter: + rules: + - always_declare_return_types + - always_put_control_body_on_new_line + - always_put_required_named_parameters_first + - always_require_non_null_named_parameters + # - always_specify_types + - always_use_package_imports + - annotate_overrides + - avoid_annotating_with_dynamic + - avoid_bool_literals_in_conditional_expressions + - avoid_catches_without_on_clauses + - avoid_catching_errors + - avoid_classes_with_only_static_members + - avoid_double_and_int_checks + - avoid_dynamic_calls + - avoid_empty_else + - avoid_equals_and_hash_code_on_mutable_classes + - avoid_escaping_inner_quotes + - avoid_field_initializers_in_const_classes + - avoid_function_literals_in_foreach_calls + - avoid_implementing_value_types + - avoid_init_to_null + - avoid_js_rounded_ints + - avoid_multiple_declarations_per_line + - avoid_null_checks_in_equality_operators + - avoid_positional_boolean_parameters + - avoid_print + - avoid_private_typedef_functions + - avoid_redundant_argument_values + - avoid_relative_lib_imports + - avoid_renaming_method_parameters + - avoid_return_types_on_setters + - avoid_returning_null + - avoid_returning_null_for_future + - avoid_returning_null_for_void + - avoid_returning_this + - avoid_setters_without_getters + - avoid_shadowing_type_parameters + - avoid_single_cascade_in_expression_statements + - avoid_slow_async_io + - avoid_type_to_string + - avoid_types_as_parameter_names + - avoid_types_on_closure_parameters + - avoid_unnecessary_containers + - avoid_unused_constructor_parameters + - avoid_void_async + - avoid_web_libraries_in_flutter + - await_only_futures + - camel_case_extensions + - camel_case_types + - cancel_subscriptions + - cascade_invocations + - cast_nullable_to_non_nullable + - close_sinks + - comment_references + - constant_identifier_names + - control_flow_in_finally + - curly_braces_in_flow_control_structures + - depend_on_referenced_packages + - deprecated_consistency + - diagnostic_describe_all_properties + - directives_ordering + - do_not_use_environment + - empty_catches + - empty_constructor_bodies + - empty_statements + - eol_at_end_of_file + - exhaustive_cases + - file_names + - flutter_style_todos + - hash_and_equals + - implementation_imports + - invariant_booleans + - iterable_contains_unrelated_type + - join_return_with_assignment + - leading_newlines_in_multiline_strings + - library_names + - library_prefixes + - library_private_types_in_public_api + - lines_longer_than_80_chars + - list_remove_unrelated_type + - literal_only_boolean_expressions + - missing_whitespace_between_adjacent_strings + - no_adjacent_strings_in_list + - no_default_cases + - no_duplicate_case_values + - no_logic_in_create_state + - no_runtimeType_toString + - non_constant_identifier_names + - noop_primitive_operations + - null_check_on_nullable_type_parameter + - null_closures + - omit_local_variable_types + - one_member_abstracts + - only_throw_errors + - overridden_fields + - package_api_docs + - package_names + - package_prefixed_library_names + - parameter_assignments + - prefer_adjacent_string_concatenation + - prefer_asserts_in_initializer_lists + - prefer_asserts_with_message + - prefer_collection_literals + - prefer_conditional_assignment + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_constructors_over_static_methods + - prefer_contains + # - prefer_double_quotes + - prefer_equal_for_default_values + - prefer_expression_function_bodies + - prefer_final_fields + - prefer_final_in_for_each + - prefer_final_locals + - prefer_final_parameters + - prefer_for_elements_to_map_fromIterable + - prefer_foreach + - prefer_function_declarations_over_variables + - prefer_generic_function_type_aliases + - prefer_if_elements_to_conditional_expressions + - prefer_if_null_operators + - prefer_initializing_formals + - prefer_inlined_adds + - prefer_int_literals + - prefer_interpolation_to_compose_strings + - prefer_is_empty + - prefer_is_not_empty + - prefer_is_not_operator + - prefer_iterable_whereType + - prefer_mixin + - prefer_null_aware_method_calls + - prefer_null_aware_operators + - prefer_relative_imports + - prefer_single_quotes + - prefer_spread_collections + - prefer_typing_uninitialized_variables + - prefer_void_to_null + - provide_deprecation_message + - public_member_api_docs + - recursive_getters + - require_trailing_commas + - sized_box_for_whitespace + - slash_for_doc_comments + - sort_child_properties_last + - sort_constructors_first + - sort_pub_dependencies + - sort_unnamed_constructors_first + - test_types_in_equals + - throw_in_finally + - tighten_type_of_initializing_formals + - type_annotate_public_apis + - type_init_formals + - unawaited_futures + - unnecessary_await_in_return + - unnecessary_brace_in_string_interps + - unnecessary_const + # - unnecessary_constructor_name + # - unnecessary_final + - unnecessary_getters_setters + - unnecessary_lambdas + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_checks + - unnecessary_null_in_if_null_operators + - unnecessary_nullable_for_final_variable_declarations + - unnecessary_overrides + - unnecessary_parenthesis + - unnecessary_raw_strings + - unnecessary_statements + - unnecessary_string_escapes + - unnecessary_string_interpolations + - unnecessary_this + - unrelated_type_equality_checks + - unsafe_html + - use_build_context_synchronously + - use_full_hex_values_for_flutter_colors + - use_function_type_syntax_for_parameters + - use_if_null_to_convert_nulls_to_bools + - use_is_even_rather_than_modulo + - use_key_in_widget_constructors + - use_late_for_private_fields_and_variables + - use_named_constants + - use_raw_strings + - use_rethrow_when_possible + - use_setters_to_change_properties + - use_string_buffers + - use_test_throws_matchers + - use_to_and_as_if_applicable + - valid_regexps + - void_checks diff --git a/analysis_options.yaml b/analysis_options.yaml index e88b5265..fea21b5a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:flutter_lints/flutter.yaml +include: all_lint_rules.yaml analyzer: errors: missing_required_param: error @@ -14,48 +14,32 @@ analyzer: - '**/*.g.dart' - '**/*.gr.dart' - '**/*.freezed.dart' + - '**/*.config.dart' linter: rules: - always_declare_return_types: true - annotate_overrides: true - avoid_dynamic_calls: true - avoid_types_on_closure_parameters: true - avoid_void_async: true - avoid_web_libraries_in_flutter: true - await_only_futures: true - camel_case_types: true - cancel_subscriptions: true - close_sinks: true - constant_identifier_names: true - control_flow_in_finally: true - directives_ordering: true - empty_statements: true - file_names: true - hash_and_equals: true - implementation_imports: true - invariant_booleans: true - iterable_contains_unrelated_type: true - library_prefixes: true - list_remove_unrelated_type: true - literal_only_boolean_expressions: true - non_constant_identifier_names: true - one_member_abstracts: true - only_throw_errors: true - overridden_fields: true - package_api_docs: true - package_names: true - package_prefixed_library_names: true - prefer_const_constructors: true - prefer_const_literals_to_create_immutables: true - prefer_final_in_for_each: true - prefer_final_locals: true - prefer_mixin: true - prefer_relative_imports: true - sort_unnamed_constructors_first: true - test_types_in_equals: true - throw_in_finally: true - type_annotate_public_apis: true - unawaited_futures: true - unnecessary_brace_in_string_interps: true - unnecessary_getters_setters: true - unnecessary_statements: true + + # too verbose + prefer_final_parameters: false + + # Too verbose with little value, and this is taken care of by the Flutter devtool anyway. + diagnostic_describe_all_properties: false + + # Conflicts with `prefer_single_quotes` + # Single quotes are easier to type and don't compromise on readability. + prefer_double_quotes: false + + # This project doesn't use Flutter-style todos + flutter_style_todos: false + + # Boring as it sometimes force a line of 81 characters to be split in two. + # As long as we try to respect that 80 characters limit, going slightly + # above is fine. + lines_longer_than_80_chars: false + + # Conflicts with disabling `implicit-dynamic` + avoid_annotating_with_dynamic: false + + # conflicts with `prefer_relative_imports` + always_use_package_imports: false + + public_member_api_docs: false diff --git a/lib/app.dart b/lib/app.dart index cef5ea23..3c15c8ca 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,6 +1,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; diff --git a/lib/authentication/authentication_controller.dart b/lib/authentication/authentication_controller.dart index 5e0bc94f..41fa7b74 100644 --- a/lib/authentication/authentication_controller.dart +++ b/lib/authentication/authentication_controller.dart @@ -7,9 +7,9 @@ import '../user/user_repository_impl.dart'; import 'authentication_state.dart'; final authenticationProvider = - StateNotifierProvider((ref) { - return AuthenticationController(ref.read); -}); + StateNotifierProvider( + (ref) => AuthenticationController(ref.read), +); class AuthenticationController extends StateNotifier { AuthenticationController(this._read) : super(AuthenticationState.loading()) { @@ -20,7 +20,7 @@ class AuthenticationController extends StateNotifier { await FirebaseAuth.instance.signInAnonymously(); } state = AuthenticationState.authenticated(); - } catch (_) { + } on FirebaseException catch (_) { state = AuthenticationState.unauthenticated(); } }); @@ -48,7 +48,8 @@ class AuthenticationController extends StateNotifier { Future signInWithFacebook() async { try { final loginResult = await _facebookLogin.login( - loginBehavior: LoginBehavior.nativeWithFallback); + loginBehavior: LoginBehavior.nativeWithFallback, + ); final credential = FacebookAuthProvider.credential(loginResult.accessToken!.token); await _tryToLinkWithCurrentUser(credential); diff --git a/lib/authentication/authentication_widget.dart b/lib/authentication/authentication_widget.dart index 1b9cccc5..5c1d3e72 100644 --- a/lib/authentication/authentication_widget.dart +++ b/lib/authentication/authentication_widget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -10,60 +11,59 @@ class AuthenticationWidget extends ConsumerWidget { }) : super(key: key); @override - Widget build(BuildContext context, WidgetRef ref) { - return Card( + Widget build(BuildContext context, WidgetRef ref) => Card( color: Colors.transparent, elevation: 0, child: Container( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - OutlinedButton( - onPressed: () { - ref - .watch(authenticationProvider.notifier) - .signInWithGoogle(); - }, - child: Padding( - padding: const EdgeInsets.all(8), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Image( - image: - AssetImage('assets/images/img_google.png')), - const SizedBox(width: 8), - Text(L10n.of(context)!.txt_button_sign_in_google, - style: Theme.of(context).textTheme.subtitle1) - ], + padding: const EdgeInsets.all(8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + OutlinedButton( + onPressed: () { + ref.watch(authenticationProvider.notifier).signInWithGoogle(); + }, + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + children: [ + const Image( + image: AssetImage('assets/images/img_google.png'), ), - ), + const SizedBox(width: 8), + Text( + L10n.of(context)!.txt_button_sign_in_google, + style: Theme.of(context).textTheme.subtitle1, + ) + ], ), - const SizedBox(height: 8), - OutlinedButton( - onPressed: () { - ref - .watch(authenticationProvider.notifier) - .signInWithFacebook(); - }, - child: Padding( - padding: const EdgeInsets.all(8), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Image( - image: - AssetImage('assets/images/img_facebook.png')), - const SizedBox(width: 8), - Text(L10n.of(context)!.txt_button_sign_in_facebook, - style: Theme.of(context).textTheme.subtitle1) - ], + ), + ), + const SizedBox(height: 8), + OutlinedButton( + onPressed: () { + ref + .watch(authenticationProvider.notifier) + .signInWithFacebook(); + }, + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + children: [ + const Image( + image: AssetImage('assets/images/img_facebook.png'), ), - ), + const SizedBox(width: 8), + Text( + L10n.of(context)!.txt_button_sign_in_facebook, + style: Theme.of(context).textTheme.subtitle1, + ) + ], ), - ]))); - } + ), + ), + ], + ), + ), + ); } diff --git a/lib/firebase_cache_manager.dart b/lib/firebase_cache_manager.dart index d2cc3306..334de459 100644 --- a/lib/firebase_cache_manager.dart +++ b/lib/firebase_cache_manager.dart @@ -3,12 +3,10 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'firebase_http_file_service.dart'; class FirebaseCacheManager extends CacheManager { - static final FirebaseCacheManager _instance = FirebaseCacheManager._(); - - factory FirebaseCacheManager.getInstance() { - return _instance; - } + factory FirebaseCacheManager.getInstance() => _instance; FirebaseCacheManager._() : super(Config('firebaseCache', fileService: FirebaseHttpFileService())); + + static final FirebaseCacheManager _instance = FirebaseCacheManager._(); } diff --git a/lib/firebase_http_file_service.dart b/lib/firebase_http_file_service.dart index 07a98857..feee82ab 100644 --- a/lib/firebase_http_file_service.dart +++ b/lib/firebase_http_file_service.dart @@ -6,8 +6,10 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart'; /// standard [HttpFileService]. class FirebaseHttpFileService extends HttpFileService { @override - Future get(String url, - {Map? headers = const {}}) async { + Future get( + String url, { + Map? headers = const {}, + }) async { final ref = FirebaseStorage.instance.ref().child(url); final _url = await ref.getDownloadURL(); diff --git a/lib/flavors.dart b/lib/flavors.dart index b790b606..fae274ae 100644 --- a/lib/flavors.dart +++ b/lib/flavors.dart @@ -4,6 +4,7 @@ enum Flavor { prod, } +// ignore: avoid_classes_with_only_static_members class F { static Flavor? appFlavor; @@ -15,7 +16,7 @@ class F { return 'Hit Notes Stage'; case Flavor.prod: return 'Hit Notes'; - default: + case null: return 'title'; } } diff --git a/lib/game/center_text_paint.dart b/lib/game/center_text_paint.dart index 9187c883..d0316474 100644 --- a/lib/game/center_text_paint.dart +++ b/lib/game/center_text_paint.dart @@ -11,12 +11,14 @@ class CenterTextPaint extends TextPaint { TextDirection textDirection = TextDirection.ltr, double? lineHeight, }) : super( - config: TextPaintConfig( - fontSize: fontSize, - color: color, - fontFamily: fontFamily, - textDirection: textDirection, - lineHeight: lineHeight)); + config: TextPaintConfig( + fontSize: fontSize, + color: color, + fontFamily: fontFamily, + textDirection: textDirection, + lineHeight: lineHeight, + ), + ); @override void render( @@ -27,9 +29,9 @@ class CenterTextPaint extends TextPaint { }) { final tp = toTextPainter(text); final translatedPosition = - anchor.translate(p, Vector2(tp.size.width, tp.size.height)); - translatedPosition.x -= tp.size.width / 2; - translatedPosition.y -= tp.size.height / 2; + anchor.translate(p, Vector2(tp.size.width, tp.size.height)) + ..x -= tp.size.width / 2 + ..y -= tp.size.height / 2; tp.paint(canvas, translatedPosition.toOffset()); } } diff --git a/lib/game/complete_widget.dart b/lib/game/complete_widget.dart index 0c836509..bb052528 100644 --- a/lib/game/complete_widget.dart +++ b/lib/game/complete_widget.dart @@ -1,48 +1,57 @@ import 'package:flutter/material.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'game_reward.dart'; class CompleteWidget extends StatelessWidget { + const CompleteWidget({ + required this.gameReward, + required this.onRestart, + Key? key, + }) : super(key: key); + final GameReward gameReward; final Function() onRestart; - const CompleteWidget( - {Key? key, required this.gameReward, required this.onRestart}) - : super(key: key); - @override - Widget build(BuildContext context) { - return Material( - child: SafeArea( - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: Align( - alignment: Alignment.center, + Widget build(BuildContext context) => Material( + child: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: Align( child: Column( - mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image( - image: AssetImage((gameReward.stars >= 1) + image: AssetImage( + (gameReward.stars >= 1) ? 'assets/images/img_star_rate.png' - : 'assets/images/img_star_rate_disable.png')), + : 'assets/images/img_star_rate_disable.png', + ), + ), const SizedBox(width: 8), Image( - image: AssetImage((gameReward.stars >= 2) + image: AssetImage( + (gameReward.stars >= 2) ? 'assets/images/img_star_rate.png' - : 'assets/images/img_star_rate_disable.png')), + : 'assets/images/img_star_rate_disable.png', + ), + ), const SizedBox(width: 8), Image( - image: AssetImage((gameReward.stars >= 3) + image: AssetImage( + (gameReward.stars >= 3) ? 'assets/images/img_star_rate.png' - : 'assets/images/img_star_rate_disable.png')), + : 'assets/images/img_star_rate_disable.png', + ), + ), ], ), const SizedBox(height: 16), @@ -50,40 +59,40 @@ class CompleteWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const Image( - image: AssetImage('assets/images/img_note.png')), + image: AssetImage('assets/images/img_note.png'), + ), const SizedBox(width: 8), Text(gameReward.playedNotes.toString()) ], ), ], - )), - ), - Row( - children: [ - const SizedBox(width: 8), - Expanded( - child: ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }, - child: Text(L10n.of(context)!.txt_button_quit), ), ), - const SizedBox(width: 8), - Expanded( - child: ElevatedButton( + ), + Row( + children: [ + const SizedBox(width: 8), + Expanded( + child: ElevatedButton( onPressed: () { - onRestart(); + Navigator.of(context).pop(); + Navigator.of(context).pop(); }, - child: Text(L10n.of(context)!.txt_button_restart)), - ), - const SizedBox(width: 8) - ], - ) - ], + child: Text(L10n.of(context)!.txt_button_quit), + ), + ), + const SizedBox(width: 8), + Expanded( + child: ElevatedButton( + onPressed: onRestart, + child: Text(L10n.of(context)!.txt_button_restart), + ), + ), + const SizedBox(width: 8) + ], + ) + ], + ), ), - ), - ); - } + ); } diff --git a/lib/game/effect.dart b/lib/game/effect.dart index 91e400b8..7fc8efa8 100644 --- a/lib/game/effect.dart +++ b/lib/game/effect.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; abstract class Effect { - bool isDone() { - return false; - } + bool isDone() => false; void update(double delta) {} diff --git a/lib/game/game_page.dart b/lib/game/game_page.dart index b06d2f42..f421b547 100644 --- a/lib/game/game_page.dart +++ b/lib/game/game_page.dart @@ -1,5 +1,7 @@ import 'package:flame/game.dart' as flame; import 'package:flutter/material.dart'; + +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -11,9 +13,12 @@ import 'game_state.dart'; import 'pause_dialog.dart'; class GamePage extends ConsumerWidget { - final Map arguments; + const GamePage({ + required this.arguments, + Key? key, + }) : super(key: key); - const GamePage({Key? key, required this.arguments}) : super(key: key); + final Map arguments; @override Widget build(BuildContext context, WidgetRef ref) { @@ -48,11 +53,16 @@ class GamePage extends ConsumerWidget { return const LoadingGiftWidget(); } else if (gameState is GameStateCompleted) { return CompleteWidget( - gameReward: gameState.gameReward, onRestart: _onRestart); + gameReward: gameState.gameReward, + onRestart: _onRestart, + ); } else if (gameState is GameStatePlaying) { - return Stack(children: [ - flame.GameWidget(game: ref.read(gameStateProvider.notifier).game), - Align( + return Stack( + children: [ + flame.GameWidget( + game: ref.read(gameStateProvider.notifier).game, + ), + Align( alignment: Alignment.topCenter, child: SafeArea( child: Column( @@ -77,19 +87,21 @@ class GamePage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text((gameState).songName, - style: Theme.of(context) - .textTheme - .headline6), + Text( + gameState.songName, + style: + Theme.of(context).textTheme.headline6, + ), Consumer( builder: (context, ref, child) { final time = ref.watch(timeProvider).state; return Text( - '${time.toInt() ~/ 60}:${(time.toInt() % 60).toString().padLeft(2, '0')}/${gameState.duration.toInt() ~/ 60}:${(gameState.duration.toInt() % 60).toString().padLeft(2, '0')}', - style: Theme.of(context) - .textTheme - .headline6); + '${time.toInt() ~/ 60}:${(time.toInt() % 60).toString().padLeft(2, '0')}/${gameState.duration ~/ 60}:${(gameState.duration % 60).toString().padLeft(2, '0')}', + style: Theme.of(context) + .textTheme + .headline6, + ); }, ), ], @@ -101,7 +113,8 @@ class GamePage extends ConsumerWidget { IconButton( iconSize: 38, icon: const Icon( - Icons.pause_circle_outline_rounded), + Icons.pause_circle_outline_rounded, + ), onPressed: () { ref.read(isPausedProvider).state = true; }, @@ -110,11 +123,13 @@ class GamePage extends ConsumerWidget { builder: (context, ref, child) { final tilesCount = ref.watch(tilesCountProvider).state; - return Text(tilesCount.toString(), - style: Theme.of(context) - .textTheme - .headline4! - .copyWith(color: secondaryColor)); + return Text( + tilesCount.toString(), + style: Theme.of(context) + .textTheme + .headline4! + .copyWith(color: secondaryColor), + ); }, ), ], @@ -128,8 +143,10 @@ class GamePage extends ConsumerWidget { ) ], ), - )) - ]); + ), + ) + ], + ); } else { return const SizedBox.shrink(); } @@ -153,11 +170,11 @@ class GuideTextWidget extends ConsumerWidget { } else if (guideText == 'txt_too_many_fingers') { text = L10n.of(context)!.txt_too_many_fingers; } - return Text(text, - style: Theme.of(context) - .textTheme - .headline5! - .copyWith(color: primaryColor)); + return Text( + text, + style: + Theme.of(context).textTheme.headline5!.copyWith(color: primaryColor), + ); } } @@ -167,28 +184,26 @@ class LoadingSoundWidget extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) { - return Material( - child: SafeArea( - child: Padding( - padding: const EdgeInsets.all(8.0), + Widget build(BuildContext context) => Material( + child: SafeArea( + child: Padding( + padding: const EdgeInsets.all(8), child: Column( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ const Expanded( child: Align( - alignment: Alignment.center, child: Image( - image: AssetImage('assets/images/img_app_icon.png')), + image: AssetImage('assets/images/img_app_icon.png'), + ), ), ), Text(L10n.of(context)!.txt_dialog_loading_sound_description) ], - )), - ), - ); - } + ), + ), + ), + ); } class LoadingGiftWidget extends StatelessWidget { @@ -197,26 +212,24 @@ class LoadingGiftWidget extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) { - return Material( - child: SafeArea( - child: Padding( - padding: const EdgeInsets.all(8.0), + Widget build(BuildContext context) => Material( + child: SafeArea( + child: Padding( + padding: const EdgeInsets.all(8), child: Column( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ const Expanded( child: Align( - alignment: Alignment.center, child: Image( - image: AssetImage('assets/images/img_app_icon.png')), + image: AssetImage('assets/images/img_app_icon.png'), + ), ), ), Text(L10n.of(context)!.txt_game_complete_loading_gift) ], - )), - ), - ); - } + ), + ), + ), + ); } diff --git a/lib/game/my_game.dart b/lib/game/my_game.dart index 7570c248..bbc01e9d 100644 --- a/lib/game/my_game.dart +++ b/lib/game/my_game.dart @@ -28,8 +28,12 @@ class MyGame extends FlameGame with MultiTouchTapDetector { late Sprite _staffSprite; late Sprite _clefSprite; - Future start(List tiles, double speedPixelsPerSecond, - Function(Tile? tile) onTouched, Function() onCompleted) async { + Future start( + List tiles, + double speedPixelsPerSecond, + Function(Tile? tile) onTouched, + Function() onCompleted, + ) async { _tilesController.initialize(tiles, speedPixelsPerSecond); _state = _MyGameState.play; _onTouched = onTouched; @@ -41,9 +45,11 @@ class MyGame extends FlameGame with MultiTouchTapDetector { Future onLoad() async { await super.onLoad(); _staffSprite = Sprite( - await images.load('${nearestDevicePixelRatioFolder}img_staff.png')); + await images.load('${nearestDevicePixelRatioFolder}img_staff.png'), + ); _clefSprite = Sprite( - await images.load('${nearestDevicePixelRatioFolder}img_clef.png')); + await images.load('${nearestDevicePixelRatioFolder}img_clef.png'), + ); await Flame.images.load('${nearestDevicePixelRatioFolder}img_touch.png'); await Flame.images .load('${nearestDevicePixelRatioFolder}img_single_note.png'); @@ -67,14 +73,18 @@ class MyGame extends FlameGame with MultiTouchTapDetector { super.render(canvas); final rect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); canvas.drawRect(rect, _backgroundPaint); - _staffSprite.render(canvas, - position: Vector2(0.0, pauseY - 96 + 24), - size: Vector2(screenWidth, 96), - overridePaint: _grayPaint); - _clefSprite.render(canvas, - position: Vector2(0.0, pauseY - 96 + 24), - size: Vector2(96, 96), - overridePaint: _grayPaint); + _staffSprite.render( + canvas, + position: Vector2(0, pauseY - 96 + 24), + size: Vector2(screenWidth, 96), + overridePaint: _grayPaint, + ); + _clefSprite.render( + canvas, + position: Vector2(0, pauseY - 96 + 24), + size: Vector2(96, 96), + overridePaint: _grayPaint, + ); _tilesController.render(canvas); for (final effect in _tileEffects) { effect.render(canvas); @@ -103,25 +113,26 @@ class MyGame extends FlameGame with MultiTouchTapDetector { _accumulator += restrictedTime; while (_accumulator >= _step) { var initialYAllowedTouch = double.negativeInfinity; - _touches.forEach((key, value) { - final tile = _tilesController.getNextUntouchedTile(); - if (tile != null) { - if (tile.initialY >= initialYAllowedTouch) { - tile.touchDown(); - _tileEffects.addAll(tile.getEffects()); - _onTouched(tile); - if (_state == _MyGameState.pause) { - _state = _MyGameState.play; + _touches + ..forEach((key, value) { + final tile = _tilesController.getNextUntouchedTile(); + if (tile != null) { + if (tile.initialY >= initialYAllowedTouch) { + tile.touchDown(); + _tileEffects.addAll(tile.getEffects()); + _onTouched(tile); + if (_state == _MyGameState.pause) { + _state = _MyGameState.play; + } + initialYAllowedTouch = tile.initialY; + } else { + _onTouched(null); } - initialYAllowedTouch = tile.initialY; } else { _onTouched(null); } - } else { - _onTouched(null); - } - }); - _touches.clear(); + }) + ..clear(); _accumulator -= _step; if (_state == _MyGameState.play) { final actualDeltaTime = _tilesController.tryUpdate(_step); @@ -141,8 +152,8 @@ class MyGame extends FlameGame with MultiTouchTapDetector { enum _MyGameState { prepare, play, pause, stop, complete } class _TouchPosition { + _TouchPosition(this.x, this.y); + final double x; final double y; - - _TouchPosition(this.x, this.y); } diff --git a/lib/game/pause_dialog.dart b/lib/game/pause_dialog.dart index f3972c71..06a8157a 100644 --- a/lib/game/pause_dialog.dart +++ b/lib/game/pause_dialog.dart @@ -1,44 +1,46 @@ import 'package:flutter/material.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; class PauseDialog extends StatelessWidget { - final Function() onRestart; + const PauseDialog({ + required this.onRestart, + Key? key, + }) : super(key: key); - const PauseDialog({Key? key, required this.onRestart}) : super(key: key); + final Function() onRestart; @override - Widget build(BuildContext context) { - return Material( - color: Colors.transparent, - child: SafeArea( - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }, - child: Text(L10n.of(context)!.txt_button_quit), - ), - ElevatedButton( - onPressed: () { - onRestart(); - Navigator.of(context).pop(); - }, - child: Text(L10n.of(context)!.txt_button_restart), - ), - ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text(L10n.of(context)!.txt_game_button_continue), - ) - ], + Widget build(BuildContext context) => Material( + color: Colors.transparent, + child: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }, + child: Text(L10n.of(context)!.txt_button_quit), + ), + ElevatedButton( + onPressed: () { + onRestart(); + Navigator.of(context).pop(); + }, + child: Text(L10n.of(context)!.txt_button_restart), + ), + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(L10n.of(context)!.txt_game_button_continue), + ) + ], + ), ), - ), - ); - } + ); } diff --git a/lib/game/ripple_effect.dart b/lib/game/ripple_effect.dart index 241faca6..990c5014 100644 --- a/lib/game/ripple_effect.dart +++ b/lib/game/ripple_effect.dart @@ -9,12 +9,17 @@ import 'effect.dart'; import 'util.dart'; Sprite touchSprite = Sprite( - Flame.images.fromCache('${nearestDevicePixelRatioFolder}img_touch.png')); + Flame.images.fromCache('${nearestDevicePixelRatioFolder}img_touch.png'), +); Paint paint = Paint() ..colorFilter = ColorFilter.mode(primaryColor.withOpacity(0.1), BlendMode.srcIn); class RippleEffect extends Effect { + RippleEffect(this._x, this._y) + : _rate = (size - initialWidth) / liveTime, + _width = initialWidth; + static const liveTime = 0.5; static const size = 96; static const initialWidth = 0.5 * size; @@ -25,14 +30,8 @@ class RippleEffect extends Effect { final double _x; final double _y; - RippleEffect(this._x, this._y) - : _rate = (size - initialWidth) / liveTime, - _width = initialWidth; - @override - bool isDone() { - return _isDone; - } + bool isDone() => _isDone; @override void update(double delta) { @@ -46,10 +45,14 @@ class RippleEffect extends Effect { @override void render(Canvas canvas) { - touchSprite.render(canvas, - position: Vector2((_x - _width / 2).toInt().toDouble(), - (_y - _width / 2).toInt().toDouble()), - size: Vector2(_width.toInt().toDouble(), _width.toInt().toDouble()), - overridePaint: paint); + touchSprite.render( + canvas, + position: Vector2( + (_x - _width / 2).toInt().toDouble(), + (_y - _width / 2).toInt().toDouble(), + ), + size: Vector2(_width.toInt().toDouble(), _width.toInt().toDouble()), + overridePaint: paint, + ); } } diff --git a/lib/game/tile/tile.dart b/lib/game/tile/tile.dart index 7e5c00dc..6d09b02e 100644 --- a/lib/game/tile/tile.dart +++ b/lib/game/tile/tile.dart @@ -1,16 +1,16 @@ import 'tile_converter.dart'; class Tile { + Tile(this.note, this.column, this.initialY); + final int note; final double initialY; - double y = 0.0; + double y = 0; TileState state = TileState.untouched; final double width = tileWidth; final double height = tileHeight; Function(Tile tile)? onTouched; final int column; - - Tile(this.note, this.column, this.initialY); } enum TileState { untouched, touched } diff --git a/lib/game/tile/tile_converter.dart b/lib/game/tile/tile_converter.dart index 54a50db7..12dcda5c 100644 --- a/lib/game/tile/tile_converter.dart +++ b/lib/game/tile/tile_converter.dart @@ -15,13 +15,12 @@ const tileWidth = 36.0; const tileHeight = tileWidth; const startVisibleY = 0; -double tickToSecond(int resolution, int bpm) { - return minuteToSecond.toDouble() / (resolution * bpm); -} +double tickToSecond(int resolution, int bpm) => + minuteToSecond.toDouble() / (resolution * bpm); List createTileChunks(MidiFile midiFile) { final tileNotes = []; - final onsets = List.filled(numberOfNotes, -1, growable: false); + final onsets = List.filled(numberOfNotes, -1); for (final midiTrack in midiFile.tracks) { var totalTicks = 0; for (final midiEvent in midiTrack) { @@ -44,15 +43,16 @@ List createTileChunks(MidiFile midiFile) { var previousStartTick = 0; Map>.fromEntries( - groupBy(tileNotes, (note) => note.startTick) - .entries - .toList() - ..sort((e1, e2) => e1.key.compareTo(e2.key))) - .forEach((key, notes) { - tileChunks.add(TileChunk( + groupBy(tileNotes, (note) => note.startTick).entries.toList() + ..sort((e1, e2) => e1.key.compareTo(e2.key)), + ).forEach((key, notes) { + tileChunks.add( + TileChunk( notes: notes, durationToPrevious: notes[0].startTick - previousStartTick, - startTick: notes[0].startTick)); + startTick: notes[0].startTick, + ), + ); previousStartTick = notes[0].startTick; }); return tileChunks; diff --git a/lib/game/tile/tile_drawer.dart b/lib/game/tile/tile_drawer.dart index c9fd5287..a9c5496a 100644 --- a/lib/game/tile/tile_drawer.dart +++ b/lib/game/tile/tile_drawer.dart @@ -7,15 +7,20 @@ import '../colors.dart'; import '../util.dart'; import 'tile.dart'; -Sprite noteSprite = Sprite(Flame.images - .fromCache('${nearestDevicePixelRatioFolder}img_single_note.png')); +Sprite noteSprite = Sprite( + Flame.images.fromCache('${nearestDevicePixelRatioFolder}img_single_note.png'), +); extension TileDrawer on Tile { void render(Canvas canvas) { - noteSprite.render(canvas, - position: Vector2( - positionsX[column].toInt().toDouble(), y.toInt().toDouble()), - size: Vector2(width.toInt().toDouble(), height.toInt().toDouble()), - overridePaint: paint); + noteSprite.render( + canvas, + position: Vector2( + positionsX[column].toInt().toDouble(), + y.toInt().toDouble(), + ), + size: Vector2(width.toInt().toDouble(), height.toInt().toDouble()), + overridePaint: paint, + ); } } diff --git a/lib/game/tile/tile_effect_spawner.dart b/lib/game/tile/tile_effect_spawner.dart index bc4c39e5..e57008b9 100644 --- a/lib/game/tile/tile_effect_spawner.dart +++ b/lib/game/tile/tile_effect_spawner.dart @@ -5,10 +5,8 @@ import 'tile.dart'; import 'tile_touch_effect.dart'; extension TileEffectSpawner on Tile { - List getEffects() { - return [ - RippleEffect(positionsX[column] + width / 2, y + height / 2), - TileTouchEffect(this) - ]; - } + List getEffects() => [ + RippleEffect(positionsX[column] + width / 2, y + height / 2), + TileTouchEffect(this) + ]; } diff --git a/lib/game/tile/tile_touch_effect.dart b/lib/game/tile/tile_touch_effect.dart index e559032e..fefb3706 100644 --- a/lib/game/tile/tile_touch_effect.dart +++ b/lib/game/tile/tile_touch_effect.dart @@ -101,22 +101,20 @@ const noteToName = { }; class TileTouchEffect extends Effect { - final _textPaint = CenterTextPaint(fontSize: 24.0, color: primaryColor); + TileTouchEffect(Tile _tile) + : _centerX = positionsX[_tile.column] + _tile.width / 2.0, + _centerY = _tile.y + _tile.height / 2.0, + _text = noteToName[_tile.note]!; + + final _textPaint = CenterTextPaint(color: primaryColor); var _time = 0.0; var _isDone = false; final double _centerX; final double _centerY; final String _text; - TileTouchEffect(Tile _tile) - : _centerX = positionsX[_tile.column] + _tile.width / 2.0, - _centerY = _tile.y + _tile.height / 2.0, - _text = noteToName[_tile.note]!; - @override - bool isDone() { - return _isDone; - } + bool isDone() => _isDone; @override void update(double delta) { @@ -128,7 +126,10 @@ class TileTouchEffect extends Effect { @override void render(Canvas canvas) { - _textPaint.render(canvas, _text, - Vector2(_centerX.toInt().toDouble(), _centerY.toInt().toDouble())); + _textPaint.render( + canvas, + _text, + Vector2(_centerX.toInt().toDouble(), _centerY.toInt().toDouble()), + ); } } diff --git a/lib/game/tile/tiles_controller.dart b/lib/game/tile/tiles_controller.dart index 2ec6df05..25912324 100644 --- a/lib/game/tile/tiles_controller.dart +++ b/lib/game/tile/tiles_controller.dart @@ -55,13 +55,14 @@ class TilesController { } } - double _getMaxDeltaY() { - return pauseY - - tiles - .firstWhere((element) => element.state == TileState.untouched, - orElse: () => Tile(0, 0, 0)) - .y; - } + double _getMaxDeltaY() => + pauseY - + tiles + .firstWhere( + (element) => element.state == TileState.untouched, + orElse: () => Tile(0, 0, 0), + ) + .y; Tile? getNextUntouchedTile() { for (var i = 0; i < _visibleTileCount; i++) { diff --git a/lib/game_config/game_config_controller.dart b/lib/game_config/game_config_controller.dart index c94fd335..68a2036a 100644 --- a/lib/game_config/game_config_controller.dart +++ b/lib/game_config/game_config_controller.dart @@ -3,9 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'game_config_state.dart'; final gameConfigStateProvider = - StateNotifierProvider((ref) { - return GameConfigController(); -}); + StateNotifierProvider( + (ref) => GameConfigController(), +); class GameConfigController extends StateNotifier { GameConfigController() : super(GameConfigState(difficulty: 1, speed: 1)); diff --git a/lib/game_config/game_config_page.dart b/lib/game_config/game_config_page.dart index c6886505..b2c07662 100644 --- a/lib/game_config/game_config_page.dart +++ b/lib/game_config/game_config_page.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:sprintf/sprintf.dart'; @@ -10,9 +11,12 @@ import '../songs/song.dart'; import 'game_config_controller.dart'; class GameConfigPage extends ConsumerWidget { - final Song song; + const GameConfigPage({ + required this.song, + Key? key, + }) : super(key: key); - const GameConfigPage({Key? key, required this.song}) : super(key: key); + final Song song; @override Widget build(BuildContext context, WidgetRef ref) { @@ -20,8 +24,11 @@ class GameConfigPage extends ConsumerWidget { return Scaffold( appBar: AppBar( - title: Text(L10n.of(context)!.txt_configure, - style: Theme.of(context).appBarTheme.toolbarTextStyle)), + title: Text( + L10n.of(context)!.txt_configure, + style: Theme.of(context).appBarTheme.toolbarTextStyle, + ), + ), body: SafeArea( child: Scrollbar( isAlwaysShown: true, @@ -30,8 +37,10 @@ class GameConfigPage extends ConsumerWidget { children: [ Column( children: [ - Text(L10n.of(context)!.txt_difficulty, - style: Theme.of(context).textTheme.headline5), + Text( + L10n.of(context)!.txt_difficulty, + style: Theme.of(context).textTheme.headline5, + ), const SizedBox(height: 8), Row( children: [ @@ -76,8 +85,10 @@ class GameConfigPage extends ConsumerWidget { const SizedBox(height: 16), Column( children: [ - Text(L10n.of(context)!.txt_speed, - style: Theme.of(context).textTheme.headline5), + Text( + L10n.of(context)!.txt_speed, + style: Theme.of(context).textTheme.headline5, + ), const SizedBox(height: 8), Row( children: [ @@ -126,50 +137,50 @@ class GameConfigPage extends ConsumerWidget { ), bottomNavigationBar: SafeArea( child: ElevatedButton( - onPressed: () { - primaryColor = Theme.of(context).colorScheme.primary; - secondaryColor = Theme.of(context).colorScheme.secondary; - backgroundColor = Theme.of(context).colorScheme.background; - onBackgroundColor = Theme.of(context).colorScheme.onBackground; - paint = Paint() - ..colorFilter = ColorFilter.mode(primaryColor, BlendMode.srcIn); - AutoRouter.of(context) - .push(GameRoute(arguments: { - 'song': song, - 'difficulty': gameConfigState.difficulty, - 'speed': gameConfigState.speed - })); - }, - child: Text(L10n.of(context)!.txt_start)), + onPressed: () { + primaryColor = Theme.of(context).colorScheme.primary; + secondaryColor = Theme.of(context).colorScheme.secondary; + backgroundColor = Theme.of(context).colorScheme.background; + onBackgroundColor = Theme.of(context).colorScheme.onBackground; + paint = Paint() + ..colorFilter = ColorFilter.mode(primaryColor, BlendMode.srcIn); + AutoRouter.of(context).push( + GameRoute( + arguments: { + 'song': song, + 'difficulty': gameConfigState.difficulty, + 'speed': gameConfigState.speed + }, + ), + ); + }, + child: Text(L10n.of(context)!.txt_start), + ), ), ); } } class CardWidget extends StatelessWidget { - final bool selected; - final String text; - final String caption; - final GestureTapCallback onTap; - const CardWidget({ - Key? key, required this.selected, required this.text, required this.caption, required this.onTap, + Key? key, }) : super(key: key); + final bool selected; + final String text; + final String caption; + final GestureTapCallback onTap; + @override - Widget build(BuildContext context) { - return Expanded( + Widget build(BuildContext context) => Expanded( child: selected ? ElevatedButton( - onPressed: () { - onTap(); - }, + onPressed: onTap, child: Column( - mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 8), @@ -178,13 +189,11 @@ class CardWidget extends StatelessWidget { Text(caption), const SizedBox(height: 8), ], - )) + ), + ) : OutlinedButton( - onPressed: () { - onTap(); - }, + onPressed: onTap, child: Column( - mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 8), @@ -193,6 +202,7 @@ class CardWidget extends StatelessWidget { Text(caption), const SizedBox(height: 8), ], - ))); - } + ), + ), + ); } diff --git a/lib/home/home_page.dart b/lib/home/home_page.dart index 91c48a5a..68e02717 100644 --- a/lib/home/home_page.dart +++ b/lib/home/home_page.dart @@ -1,5 +1,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; + +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -16,86 +18,102 @@ class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @override - Widget build(BuildContext context) { - return Scaffold(body: HookConsumer(builder: (context, ref, child) { - final tabController = useTabController(initialLength: songTags.length); - return NestedScrollView( - headerSliverBuilder: (context, innerBoxIsScrolled) { - return [ - SliverAppBar( - title: Text(L10n.of(context)!.txt_all_songs, - style: Theme.of(context).appBarTheme.toolbarTextStyle), - elevation: 2, - floating: true, - pinned: true, - snap: true, - forceElevated: true, - actions: [ - IconButton( - icon: const Icon(Icons.search_rounded), - onPressed: () { - showSearch( - context: context, delegate: SearchWidget(ref.read)); - }, + Widget build(BuildContext context) => Scaffold( + body: HookConsumer( + builder: (context, ref, child) { + final tabController = + useTabController(initialLength: songTags.length); + return NestedScrollView( + headerSliverBuilder: (context, innerBoxIsScrolled) => [ + SliverAppBar( + title: Text( + L10n.of(context)!.txt_all_songs, + style: Theme.of(context).appBarTheme.toolbarTextStyle, ), - IconButton( + elevation: 2, + floating: true, + pinned: true, + snap: true, + forceElevated: true, + actions: [ + IconButton( + icon: const Icon(Icons.search_rounded), + onPressed: () { + showSearch( + context: context, + delegate: SearchWidget(ref.read), + ); + }, + ), + IconButton( icon: Image( image: const AssetImage('assets/images/img_guitar.png'), color: Theme.of(context).appBarTheme.iconTheme!.color, ), - onPressed: () => AutoRouter.of(context) - .push(const InstrumentsRoute())), - IconButton( - icon: ClipOval(child: Consumer( - builder: (context, ref, child) { - // FIXME To load midi - ref.watch(midiLoadedProvider); - final user = ref.watch(userProvider); - return user.when( + onPressed: () => + AutoRouter.of(context).push(const InstrumentsRoute()), + ), + IconButton( + icon: ClipOval( + child: Consumer( + builder: (context, ref, child) { + // FIXME To load midi + ref.watch(midiLoadedProvider); + final user = ref.watch(userProvider); + return user.when( data: (user) => Image.network( - user.photoUrl, - errorBuilder: - (context, exception, stackTrace) { - return const Icon( - Icons.account_circle_rounded); - }, - ), + user.photoUrl, + errorBuilder: + (context, exception, stackTrace) => + const Icon( + Icons.account_circle_rounded, + ), + ), loading: (_) => const Icon(Icons.account_circle_rounded), error: (_, __, ___) => - const Icon(Icons.account_circle_rounded)); - }, - )), + const Icon(Icons.account_circle_rounded), + ); + }, + ), + ), onPressed: () => - AutoRouter.of(context).push(const UserRoute())), - IconButton( + AutoRouter.of(context).push(const UserRoute()), + ), + IconButton( icon: const Icon(Icons.settings), onPressed: () => - AutoRouter.of(context).push(const SettingsRoute())), - ], - bottom: TabBar( - isScrollable: true, - controller: tabController, - tabs: songTags - .map((tabName) => Tab( + AutoRouter.of(context).push(const SettingsRoute()), + ), + ], + bottom: TabBar( + isScrollable: true, + controller: tabController, + tabs: songTags + .map( + (tabName) => Tab( text: getSongTagName(context, tabName), - )) - .toList(), - )), - ]; - }, - body: TabBarView( - controller: tabController, - children: songTags - .asMap() - .map((index, tabName) => - MapEntry(index, SongsWidget(tag: tabName))) - .values - .toList(), + ), + ) + .toList(), + ), + ), + ], + body: TabBarView( + controller: tabController, + children: songTags + .asMap() + .map( + (index, tabName) => + MapEntry(index, SongsWidget(tag: tabName)), + ) + .values + .toList(), + ), + ); + }, ), ); - })); - } } String getSongTagName(BuildContext context, String tabName) { diff --git a/lib/instrument/instruments_controller.dart b/lib/instrument/instruments_controller.dart index bd49ac78..f9e35051 100644 --- a/lib/instrument/instruments_controller.dart +++ b/lib/instrument/instruments_controller.dart @@ -1,27 +1,26 @@ import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:state_notifier/state_notifier.dart'; import 'instrument.dart'; import 'instruments_repository_impl.dart'; final selectedInstrumentIdProvider = StateProvider((_) => null); -final selectedInstrumentProvider = Provider(((ref) { +final selectedInstrumentProvider = Provider((ref) { final selectedInstrumentId = ref.watch(selectedInstrumentIdProvider).state; final instruments = ref.watch(instrumentsProvider); return instruments.when( - data: (instruments) => - instruments.firstWhereOrNull((e) => e.id == selectedInstrumentId), - loading: (_) => null, - error: (_, __, ___) => null); -})); + data: (instruments) => + instruments.firstWhereOrNull((e) => e.id == selectedInstrumentId), + loading: (_) => null, + error: (_, __, ___) => null, + ); +}); final instrumentsProvider = StateNotifierProvider>>( - (ref) { - return InstrumentsController(ref.read).._loadInstruments(); -}); + (ref) => InstrumentsController(ref.read).._loadInstruments(), +); class InstrumentsController extends StateNotifier>> { diff --git a/lib/instrument/instruments_page.dart b/lib/instrument/instruments_page.dart index c9713bbb..483ff59c 100644 --- a/lib/instrument/instruments_page.dart +++ b/lib/instrument/instruments_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -15,29 +16,35 @@ class InstrumentsPage extends ConsumerWidget { final selectedInstrumentId = ref.watch(selectedInstrumentIdProvider).state; return Scaffold( appBar: AppBar( - title: Text(L10n.of(context)!.txt_instrument_title_instruments, - style: Theme.of(context).appBarTheme.toolbarTextStyle)), + title: Text( + L10n.of(context)!.txt_instrument_title_instruments, + style: Theme.of(context).appBarTheme.toolbarTextStyle, + ), + ), body: instruments.when( - data: (instruments) => ListView.builder( - itemCount: instruments.length, - itemBuilder: (context, index) { - final instrument = instruments[index]; - return RadioListTile( - title: Text(getInstrumentName(context, instrument.id), - style: Theme.of(context).textTheme.headline6), - value: instrument.id, - groupValue: selectedInstrumentId, - onChanged: (value) { - ref - .watch(instrumentRepositoryProvider) - .changeInstrument(value!); - ref.read(selectedInstrumentIdProvider).state = value; - }, - ); - }, + data: (instruments) => ListView.builder( + itemCount: instruments.length, + itemBuilder: (context, index) { + final instrument = instruments[index]; + return RadioListTile( + title: Text( + getInstrumentName(context, instrument.id), + style: Theme.of(context).textTheme.headline6, ), - loading: (_) => const LoadingWidget(), - error: (_, __, ___) => const LoadingWidget()), + value: instrument.id, + groupValue: selectedInstrumentId, + onChanged: (value) { + ref + .watch(instrumentRepositoryProvider) + .changeInstrument(value!); + ref.read(selectedInstrumentIdProvider).state = value; + }, + ); + }, + ), + loading: (_) => const LoadingWidget(), + error: (_, __, ___) => const LoadingWidget(), + ), ); } } diff --git a/lib/instrument/instruments_repository_impl.dart b/lib/instrument/instruments_repository_impl.dart index 07a21c9d..8733a806 100644 --- a/lib/instrument/instruments_repository_impl.dart +++ b/lib/instrument/instruments_repository_impl.dart @@ -12,12 +12,11 @@ final instrumentRepositoryProvider = class InstrumentsRepositoryImpl implements InstrumentsRepository { @override - Future> instruments() async { - return (await FirebaseFirestore.instance.collection('instruments').get()) - .docs - .map((e) => Instrument.fromJson(e.data())) - .toList(); - } + Future> instruments() async => + (await FirebaseFirestore.instance.collection('instruments').get()) + .docs + .map((e) => Instrument.fromJson(e.data())) + .toList(); @override void changeInstrument(String instrumentId) { diff --git a/lib/instrument_checker.dart b/lib/instrument_checker.dart index 95b31300..e0f9a2e9 100644 --- a/lib/instrument_checker.dart +++ b/lib/instrument_checker.dart @@ -25,7 +25,7 @@ void main() { soundPaths[file.path.split('/').last.split('.').first] = file.path.replaceAll('storage/', ''); }); - final notes = soundPaths.keys.map((e) => int.parse(e)).toList()..sort(); + final notes = soundPaths.keys.map(int.parse).toList()..sort(); final maxNote = notes.last; final minNote = notes.first; var i = 0; @@ -36,7 +36,7 @@ void main() { } else if ((i + 1) < notes.length && note > (notes[i] + 2)) { i++; } - final int = notes[i].toInt(); + final int = notes[i]; baseNotes[note.toString()] = int; } diff --git a/lib/loading_widget.dart b/lib/loading_widget.dart index 336d1d43..968c309f 100644 --- a/lib/loading_widget.dart +++ b/lib/loading_widget.dart @@ -4,9 +4,7 @@ class LoadingWidget extends StatelessWidget { const LoadingWidget({Key? key}) : super(key: key); @override - Widget build(BuildContext context) { - return const Center( - child: CircularProgressIndicator(), - ); - } + Widget build(BuildContext context) => const Center( + child: CircularProgressIndicator(), + ); } diff --git a/lib/locale/locale_controller.dart b/lib/locale/locale_controller.dart index 0c3c565c..3de72dd5 100644 --- a/lib/locale/locale_controller.dart +++ b/lib/locale/locale_controller.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:state_notifier/state_notifier.dart'; import '../preferences.dart'; @@ -13,10 +12,10 @@ final localeProvider = StateNotifierProvider((ref) { }); class LocaleController extends StateNotifier { - final Reader _read; - LocaleController(this._read, Locale? defaultLocale) : super(defaultLocale); + final Reader _read; + Future setLocale(BuildContext context, Locale locale) async { await _read(sharedUtilityProvider).setLocaleName(locale.toString()); state = locale; diff --git a/lib/locale/locale_page.dart b/lib/locale/locale_page.dart index 90dd83ef..7a33edda 100644 --- a/lib/locale/locale_page.dart +++ b/lib/locale/locale_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -15,28 +16,29 @@ class LocalePage extends ConsumerWidget { const LocalePage({Key? key}) : super(key: key); @override - Widget build(BuildContext context, WidgetRef ref) { - return Scaffold( + Widget build(BuildContext context, WidgetRef ref) => Scaffold( appBar: AppBar( - title: Text(L10n.of(context)!.txt_language, - style: Theme.of(context).appBarTheme.toolbarTextStyle)), + title: Text( + L10n.of(context)!.txt_language, + style: Theme.of(context).appBarTheme.toolbarTextStyle, + ), + ), body: Scrollbar( isAlwaysShown: true, child: ListView.builder( itemCount: L10n.supportedLocales.length, - itemBuilder: (context, index) { - return RadioListTile( - title: Text( - localeStrings[L10n.supportedLocales[index].languageCode]!, - style: Theme.of(context).textTheme.headline6), - value: L10n.supportedLocales[index], - groupValue: Localizations.localeOf(context), - onChanged: (value) { - ref.read(localeProvider.notifier).setLocale(context, value!); - }, - ); - }, + itemBuilder: (context, index) => RadioListTile( + title: Text( + localeStrings[L10n.supportedLocales[index].languageCode]!, + style: Theme.of(context).textTheme.headline6, + ), + value: L10n.supportedLocales[index], + groupValue: Localizations.localeOf(context), + onChanged: (value) { + ref.read(localeProvider.notifier).setLocale(context, value!); + }, + ), ), - )); - } + ), + ); } diff --git a/lib/main-dev.dart b/lib/main-dev.dart index 2fc3ab75..49eeffb9 100644 --- a/lib/main-dev.dart +++ b/lib/main-dev.dart @@ -12,30 +12,46 @@ import 'preferences.dart'; class LogProviderObserver extends ProviderObserver { @override void didAddProvider( - ProviderBase provider, Object? value, ProviderContainer container) { - Logger.root.info('''Add + ProviderBase provider, + Object? value, + ProviderContainer container, + ) { + Logger.root.info( + ''' +Add { "provider": "${provider.name ?? provider.runtimeType}", "value": "$value" -}'''); +}''', + ); } @override - void didUpdateProvider(ProviderBase provider, Object? previousValue, - Object? newValue, ProviderContainer container) { - Logger.root.info('''Update + void didUpdateProvider( + ProviderBase provider, + Object? previousValue, + Object? newValue, + ProviderContainer container, + ) { + Logger.root.info( + ''' +Update { "provider": "${provider.name ?? provider.runtimeType}", "newValue": "$newValue" -}'''); +}''', + ); } @override void didDisposeProvider(ProviderBase provider, ProviderContainer containers) { - Logger.root.info('''Dispose + Logger.root.info( + ''' +Dispose { "provider": "${provider.name ?? provider.runtimeType}" -}'''); +}''', + ); } } diff --git a/lib/midi/midi_controller.dart b/lib/midi/midi_controller.dart index 9afad243..92da15a8 100644 --- a/lib/midi/midi_controller.dart +++ b/lib/midi/midi_controller.dart @@ -26,11 +26,12 @@ class MidiController extends StateNotifier { _soundPlayer.release(); _maxNote = instrument.maxNote; _minNote = instrument.minNote; - Future.wait(instrument.soundPaths - .map((note, path) => MapEntry(note, _getFile(note, path))) - .values - .toList()) - .then((_) async { + Future.wait( + instrument.soundPaths + .map((note, path) => MapEntry(note, _getFile(note, path))) + .values + .toList(), + ).then((_) async { await _soundPlayer.load(_soundPaths, instrument.baseNotes); state = true; }); @@ -42,7 +43,7 @@ class MidiController extends StateNotifier { } Future playNote(int note) async { - var pitchNote = note.toInt(); + var pitchNote = note; while (pitchNote > _maxNote) { pitchNote -= 12; } diff --git a/lib/preferences.dart b/lib/preferences.dart index 90ec24f1..b3875432 100644 --- a/lib/preferences.dart +++ b/lib/preferences.dart @@ -20,17 +20,13 @@ class SharedUtility { static const preferenceLocaleName = 'LocaleName'; static const preferenceThemeName = 'ThemeName'; - String? getLocaleName() { - return pref.getString(preferenceLocaleName); - } + String? getLocaleName() => pref.getString(preferenceLocaleName); Future setLocaleName(String localeName) async { await pref.setString(preferenceLocaleName, localeName); } - String? getThemeName() { - return pref.getString(preferenceThemeName); - } + String? getThemeName() => pref.getString(preferenceThemeName); Future setThemeName(String themeName) async { await pref.setString(preferenceThemeName, themeName); diff --git a/lib/router/router.gr.dart b/lib/router/router.gr.dart index ba5efba1..41c99319 100644 --- a/lib/router/router.gr.dart +++ b/lib/router/router.gr.dart @@ -40,13 +40,13 @@ class RootRouter extends _i10.RootStackRouter { final args = routeData.argsAs(); return _i10.MaterialPageX( routeData: routeData, - child: _i3.GameConfigPage(key: args.key, song: args.song)); + child: _i3.GameConfigPage(song: args.song, key: args.key)); }, GameRoute.name: (routeData) { final args = routeData.argsAs(); return _i10.MaterialPageX( routeData: routeData, - child: _i4.GamePage(key: args.key, arguments: args.arguments)); + child: _i4.GamePage(arguments: args.arguments, key: args.key)); }, UserRoute.name: (routeData) { return _i10.MaterialPageX( @@ -100,38 +100,38 @@ class HomeRoute extends _i10.PageRouteInfo { /// generated route for [_i3.GameConfigPage] class GameConfigRoute extends _i10.PageRouteInfo { - GameConfigRoute({_i11.Key? key, required _i12.Song song}) + GameConfigRoute({required _i12.Song song, _i11.Key? key}) : super(name, path: '/game-config-page', - args: GameConfigRouteArgs(key: key, song: song)); + args: GameConfigRouteArgs(song: song, key: key)); static const String name = 'GameConfigRoute'; } class GameConfigRouteArgs { - const GameConfigRouteArgs({this.key, required this.song}); - - final _i11.Key? key; + const GameConfigRouteArgs({required this.song, this.key}); final _i12.Song song; + + final _i11.Key? key; } /// generated route for [_i4.GamePage] class GameRoute extends _i10.PageRouteInfo { - GameRoute({_i11.Key? key, required Map arguments}) + GameRoute({required Map arguments, _i11.Key? key}) : super(name, path: '/game-page', - args: GameRouteArgs(key: key, arguments: arguments)); + args: GameRouteArgs(arguments: arguments, key: key)); static const String name = 'GameRoute'; } class GameRouteArgs { - const GameRouteArgs({this.key, required this.arguments}); - - final _i11.Key? key; + const GameRouteArgs({required this.arguments, this.key}); final Map arguments; + + final _i11.Key? key; } /// generated route for [_i5.UserPage] diff --git a/lib/search/search_widget.dart b/lib/search/search_widget.dart index 0448a9a3..6a1ae09e 100644 --- a/lib/search/search_widget.dart +++ b/lib/search/search_widget.dart @@ -9,37 +9,35 @@ import '../songs/song_widget.dart'; import '../songs/songs_repository_impl.dart'; class SearchWidget extends SearchDelegate { - final Reader _read; - SearchWidget(this._read); + final Reader _read; + @override - List buildActions(BuildContext context) { - return [ - IconButton( - icon: const Icon(Icons.close), - onPressed: () { - query = ''; - }, - ), - ]; - } + List buildActions(BuildContext context) => [ + IconButton( + icon: const Icon(Icons.close), + onPressed: () { + query = ''; + }, + ), + ]; @override ThemeData appBarTheme(BuildContext context) { final theme = Theme.of(context); - if (theme.brightness == Brightness.light) return super.appBarTheme(context); + if (theme.brightness == Brightness.light) { + return super.appBarTheme(context); + } return theme; } @override - Widget buildLeading(BuildContext context) { - return BackButton( - onPressed: () { - Navigator.pop(context); - }, - ); - } + Widget buildLeading(BuildContext context) => BackButton( + onPressed: () { + Navigator.pop(context); + }, + ); @override Widget buildResults(BuildContext context) { @@ -47,29 +45,29 @@ class SearchWidget extends SearchDelegate { return const SizedBox.shrink(); } else { return FutureBuilder>( - future: _read(songRepositoryProvider).searchSongs(query), - builder: (context, recentList) { - if (recentList.connectionState == ConnectionState.done) { - final songs = recentList.requireData; - return ListView.builder( - itemCount: songs.length, - itemBuilder: (context, index) { - final song = songs[index]; - return SongWidget( - song: song, - onTap: () => AutoRouter.of(context) - .push(GameConfigRoute(song: song))); - }, - ); - } else { - return const LoadingWidget(); - } - }); + future: _read(songRepositoryProvider).searchSongs(query), + builder: (context, recentList) { + if (recentList.connectionState == ConnectionState.done) { + final songs = recentList.requireData; + return ListView.builder( + itemCount: songs.length, + itemBuilder: (context, index) { + final song = songs[index]; + return SongWidget( + song: song, + onTap: () => + AutoRouter.of(context).push(GameConfigRoute(song: song)), + ); + }, + ); + } else { + return const LoadingWidget(); + } + }, + ); } } @override - Widget buildSuggestions(BuildContext context) { - return const LoadingWidget(); - } + Widget buildSuggestions(BuildContext context) => const LoadingWidget(); } diff --git a/lib/setting/settings_page.dart b/lib/setting/settings_page.dart index a662e725..dccbacd4 100644 --- a/lib/setting/settings_page.dart +++ b/lib/setting/settings_page.dart @@ -4,6 +4,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:device_info/device_info.dart'; import 'package:flutter/material.dart'; import 'package:flutter_email_sender/flutter_email_sender.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:in_app_review/in_app_review.dart'; @@ -18,34 +19,42 @@ class SettingsPage extends StatelessWidget { const SettingsPage({Key? key}) : super(key: key); @override - Widget build(BuildContext context) { - return Scaffold( + Widget build(BuildContext context) => Scaffold( appBar: AppBar( - title: Text(L10n.of(context)!.txt_settings, - style: Theme.of(context).appBarTheme.toolbarTextStyle)), + title: Text( + L10n.of(context)!.txt_settings, + style: Theme.of(context).appBarTheme.toolbarTextStyle, + ), + ), body: ListView( children: [ ListTile( - title: Text( - L10n.of(context)!.txt_language, - style: Theme.of(context).textTheme.headline6, - ), - subtitle: Text( - localeStrings[ - Localizations.localeOf(context).languageCode]!, - style: Theme.of(context).textTheme.subtitle1), - onTap: () => AutoRouter.of(context).push(const LocaleRoute())), + title: Text( + L10n.of(context)!.txt_language, + style: Theme.of(context).textTheme.headline6, + ), + subtitle: Text( + localeStrings[Localizations.localeOf(context).languageCode]!, + style: Theme.of(context).textTheme.subtitle1, + ), + onTap: () => AutoRouter.of(context).push(const LocaleRoute()), + ), ListTile( - title: Text( - L10n.of(context)!.txt_theme, - style: Theme.of(context).textTheme.headline6, - ), - subtitle: Consumer(builder: (context, ref, child) { + title: Text( + L10n.of(context)!.txt_theme, + style: Theme.of(context).textTheme.headline6, + ), + subtitle: Consumer( + builder: (context, ref, child) { final themeMode = ref.watch(themeModeProvider); - return Text(getThemeName(context, themeMode), - style: Theme.of(context).textTheme.subtitle1); - }), - onTap: () => AutoRouter.of(context).push(const ThemeRoute())), + return Text( + getThemeName(context, themeMode), + style: Theme.of(context).textTheme.subtitle1, + ); + }, + ), + onTap: () => AutoRouter.of(context).push(const ThemeRoute()), + ), ListTile( title: Text( L10n.of(context)!.txt_button_feedback, @@ -89,18 +98,19 @@ class SettingsPage extends StatelessWidget { }, ), ListTile( - title: Text( - L10n.of(context)!.txt_about_rate, - style: Theme.of(context).textTheme.headline6, - ), - onTap: () async { - final _inAppReview = InAppReview.instance; - await _inAppReview.openStoreListing( - appStoreId: '_appStoreId', - microsoftStoreId: '_microsoftStoreId', - ); - }), + title: Text( + L10n.of(context)!.txt_about_rate, + style: Theme.of(context).textTheme.headline6, + ), + onTap: () async { + final _inAppReview = InAppReview.instance; + await _inAppReview.openStoreListing( + appStoreId: '_appStoreId', + microsoftStoreId: '_microsoftStoreId', + ); + }, + ), ], - )); - } + ), + ); } diff --git a/lib/song_checker.dart b/lib/song_checker.dart index 586923b1..17912a5e 100644 --- a/lib/song_checker.dart +++ b/lib/song_checker.dart @@ -21,7 +21,8 @@ void main() { final temp = file.uri.toString().split('/'); final genre = temp[temp.length - 2]; Logger.root.info( - '*************************** Checking $id ***************************'); + '*************************** Checking $id ***************************', + ); final artist = id.split('-').first; final title = id.replaceAll('$artist-', '').replaceAll('_', ' '); final artist1 = artist.replaceAll('_', ' '); @@ -45,19 +46,21 @@ void main() { Logger.root.info('Select largest tempo ${song['bpm']}'); final tileChunks = createTileChunks(midiFile); final groupByDurationToPrevious = Map>.fromEntries( - groupBy( - tileChunks, (tileChunk) => tileChunk.durationToPrevious) - .entries - .toList() - ..sort((e1, e2) => e1.key.compareTo(e2.key))); + groupBy( + tileChunks, + (tileChunk) => tileChunk.durationToPrevious, + ).entries.toList() + ..sort((e1, e2) => e1.key.compareTo(e2.key)), + ); final countDurationToPrevious = { for (var e in groupByDurationToPrevious.keys) e: groupByDurationToPrevious[e]!.length }; final sortCountDurationToPrevious = Map.fromEntries( - countDurationToPrevious.entries.toList() - ..sort((e1, e2) => e1.value.compareTo(e2.value))); + countDurationToPrevious.entries.toList() + ..sort((e1, e2) => e1.value.compareTo(e2.value)), + ); final mostCountDurationToPrevious = sortCountDurationToPrevious.keys.last; for (final it in groupByDurationToPrevious.entries) { diff --git a/lib/songs/song_widget.dart b/lib/songs/song_widget.dart index d559031f..91f10a33 100644 --- a/lib/songs/song_widget.dart +++ b/lib/songs/song_widget.dart @@ -3,31 +3,29 @@ import 'package:flutter/material.dart'; import 'song.dart'; class SongWidget extends StatelessWidget { - final GestureTapCallback onTap; - final Song song; - const SongWidget({ - Key? key, required this.onTap, required this.song, + Key? key, }) : super(key: key); + final GestureTapCallback onTap; + final Song song; + @override - Widget build(BuildContext context) { - return ListTile( - onTap: onTap, - title: Text( - song.title, - style: Theme.of(context).textTheme.headline6, - ), - subtitle: song.artist.isNotEmpty - ? Text( - song.artist, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.subtitle1, - ) - : null, - ); - } + Widget build(BuildContext context) => ListTile( + onTap: onTap, + title: Text( + song.title, + style: Theme.of(context).textTheme.headline6, + ), + subtitle: song.artist.isNotEmpty + ? Text( + song.artist, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.subtitle1, + ) + : null, + ); } diff --git a/lib/songs/songs_controller.dart b/lib/songs/songs_controller.dart index 8991bfee..b70df893 100644 --- a/lib/songs/songs_controller.dart +++ b/lib/songs/songs_controller.dart @@ -1,5 +1,4 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:state_notifier/state_notifier.dart'; import 'song.dart'; import 'songs_repository_impl.dart'; @@ -17,10 +16,10 @@ final isLoadingNextPageByTagProvider = final isLoadedByTagsProvider = StateProvider.family((_, __) => false); -final songsByTagProvider = StateNotifierProvider.family>, String>((ref, tag) { - return SongsController(ref.read, tag); -}); +final songsByTagProvider = StateNotifierProvider.family< + SongsController, + AsyncValue>, + String>((ref, tag) => SongsController(ref.read, tag)); class SongsController extends StateNotifier>> { SongsController(this._read, this._tag) : super(const AsyncValue.loading()) { @@ -38,13 +37,15 @@ class SongsController extends StateNotifier>> { if (!isLoading && !isLoaded) { _read(isLoadingNextPageByTagProvider(_tag)).state = true; final titleStart = state.when( - data: (songs) => songs.isEmpty ? '' : songs.last.title, - loading: (_) => '', - error: (_, __, ___) => ''); + data: (songs) => songs.isEmpty ? '' : songs.last.title, + loading: (_) => '', + error: (_, __, ___) => '', + ); final loadedSongs = state.when( - data: (songs) => songs, - loading: (_) => [], - error: (_, __, ___) => []); + data: (songs) => songs, + loading: (_) => [], + error: (_, __, ___) => [], + ); final songs = await _read(songRepositoryProvider) .getSongsByTag(_tag, titleStart, 20); _read(isLoadingNextPageByTagProvider(_tag)).state = false; diff --git a/lib/songs/songs_repository_impl.dart b/lib/songs/songs_repository_impl.dart index f63cf741..af9f6a62 100644 --- a/lib/songs/songs_repository_impl.dart +++ b/lib/songs/songs_repository_impl.dart @@ -12,28 +12,29 @@ final songRepositoryProvider = class SongsRepositoryImpl implements SongsRepository { @override Future> getSongsByTag( - String tag, String titleStart, int limit) async { - return (await FirebaseFirestore.instance - .collection('songs') - .orderBy('title') - .where('tags', arrayContains: tag) - .startAfter([titleStart]) - .limit(limit) - .get()) - .docs - .map((e) => Song.fromJson(e.data())) - .toList(); - } + String tag, + String titleStart, + int limit, + ) async => + (await FirebaseFirestore.instance + .collection('songs') + .orderBy('title') + .where('tags', arrayContains: tag) + .startAfter([titleStart]) + .limit(limit) + .get()) + .docs + .map((e) => Song.fromJson(e.data())) + .toList(); @override - Future> searchSongs(String text) async { - return (await FirebaseFirestore.instance - .collection('songs') - .where('title', isGreaterThanOrEqualTo: text) - .where('title', isLessThanOrEqualTo: text + '\uf8ff') - .get()) - .docs - .map((e) => Song.fromJson(e.data())) - .toList(); - } + Future> searchSongs(String text) async => + (await FirebaseFirestore.instance + .collection('songs') + .where('title', isGreaterThanOrEqualTo: text) + .where('title', isLessThanOrEqualTo: '$text\uf8ff') + .get()) + .docs + .map((e) => Song.fromJson(e.data())) + .toList(); } diff --git a/lib/songs/songs_widget.dart b/lib/songs/songs_widget.dart index f3834615..cceb88b9 100644 --- a/lib/songs/songs_widget.dart +++ b/lib/songs/songs_widget.dart @@ -8,70 +8,70 @@ import 'song_widget.dart'; import 'songs_controller.dart'; class SongsWidget extends ConsumerWidget { - final String _tag; - - const SongsWidget({Key? key, required String tag}) - : _tag = tag, + const SongsWidget({ + required String tag, + Key? key, + }) : _tag = tag, super(key: key); + final String _tag; + @override Widget build(BuildContext context, WidgetRef ref) { final songsByTag = ref.watch(songsByTagProvider(_tag)); return songsByTag.when( - data: (songsByTag) => Column( - children: [ - Expanded( - child: Scrollbar( - isAlwaysShown: true, - child: NotificationListener( - onNotification: (notification) { - if (notification.metrics.pixels > 0 && - notification.metrics.atEdge) { - ref - .read(songsByTagProvider(_tag).notifier) - .loadMoreSongs(); - } - return true; - }, - child: ListView.separated( - itemCount: songsByTag.length, - itemBuilder: (context, index) { - final song = songsByTag[index]; - return SongWidget( - song: song, - onTap: () => AutoRouter.of(context) - .push(GameConfigRoute(song: song))); - }, - separatorBuilder: (context, index) { - return const Divider( - height: 4, - ); - }, - ), - ), + data: (songsByTag) => Column( + children: [ + Expanded( + child: Scrollbar( + isAlwaysShown: true, + child: NotificationListener( + onNotification: (notification) { + if (notification.metrics.pixels > 0 && + notification.metrics.atEdge) { + ref.read(songsByTagProvider(_tag).notifier).loadMoreSongs(); + } + return true; + }, + child: ListView.separated( + itemCount: songsByTag.length, + itemBuilder: (context, index) { + final song = songsByTag[index]; + return SongWidget( + song: song, + onTap: () => AutoRouter.of(context) + .push(GameConfigRoute(song: song)), + ); + }, + separatorBuilder: (context, index) => const Divider( + height: 4, ), ), - Consumer( - builder: (context, ref, child) { - final isLoadingNextPage = - ref.watch(isLoadingNextPageByTagProvider(_tag)).state; - return isLoadingNextPage - ? const Center( - child: SizedBox( - height: 32, - width: 32, - child: Padding( - padding: EdgeInsets.all(8.0), - child: CircularProgressIndicator(), - ), - ), - ) - : const SizedBox.shrink(); - }, - ) - ], + ), ), - loading: (_) => const LoadingWidget(), - error: (_, __, ___) => const LoadingWidget()); + ), + Consumer( + builder: (context, ref, child) { + final isLoadingNextPage = + ref.watch(isLoadingNextPageByTagProvider(_tag)).state; + return isLoadingNextPage + ? const Center( + child: SizedBox( + height: 32, + width: 32, + child: Padding( + padding: EdgeInsets.all(8), + child: CircularProgressIndicator(), + ), + ), + ) + : const SizedBox.shrink(); + }, + ) + ], + ), + loading: (_) => const LoadingWidget(), + error: (_, __, ___) => const LoadingWidget(), + ); } } diff --git a/lib/sound_player.dart b/lib/sound_player.dart index 4116e0f8..1f9e43a9 100644 --- a/lib/sound_player.dart +++ b/lib/sound_player.dart @@ -7,8 +7,13 @@ class SoundPlayer { Future load(Map soundPaths, Map baseNotes) { Logger.root.info('SoundPlayer load'); - return channel.invokeMethod('load', - {'soundPaths': soundPaths, 'baseNotes': baseNotes}); + return channel.invokeMethod( + 'load', + { + 'soundPaths': soundPaths, + 'baseNotes': baseNotes, + }, + ); } Future play(int note) { diff --git a/lib/splash_page.dart b/lib/splash_page.dart index 73d70aa2..e71cb70d 100644 --- a/lib/splash_page.dart +++ b/lib/splash_page.dart @@ -18,7 +18,9 @@ class SplashPage extends ConsumerWidget { } }); return const Scaffold( - body: Center( - child: Image(image: AssetImage('assets/images/img_app_icon.png')))); + body: Center( + child: Image(image: AssetImage('assets/images/img_app_icon.png')), + ), + ); } } diff --git a/lib/theme/theme_controller.dart b/lib/theme/theme_controller.dart index 21bc0e93..360526d8 100644 --- a/lib/theme/theme_controller.dart +++ b/lib/theme/theme_controller.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:state_notifier/state_notifier.dart'; import '../preferences.dart'; @@ -8,8 +7,9 @@ final themeModeProvider = StateNotifierProvider((ref) { final themeName = ref.read(sharedUtilityProvider).getThemeName(); final themeMode = ThemeMode.values.firstWhere( - (element) => element.toString() == themeName, - orElse: () => ThemeMode.system); + (element) => element.toString() == themeName, + orElse: () => ThemeMode.system, + ); return ThemeController(ref.read, themeMode); }); @@ -30,12 +30,11 @@ ThemeData buildTheme({bool isDark = false}) { const secondaryColor = Color(0xfffd7c6e); final backgroundColor = isDark ? Colors.black : Colors.white; final onBackgroundColor = isDark ? Colors.white : Colors.black; - const screenHeadingTextStyle = - TextStyle(fontSize: 32.0, color: secondaryColor); + const screenHeadingTextStyle = TextStyle(fontSize: 32, color: secondaryColor); final screenTaskNameTextStyle = - TextStyle(fontSize: 20.0, color: onBackgroundColor); + TextStyle(fontSize: 20, color: onBackgroundColor); final screenTaskDurationTextStyle = - TextStyle(fontSize: 16.0, color: onBackgroundColor); + TextStyle(fontSize: 16, color: onBackgroundColor); final textTheme = TextTheme( headline5: screenHeadingTextStyle, bodyText2: screenTaskNameTextStyle, @@ -43,24 +42,31 @@ ThemeData buildTheme({bool isDark = false}) { ); return ThemeData( elevatedButtonTheme: ElevatedButtonThemeData( - style: ElevatedButton.styleFrom( - primary: primaryColor, onPrimary: onPrimaryColor)), + style: ElevatedButton.styleFrom( + primary: primaryColor, + onPrimary: onPrimaryColor, + ), + ), outlinedButtonTheme: OutlinedButtonThemeData( - style: OutlinedButton.styleFrom( - primary: onBackgroundColor, onSurface: onBackgroundColor)), + style: OutlinedButton.styleFrom( + primary: onBackgroundColor, + onSurface: onBackgroundColor, + ), + ), scaffoldBackgroundColor: backgroundColor, tabBarTheme: TabBarTheme( - indicator: const BoxDecoration( - border: Border( - bottom: BorderSide( - color: primaryColor, - width: 2, - ), + indicator: const BoxDecoration( + border: Border( + bottom: BorderSide( + color: primaryColor, + width: 2, ), ), - labelStyle: screenTaskNameTextStyle, - labelColor: primaryColor, - unselectedLabelColor: onBackgroundColor), + ), + labelStyle: screenTaskNameTextStyle, + labelColor: primaryColor, + unselectedLabelColor: onBackgroundColor, + ), appBarTheme: AppBarTheme( toolbarTextStyle: screenHeadingTextStyle, color: backgroundColor, diff --git a/lib/theme/theme_page.dart b/lib/theme/theme_page.dart index 6c8b3b53..d2f36bfc 100644 --- a/lib/theme/theme_page.dart +++ b/lib/theme/theme_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -8,21 +9,25 @@ class ThemePage extends StatelessWidget { const ThemePage({Key? key}) : super(key: key); @override - Widget build(BuildContext context) { - return Scaffold( + Widget build(BuildContext context) => Scaffold( appBar: AppBar( - title: Text(L10n.of(context)!.txt_theme, - style: Theme.of(context).appBarTheme.toolbarTextStyle)), - body: Consumer(builder: (context, ref, child) { - final themeMode = ref.watch(themeModeProvider); - return Scrollbar( - isAlwaysShown: true, - child: ListView.builder( - itemCount: ThemeMode.values.length, - itemBuilder: (context, index) { - return RadioListTile( - title: Text(getThemeName(context, ThemeMode.values[index]), - style: Theme.of(context).textTheme.headline6), + title: Text( + L10n.of(context)!.txt_theme, + style: Theme.of(context).appBarTheme.toolbarTextStyle, + ), + ), + body: Consumer( + builder: (context, ref, child) { + final themeMode = ref.watch(themeModeProvider); + return Scrollbar( + isAlwaysShown: true, + child: ListView.builder( + itemCount: ThemeMode.values.length, + itemBuilder: (context, index) => RadioListTile( + title: Text( + getThemeName(context, ThemeMode.values[index]), + style: Theme.of(context).textTheme.headline6, + ), value: ThemeMode.values[index], groupValue: themeMode, onChanged: (value) { @@ -30,12 +35,12 @@ class ThemePage extends StatelessWidget { .read(themeModeProvider.notifier) .setThemeMode(context, value!); }, - ); - }, - ), - ); - })); - } + ), + ), + ); + }, + ), + ); } String getThemeName(BuildContext context, ThemeMode themeMode) { @@ -46,7 +51,5 @@ String getThemeName(BuildContext context, ThemeMode themeMode) { return L10n.of(context)!.light; case ThemeMode.system: return L10n.of(context)!.system; - default: - return ''; } } diff --git a/lib/timestamp_converter.dart b/lib/timestamp_converter.dart index ae287ae2..bdd76bf8 100644 --- a/lib/timestamp_converter.dart +++ b/lib/timestamp_converter.dart @@ -5,9 +5,7 @@ class TimestampConverter implements JsonConverter { const TimestampConverter(); @override - DateTime fromJson(Timestamp timestamp) { - return timestamp.toDate(); - } + DateTime fromJson(Timestamp timestamp) => timestamp.toDate(); @override Timestamp toJson(DateTime date) => Timestamp.fromDate(date); diff --git a/lib/user/user_controller.dart b/lib/user/user_controller.dart index 790a0e59..6e99b623 100644 --- a/lib/user/user_controller.dart +++ b/lib/user/user_controller.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:firebase_auth/firebase_auth.dart' as firebase; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:state_notifier/state_notifier.dart'; import '../authentication/authentication_controller.dart'; import '../instrument/instruments_controller.dart'; diff --git a/lib/user/user_page.dart b/lib/user/user_page.dart index 5cd1a33a..072e7cb9 100644 --- a/lib/user/user_page.dart +++ b/lib/user/user_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +// ignore: depend_on_referenced_packages import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; @@ -14,184 +15,199 @@ class UserPage extends StatelessWidget { const UserPage({Key? key}) : super(key: key); @override - Widget build(BuildContext context) { - return Scaffold( + Widget build(BuildContext context) => Scaffold( appBar: AppBar( - title: Text(L10n.of(context)!.txt_page_title_account, - style: Theme.of(context).appBarTheme.toolbarTextStyle)), - body: Consumer(builder: (context, ref, child) { - final user = ref.watch(userProvider); - return user.when( - data: (user) { - return Padding( - padding: const EdgeInsets.all(8), - child: Scrollbar( - isAlwaysShown: true, - child: SingleChildScrollView( - child: _buildUI(user, context)))); - }, + title: Text( + L10n.of(context)!.txt_page_title_account, + style: Theme.of(context).appBarTheme.toolbarTextStyle, + ), + ), + body: Consumer( + builder: (context, ref, child) { + final user = ref.watch(userProvider); + return user.when( + data: (user) => Padding( + padding: const EdgeInsets.all(8), + child: Scrollbar( + isAlwaysShown: true, + child: SingleChildScrollView( + child: _buildUI(user, context), + ), + ), + ), loading: (_) => const LoadingWidget(), - error: (_, __, ___) => const LoadingWidget()); - })); - } + error: (_, __, ___) => const LoadingWidget(), + ); + }, + ), + ); - Widget _buildUI(User user, BuildContext context) { - return SafeArea( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - () { - if (!user.anonymous) { - return _buildUserDetailsUI(user, context); - } else { - return const AuthenticationWidget(); - } - }(), - Card( + Widget _buildUI(User user, BuildContext context) => SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + () { + if (!user.anonymous) { + return _buildUserDetailsUI(user, context); + } else { + return const AuthenticationWidget(); + } + }(), + Card( color: Colors.transparent, elevation: 0, - child: _buildUserStatisticUI(user, context)), - ], - ), - ); - } - - Table _buildUserStatisticUI(User user, BuildContext context) { - return Table( - children: [ - TableRow(children: [ - Card( - color: Colors.transparent, - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - // if you need this - side: BorderSide( - color: Colors.grey.withOpacity(0.2), - width: 1, - ), + child: _buildUserStatisticUI(user, context), ), - child: Column( - children: [ - const SizedBox(height: 8), - const Image( - image: AssetImage('assets/images/img_star.png'), + ], + ), + ); + + Table _buildUserStatisticUI(User user, BuildContext context) => Table( + children: [ + TableRow( + children: [ + Card( + color: Colors.transparent, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // if you need this + side: BorderSide( + color: Colors.grey.withOpacity(0.2), + ), + ), + child: Column( + children: [ + const SizedBox(height: 8), + const Image( + image: AssetImage('assets/images/img_star.png'), + ), + const SizedBox(height: 8), + Text( + user.stars.toString(), + style: Theme.of(context).textTheme.subtitle1, + ), + const SizedBox(height: 8), + ], ), - const SizedBox(height: 8), - Text(user.stars.toString(), - style: Theme.of(context).textTheme.subtitle1), - const SizedBox(height: 8), - ], - ), - ), - Card( - color: Colors.transparent, - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - // if you need this - side: BorderSide( - color: Colors.grey.withOpacity(0.2), - width: 1, ), - ), - child: Column( - children: [ - const SizedBox(height: 8), - const Image( - image: AssetImage('assets/images/img_note.png'), + Card( + color: Colors.transparent, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // if you need this + side: BorderSide( + color: Colors.grey.withOpacity(0.2), + ), + ), + child: Column( + children: [ + const SizedBox(height: 8), + const Image( + image: AssetImage('assets/images/img_note.png'), + ), + const SizedBox(height: 8), + Text( + user.playedNotes.toString(), + style: Theme.of(context).textTheme.subtitle1, + ), + const SizedBox(height: 8), + ], ), - const SizedBox(height: 8), - Text(user.playedNotes.toString(), - style: Theme.of(context).textTheme.subtitle1), - const SizedBox(height: 8), - ], - ), - ), - Card( - color: Colors.transparent, - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - // if you need this - side: BorderSide( - color: Colors.grey.withOpacity(0.2), - width: 1, ), - ), - child: Column( - children: [ - const SizedBox(height: 8), - const Image( - image: AssetImage('assets/images/img_clock.png'), + Card( + color: Colors.transparent, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // if you need this + side: BorderSide( + color: Colors.grey.withOpacity(0.2), + ), ), - const SizedBox(height: 8), - Text(user.playedTime.toString().substring(0, 4).toString(), - style: Theme.of(context).textTheme.subtitle1), - const SizedBox(height: 8), - ], - ), + child: Column( + children: [ + const SizedBox(height: 8), + const Image( + image: AssetImage('assets/images/img_clock.png'), + ), + const SizedBox(height: 8), + Text( + user.playedTime.toString().substring(0, 4), + style: Theme.of(context).textTheme.subtitle1, + ), + const SizedBox(height: 8), + ], + ), + ), + ], ), - ]), - ], - ); - } + ], + ); - Card _buildUserDetailsUI(User user, BuildContext context) { - return Card( + Card _buildUserDetailsUI(User user, BuildContext context) => Card( color: Colors.transparent, elevation: 0, child: Container( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - ClipOval( - child: Image.network(user.photoUrl, - errorBuilder: (context, exception, stackTrace) { - return const Icon( - Icons.account_circle_rounded, - size: 72, - ); - }, width: 72), + padding: const EdgeInsets.all(8), + child: Row( + children: [ + ClipOval( + child: Image.network( + user.photoUrl, + errorBuilder: (context, exception, stackTrace) => const Icon( + Icons.account_circle_rounded, + size: 72, + ), + width: 72, ), - const SizedBox(width: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - user.name, - style: Theme.of(context).textTheme.headline5, - ), - const SizedBox(height: 8), - Row( - children: [ - const Icon( - Icons.today, - size: 16, - ), - const SizedBox(width: 8), - Text( - sprintf(L10n.of(context)!.txt_joined, - [DateFormat.yMMMd().format(user.creationTime)]), - style: Theme.of(context).textTheme.subtitle1, + ), + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + user.name, + style: Theme.of(context).textTheme.headline5, + ), + const SizedBox(height: 8), + Row( + children: [ + const Icon( + Icons.today, + size: 16, + ), + const SizedBox(width: 8), + Text( + sprintf( + L10n.of(context)!.txt_joined, + [DateFormat.yMMMd().format(user.creationTime)], ), - ], - ), - Row( - children: [ - const Image( - image: AssetImage('assets/images/img_guitar.png'), - width: 16, - height: 16, + style: Theme.of(context).textTheme.subtitle1, + ), + ], + ), + Row( + children: [ + const Image( + image: AssetImage('assets/images/img_guitar.png'), + width: 16, + height: 16, + ), + const SizedBox(width: 8), + Text( + sprintf( + L10n.of(context)!.txt_using, + [getInstrumentName(context, user.instrumentId)], ), - const SizedBox(width: 8), - Text(sprintf(L10n.of(context)!.txt_using, - [getInstrumentName(context, user.instrumentId)])), - ], - ), - ], - ) - ], - ))); - } + ), + ], + ), + ], + ) + ], + ), + ), + ); } diff --git a/pubspec.lock b/pubspec.lock index e495719d..24093ab0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -407,13 +407,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.9.2" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.4" flutter_localizations: dependency: "direct main" description: flutter @@ -578,13 +571,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.0.0" - lints: - dependency: transitive - description: - name: lints - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" logging: dependency: "direct main" description: @@ -642,7 +628,7 @@ packages: source: hosted version: "1.8.0" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index 5d75bb56..eddf2ac4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,10 +4,6 @@ environment: sdk: '>=2.12.0 <3.0.0' dependencies: - flutter: - sdk: flutter - flutter_localizations: - sdk: flutter auto_route: any cloud_firestore: any cloud_functions: any @@ -21,28 +17,32 @@ dependencies: firebase_crashlytics: any firebase_storage: any flame: any + flutter: + sdk: flutter flutter_cache_manager: any flutter_email_sender: any flutter_facebook_auth: any flutter_hooks: any - flutter_riverpod: ^1.0.0-dev.6 + flutter_localizations: + sdk: flutter + flutter_riverpod: ^1.0.0-dev.0 freezed_annotation: any google_sign_in: any - hooks_riverpod: ^1.0.0-dev.6 + hooks_riverpod: ^1.0.0-dev.0 in_app_review: any intl: any json_annotation: any logging: any package_info: any + path_provider: any shared_preferences: any sprintf: any dev_dependencies: auto_route_generator: any build_runner: any - flutter_launcher_icons: any flutter_flavorizr: any - flutter_lints: any + flutter_launcher_icons: any flutter_native_splash: any freezed: any json_serializable: any