Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
target: lib/screens/analytics_screen.dart
total_score: 23
p0_count: 0
p1_count: 2
timestamp: 2026-06-07T13-10-40Z
slug: lib-screens-analytics-screen-dart
---
## Analytics Screen — Design Critique

### Design Health Score

| # | Heuristic | Score | Key Issue |
|---|-----------|-------|-----------|
| 1 | Visibility of System Status | 3 | Loading states present; chart doesn't visually react to period change |
| 2 | Match System / Real World | 2 | Period toggle changes summary totals but leaves chart data untouched — broken contract |
| 3 | User Control and Freedom | 3 | All toggles freely reversible; no traps |
| 4 | Consistency and Standards | 2 | Period applies to summary card but not to SubsBar/SubsPie — same control, inconsistent scope |
| 5 | Error Prevention | 3 | Few error vectors; empty state handled; no destructive inputs |
| 6 | Recognition Rather Than Recall | 2 | `$`/`%` symbols require recall; no label or tooltip |
| 7 | Flexibility and Efficiency | 2 | 3 period presets, 2 chart modes; no sort, filter, or export |
| 8 | Aesthetic and Minimalist Design | 2 | Chart container double-boxed; N-identical breakdown cards |
| 9 | Error Recovery | 3 | Generic error string present; graceful empty state |
| 10 | Help and Documentation | 1 | Zero contextual help; no tooltips; no explanation of toggle scope |
| **Total** | | **23/40** | **Acceptable — significant improvements needed** |

### Anti-Patterns Verdict

Deterministic scan: clean (0 findings). No slop patterns detected. Design is coherent — coral accent restrained, border-radius 12px correct, no gradient text, no side-stripe borders.

### Overall Impression

Structurally sound but analytically misleading. The period toggle is the most prominent control and it only does half its job — charts always show monthly data regardless of period selected. That's the single biggest trust problem. Everything else is refinement.

### What's Working

1. Color-coded breakdown items — meaningful color, not decoration; semanticsLabel on progress bars is a deliberate accessibility choice.
2. Summary card data density — compact two-column layout with coral accent on total is clean and effective.
3. SegmentedButton reuse — consistent component for both controls; users learn it once.

### Priority Issues

**[P1] Period filter doesn't apply to chart data**
SubsBar and SubsPie always render `monthlyAmount` with no multiplier. Summary card says "Total Yearly: $1,200." Chart still shows monthly bars. Creates a misleading mental model. Fix: pass multiplier to chart widgets as a constructor parameter.

**[P1] Chart section has no contextual label**
Chart container begins directly with SegmentedButton — no heading, no sub-title. Users don't know what temporal scope the chart represents. Fix: add a text heading or subtitle tying the chart to the active period.

**[P2] `$`/`%` chart toggle labels too terse**
Symbols tell you the unit but not the chart type. First-time users have no way to infer `$` = bar chart, `%` = pie chart. Fix: use "Amount" / "Share" labels, or add icons.

**[P2] N-identical breakdown cards, no visual rhythm**
ListView emits N copies of the same structure. On 5+ subscriptions this creates visual fatigue. Fix: differentiate the top item; add grouping by price tier or size variation.

**[P3] Chart container double-boxes the chart widget**
Outer Container with surfaceContainer fill + 16px padding wraps SubsBar which already has its own data area. Fix: remove container fill color or reduce padding.

### Persona Red Flags

**Alex (Power User):** Selects "Year", summary updates, chart doesn't change → trust erosion. No sort, filter, date range, or export. Two controls total for an analytics screen.

**Sam (Accessibility-Dependent):** SegmentedButton and LinearProgressIndicator semantics are good. fl_chart widgets expose no semantic descriptions of chart data — screen reader users hear nothing on the chart region.

**Casey (Distracted Mobile User):** No loading skeleton — full-screen spinner on cold start. Period toggles sit above fold (good). State loss on backgrounding if period is non-default.

### Minor Observations

- `IntrinsicHeight` in `_SummaryCard` triggers double layout pass — cheaper to use fixed-height column children.
- `SubsPie` appends `SizedBox(height: 12)` inside already-padded chart container — ~28px dead space.
- `_coral` in `_SummaryCard` hardcodes `Color(0xFFD83434)` — same value as `kCoralAccent` in `app_theme.dart`; single source preferred.

### Questions to Consider

- "What does 'analytics' mean to a user who's tracked subscriptions for 6 months — 'what am I spending?' or 'what changed?'"
- "The period selector and chart mode toggle are the only two interactions — if you had to cut one, which gives more value?"
- "What if the chart were removed and the breakdown list did more heavy lifting — larger numbers, trend arrows, category grouping?"
25 changes: 5 additions & 20 deletions .metadata
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.

version:
revision: "edada7c56edf4a183c1735310e123c7f923584f1"
revision: "559ffa3f75e7402d65a8def9c28389a9b2e6fe42"
channel: "stable"

project_type: app
Expand All @@ -13,26 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
- platform: android
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
create_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42
base_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42
- platform: ios
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
- platform: linux
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
- platform: macos
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
- platform: web
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
- platform: windows
create_revision: edada7c56edf4a183c1735310e123c7f923584f1
base_revision: edada7c56edf4a183c1735310e123c7f923584f1
create_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42
base_revision: 559ffa3f75e7402d65a8def9c28389a9b2e6fe42

# User provided section

Expand Down
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Communication Style

Use caveman skill. Terse responses. Drop filler, articles, hedging. Fragments OK. Technical terms exact. Code unchanged.
Must use caveman skill. Terse responses. Drop filler, articles, hedging. Fragments OK. Technical terms exact. Code unchanged.

**Prefer MCP over raw terminal** for tooling — especially **Dart/Flutter**: use the configured Dart MCP server (analysis, fixes, project queries) before defaulting to CLI-only workflows when the MCP tool covers the task.

Expand All @@ -17,7 +17,10 @@ Use caveman skill. Terse responses. Drop filler, articles, hedging. Fragments OK
- **MCP over CLI** for Dart/Flutter tooling (analysis, fixes, project queries) when the MCP tool covers the task.
- dont use git commands.
- **UI/UX Excellence:** Exhibit impeccable skill in frontend design. Deliver modern, polished, and pixel-perfect layouts with exceptional attention to spacing, typography, and visual hierarchy.
- dont use git commands.
- NEVER use `shrinkWrap: true` — use `ConstrainedBox(maxHeight: N)` or `Expanded`/`Flexible`
- NEVER spread `.map()` results inline in a Column — use `ListView.builder` or `GridView.builder`
- NEVER use raw `Map` in the presentation layer — wrap in typed model classes
- Prefer widget classes (`StatelessWidget`/`StatefulWidget`/`ConsumerWidget`) over widget functions or inline builders

## 1. Think Before Coding

Expand Down
237 changes: 237 additions & 0 deletions all_lint_rules.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
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
- annotate_redeclares
- 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_futureor_void
- 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
- document_ignores
- 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
- invalid_runtime_check_with_js_interop_types
- 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_code_block_language_in_doc_comment
- 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
- omit_obvious_local_variable_types
- omit_obvious_property_types
- one_member_abstracts
- only_throw_errors
- overridden_fields
- 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
- remove_deprecations_in_breaking_versions
- 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
- specify_nonobvious_local_variable_types
- specify_nonobvious_property_types
- strict_top_level_inference
- switch_on_type
- 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
- unintended_html_in_doc_comment
- unnecessary_async
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_breaks
- unnecessary_const
- unnecessary_constructor_name
- unnecessary_final
- unnecessary_getters_setters
- unnecessary_ignore
- unnecessary_lambdas
- unnecessary_late
- unnecessary_library_directive
- unnecessary_library_name
- 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
- unnecessary_unawaited
- unnecessary_underscores
- unreachable_from_main
- unrelated_type_equality_checks
- unsafe_variance
- 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_null_aware_elements
- 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
- use_truncating_division
- valid_regexps
- void_checks
9 changes: 8 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@

# 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
include: analysis_options_without_plugins.yaml

plugins:
#! DONT REMOVE: its false positive and riverpod lints dont work without it
riverpod_lint: ^3.1.4


analyzer:
errors:
public_member_api_docs: ignore
exclude:
- '**/*.g.dart' # Exclude generated files
- '**/*.freezed.dart' # Exclude freezed files
Expand Down
Loading
Loading