From 6efa43f3bb6c693460daf5b0c7ac8aeb088f8e56 Mon Sep 17 00:00:00 2001 From: plotsklapps Date: Tue, 11 Jul 2023 16:16:27 +0200 Subject: [PATCH] - Refactored a LOT; - Added RydMike's linter; - Satisfied linter; - Changed Snackbars, but not working everywhere. --- all_lint_rules.yaml | 218 +++++ analysis_options.yaml | 868 +++++++++++++++++- lib/all_imports.dart | 13 +- lib/components/delete_keys_dialog.dart | 82 -- lib/components/deletekeys_dialog.dart | 52 ++ lib/components/feedscreen_appbar.dart | 6 +- lib/components/feedscreen_card.dart | 8 +- lib/components/feedscreen_fab.dart | 115 +-- lib/components/keys_exist_dialog.dart | 126 --- .../keys_option_modal_bottom_sheet.dart | 58 -- lib/components/keysexist_dialog.dart | 84 ++ lib/components/keysoption_bottomsheet.dart | 46 + lib/components/noost_curve_button.dart | 42 - lib/components/noost_ok_button.dart | 33 - lib/components/noost_popup_menu.dart | 90 -- lib/components/noost_snackbar.dart | 46 - lib/components/noost_text_button.dart | 27 - lib/components/noost_text_form_field.dart | 31 - lib/components/paste_private_key_dialog.dart | 70 -- lib/components/pasteprivatekey_dialog.dart | 51 + lib/components/scaffold_appbar.dart | 2 +- lib/components/scaffold_drawer.dart | 20 +- lib/models/timeago.dart | 12 +- lib/providers/feedscreen_providers.dart | 27 +- lib/providers/theme_providers.dart | 4 +- lib/responsive_layout.dart | 2 +- lib/screens/feed_screen.dart | 282 +++--- lib/screens/scaffold_screen.dart | 2 +- pubspec.lock | 16 - pubspec.yaml | 13 +- 30 files changed, 1587 insertions(+), 859 deletions(-) create mode 100644 all_lint_rules.yaml delete mode 100644 lib/components/delete_keys_dialog.dart create mode 100644 lib/components/deletekeys_dialog.dart delete mode 100644 lib/components/keys_exist_dialog.dart delete mode 100644 lib/components/keys_option_modal_bottom_sheet.dart create mode 100644 lib/components/keysexist_dialog.dart create mode 100644 lib/components/keysoption_bottomsheet.dart delete mode 100644 lib/components/noost_curve_button.dart delete mode 100644 lib/components/noost_ok_button.dart delete mode 100644 lib/components/noost_popup_menu.dart delete mode 100644 lib/components/noost_snackbar.dart delete mode 100644 lib/components/noost_text_button.dart delete mode 100644 lib/components/noost_text_form_field.dart delete mode 100644 lib/components/paste_private_key_dialog.dart create mode 100644 lib/components/pasteprivatekey_dialog.dart diff --git a/all_lint_rules.yaml b/all_lint_rules.yaml new file mode 100644 index 0000000..ebb5995 --- /dev/null +++ b/all_lint_rules.yaml @@ -0,0 +1,218 @@ +linter: + rules: + - always_declare_return_types + - always_put_control_body_on_new_line + - always_put_required_named_parameters_first + - 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_final_parameters + - 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_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 + - collection_methods_unrelated_type + - combinators_ordering + - comment_references + - conditional_uri_does_not_exist + - constant_identifier_names + - control_flow_in_finally + - curly_braces_in_flow_control_structures + - dangling_library_doc_comments + - depend_on_referenced_packages + - deprecated_consistency + - deprecated_member_use_from_same_package + - diagnostic_describe_all_properties + - directives_ordering + - discarded_futures + - 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 + - implicit_call_tearoffs + - implicit_reopen + - invalid_case_patterns + - join_return_with_assignment + - leading_newlines_in_multiline_strings + - library_annotations + - library_names + - library_prefixes + - library_private_types_in_public_api + - lines_longer_than_80_chars + - literal_only_boolean_expressions + - matching_super_parameters + - missing_whitespace_between_adjacent_strings + - no_adjacent_strings_in_list + - no_default_cases + - no_duplicate_case_values + - no_leading_underscores_for_library_prefixes + - no_leading_underscores_for_local_identifiers + - no_literal_bool_comparisons + - no_logic_in_create_state + - no_runtimeType_toString + - no_self_assignments + - no_wildcard_variable_uses + - 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_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 + - secure_pubspec_urls + - sized_box_for_whitespace + - sized_box_shrink_expand + - 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 + - type_literal_in_constant_pattern + - unawaited_futures + - unnecessary_await_in_return + - unnecessary_brace_in_string_interps + - unnecessary_breaks + - unnecessary_const + - unnecessary_constructor_name + - unnecessary_final + - unnecessary_getters_setters + - unnecessary_lambdas + - unnecessary_late + - unnecessary_library_directive + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_aware_operator_on_extension_on_nullable + - 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 + - unnecessary_to_list_in_spreads + - unreachable_from_main + - unrelated_type_equality_checks + - unsafe_html + - use_build_context_synchronously + - use_colored_box + - use_decorated_box + - use_enums + - 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_string_in_part_of_directives + - use_super_parameters + - 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 61b6c4d..df03dae 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,29 +1,851 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. +# RydMike LINTER Preferences v2.0.1 # -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. +# Get this file here: https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c +# +# We include and activate all lint rules, later below we disable the not used or desired ones. +# You can find a list of all lint rules to put in your all_lint_rules.yaml file here: +# https://dart-lang.github.io/linter/lints/options/options.html +# +# For a full comparison of all lint rules settings in rule styles listed below, please see this +# sheet: https://docs.google.com/spreadsheets/d/1Nc1gFjmCOMubWZD7f2E4fLhWN7LYaOE__tsA7bf2NjA +# +# The version used for comparing setting with other linters for the settings +# that are turned OFF here, or have a quick placeholder for turning it OFF, are +# as follows: +# +# Core v2.1.0 : https://pub.dev/packages/lints +# Recommended v2.0.0 : https://pub.dev/packages/lints +# Flutter Lints v2.1.0 : https://pub.dev/packages/flutter_lints +# Pedantic v1.11.1 : https://pub.dev/packages/pedantic +# Effective Dart v1.3.2 : https://pub.dev/packages/effective_dart +# Flutter repo master : https://github.com/flutter/flutter/blob/master/analysis_options.yaml +# Lint v1.8.2 : https://pub.dev/packages/lint +# VG Analysis v3.0.0 : https://pub.dev/packages/very_good_analysis +# RydMike v2.0.1 : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c +# +include: all_lint_rules.yaml +analyzer: + exclude: + - "**/*.g.dart" + - "**/*.freezed.dart" + - "test/.test_coverage.dart" + - "bin/cache/**" + - "lib/generated_plugin_registrant.dart" + + # For more information see: + # https://dart.dev/guides/language/analysis-options#enabling-additional-type-checks + language: + strict-casts: true + strict-inference: true + strict-raw-types: true + + errors: + # Without ignore here, we cause import of all_lint_rules to warn, because some rules conflict. + # We explicitly enabled even conflicting rules and are fixing the conflicts in this file. + # Put it to warning temporarily if you need to troubleshoot lint rule settings. + included_file_warning: ignore + + # Treat missing required parameters as an error, not as a hint or a warning. + missing_required_param: error + + # Treat missing returns as an error, not as a hint or a warning. + missing_return: error + + # Allow self-reference to deprecated members. This is done because otherwise we have + # to annotate every member in every test, assert, etc. when we deprecate something. + deprecated_member_use_from_same_package: ignore -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml + # DON'T assign new values to parameters of methods or functions. + # + # https://dart-lang.github.io/linter/lints/parameter_assignments.html + # + # Treats assigning new values to a parameter as a warning. We would almost like to set this + # to an error. However, this warning rule, or even more so if you set it to be an error, may + # be a bit problematic if/when you include other code directly that does it a lot. + # It does, however, make code safer when this cannot be done without involving + # an extra local variable for clarity and safety. Enabling this error, even as just a warning, + # does get in the way a bit if all you want to do is a null to default value release runtime + # safety/fallback assignment. For that use case, you have to add a local rule override. With + # null-safety, the need for this kind of null check and re-assignment to default if null, + # is rarely needed. Considering the comment in: + # https://dart-lang.github.io/linter/lints/parameter_assignments.html: + # "Assigning new values to parameters is generally a bad practice unless an operator + # such as ??= is used. Otherwise, arbitrarily reassigning parameters is usually a mistake." + # One might even think the rule would allow using the ??= operator, but it does not. For now, + # we keep this lint as warning and overriding locally with: + # + # When we need it for the ??= operator, or some copy/pasted in code that does things that + # require it, that we don't want to deal with fixing at the moment. + parameter_assignments: warning + # Allow having TODOs in the code. + todo: ignore + +# LINTER Preferences +# +# Explicitly disable only the rules we do not want. linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + # ALWAYS separate the control structure expression from its statement. + # + # https://dart-lang.github.io/linter/lints/always_put_control_body_on_new_line.html + # + # This sometimes makes things more unclear when one line is enough. Also, single line if:s are + # fine and also recommended in Effective Dart "DO format your code using dartfmt". + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo enabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + always_put_control_body_on_new_line: false + + # ALWAYS specify @required on named parameter before other named parameters. + # + # https://dart-lang.github.io/linter/lints/always_put_required_named_parameters_first.html + # + # Conflicts with the convention used by Flutter, which puts `Key key` first + # and `@required Widget child` last. + # + # Other known linters use: + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + always_put_required_named_parameters_first: false + + # ALWAYS specify type annotations. + # + # https://dart-lang.github.io/linter/lints/always_specify_types.html + # + # Avoid var when specifying that a type is unknown and short-hands that elude type annotations. Use + # dynamic if you are being explicit that the type is unknown. Use Object if you are being explicit + # that you want an object that implements == and hashCode. + # The linter rule link above states this rule is from the Flutter style guide. + # + # This makes most code intent very explicit, sometimes this may help you + # reason about unfamiliar libs, but it might get tedious when dealing with very familiar ones. + # For devs used to more relaxed or no type declaration, it is probably the other way around. + # This rule is, of course, also in conflict with some other lint rules. Most notably, it + # violates Effective Dart "AVOID type annotating initialized local variables". + # https://dart-lang.github.io/linter/lints/omit_local_variable_types.html + # Which we find to be a strange rule, the package lint agrees with the statement that + # "Types for local variables may improve readability" and keeps that avoid rule disabled. + # + # Turning always_specify_types lint rule on in a project at a later stage is very tedious, + # fixing all the analyzer warnings will take quite some time. Having it on as you write new code + # is not so bad though, the IDE will handle it most of the time. + # + # Most people probably want this lint rule OFF, but for now we keep it on in our projects. + # We might reconsider this choice later. For example, this issue has requested + # a new softer related lint rule that could be used only for declarations: + # https://github.com/dart-lang/linter/issues/1620 + # If such a lint rule materializes, we might switch to using it instead and turn off this lint. + # + # Using always_specify_type ON lint like Flutter repo does, makes it easier to reason about + # unfamiliar codebases, especially when you read it on GitHub where the IDE cannot be used to + # look into what type an object is. + # + # We felt the above long explanation was warranted as a reminder. Keeping the rule listed here + # and the setting below, in order to easily turn it OFF permanently some day, or in some + # projects. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo enabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike enabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + always_specify_types: true + + # ALWAYS use package imports for files in lib/. + # + # https://dart-lang.github.io/linter/lints/always_use_package_imports.html + # + # This rule conflicts with `prefer_relative_imports` so we turn it OFF. + # We are still conflicted about which version to use, keeping it this way for now. Support + # for relative imports has improved in both IDEs. Adding imports still often get imported as + # package imports, and then you have to edit them manually. The IDEs can help with fixing them. + # The relative paths can be a bit messy to keep track off, package imports are actually + # a bit easier from that point of view. + # Flutter repo now also prefers relative imports over package imports, so that is + # another reason to use that. + # + # Use what you prefer, but you have to be consistent though, since mixing and matching can + # cause issues as the same file imported with the different options are considered to be + # different libs and code, even if it is the same file. This may impact the functionality + # of e.g. singletons, service locators and increase code size. + # + # When you refactor and move folders with a lot of code in them, that other code depends + # on for imports via relative imports, then they get messed up by Flutter IDEs + # VS-Code and AS/IntelliJ. Both main Flutter IDEs may fail to correctly refactor moved folders + # and imports that depend on files in the moved folders. + # + # Other known linters use: + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + always_use_package_imports: true + prefer_relative_imports: false + + # AVOID annotating with dynamic when not required. + # + # https://dart-lang.github.io/linter/lints/avoid_annotating_with_dynamic.html + # + # Violates Effective Dart "PREFER annotating with dynamic instead of letting inference fail", it + # also conflicts with strong mode disabling `implicit-dynamic`. Turning it OFF. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + avoid_annotating_with_dynamic: false + + # AVOID catches without on clauses. + # + # https://dart-lang.github.io/linter/lints/avoid_catches_without_on_clauses.html + # + # Using catch clauses without on clauses makes your code prone to encountering unexpected + # errors that won't be thrown (and thus will go unnoticed). However, there are situations + # where we voluntarily want to catch everything, especially as a library. + # See https://github.com/dart-lang/linter/issues/3023 + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart enabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + avoid_catches_without_on_clauses: false + + # AVOID defining a class that contains only static members. + # + # https://dart-lang.github.io/linter/lints/avoid_classes_with_only_static_members.html + # + # Creating classes with the sole purpose of providing utility, or otherwise static methods, is + # discouraged in effective Dart. Dart allows functions to exist outside of classes for this + # very reason. Effective Dart says avoid classes with only static members: + # https://dart.dev/guides/language/effective-dart/design#avoid-defining-a-class-that-contains-only-static-members + # However, the Flutter style guide says use them when it makes sense: + # https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#begin-global-constant-names-with-prefix-k + # Colors is an example of such a class, but they still enable this rule in the repo though, go figure. + # + # Like Pedantic, we like util and static classes too, so we use them. + # We tried the Effective Dart style and used kConstants in different const files. This + # is more cumbersome to use than static classes. The import is simpler with static classes and + # the code looks cleaner. If you use a lot of constant files, importing them is more tedious, + # and you cannot enforce a given 'as' name to have a consistent name space prefix. A static + # class gives you that automatically, thus providing context for the constants and static functions. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart enabled : https://pub.dev/packages/effective_dart + # Flutter repo enabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint enabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + avoid_classes_with_only_static_members: false + + # AVOID declaring parameters as final. + # + # https://dart-lang.github.io/linter/lints/avoid_final_parameters.html + # + # Declaring parameters as final can lead to unnecessarily verbose code, + # especially when using the "parameter_assignments" rule. + # + # This rule is turned off, so we can define final parameters when it makes + # sense to do so without triggering a lint rule. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + avoid_final_parameters: false + + # AVOID positional boolean parameters. + # + # https://dart-lang.github.io/linter/lints/avoid_positional_boolean_parameters.html + # + # Positional boolean parameters are considered a bad practice because they are very ambiguous. + # Using named boolean parameters is much more readable because it inherently describes + # what the boolean value represents. + # In principle, we agree with the argument against positional booleans. However, positional booleans + # are OK when they are the ONLY boolean parameter in a callback, and also very handy when used in a + # model setter from the callback directly. + # + # Flutter API contains many callbacks with the signature: {void Function(bool) onChanged} often + # for UI toggle switches. To keep things tidy and clean with a model setter for such a callback, + # a setter method with a positional boolean is needed, a typical pattern is: + # Switch.adaptive( + # value: model.hideTooltips, + # onChanged: model.setHideTooltips, + # ), + # + # We are turning OFF this AVOID rule. Willing to reconsider if I get convinced there are better ways, + # and it does not get in the way of single none named bool callbacks. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart enabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint enabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + avoid_positional_boolean_parameters: false + + # AVOID print calls in production code. + # + # https://dart-lang.github.io/linter/lints/avoid_print.html + # + # Our default is to have this rule enabled. + # + # In example apps you may want to print to the console. You may want to do so during development + # too. We keep the rule here, to handily disable/enable as and when needed. This lint rule is + # a good way to find print statements that you may have used during development in code that + # should not have them in production, so at least before committing the code in such + # projects, make sure to keep this rule enabled by commenting it out here. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints enabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo enabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint enabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + # RELEASE: enabled : By commenting it out. (default) + # DEVELOPMENT: disabled : Uncomment below if the warnings bother you during dev or making a console app. + # + # avoid_print: false + + # AVOID redundant argument values. + # + # https://dart-lang.github.io/linter/lints/avoid_redundant_argument_values.html + # + # Using redundant (default) argument values can be useful for in-code documentation + # purposes and also handy as a template when trying different settings in Flutter. It is often + # quicker when dealing with not well-known APIs to see parameter values in the call/constructor, + # instead of using the IDE to peek into its default to figure out what the defaults are. + # Occasionally, leaving a few redundant default valued parameters in the code is not that bad + # when you are developing something new. For public packages, you probably want to keep this + # rule enabled. I like to sometimes be explicit and specify values that are the same as + # default one, mostly to make an unfamiliar API more readable on GitHub. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo enabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint enabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + # PACKAGE: enabled : By commenting it out below, often a good idea in packages. + # APPLICATION: disabled : With false value. + avoid_redundant_argument_values: false + + # AVOID annotating types for function expression parameters. + # + # https://dart-lang.github.io/linter/lints/avoid_catches_without_on_clauses.html + # + # Annotating types for function expression parameters is usually unnecessary because the + # parameter types can almost always be inferred from the context, thus making the practice redundant. + # However, since we are using `always_specify_types`, we should not have this one ON either + # as it conflicts with it. Even if you do not do that, we still recommend keeping this rule OFF. + # While always specifying the type on callbacks is certainly a bit tedious and not necessary, + # it can sometimes improve readability, so let's not force them to not be allowed. + # Thus, even if you don't use `always_specify_types`, it is possible to sometimes specify + # them on closures when it improves the readability of unfamiliar closures. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart enabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + avoid_types_on_closure_parameters: false + + # DO Use the cascading style when successively invoking methods on the same reference. + # + # https://dart-lang.github.io/linter/lints/cascade_invocations.html + # + # We disable this rule, just a personal preference, using them is fine though, + # but let's not enforce it. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + cascade_invocations: false + + # DO invoke close on instances of dart.core.Sink. + # + # https://dart-lang.github.io/linter/lints/close_sinks.html + # + # Disabling it, may generate false positives. https://github.com/dart-lang/linter/issues/1381. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + #close_sinks: false + + # DO reference all public properties in debug method implementations. + # + # https://dart-lang.github.io/linter/lints/diagnostic_describe_all_properties.html + # + # Consider using this lint rule if you are making a public Flutter package, for private ones and private apps + # we recommend keeping it off as you probably won't be making diagnostic properties for all your + # classes, unless you are using a data class lib that does it for you via code generation. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Lint disabled : https://pub.dev/packages/lint + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + # PACKAGE: enabled : By commenting it out. + # APPLICATION: disabled : With false value. (Default, assume we are making an app most of the time.) + diagnostic_describe_all_properties: false + + # DO Use Flutter TO-DO format. + # + # https://dart-lang.github.io/linter/lints/flutter_style_todos.html + # + # Use Flutter-style todos with GitHub username. Useful if you are coding in a + # larger team, if not, you may also consider turning off the rule by removing + # its comment below. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo enabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike enabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + #flutter_style_todos: false + + # AVOID lines longer than 80 characters + # + # https://dart-lang.github.io/linter/lints/lines_longer_than_80_chars.html + # + # Using this rule will sometimes force a line of 81 characters to be split in two. + # As long as we try to respect the 80-character limit, going slightly above is fine. + # + # For packages, keep this rule enabled though, because the pub.dev dart format check will + # penalize package points if it does not adhere to strict Dart format rules, which + # requires max 80 char lines. This rule will then help you find cases where you go over + # and fix them manually when possible. Ironically, Flutter repo violates this rule, but + # if you do it in a package for pub.dev you get a score deduction. We often disable this rule + # if this is not a package, so we keep it listed here as a handy toggle. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart enabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + # PACKAGE: enabled : By commenting it out. (Default, even if it is not a package, we start with this.) + # APPLICATION: disabled : With false value. (When/if short lines become problematic. We sometimes like 100 chars.) + # lines_longer_than_80_chars: false + + # DO define default behavior outside switch statements. + # + # https://dart-lang.github.io/linter/lints/no_default_cases.html + # + # An experimental lint rule maturity wise. I enabled it, it seems to work well. + # Remove the comment below if it is causing issues. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo enabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike enabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + #no_default_cases: false + + # CONSIDER omitting type annotations for local variables. + # + # https://dart-lang.github.io/linter/lints/omit_local_variable_types.html + # + # Conflicts with 'always_specify_types' that is used, so then we can't have this rule either, + # besides we like being verbose and specific. Why and when would omitting the type for local + # variables be a good thing anyway, be specific, is our take on this. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic enabled : https://pub.dev/packages/pedantic + # Effective Dart enabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + omit_local_variable_types: false + + # PREFER asserts with a message string. + # + # https://dart-lang.github.io/linter/lints/prefer_asserts_with_message.html + # + # When assertions fail, it's not always simple to understand why. Adding a message to the + # assert function helps the developer to understand why the AssertionError occurs. + # + # While this is true, Dart does nowadays create very clear messages from assert-terms by default. + # Flutter SDK does not use this rule or style. When you use code from it for customized + # widgets, you will end up having to write your own messages for the code snippet. + # + # Rationale for not using this in Flutter SDK: + # "Assertions blocks don't require a message because they throw simple to understand errors" + # + # We agree, so we do not mind turning OFF this rule when it becomes tedious, but we start + # with it ON. With NNBD, you also usually need fewer asserts than you did before in Dart code, + # since NNBD made almost all "not null" assertions unnecessary. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + # PACKAGE: enabled : By commenting it out. (default) + # APPLICATION: disabled : With false value. (If it gets tedious in an app, we may turn it off) + # prefer_asserts_with_message: false + + # PREFER to define constructors, instead of static methods to create instances. + # + # https://dart-lang.github.io/linter/lints/prefer_constructors_over_static_methods.html + # + # Dart has named constructors. Static methods in other languages (java) are a workaround to + # not having named constructors. + # + # We don't mind this lint rule, it is OK, BUT we noticed that + # if you want/need to create instances of classes via static helpers in another class, that + # this lint rules complains about it. We are OK with preferring a named constructor over a + # static method to create an instance from within the same class. However, this lint rule + # complained about the above usage too, where we think it makes sense to use a static method. + # This rule currently complains about use cases that in some scenarios are impossible to comply + # with. Maybe this is another issue with this lint rule. We should investigate it further and + # report it if it is an issue. For now, we disable this rule. + # A past now resolved issue with this lint rule was: https://github.com/dart-lang/linter/issues/2149 + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint enabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + prefer_constructors_over_static_methods: false + + # DO use double quotes where they wouldn't require additional escapes. + # + # https://dart-lang.github.io/linter/lints/prefer_double_quotes.html + # + # This rule is mostly about what style you want to use and enforce, if any. + # It of course conflicts with rule: + # `prefer_single_quotes` : "DO use single quotes where they wouldn't require additional escapes." + # https://dart-lang.github.io/linter/lints/prefer_single_quotes.html + # + # For us single quotes are easier to type. On our ISO keyboards it is next to Enter key, and + # we don't need the Shift plus the far to reach nr 2 key on R1 to type it. Also, we don't think + # they compromise on readability. + # Then again, if you don't care and don't mind mixing and matching, then ALSO + # turning OFF `prefer_single_quotes` works fine too, and then you can use both options. + # + # We thought it was cleaner to stick to one style. Single quotes are easier to type for us, + # thus we turn OFF this `prefer_double_quotes` rule. There is another lint rule that recommends + # you to use double quotes when you otherwise would need to escape the single quote char, it works + # well when you use the prefer_single_quotes rule as well. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + prefer_double_quotes: false + + # CONSIDER using => for short members whose body is a single return statement. + # + # https://dart-lang.github.io/linter/lints/prefer_expression_function_bodies.html + # + # Certainly a good idea in many cases, but not always. For example, not always suitable for + # Flutter, which may have a `build` method with a single return, but that return is still + # complex enough that a "body" is worth it, and it might not even fit on a single line. + # https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + prefer_expression_function_bodies: false + + # DO prefer declaring parameters as final if they are not reassigned in the function body. + # + # https://dart-lang.github.io/linter/lints/prefer_final_parameters.html + # + # Declaring parameters as final when possible is a good practice because it helps + # avoid accidental reassignments. + # + # Certainly a good idea in many cases. There seems to be one "small" false positive issue with it. + # Lint is triggered by final constructor properties, e.g. in + # `final int i;` the parameter `this.i` is not also final, which is not really needed + # since the property is final. However, this triggers the rule unnecessarily. We had to + # turn OFF this rule due to it. + # + # We turned OFF the rule. In a test project, after we cleaned up all that could be after Flutter 2.5 upgrade. + # There were still 150 positives from the rule, from above issue. So after having it on and doing cleanup + # where it could be used, we turned off the rule for now. Pity, it is a useful and nice rule otherwise. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + prefer_final_parameters: false + + # DO use int literals rather than the corresponding double literal. + # + # https://dart-lang.github.io/linter/lints/prefer_int_literals.html + # + # This rule goes against the preferred style of being explicit with + # declarations and hides when a number is double, since we cannot declare it + # as 0.0 or 1.0 when it is double, it has to be 0 or 1, making it look + # like an integer, even if it is not. Sometimes doing that is OK, but let's + # not enforce it. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + prefer_int_literals: false + + # DO document all public members. + # + # https://dart-lang.github.io/linter/lints/public_member_api_docs.html + # + # All non-overriding public members should be documented with /// doc-style comments. + # Not necessary for an app or the examples in a pub.dev package. I always enable this for + # public packages. + # + # NOTE: There is also the lint rule "package_api_docs", that is enabled as well via all being enabled. + # https://dart-lang.github.io/linter/lints/package_api_docs.html + # "DO provide doc comments for all public APIs.", is what it is supposed to do, but only for + # packages. However, if we turn OFF the rule "public_member_api_docs", then the + # "package_api_docs" offers no warnings on missing API doc comments alone. So our conclusion + # for now is that this rule has to be used instead to ensure we find all APIs that should + # have documentation comments in a package as well. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart enabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + # PACKAGE: enabled : By commenting it out. (My default, I start with this) + # APPLICATION: disabled : With false value. (But usually uncomment the false value if it is an app) + public_member_api_docs: false + + # DO use trailing commas for all function calls and declarations unless the function call or + # definition, from the start of the function name up to the closing parenthesis, + # fits in a single line. + # + # https://dart-lang.github.io/linter/lints/require_trailing_commas.html + # + # This rule forces commas even in places where it just adds extra lines, that + # adds little value. There is also not a bulk dart fix for it: + # https://github.com/dart-lang/sdk/issues/47441 + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint enabled : https://pub.dev/packages/lint + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + require_trailing_commas: true + + # DO sort constructor declarations before other members. + # + # We do like this lint rule, but we want to have the default constructor first, followed + # by its properties, after this, other named constructors and factories. This rule gets + # in the way of that and forces you to put (often final) constructor properties after all + # the named constructors and factories, making them tedious to find and disconnected from + # where we want to see, read and handily edit them. This is especially the case if there are + # many constructors and factories, and they have a lot of parameters. For now, we disable + # this rule and order things as described above, which apart from the default constructor + # properties coming right after the constructor, is the only part where we in practice + # deviate from this rule, so other yes, we do put constructors first as well anyway. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Flutter repo enabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # Lint disabled : https://pub.dev/packages/lint + # Discussion https://github.com/passsy/dart-lint/issues/1 + # VG Analysis enabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + sort_constructors_first: false + + # DON'T use final for local variables. + # + # https://dart-lang.github.io/linter/lints/unnecessary_final.html + # + # Incompatible with `prefer_final_locals` that we want because having immutable local variables when + # applicable makes larger functions more predictable and easier to reason about, so we + # use `prefer_final_locals` instead. + # + # Other known linters use: + # + # Core disabled : https://pub.dev/packages/lints + # Recommended disabled : https://pub.dev/packages/lints + # Flutter Lints disabled : https://pub.dev/packages/flutter_lints + # Pedantic disabled : https://pub.dev/packages/pedantic + # Effective Dart disabled : https://pub.dev/packages/effective_dart + # Lint disabled : https://pub.dev/packages/lint + # Flutter repo disabled : https://github.com/flutter/flutter/blob/master/analysis_options.yaml + # VG Analysis disabled : https://pub.dev/packages/very_good_analysis + # RydMike disabled : https://gist.github.com/rydmike/fdb53ddd933c37d20e6f3188a936cd4c + unnecessary_final: false diff --git a/lib/all_imports.dart b/lib/all_imports.dart index 6a12642..6457522 100644 --- a/lib/all_imports.dart +++ b/lib/all_imports.dart @@ -1,17 +1,12 @@ export 'dart:convert'; -export 'package:cosanostr/components/delete_keys_dialog.dart'; +export 'package:cosanostr/components/deletekeys_dialog.dart'; export 'package:cosanostr/components/feedscreen_appbar.dart'; export 'package:cosanostr/components/feedscreen_card.dart'; export 'package:cosanostr/components/feedscreen_fab.dart'; -export 'package:cosanostr/components/keys_exist_dialog.dart'; -export 'package:cosanostr/components/keys_option_modal_bottom_sheet.dart'; -export 'package:cosanostr/components/noost_curve_button.dart'; -export 'package:cosanostr/components/noost_ok_button.dart'; -export 'package:cosanostr/components/noost_snackbar.dart'; -export 'package:cosanostr/components/noost_text_button.dart'; -export 'package:cosanostr/components/noost_text_form_field.dart'; -export 'package:cosanostr/components/paste_private_key_dialog.dart'; +export 'package:cosanostr/components/keysexist_dialog.dart'; +export 'package:cosanostr/components/keysoption_bottomsheet.dart'; +export 'package:cosanostr/components/pasteprivatekey_dialog.dart'; export 'package:cosanostr/components/phone_container.dart'; export 'package:cosanostr/components/scaffold_appbar.dart'; export 'package:cosanostr/components/scaffold_drawer.dart'; diff --git a/lib/components/delete_keys_dialog.dart b/lib/components/delete_keys_dialog.dart deleted file mode 100644 index 5bd3a4e..0000000 --- a/lib/components/delete_keys_dialog.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:cosanostr/all_imports.dart'; - -class DeleteKeysDialog extends StatelessWidget { - const DeleteKeysDialog({ - super.key, - required this.onNoPressed, - required this.onYesPressed, - }); - - final void Function()? onNoPressed; - final void Function()? onYesPressed; - - @override - Widget build(BuildContext context) { - return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Container( - constraints: const BoxConstraints(maxWidth: 600), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Colors.white, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: const EdgeInsets.symmetric(vertical: 24), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Colors.redAccent, - ), - child: const Center( - child: Text( - 'Delete Keys!', - style: TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ), - ), - const SizedBox(height: 24), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Do you want to delete your keys?', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.grey[700], - ), - ), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - NoostTextButton( - onPressed: onNoPressed, - label: 'NO', - color: Colors.grey, - ), - NoostOkButton( - onPressed: onYesPressed, - label: 'YES', - ), - ], - ), - ], - ), - ) - ], - ), - ), - ); - } -} diff --git a/lib/components/deletekeys_dialog.dart b/lib/components/deletekeys_dialog.dart new file mode 100644 index 0000000..05a64ce --- /dev/null +++ b/lib/components/deletekeys_dialog.dart @@ -0,0 +1,52 @@ +import 'package:cosanostr/all_imports.dart'; + +// The Dialog that is only shown to user when keys already +// exist and the trashcan icon is clicked on the FeedScreen(). +// Takes two functions, onNoPressed to cancel and onYesPressed +// to delete the keys from this client and go anonymous again. +class DeleteKeysDialog extends ConsumerWidget { + const DeleteKeysDialog({ + super.key, + required this.onNoPressed, + required this.onYesPressed, + }); + + final void Function() onNoPressed; + final void Function() onYesPressed; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return AlertDialog( + icon: const Icon(FontAwesomeIcons.circleExclamation), + title: const Text('DELETE KEYS'), + content: const Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Are you sure you want to delete your keys?', + textAlign: TextAlign.center, + ), + Divider(), + Text( + "This action is irreversible, so make sure you've stored your " + 'nsec somewhere safe.', + textAlign: TextAlign.center, + ), + ], + ), + actions: [ + TextButton( + onPressed: onNoPressed, + child: const Text('CANCEL'), + ), + IconButton( + onPressed: onYesPressed, + icon: const Icon( + FontAwesomeIcons.solidTrashCan, + color: Colors.red, + ), + ), + ], + ); + } +} diff --git a/lib/components/feedscreen_appbar.dart b/lib/components/feedscreen_appbar.dart index 0cfbc93..888fe37 100644 --- a/lib/components/feedscreen_appbar.dart +++ b/lib/components/feedscreen_appbar.dart @@ -2,13 +2,13 @@ import 'package:cosanostr/all_imports.dart'; class FeedScreenAppBar extends ConsumerWidget implements PreferredSizeWidget { const FeedScreenAppBar({ - Key? key, + super.key, required this.title, this.keysDialog, this.popupMenu, required this.isConnected, this.deleteKeysDialog, - }) : super(key: key); + }); final String title; final Widget? keysDialog; @@ -28,7 +28,7 @@ class FeedScreenAppBar extends ConsumerWidget implements PreferredSizeWidget { elevation: 0, title: Text(title), centerTitle: true, - actions: [ + actions: [ popupMenu ?? Container(), deleteKeysDialog ?? Container(), ], diff --git a/lib/components/feedscreen_card.dart b/lib/components/feedscreen_card.dart index f82dd40..10b35b5 100644 --- a/lib/components/feedscreen_card.dart +++ b/lib/components/feedscreen_card.dart @@ -10,14 +10,14 @@ class FeedScreenCard extends StatelessWidget { List? extractImage(String text) { final RegExp exp = RegExp( - r"(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|gif|png|jpeg)", + r'(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|gif|png|jpeg)', caseSensitive: false, multiLine: true, ); final Iterable matches = exp.allMatches(text); - final List imageLinks = matches.map((match) { + final List imageLinks = matches.map((Match match) { return match.group(0)!; }).toList(); @@ -31,7 +31,7 @@ class FeedScreenCard extends StatelessWidget { margin: const EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ + children: [ ListTile( leading: CircleAvatar( backgroundImage: FadeInImage( @@ -52,7 +52,7 @@ class FeedScreenCard extends StatelessWidget { Card( child: Center( child: Stack( - children: [ + children: [ const Placeholder( fallbackHeight: 200, color: Colors.transparent, diff --git a/lib/components/feedscreen_fab.dart b/lib/components/feedscreen_fab.dart index 6d36b9a..e840f1d 100644 --- a/lib/components/feedscreen_fab.dart +++ b/lib/components/feedscreen_fab.dart @@ -2,12 +2,12 @@ import 'package:cosanostr/all_imports.dart'; class FeedScreenFAB extends StatefulWidget { const FeedScreenFAB({ - Key? key, + super.key, required this.publishNote, required this.isNotePublishing, - }) : super(key: key); + }); - final Function(String?) publishNote; + final void Function(String?) publishNote; final bool isNotePublishing; @override @@ -17,8 +17,9 @@ class FeedScreenFAB extends StatefulWidget { } class FeedScreenFABState extends State { - final noteController = TextEditingController(); - final GlobalKey formKey = GlobalKey(); + final TextEditingController noteController = TextEditingController(); + final GlobalKey> formKey = + GlobalKey>(); @override Widget build(BuildContext context) { @@ -26,7 +27,7 @@ class FeedScreenFABState extends State { tooltip: 'Create a new Nost', label: const Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ + children: [ Text('NEW'), SizedBox(width: 8.0), Icon(FontAwesomeIcons.featherPointed), @@ -34,56 +35,58 @@ class FeedScreenFABState extends State { ), onPressed: () async { noteController.clear(); - await showDialog( - barrierDismissible: false, - context: context, - builder: ((context) { - return AlertDialog( - icon: const Icon(FontAwesomeIcons.featherPointed), - title: const Text('Create a Nost'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextFormField( - controller: noteController, - key: formKey, - maxLines: 5, - validator: (value) { - if (value == null || value.trim().isEmpty) { - return 'Please enter your nost'; - } - return null; - }), - const SizedBox(height: 16.0), - widget.isNotePublishing - ? const Center(child: CircularProgressIndicator()) - : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text('Cancel'), - ), - const SizedBox(width: 16), - ElevatedButton( - onPressed: () { - if (formKey.currentState!.validate()) { - widget.publishNote( - noteController.text.trim()); - } else { - formKey.currentState?.setState(() {}); - } - }, - child: const Text('NOST!'), - ), - ], - ) - ], - ), - ); - })); + await showDialog( + barrierDismissible: false, + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(FontAwesomeIcons.featherPointed), + title: const Text('Create a Nost'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + controller: noteController, + key: formKey, + maxLines: 5, + validator: (String? value) { + if (value == null || value.trim().isEmpty) { + return 'Please enter your nost'; + } + return null; + }, + ), + const SizedBox(height: 16.0), + if (widget.isNotePublishing) + const Center(child: CircularProgressIndicator()) + else + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Cancel'), + ), + const SizedBox(width: 16), + ElevatedButton( + onPressed: () { + if (formKey.currentState!.validate()) { + widget.publishNote(noteController.text.trim()); + } else { + formKey.currentState?.setState(() {}); + } + }, + child: const Text('NOST!'), + ), + ], + ) + ], + ), + ); + }, + ); }, ); } diff --git a/lib/components/keys_exist_dialog.dart b/lib/components/keys_exist_dialog.dart deleted file mode 100644 index 0858fa0..0000000 --- a/lib/components/keys_exist_dialog.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:cosanostr/all_imports.dart'; - -class KeysExistDialog extends StatefulWidget { - const KeysExistDialog({ - super.key, - required this.npubEncoded, - required this.nsecEncoded, - required this.hexPriv, - required this.hexPub, - }); - - final String npubEncoded; - final String nsecEncoded; - final String hexPriv; - final String hexPub; - - @override - State createState() => _KeysExistDialogState(); -} - -class _KeysExistDialogState extends State { - bool _toHex = false; - - @override - Widget build(BuildContext context) { - return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Container( - constraints: const BoxConstraints(maxWidth: 600), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Colors.white, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: const EdgeInsets.symmetric(vertical: 24), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Colors.indigo, - ), - child: const Center( - child: Text( - 'Keys', - style: TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ), - ), - const SizedBox(height: 24), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Public Key', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.grey[700], - ), - ), - const SizedBox(height: 12), - SelectableText( - _toHex ? widget.hexPub : widget.npubEncoded, - style: TextStyle( - fontSize: 16, - color: Colors.grey[800], - ), - ), - const SizedBox(height: 24), - Text( - 'Private Key', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.grey[700], - ), - ), - const SizedBox(height: 12), - SelectableText( - _toHex ? widget.hexPriv : widget.nsecEncoded, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.redAccent, - ), - ), - const SizedBox(height: 24), - Row( - mainAxisAlignment: MainAxisAlignment - .spaceBetween, // Changed to space between to create space for icon buttons - children: [ - IconButton( - onPressed: () { - setState(() { - _toHex = !_toHex; - }); - }, - icon: const Icon(Icons.autorenew_outlined), - color: Colors.grey[700], - ), - NoostOkButton( - onPressed: () { - Navigator.pop(context); - }, - label: 'OK', - ), - ], - ) - ], - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/components/keys_option_modal_bottom_sheet.dart b/lib/components/keys_option_modal_bottom_sheet.dart deleted file mode 100644 index 7aa78f4..0000000 --- a/lib/components/keys_option_modal_bottom_sheet.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:cosanostr/all_imports.dart'; - -class KeysOptionModalBottomSheet extends StatelessWidget { - const KeysOptionModalBottomSheet({ - super.key, - required this.generateNewKeyPressed, - required this.inputPrivateKeyPressed, - }); - - final void Function()? generateNewKeyPressed; - final void Function()? inputPrivateKeyPressed; - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - color: Theme.of(context).primaryColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20), - ), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.2), - blurRadius: 10, - offset: const Offset(0, -2), - ), - ], - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text( - 'Choose an option', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - const SizedBox(height: 20), - NoostCurveButton( - onPressed: generateNewKeyPressed, - label: 'Generate New Key', - backgroundColor: Colors.white, - ), - const SizedBox(height: 10), - NoostCurveButton( - onPressed: inputPrivateKeyPressed, - label: 'Input your private key', - textColor: Colors.white, - ), - ], - ), - ); - } -} diff --git a/lib/components/keysexist_dialog.dart b/lib/components/keysexist_dialog.dart new file mode 100644 index 0000000..bc3e8d5 --- /dev/null +++ b/lib/components/keysexist_dialog.dart @@ -0,0 +1,84 @@ +import 'package:cosanostr/all_imports.dart'; + +final StateProvider isHexProvider = + StateProvider((StateProviderRef ref) { + return false; +}); + +// The Dialog that is only shown to users when they click on the +// 'key' icon on the FeedScreen() IF keys already exist. +// Takes in keys as String and shows them as HEX or NPUB, +// according to the isHexProvider boolean. +// The SelectableText widget makes it possible for a user +// to copy/paste the keys for use elsewhere. +class KeysExistDialog extends ConsumerWidget { + const KeysExistDialog( + this.npubEncoded, + this.nsecEncoded, + this.hexPriv, + this.hexPub, { + super.key, + }); + + final String npubEncoded; + final String nsecEncoded; + final String hexPriv; + final String hexPub; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return AlertDialog( + icon: const Icon(FontAwesomeIcons.key), + title: const Text('KEYS'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Public Key', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + const Divider(), + SelectableText( + ref.watch(isHexProvider) ? hexPub : npubEncoded, + ), + const Text( + 'Private Key', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + const Divider(), + SelectableText( + ref.watch(isHexProvider) ? hexPriv : nsecEncoded, + ), + ], + ), + actions: [ + if (ref.watch(isHexProvider)) + TextButton( + onPressed: () { + ref.read(isHexProvider.notifier).state = + !ref.watch(isHexProvider); + }, + child: const Text('NPUB'), + ) + else + TextButton( + onPressed: () { + ref.read(isHexProvider.notifier).state = + !ref.watch(isHexProvider); + }, + child: const Text('HEX'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('OK'), + ), + ], + ); + } +} diff --git a/lib/components/keysoption_bottomsheet.dart b/lib/components/keysoption_bottomsheet.dart new file mode 100644 index 0000000..0c68b78 --- /dev/null +++ b/lib/components/keysoption_bottomsheet.dart @@ -0,0 +1,46 @@ +import 'package:cosanostr/all_imports.dart'; + +class KeysOptionBottomSheet extends StatelessWidget { + const KeysOptionBottomSheet({ + super.key, + required this.generateNewKeyPressed, + required this.inputPrivateKeyPressed, + }); + + final void Function()? generateNewKeyPressed; + final void Function()? inputPrivateKeyPressed; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Welcome to CosaNostr', + style: TextStyle(fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + const Text( + 'Anonymous, open-source, free, lightweight and cross-platform ' + 'Nostr client.', + textAlign: TextAlign.center, + ), + const Divider(), + const Text('Please choose your poison:'), + const SizedBox(height: 16.0), + ElevatedButton( + onPressed: generateNewKeyPressed, + child: const Text('GENERATE NEW KEYS'), + ), + const SizedBox(height: 16.0), + ElevatedButton( + onPressed: inputPrivateKeyPressed, + child: const Text('USE YOUR PRIVATE KEY'), + ), + ], + ), + ); + } +} diff --git a/lib/components/noost_curve_button.dart b/lib/components/noost_curve_button.dart deleted file mode 100644 index 17e17d9..0000000 --- a/lib/components/noost_curve_button.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; - -class NoostCurveButton extends StatelessWidget { - const NoostCurveButton({ - super.key, - required this.onPressed, - this.backgroundColor, - required this.label, - this.textColor, - }); - - final void Function()? onPressed; - final Color? backgroundColor; - final String label; - final Color? textColor; - - @override - Widget build(BuildContext context) { - return ElevatedButton( - onPressed: onPressed, - style: ElevatedButton.styleFrom( - foregroundColor: Theme.of(context).primaryColor, - backgroundColor: backgroundColor, - padding: const EdgeInsets.symmetric( - horizontal: 30, - vertical: 15, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), - ), - ), - child: Text( - label, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: textColor, - ), - ), - ); - } -} diff --git a/lib/components/noost_ok_button.dart b/lib/components/noost_ok_button.dart deleted file mode 100644 index 322c4c8..0000000 --- a/lib/components/noost_ok_button.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; - -class NoostOkButton extends StatelessWidget { - const NoostOkButton({ - super.key, - required this.onPressed, - required this.label, - }); - - final void Function()? onPressed; - final String label; - - @override - Widget build(BuildContext context) { - return ElevatedButton( - onPressed: onPressed, - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).primaryColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: Text( - label, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ); - } -} diff --git a/lib/components/noost_popup_menu.dart b/lib/components/noost_popup_menu.dart deleted file mode 100644 index 3184cd2..0000000 --- a/lib/components/noost_popup_menu.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:flutter/material.dart'; - -class NoostPopupMenu extends StatelessWidget { - final Set connectedRelays; - final Set failedRelays; - final List relaysList; - - const NoostPopupMenu({ - Key? key, - required this.connectedRelays, - required this.failedRelays, - required this.relaysList, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - Set missingRelays = Set.from(relaysList) - .difference(connectedRelays) - .difference(failedRelays); - - failedRelays.addAll(missingRelays); - - List> menuItems = []; - - // Add connected relays to menu - for (String relay in connectedRelays) { - menuItems.add( - PopupMenuItem( - value: relay, - child: Row( - children: [ - const Icon( - Icons.check_circle_outline, - color: Colors.green, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - relay, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ), - ); - } - - // Add failed relays to menu - for (String relay in failedRelays) { - menuItems.add( - PopupMenuItem( - value: relay, - child: Row( - children: [ - const Icon( - Icons.error_outline, - color: Colors.red, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - relay, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ), - ); - } - - return PopupMenuButton( - icon: const Icon(Icons.more_vert), - offset: const Offset(0, 50), - itemBuilder: (BuildContext context) => menuItems, - onSelected: (String value) { - // Do something when a menu item is selected - }, - ); - } -} diff --git a/lib/components/noost_snackbar.dart b/lib/components/noost_snackbar.dart deleted file mode 100644 index 79bbfef..0000000 --- a/lib/components/noost_snackbar.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; - -class NoostSnackBar extends SnackBar { - NoostSnackBar({Key? key, required this.label, this.isWarning = false}) - : super( - key: key, - content: _GenericErrorSnackBarMessage( - label: label, - isWarning: isWarning, - ), - backgroundColor: isWarning! ? Colors.red : Colors.white, - elevation: 6.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - behavior: SnackBarBehavior.fixed, - ); - - final String label; - final bool? isWarning; -} - -class _GenericErrorSnackBarMessage extends StatelessWidget { - const _GenericErrorSnackBarMessage({ - Key? key, - required this.label, - this.isWarning, - }) : super(key: key); - - final String label; - final bool? isWarning; - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), - child: Text( - label, - style: TextStyle( - color: isWarning! ? Colors.white : Colors.black, - fontSize: 16.0, - ), - ), - ); - } -} diff --git a/lib/components/noost_text_button.dart b/lib/components/noost_text_button.dart deleted file mode 100644 index b0fde88..0000000 --- a/lib/components/noost_text_button.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -class NoostTextButton extends StatelessWidget { - const NoostTextButton({ - super.key, - required this.onPressed, - required this.label, - this.color, - }); - - final void Function()? onPressed; - final String label; - final Color? color; - - @override - Widget build(BuildContext context) { - return TextButton( - onPressed: onPressed, - child: Text( - label, - style: TextStyle( - color: color, - ), - ), - ); - } -} diff --git a/lib/components/noost_text_form_field.dart b/lib/components/noost_text_form_field.dart deleted file mode 100644 index 0ec0757..0000000 --- a/lib/components/noost_text_form_field.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -class NoostTextFormField extends StatelessWidget { - const NoostTextFormField({ - super.key, - required this.hintText, - required this.controller, - required this.formKey, - required this.validator, - this.maxLines, - }); - - final String hintText; - final TextEditingController controller; - final Key formKey; - final String? Function(String?)? validator; - final int? maxLines; - - @override - Widget build(BuildContext context) { - return TextFormField( - controller: controller, - key: formKey, - validator: validator, - maxLines: maxLines, - decoration: InputDecoration( - hintText: hintText, - ), - ); - } -} diff --git a/lib/components/paste_private_key_dialog.dart b/lib/components/paste_private_key_dialog.dart deleted file mode 100644 index b983b30..0000000 --- a/lib/components/paste_private_key_dialog.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:cosanostr/all_imports.dart'; - -class PastePrivateKeyDialog extends StatelessWidget { - const PastePrivateKeyDialog({ - super.key, - required this.keyController, - required this.formKey, - required this.keyValidator, - required this.onCancelPressed, - required this.onOKPressed, - }); - - final TextEditingController keyController; - final Key formKey; - final String? Function(String?)? keyValidator; - final void Function()? onCancelPressed; - final void Function()? onOKPressed; - - @override - Widget build(BuildContext context) { - return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), - constraints: const BoxConstraints(maxWidth: 600), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Colors.white, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 10), - const Text( - 'Enter your private key', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18, - color: Colors.black, - ), - ), - const SizedBox(height: 16), - NoostTextFormField( - hintText: 'Enter nsec or hex', - controller: keyController, - formKey: formKey, - validator: keyValidator), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - NoostTextButton( - onPressed: onCancelPressed, - label: 'Cancel', - color: Colors.grey, - ), - NoostOkButton( - onPressed: onOKPressed, - label: 'OK', - ), - ], - ), - ], - ), - ), - ); - } -} diff --git a/lib/components/pasteprivatekey_dialog.dart b/lib/components/pasteprivatekey_dialog.dart new file mode 100644 index 0000000..686c3a1 --- /dev/null +++ b/lib/components/pasteprivatekey_dialog.dart @@ -0,0 +1,51 @@ +import 'package:cosanostr/all_imports.dart'; + +class PastePrivateKeyDialog extends StatelessWidget { + const PastePrivateKeyDialog({ + super.key, + required this.keyController, + required this.formKey, + required this.keyValidator, + required this.onCancelPressed, + required this.onOKPressed, + }); + + final TextEditingController keyController; + final Key formKey; + final String? Function(String?)? keyValidator; + final void Function()? onCancelPressed; + final void Function()? onOKPressed; + + @override + Widget build(BuildContext context) { + return AlertDialog( + icon: const Icon(FontAwesomeIcons.userLock), + title: const Text('Enter your private key'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + decoration: const InputDecoration( + hintText: 'Enter NSEC or HEX', + ), + controller: keyController, + key: formKey, + validator: keyValidator, + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('CANCEL'), + ), + ElevatedButton( + onPressed: onOKPressed, + child: const Text('OK'), + ), + ], + ); + } +} diff --git a/lib/components/scaffold_appbar.dart b/lib/components/scaffold_appbar.dart index 15e3b28..296b60f 100644 --- a/lib/components/scaffold_appbar.dart +++ b/lib/components/scaffold_appbar.dart @@ -15,7 +15,7 @@ class ScaffoldAppBar extends ConsumerWidget implements PreferredSizeWidget { return AppBar( title: const Text('CosaNostr'), centerTitle: true, - actions: [ + actions: [ Padding( padding: const EdgeInsets.only(right: 16.0), child: Icon( diff --git a/lib/components/scaffold_drawer.dart b/lib/components/scaffold_drawer.dart index 8747545..9607a2a 100644 --- a/lib/components/scaffold_drawer.dart +++ b/lib/components/scaffold_drawer.dart @@ -16,16 +16,18 @@ class ScaffoldDrawer extends StatelessWidget { Widget build(BuildContext context) { return Drawer( child: Column( - children: [ + children: [ const DrawerHeader( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('CosaNostr'), - // Bump this version every time something insanely cool is added. - Text('Version: 0.0.1'), - ], - )), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('CosaNostr'), + // Bump this version every time something insanely cool is + // added. + Text('Version: 0.0.1'), + ], + ), + ), ListTile( onTap: () { // Riverpod's way of toggling a bool (I think). diff --git a/lib/models/timeago.dart b/lib/models/timeago.dart index 11e25cc..9e62691 100644 --- a/lib/models/timeago.dart +++ b/lib/models/timeago.dart @@ -1,7 +1,8 @@ class TimeAgo { static String format(int timestamp) { - DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); - Duration difference = DateTime.now().difference(dateTime); + final DateTime dateTime = + DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); + final Duration difference = DateTime.now().difference(dateTime); String timeAgo = ''; @@ -9,11 +10,12 @@ class TimeAgo { timeAgo = '${difference.inDays} ${difference.inDays == 1 ? 'day' : 'days'} ago'; } else if (difference.inHours > 0) { - timeAgo = - '${difference.inHours} ${difference.inHours == 1 ? 'hour' : 'hours'} ago'; + timeAgo = ''' +${difference.inHours} ${difference.inHours == 1 ? 'hour' : 'hours'} ago'''; } else if (difference.inMinutes > 0) { timeAgo = - '${difference.inMinutes} ${difference.inMinutes == 1 ? 'minute' : 'minutes'} ago'; + '${difference.inMinutes} ${difference.inMinutes == 1 ? 'minute' : ''' +minutes'''} ago'; } else { timeAgo = 'just now'; } diff --git a/lib/providers/feedscreen_providers.dart b/lib/providers/feedscreen_providers.dart index 547545a..58f0cf2 100644 --- a/lib/providers/feedscreen_providers.dart +++ b/lib/providers/feedscreen_providers.dart @@ -1,42 +1,49 @@ import 'package:cosanostr/all_imports.dart'; // isConnectedProvider holds the status of our relay connection. -final StateProvider isConnectedProvider = StateProvider((ref) { +final StateProvider isConnectedProvider = + StateProvider((StateProviderRef ref) { return false; }); // relayApiProvider creates an instance of RelayApi, which is defined in // nostr_tools. It requires the relay URL as an argument. -final StateProvider relayApiProvider = StateProvider((ref) { +final StateProvider relayApiProvider = + StateProvider((StateProviderRef ref) { return RelayApi(relayUrl: 'wss://relay.damus.io'); }); // eventsProvider is a list that will hold all the events that we'll get // from the relay after subscribing. final StateProvider> eventsProvider = - StateProvider>((ref) { - return []; + StateProvider>((StateProviderRef> ref) { + return []; }); // metaDataProvider is a map that will hold the mapping of the public key // with the user metadata. final StateProvider> metaDataProvider = - StateProvider((ref) { - return {}; + StateProvider>( + (StateProviderRef> ref) { + return {}; }); -final StateProvider privateKeyProvider = StateProvider((ref) { +final StateProvider privateKeyProvider = + StateProvider((StateProviderRef ref) { return ''; }); -final StateProvider publicKeyProvider = StateProvider((ref) { +final StateProvider publicKeyProvider = + StateProvider((StateProviderRef ref) { return ''; }); -final StateProvider keysExistProvider = StateProvider((ref) { +final StateProvider keysExistProvider = + StateProvider((StateProviderRef ref) { return false; }); -final StateProvider isNotePublishingProvider = StateProvider((ref) { +final StateProvider isNotePublishingProvider = + StateProvider((StateProviderRef ref) { return false; }); diff --git a/lib/providers/theme_providers.dart b/lib/providers/theme_providers.dart index 25fef54..f8f7f46 100644 --- a/lib/providers/theme_providers.dart +++ b/lib/providers/theme_providers.dart @@ -18,7 +18,7 @@ final StateProvider themeModeProvider = }); final StateProvider lightThemeProvider = - StateProvider((ref) { + StateProvider((StateProviderRef ref) { return FlexThemeData.light( scheme: FlexScheme.indigo, surfaceMode: FlexSurfaceMode.levelSurfacesLowScaffold, @@ -85,7 +85,7 @@ final StateProvider lightThemeProvider = }); final StateProvider darkThemeProvider = - StateProvider((ref) { + StateProvider((StateProviderRef ref) { return FlexThemeData.dark( scheme: FlexScheme.indigo, surfaceMode: FlexSurfaceMode.highScaffoldLowSurface, diff --git a/lib/responsive_layout.dart b/lib/responsive_layout.dart index 0e55135..96059bb 100644 --- a/lib/responsive_layout.dart +++ b/lib/responsive_layout.dart @@ -6,7 +6,7 @@ class ResponsiveLayout extends StatelessWidget { @override Widget build(BuildContext context) { return LayoutBuilder( - builder: (context, constraints) { + builder: (BuildContext context, BoxConstraints constraints) { // Pretty basic, just show a Scaffold with a PhoneContainer in the // center containing the entire application as a form of // responsiveness for bigger screens. diff --git a/lib/screens/feed_screen.dart b/lib/screens/feed_screen.dart index 6c99c4f..01343f2 100644 --- a/lib/screens/feed_screen.dart +++ b/lib/screens/feed_screen.dart @@ -17,22 +17,26 @@ class FeedScreenState extends ConsumerState { // providers. This is because they will soon be used in other screens as // well. Next step is setting up a StreamProvider to handle the stream // from the relay. - // TODO: Set up StreamProvider so it can be used throughout the app. + // TODO(plotsklapps): Set up StreamProvider so it can be used throughout + // the app. late Stream stream; - final streamController = StreamController(); + final StreamController streamController = StreamController(); - final secureStorage = const FlutterSecureStorage(); + final FlutterSecureStorage secureStorage = const FlutterSecureStorage(); - final keyController = TextEditingController(); - final formKey = GlobalKey(); - final keyGenerator = KeyApi(); - final nip19 = Nip19(); + final TextEditingController keyController = TextEditingController(); + final GlobalKey> formKey = + GlobalKey>(); + final KeyApi keyGenerator = KeyApi(); + final Nip19 nip19 = Nip19(); @override void initState() { super.initState(); - getKeysFromStorage(); - initStream(); + Future.delayed(Duration.zero, () async { + await getKeysFromStorage(); + await initStream(); + }); } @override @@ -42,8 +46,9 @@ class FeedScreenState extends ConsumerState { } Future getKeysFromStorage() async { - final storedPrivateKey = await secureStorage.read(key: 'privateKey'); - final storedPublicKey = await secureStorage.read(key: 'publicKey'); + final String? storedPrivateKey = + await secureStorage.read(key: 'privateKey'); + final String? storedPublicKey = await secureStorage.read(key: 'publicKey'); if (storedPrivateKey != null && storedPublicKey != null) { ref.read(privateKeyProvider.notifier).state = storedPrivateKey; @@ -56,7 +61,7 @@ class FeedScreenState extends ConsumerState { String privateKeyHex, String publicKeyHex, ) async { - Future.wait([ + await Future.wait(>[ secureStorage.write(key: 'privateKey', value: privateKeyHex), secureStorage.write(key: 'publicKey', value: publicKeyHex), ]); @@ -68,7 +73,7 @@ class FeedScreenState extends ConsumerState { } Future deleteKeysFromStorage() async { - Future.wait([ + await Future.wait(>[ secureStorage.delete(key: 'privateKey'), secureStorage.delete(key: 'publicKey'), ]); @@ -78,18 +83,18 @@ class FeedScreenState extends ConsumerState { } Future generateNewKeys() async { - final newPrivateKey = keyGenerator.generatePrivateKey(); - final newPublicKey = keyGenerator.getPublicKey(newPrivateKey); + final String newPrivateKey = keyGenerator.generatePrivateKey(); + final String newPublicKey = keyGenerator.getPublicKey(newPrivateKey); - return await addKeysToStorage(newPrivateKey, newPublicKey); + return addKeysToStorage(newPrivateKey, newPublicKey); } Future> connectToRelay() async { - final stream = await ref.read(relayApiProvider).connect(); + final Stream stream = await ref.read(relayApiProvider).connect(); // This sets up an event listener for relayApiProvider, which will be // triggered whenever relayApiProvider emits a RelayEvent. - ref.read(relayApiProvider).on((event) { + ref.read(relayApiProvider).on((RelayEvent event) { // This code block checks the type of the emitted RelayEvent. // If it is a connect event, isConnectedProvider is set to true. // If it is an error event, isConnectedProvider is set to false. @@ -105,22 +110,20 @@ class FeedScreenState extends ConsumerState { // text note. We also set a limit of 100 events and a tag of nostr. // This tag helps us filter out unwanted events and only receive the ones // that has the "nostr" tag. - ref.read(relayApiProvider).sub([ + ref.read(relayApiProvider).sub([ Filter( - kinds: [1], + kinds: [1], limit: 100, - t: ["nostr"], + t: ['nostr'], ) ]); - return stream.where((message) { - return message.type == 'EVENT'; - }).map((message) { - return message.message; - }); + return stream + .where((Message message) => message.type == 'EVENT') + .map((Message message) => message.message as Event); } - void initStream() async { + Future initStream() async { // This line sets up a loop that listens for events from the stream object. // The await for construct allows the loop to be asynchronous, meaning // that it can listen for incoming events while continuing to run other @@ -130,11 +133,11 @@ class FeedScreenState extends ConsumerState { // message, the code inside the if block is executed. There are other // types of messages that can be received as well, such as "REQ", "CLOSE", // "NOTICE", and "OK", but we're only interested in events for now. - stream.listen((message) { + stream.listen((Event message) { // This line extracts the message object from the incoming message and // assigns it to the event variable. This event object contains all of // the information we need about the incoming event. - final event = message; + final Event event = message; // If the kind of the event is 1, the event object is added to the // eventProvider list. Then, a new subscription is created to receive // messages from the event.pubkey of the author with a kind value of 0. @@ -154,8 +157,8 @@ class FeedScreenState extends ConsumerState { // can associate each note with its corresponding user metadata. if (event.kind == 1) { ref.read(eventsProvider).add(event); - ref.read(relayApiProvider).sub([ - Filter(kinds: [0], authors: [event.pubkey]) + ref.read(relayApiProvider).sub([ + Filter(kinds: [0], authors: [event.pubkey]) ]); // If the kind of the event is 0, the content of the event is decoded // from JSON and assigned to a metadata variable. This metadata is @@ -163,7 +166,9 @@ class FeedScreenState extends ConsumerState { // metaDataProvider map. This allows us to keep track of the metadata // for each user who creates a note. } else if (event.kind == 0) { - final metadata = Metadata.fromJson(jsonDecode(event.content)); + final Metadata metadata = Metadata.fromJson( + jsonDecode(event.content) as Map, + ); ref.read(metaDataProvider)[event.pubkey] = metadata; } // Finally, the incoming message is added to the StreamController. @@ -174,36 +179,36 @@ class FeedScreenState extends ConsumerState { } Future resubscribeStream() async { - await Future.delayed(const Duration(seconds: 1), () { - setState(() { - ref.read(eventsProvider).clear(); - ref.read(metaDataProvider).clear(); - }); + await Future.delayed(const Duration(seconds: 1), () { + ref.read(eventsProvider).clear(); + ref.read(metaDataProvider).clear(); initStream(); // Reconnect and resubscribe to the filter }); } @override Widget build(BuildContext context) { - final nip19 = Nip19(); + final Nip19 nip19 = Nip19(); return Scaffold( appBar: FeedScreenAppBar( title: '', isConnected: ref.watch(isConnectedProvider), keysDialog: IconButton( - icon: const Icon(Icons.key), - onPressed: () { - ref.watch(keysExistProvider) - ? keysExistDialog( - nip19.npubEncode(ref.watch(publicKeyProvider)), - nip19.nsecEncode(ref.watch(privateKeyProvider))) - : modalBottomSheet(); - }), + icon: const Icon(Icons.key), + onPressed: () async { + await ref.watch(keysExistProvider) + ? await keysExistDialog( + nip19.npubEncode(ref.watch(publicKeyProvider)), + nip19.nsecEncode(ref.watch(privateKeyProvider)), + ) + : modalBottomSheet(); + }, + ), deleteKeysDialog: ref.watch(keysExistProvider) ? IconButton( icon: const Icon(Icons.delete), - onPressed: () => deleteKeysDialog(), + onPressed: deleteKeysDialog, ) : Container(), ), @@ -227,11 +232,11 @@ class FeedScreenState extends ConsumerState { ), // StreamBuilder is a widget that listens to the streamController // and returns a widget tree based on the state of the stream. - child: StreamBuilder( + child: StreamBuilder( stream: streamController.stream, // The builder callback is called whenever a new event is // emitted from the stream. - builder: (context, snapshot) { + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { // Inside the builder callback, the snapshot object // contains the latest event from the stream. @@ -242,16 +247,17 @@ class FeedScreenState extends ConsumerState { // The itemCount property of the ListView.builder is set // to eventProvider list length. itemCount: ref.watch(eventsProvider).length, - itemBuilder: (context, index) { + itemBuilder: (BuildContext context, int index) { // For each event, we create a Nost object that // encapsulates the details of the event, including the // id, avatarUrl, name, username, time, content, and // pubkey. Here is the power of the metaDataProvider map // as we're able to map the event pubkey with the // metadata of the author. - final event = ref.watch(eventsProvider)[index]; - final metadata = ref.watch(metaDataProvider)[event.pubkey]; - final nost = Nost( + final Event event = ref.watch(eventsProvider)[index]; + final Metadata? metadata = + ref.watch(metaDataProvider)[event.pubkey]; + final Nost nost = Nost( noteId: event.id, avatarUrl: metadata?.picture ?? 'https://robohash.org/${event.pubkey}', @@ -271,13 +277,14 @@ class FeedScreenState extends ConsumerState { // If snapshot.connectionState is ConnectionState.waiting, // we display a loading indicator. return const Center( - child: Column( - children: [ - Text('LOADING...'), - SizedBox(height: 8.0), - CircularProgressIndicator(), - ], - )); + child: Column( + children: [ + Text('LOADING...'), + SizedBox(height: 8.0), + CircularProgressIndicator(), + ], + ), + ); } else if (snapshot.hasError) { // If snapshot.hasError is true, we display an error message. return Center(child: Text('Error: ${snapshot.error}')); @@ -286,7 +293,7 @@ class FeedScreenState extends ConsumerState { // loading indicator. return const Center( child: Column( - children: [ + children: [ Text('LOADING...'), SizedBox(height: 8.0), CircularProgressIndicator(), @@ -299,15 +306,15 @@ class FeedScreenState extends ConsumerState { ), floatingActionButton: ref.watch(keysExistProvider) ? FeedScreenFAB( - publishNote: (note) { + publishNote: (String? note) async { ref.read(isNotePublishingProvider.notifier).state = true; - final eventApi = EventApi(); - final event = eventApi.finishEvent( + final EventApi eventApi = EventApi(); + final Event event = eventApi.finishEvent( Event( kind: 1, - tags: [ - ['t', 'nostr'] + tags: >[ + ['t', 'nostr'] ], content: note!, created_at: DateTime.now().millisecondsSinceEpoch ~/ 1000, @@ -318,19 +325,39 @@ class FeedScreenState extends ConsumerState { if (eventApi.verifySignature(event)) { try { ref.read(relayApiProvider).publish(event); - resubscribeStream(); + await resubscribeStream().then((_) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text('Yay! Nost Published!'), + action: SnackBarAction( + label: 'OK', + onPressed: () { + Navigator.pop(context); + }, + ), + behavior: SnackBarBehavior.floating, + ), + ); + }); + } catch (error) { ScaffoldMessenger.of(context).showSnackBar( - NoostSnackBar(label: 'Congratulations! Noost Published!'), + SnackBar( + content: Text('Oops: $error'), + action: SnackBarAction( + label: 'OK', + onPressed: () { + Navigator.pop(context); + }, + ), + behavior: SnackBarBehavior.floating, + ), ); - } catch (_) { - ScaffoldMessenger.of(context).showSnackBar(NoostSnackBar( - label: 'Oops! Something went wrong!', - isWarning: true, - )); } } ref.read(isNotePublishingProvider.notifier).state = false; - Navigator.pop(context); + if (mounted) { + Navigator.pop(context); + } }, isNotePublishing: ref.watch(isNotePublishingProvider), ) @@ -338,17 +365,41 @@ class FeedScreenState extends ConsumerState { ); } - void modalBottomSheet() { - showModalBottomSheet( + Future modalBottomSheet() async { + await showModalBottomSheet( context: context, builder: (BuildContext context) { - return KeysOptionModalBottomSheet( + return KeysOptionBottomSheet( generateNewKeyPressed: () { - final currentContext = context; - generateNewKeys().then((keysGenerated) { + final BuildContext currentContext = context; + generateNewKeys().then((bool keysGenerated) { if (keysGenerated) { ScaffoldMessenger.of(currentContext).showSnackBar( - NoostSnackBar(label: 'Congratulations! Keys Generated!')); + SnackBar( + content: + const Text('Welcome to CosaNostr! Keys generated!'), + action: SnackBarAction( + label: 'OK', + onPressed: () { + Navigator.pop(context); + }, + ), + behavior: SnackBarBehavior.floating, + ), + ); + } else { + ScaffoldMessenger.of(currentContext).showSnackBar( + SnackBar( + content: const Text('Something went wrong...'), + action: SnackBarAction( + label: 'OK', + onPressed: () { + Navigator.pop(context); + }, + ), + behavior: SnackBarBehavior.floating, + ), + ); } }); Navigator.pop(context); @@ -363,22 +414,23 @@ class FeedScreenState extends ConsumerState { ); } - void pastePrivateKeyDialog() { - showDialog( + Future pastePrivateKeyDialog() async { + await showDialog( context: context, builder: (BuildContext context) { return PastePrivateKeyDialog( keyController: keyController, formKey: formKey, - keyValidator: (value) { + keyValidator: (String? value) { if (value == null || value.isEmpty) { return 'Please enter your private key.'; } try { - bool isValidHexKey = keyGenerator.isValidPrivateKey(value); - bool isValidNsec = value.trim().startsWith('nsec') && - keyGenerator.isValidPrivateKey(nip19.decode(value)['data']); + final bool isValidHexKey = keyGenerator.isValidPrivateKey(value); + final bool isValidNsec = value.trim().startsWith('nsec') && + keyGenerator + .isValidPrivateKey(nip19.decode(value)['data'] as String); if (!(isValidHexKey || isValidNsec)) { return 'Your private key is not valid.'; @@ -397,18 +449,29 @@ class FeedScreenState extends ConsumerState { String publicKeyHex; if (privateKeyHex.startsWith('nsec')) { - final decoded = nip19.decode(privateKeyHex); - privateKeyHex = decoded['data']; + final Map decoded = + nip19.decode(privateKeyHex); + privateKeyHex = decoded['data'] as String; publicKeyHex = keyGenerator.getPublicKey(privateKeyHex); } else { publicKeyHex = keyGenerator.getPublicKey(privateKeyHex); } - addKeysToStorage(privateKeyHex, publicKeyHex).then((keysAdded) { + addKeysToStorage(privateKeyHex, publicKeyHex) + .then((bool keysAdded) { if (keysAdded) { keyController.clear(); ScaffoldMessenger.of(context).showSnackBar( - NoostSnackBar(label: 'Congratulations! Keys Stored!'), + SnackBar( + content: const Text('Keys successfully deleted!'), + action: SnackBarAction( + label: 'OK', + onPressed: () { + Navigator.pop(context); + }, + ), + behavior: SnackBarBehavior.floating, + ), ); } }); @@ -426,36 +489,41 @@ class FeedScreenState extends ConsumerState { ); } - void keysExistDialog(String npubEncode, String nsecEncode) async { - await showDialog( + Future keysExistDialog(String npubEncode, String nsecEncode) async { + await showDialog( context: context, - builder: ((context) { + builder: (BuildContext context) { return KeysExistDialog( - npubEncoded: npubEncode, - nsecEncoded: nsecEncode, - hexPriv: ref.watch(privateKeyProvider), - hexPub: ref.watch(publicKeyProvider), + npubEncode, + nsecEncode, + ref.watch(privateKeyProvider), + ref.watch(publicKeyProvider), ); - }), + }, ); } - void deleteKeysDialog() async { - await showDialog( + Future deleteKeysDialog() async { + await showDialog( context: context, - builder: ((context) { + builder: (BuildContext context) { return DeleteKeysDialog( onNoPressed: () { Navigator.pop(context); }, onYesPressed: () { - final currentContext = context; deleteKeysFromStorage().then((_) { if (!ref.watch(keysExistProvider)) { - ScaffoldMessenger.of(currentContext).showSnackBar( - NoostSnackBar( - label: 'Keys successfully deleted!', - isWarning: true, + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text('Keys successfully deleted!'), + action: SnackBarAction( + label: 'OK', + onPressed: () { + Navigator.pop(context); + }, + ), + behavior: SnackBarBehavior.floating, ), ); } @@ -463,7 +531,7 @@ class FeedScreenState extends ConsumerState { Navigator.pop(context); }, ); - }), + }, ); } } diff --git a/lib/screens/scaffold_screen.dart b/lib/screens/scaffold_screen.dart index a67b175..4f6fd25 100644 --- a/lib/screens/scaffold_screen.dart +++ b/lib/screens/scaffold_screen.dart @@ -43,7 +43,7 @@ class _ScaffoldScreenState extends ConsumerState { drawer: ScaffoldDrawer(ref: ref), body: PageView( controller: pageController, - children: const [ + children: const [ FeedScreen(), Placeholder(), Placeholder(), diff --git a/pubspec.lock b/pubspec.lock index 60570d5..6b4df1e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -166,14 +166,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" - url: "https://pub.dev" - source: hosted - version: "2.0.2" flutter_riverpod: dependency: "direct main" description: @@ -296,14 +288,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.3" - lints: - dependency: transitive - description: - name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.dev" - source: hosted - version: "2.1.1" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b92eec3..6e82244 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,21 +9,20 @@ environment: sdk: '>=3.0.5 <4.0.0' dependencies: - flutter: - sdk: flutter cupertino_icons: ^1.0.2 - flutter_secure_storage: ^8.0.0 - nostr_tools: ^1.0.7 flex_color_scheme: ^7.1.2 - google_fonts: ^4.0.4 + flutter: + sdk: flutter + flutter_animate: ^4.2.0 flutter_riverpod: ^2.3.6 + flutter_secure_storage: ^8.0.0 font_awesome_flutter: ^10.5.0 - flutter_animate: ^4.2.0 + google_fonts: ^4.0.4 + nostr_tools: ^1.0.7 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 flutter: