diff --git a/sites/docs/lib/_sass/_site.scss b/sites/docs/lib/_sass/_site.scss index b5078123f61..bbfaaf54b34 100644 --- a/sites/docs/lib/_sass/_site.scss +++ b/sites/docs/lib/_sass/_site.scss @@ -50,6 +50,7 @@ @use 'package:site_shared/_sass/components/tooltip'; // Styles for specific pages, alphabetically ordered. +@use 'pages/cuj-index'; @use 'pages/glossary'; @use 'pages/learning-resources-index'; @use 'pages/not-found'; diff --git a/sites/docs/lib/_sass/components/_filterable-index.scss b/sites/docs/lib/_sass/components/_filterable-index.scss index 99022c2e3cb..f21a0c155e4 100644 --- a/sites/docs/lib/_sass/components/_filterable-index.scss +++ b/sites/docs/lib/_sass/components/_filterable-index.scss @@ -3,8 +3,9 @@ // classes. Each page styles its own results list separately, keyed off the // id of that list. +$mobile-breakpoint: 839px; + .filterable-index { - $mobile-breakpoint: 839px; $sidebar-width: 220px; display: flex; diff --git a/sites/docs/lib/_sass/pages/_cuj-index.scss b/sites/docs/lib/_sass/pages/_cuj-index.scss new file mode 100644 index 00000000000..b4c1024e06b --- /dev/null +++ b/sites/docs/lib/_sass/pages/_cuj-index.scss @@ -0,0 +1,136 @@ +@use '../components/filterable-index'; + +$font-size-sm: 0.875rem; +$font-size-xl: 1.5rem; +$spacing-xs: 0.25rem; +$spacing-sm: 0.5rem; +$spacing-md: 1rem; +$spacing-lg: 1.5rem; +$transition-normal: 0.2s ease; + +// The critical user journey index reuses the two column layout, search field, +// and filter sidebar of the learning resources index, which are styled in +// `_filterable-index.scss`. +// +// The journeys themselves render as full-width expandable cards, following +// the glossary in `_glossary.scss`. Expanding and collapsing is wired up +// by the `_setUpExpandableCards` global script. + +// The feedback button is the sidebar footer, so it sits below the filter card +// and slides in with it when the sidebar becomes a drawer. +.cuj-feedback { + margin-block-start: $spacing-md; + + .outlined-button { + width: 100%; + justify-content: center; + } + + @media (max-width: filterable-index.$mobile-breakpoint) { + margin-block-start: 0; + padding: 0.75rem; + border-block-start: 1px solid var(--site-inset-borderColor); + } +} + +#all-cujs-list { + margin-block-start: $spacing-md; + + .cuj-card { + padding: 0.75rem $spacing-md; + gap: $spacing-xs; + + .card-header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-start; + gap: $spacing-sm; + } + + .cuj-card-heading { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.4rem; + // Allow long goals to wrap instead of widening the flex item. + min-width: 0; + } + + .card-title { + display: block; + margin: 0; + font-family: var(--site-ui-fontFamily); + font-size: 1.1rem; + font-weight: 500; + line-height: 1.35; + text-wrap: pretty; + } + + .card-header-buttons { + display: flex; + flex-direction: row; + align-items: center; + gap: $spacing-xs; + flex-shrink: 0; + + .icon-button { + border-radius: $spacing-lg; + + > span { + font-size: $font-size-xl; + } + } + } + + .cuj-task-count { + margin: 0; + font-size: $font-size-sm; + color: var(--site-base-fgColor-lighter); + } + + // The shared card styles lay `.card-content` out as a centered row. + // Journeys need a plain block so the task list stacks below the divider. + .card-content { + display: block; + border-block-start: 0.05rem solid var(--site-inset-borderColor); + margin-block-start: $spacing-sm; + padding-block-start: $spacing-sm; + } + + &.collapsed { + .card-content { + display: none; + } + + .expand-button { + transform: rotate(180deg); + } + } + + .expand-button { + transition: transform $transition-normal; + + @media (prefers-reduced-motion: reduce) { + transition: none; + } + } + + .cuj-task-list { + margin: 0; + padding-inline-start: 1.15rem; + + li { + padding-inline-start: 0; + margin-block-end: $spacing-sm; + font-size: $font-size-sm; + line-height: 1.45; + color: var(--site-base-fgColor-lighter); + + &:last-child { + margin-block-end: 0; + } + } + } + } +} diff --git a/sites/docs/lib/main.client.options.dart b/sites/docs/lib/main.client.options.dart index 23c1b571e81..97a973b82fb 100644 --- a/sites/docs/lib/main.client.options.dart +++ b/sites/docs/lib/main.client.options.dart @@ -12,6 +12,10 @@ import 'package:docs_flutter_dev_site/src/components/common/client/os_selector.d deferred as _os_selector; import 'package:docs_flutter_dev_site/src/components/layout/client/pagenav.dart' deferred as _pagenav; +import 'package:docs_flutter_dev_site/src/components/pages/cuj/cuj_filters.dart' + deferred as _cuj_filters; +import 'package:docs_flutter_dev_site/src/components/pages/cuj/cuj_filters_sidebar.dart' + deferred as _cuj_filters_sidebar; import 'package:docs_flutter_dev_site/src/components/pages/archive_table.dart' deferred as _archive_table; import 'package:docs_flutter_dev_site/src/components/pages/glossary_search_section.dart' @@ -96,6 +100,14 @@ ClientOptions get defaultClientOptions => ClientOptions( ), loader: _archive_table.loadLibrary, ), + 'cuj_filters': ClientLoader( + (p) => _cuj_filters.CujFilters(), + loader: _cuj_filters.loadLibrary, + ), + 'cuj_filters_sidebar': ClientLoader( + (p) => _cuj_filters_sidebar.CujFiltersSidebar(), + loader: _cuj_filters_sidebar.loadLibrary, + ), 'glossary_search_section': ClientLoader( (p) => _glossary_search_section.GlossarySearchSection(), loader: _glossary_search_section.loadLibrary, diff --git a/sites/docs/lib/main.server.dart b/sites/docs/lib/main.server.dart index 2738c1fd439..33e35648c9a 100644 --- a/sites/docs/lib/main.server.dart +++ b/sites/docs/lib/main.server.dart @@ -27,6 +27,7 @@ import 'src/components/common/code_preview.dart'; import 'src/components/common/dash_image.dart'; import 'src/components/pages/architecture_recommendations.dart'; import 'src/components/pages/archive_table.dart'; +import 'src/components/pages/cuj/cuj_index.dart'; import 'src/components/pages/devtools_release_notes_index.dart'; import 'src/components/pages/expansion_list.dart'; import 'src/components/pages/learning_resource_index.dart'; @@ -116,6 +117,7 @@ List get _embeddableComponents => [ defineComponent('OSSelector', const OsSelector()), defineComponentWithChild('Card', Card.fromAttributes), defineComponent('LearningResourceIndex', const LearningResourceIndex()), + defineComponent('CujIndex', const CujIndex()), defineComponentWithAttrs('ArchiveTable', ArchiveTable.fromAttributes), defineComponentWithAttrs( 'DownloadLatestButton', diff --git a/sites/docs/lib/main.server.options.dart b/sites/docs/lib/main.server.options.dart index fcbd5e0a090..589bc563221 100644 --- a/sites/docs/lib/main.server.options.dart +++ b/sites/docs/lib/main.server.options.dart @@ -11,6 +11,10 @@ import 'package:docs_flutter_dev_site/src/components/common/client/os_selector.d as _os_selector; import 'package:docs_flutter_dev_site/src/components/layout/client/pagenav.dart' as _pagenav; +import 'package:docs_flutter_dev_site/src/components/pages/cuj/cuj_filters.dart' + as _cuj_filters; +import 'package:docs_flutter_dev_site/src/components/pages/cuj/cuj_filters_sidebar.dart' + as _cuj_filters_sidebar; import 'package:docs_flutter_dev_site/src/components/pages/archive_table.dart' as _archive_table; import 'package:docs_flutter_dev_site/src/components/pages/glossary_search_section.dart' @@ -80,6 +84,13 @@ ServerOptions get defaultServerOptions => ServerOptions( 'archive_table', params: __archive_tableArchiveTable, ), + _cuj_filters.CujFilters: ClientTarget<_cuj_filters.CujFilters>( + 'cuj_filters', + ), + _cuj_filters_sidebar.CujFiltersSidebar: + ClientTarget<_cuj_filters_sidebar.CujFiltersSidebar>( + 'cuj_filters_sidebar', + ), _glossary_search_section.GlossarySearchSection: ClientTarget<_glossary_search_section.GlossarySearchSection>( 'glossary_search_section', diff --git a/sites/docs/lib/src/components/pages/cuj/cuj_filters.dart b/sites/docs/lib/src/components/pages/cuj/cuj_filters.dart new file mode 100644 index 00000000000..201a21571d7 --- /dev/null +++ b/sites/docs/lib/src/components/pages/cuj/cuj_filters.dart @@ -0,0 +1,138 @@ +// Copyright 2026 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:site_shared/components/common/button.dart'; +import 'package:universal_web/web.dart' as web; + +import '../../../models/cuj_model.dart'; +import '../filterable_index.dart'; +import 'cuj_filters_sidebar.dart'; + +/// The id of the search field, so its result count can label it. +const _searchId = 'cuj-search'; + +/// The search controls and result summary for the critical user journey index. +@client +class CujFilters extends StatefulComponent { + const CujFilters({super.key}); + + /// The ID of the checkbox that toggles the filter drawer on narrow screens. + static const String drawerToggleId = 'cuj-filter-toggle'; + + @override + State createState() => _CujFiltersState(); +} + +class _CujFiltersState extends State { + /// The filters selected in the critical user journey sidebar. + static CujFiltersNotifier get _filters => CujFiltersSidebar.filters; + + /// The journeys reconstructed from the rendered journey cards. + final List _cujs = []; + + /// The current search query. + String _searchQuery = ''; + + /// The number of journeys matching the active search and filters. + int _filteredCujCount = 0; + + @override + void initState() { + super.initState(); + + if (kIsWeb) { + _filters.addListener(_setFilters); + + final cujList = web.document.getElementById('all-cujs-list'); + if (cujList == null) { + return; + } + + _recreateCujs(cujList.querySelectorAll('.cuj-card')); + } + } + + /// Populates [_cujs] from [cujCards]. + void _recreateCujs(web.NodeList cujCards) { + for (var i = 0; i < cujCards.length; i++) { + final element = cujCards.item(i) as web.Element; + _cujs.add(Cuj.fromElement(element)); + } + _filteredCujCount = _cujs.length; + } + + /// Updates the filter state and re-evaluates which journeys to show. + /// + /// Use like the `setState` method by passing a callback that updates + /// the relevant state variables. + void _setFilters([void Function()? callback]) { + setState(callback ?? () {}); + + final cujsToShow = _filters.filterCujs(_cujs, _searchQuery); + _filteredCujCount = cujsToShow.length; + for (final cuj in _cujs) { + final element = + web.document.getElementById(cuj.elementId) as web.HTMLElement?; + if (element == null) { + continue; + } + + if (cujsToShow.contains(cuj)) { + element.classList.remove('hidden'); + } else { + element.classList.add('hidden'); + } + } + } + + @override + void dispose() { + if (kIsWeb) { + _filters.removeListener(_setFilters); + } + super.dispose(); + } + + @override + Component build(BuildContext context) { + return FilterSearchGroup( + drawerToggleId: CujFilters.drawerToggleId, + searchId: _searchId, + placeholder: 'Try "testing" or "architecture"...', + label: 'Search critical user journeys by goal, persona, and task', + value: _searchQuery, + onInput: (value) { + _setFilters(() { + _searchQuery = value; + }); + }, + children: [ + div(classes: 'label-row', [ + label( + attributes: {'for': _searchId, 'aria-live': 'polite'}, + [ + const .text('Showing '), + span([.text('$_filteredCujCount')]), + const .text(' / '), + span([.text('${_cujs.length}')]), + ], + ), + Button( + icon: 'close_small', + content: 'Clear filters', + size: ButtonSize.compact, + disabled: _searchQuery.isEmpty && !_filters.hasSelectedPersonas, + onClick: () { + // No setState needed, since resetting filters will trigger it. + _searchQuery = ''; + _filters.reset(); + }, + ), + ]), + ], + ); + } +} diff --git a/sites/docs/lib/src/components/pages/cuj/cuj_filters_sidebar.dart b/sites/docs/lib/src/components/pages/cuj/cuj_filters_sidebar.dart new file mode 100644 index 00000000000..34aff7ae6ca --- /dev/null +++ b/sites/docs/lib/src/components/pages/cuj/cuj_filters_sidebar.dart @@ -0,0 +1,143 @@ +// Copyright 2026 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:site_shared/components/common/button.dart'; + +import '../../../models/cuj_model.dart'; +import '../filterable_index.dart'; +import 'cuj_filters.dart'; + +// TODO(ewindmill): Replace with the real feedback destination once it exists. +const _feedbackUrl = 'https://github.com/flutter/evals/issues'; + +/// The persona filters for the critical user journey index. +@client +class CujFiltersSidebar extends StatelessComponent { + const CujFiltersSidebar({super.key}); + + /// The filter state for the critical user journey list. + /// + /// This is static so that [CujFilters] can access it, + /// since both client components don't share a common ancestor. + static final CujFiltersNotifier filters = CujFiltersNotifier(); + + @override + Component build(BuildContext context) { + return FiltersSidebar( + drawerToggleId: CujFilters.drawerToggleId, + footer: const [ + div(classes: 'cuj-feedback', [ + Button( + href: _feedbackUrl, + content: 'Provide feedback', + style: ButtonStyle.outlined, + title: 'Leave feedback or suggest new CUJs.', + attributes: { + 'target': '_blank', + 'rel': 'noopener', + }, + ), + ]), + ], + children: [ + ListenableBuilder( + listenable: filters, + builder: (context) { + return div(classes: 'table-content', [ + const h4([.text('Persona')]), + ul([ + for (final persona in CujPersona.values) + li([ + input( + type: InputType.checkbox, + attributes: {'name': 'cuj-filter-${persona.name}'}, + id: 'cuj-filter-${persona.name}', + checked: filters.isPersonaSelected(persona), + onChange: (checked) { + filters.setPersona( + persona, + isSelected: checked as bool? ?? false, + ); + }, + ), + label( + attributes: {'for': 'cuj-filter-${persona.name}'}, + [.text(persona.label)], + ), + ]), + ]), + ]); + }, + ), + ], + ); + } +} + +/// Stores the selected critical user journey filters and +/// notifies listeners when they change. +final class CujFiltersNotifier extends ChangeNotifier { + /// The currently selected personas. + final Set _selectedPersonas = {}; + + /// Whether any persona filters are selected. + bool get hasSelectedPersonas => _selectedPersonas.isNotEmpty; + + /// Whether [persona] is selected. + bool isPersonaSelected(CujPersona persona) => + _selectedPersonas.contains(persona); + + /// Updates whether [persona] is selected and notifies listeners. + void setPersona(CujPersona persona, {required bool isSelected}) { + if (isSelected) { + _selectedPersonas.add(persona); + } else { + _selectedPersonas.remove(persona); + } + notifyListeners(); + } + + /// Clears all selected personas. + void reset() { + _selectedPersonas.clear(); + notifyListeners(); + } + + /// Returns the journeys matching [searchQuery] and the selected filters. + Set filterCujs(List cujs, String searchQuery) { + searchQuery = searchQuery.trim().toLowerCase(); + + if (searchQuery.isEmpty && _selectedPersonas.isEmpty) { + // No filters applied, return all journeys. + return cujs.toSet(); + } + + final cujsToShow = {}; + + for (final cuj in cujs) { + final matchesPersona = + _selectedPersonas.isEmpty || isPersonaSelected(cuj.persona); + if (!matchesPersona) { + continue; + } + + final matchesSearchQuery = + searchQuery.isEmpty || + cuj.goal.toLowerCase().contains(searchQuery) || + cuj.persona.label.toLowerCase().contains(searchQuery) || + cuj.tasks.any( + (task) => task.task.toLowerCase().contains(searchQuery), + ); + if (!matchesSearchQuery) { + continue; + } + + cujsToShow.add(cuj); + } + + return cujsToShow; + } +} diff --git a/sites/docs/lib/src/components/pages/cuj/cuj_index.dart b/sites/docs/lib/src/components/pages/cuj/cuj_index.dart new file mode 100644 index 00000000000..2e551ae5ecb --- /dev/null +++ b/sites/docs/lib/src/components/pages/cuj/cuj_index.dart @@ -0,0 +1,109 @@ +// Copyright 2026 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert' show jsonEncode; + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:jaspr_content/jaspr_content.dart'; +import 'package:site_shared/components/common/button.dart'; +import 'package:site_shared/components/common/card.dart'; +import 'package:site_shared/components/common/tags.dart'; + +import '../../../models/cuj_model.dart'; +import 'cuj_filters.dart'; +import 'cuj_filters_sidebar.dart'; + +/// Renders the filterable critical user journey catalog. +final class CujIndex extends StatelessComponent { + const CujIndex({super.key}); + + @override + Component build(BuildContext context) { + final cujData = context.page.data['cujs'] as List; + + final cujs = [ + for (final cuj in cujData) Cuj.fromMap(cuj as Map), + ]; + + return div(classes: 'filterable-index', [ + div(classes: 'left-col', [ + const CujFilters(), + div(classes: 'card-list', id: 'all-cujs-list', [ + for (final cuj in cujs) _CujCard(cuj), + ]), + ]), + const CujFiltersSidebar(), + ]); + } +} + +/// An expandable card that summarizes a critical user journey and its tasks. +final class _CujCard extends StatelessComponent { + const _CujCard(this.cuj); + + /// The critical user journey displayed by this card. + final Cuj cuj; + + @override + Component build(BuildContext context) { + final cardId = cuj.elementId; + final taskCount = cuj.tasks.length; + + // Expanding and collapsing is handled for every `.expandable-card` + // by the `_setUpExpandableCards` global script. + return Card.expandable( + id: cardId, + outlined: true, + additionalClasses: 'cuj-card', + initiallyExpanded: false, + attributes: { + 'data-persona': cuj.persona.name, + 'data-goal': cuj.goal, + 'data-tasks': jsonEncode(cuj.tasks), + }, + header: [ + div(classes: 'cuj-card-heading', [ + Tag( + cuj.persona.label, + color: cuj.persona.tagColor, + size: TagSize.small, + ), + h2(classes: 'card-title', [.text(cuj.goal)]), + ]), + div(classes: 'card-header-buttons', [ + Button( + href: '#$cardId', + icon: 'tag', + classes: const ['share-button'], + title: 'Link to journey', + attributes: { + 'aria-label': 'Link to the "${cuj.goal}" journey', + }, + ), + Button( + icon: 'keyboard_arrow_up', + classes: const ['expand-button'], + title: 'Expand or collapse tasks', + attributes: { + 'aria-expanded': 'false', + 'aria-controls': '$cardId-content', + 'aria-label': 'Expand or collapse the tasks for "${cuj.goal}"', + }, + ), + ]), + ], + collapsedContent: [ + p(classes: 'cuj-task-count', [ + .text(taskCount == 1 ? '1 task' : '$taskCount tasks'), + ]), + ], + expandedContent: [ + ul(classes: 'cuj-task-list', [ + for (final task in cuj.tasks) li([.text(task.task)]), + ]), + ], + ); + } +} diff --git a/sites/docs/lib/src/models/cuj_model.dart b/sites/docs/lib/src/models/cuj_model.dart new file mode 100644 index 00000000000..a553ebb9a13 --- /dev/null +++ b/sites/docs/lib/src/models/cuj_model.dart @@ -0,0 +1,152 @@ +// Copyright 2026 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert'; + +import 'package:site_shared/components/common/tags.dart'; +import 'package:universal_web/web.dart' as web; + +/// Prefix used for the DOM ID of each journey's card element. +const _elementIdPrefix = 'cuj-'; + +/// A critical user journey and its associated tasks. +final class Cuj { + const Cuj._({ + required this.id, + required this.goal, + required this.persona, + required this.tasks, + }); + + /// Creates a journey from YAML-backed page data. + factory Cuj.fromMap(Map map) { + return Cuj._( + id: map['id'] as int, + goal: map['goal'] as String, + persona: CujPersona.fromDataValue(map['persona'] as String), + tasks: [ + for (final task in map['tasks'] as List) + CujTask.fromMap(task as Map), + ], + ); + } + + /// Creates a journey from data attributes on [element]. + factory Cuj.fromElement(web.Element element) { + final dataPersona = + element.getAttribute('data-persona') ?? + (throw StateError('CUJ card ${element.id} has no persona.')); + final dataGoal = + element.getAttribute('data-goal') ?? + (throw StateError('CUJ card ${element.id} has no goal.')); + final dataTasks = + element.getAttribute('data-tasks') ?? + (throw StateError('CUJ card ${element.id} has no tasks.')); + + return Cuj._( + id: int.parse(element.id.replaceFirst(_elementIdPrefix, '')), + goal: dataGoal, + persona: CujPersona.values.byName(dataPersona), + tasks: [ + for (final task in jsonDecode(dataTasks) as List) + CujTask.fromMap(task as Map), + ], + ); + } + + /// The stable numeric identifier for this journey. + final int id; + + /// The developer goal that this journey represents. + final String goal; + + /// The developer persona associated with this journey. + final CujPersona persona; + + /// The tasks that contribute to [goal]. + final List tasks; + + /// The identifier of the card element that renders this journey. + String get elementId => '$_elementIdPrefix$id'; +} + +/// A concrete task within a critical user journey. +final class CujTask { + const CujTask({ + required this.id, + required this.name, + required this.task, + }); + + /// Creates a task from YAML-backed page data or decoded JSON. + factory CujTask.fromMap(Map map) { + return CujTask( + id: map['id'] as int, + name: map['name'] as String, + task: map['task'] as String, + ); + } + + /// The stable numeric identifier for this task. + final int id; + + /// The stable machine-readable name of this task. + final String name; + + /// The reader-facing task description. + final String task; + + /// A JSON-compatible representation of this task. + Map toJson() => { + 'id': id, + 'name': name, + 'task': task, + }; +} + +/// The developer personas a critical user journey can belong to. +/// +/// [dataValue] must match the `persona` values used in `src/data/cujs.yaml`. +enum CujPersona { + appDeveloper('App developer', 'The App Developer', TagColor.blue), + techLead( + 'Tech lead / architect', + 'The Tech Lead / Architect', + TagColor.purple, + ), + pluginDeveloper('Plugin developer', 'The Plugin Developer', TagColor.teal), + fullStackDeveloper( + 'Full-stack developer', + 'The Full Stack Developer', + TagColor.magenta, + ), + hybridDeveloper( + 'Hybrid (native + Flutter) developer', + 'The Hybrid (Native + Flutter) Developer', + TagColor.amber, + ); + + const CujPersona(this.label, this.dataValue, this.tagColor); + + /// Returns the persona whose [dataValue] matches the YAML data. + /// + /// Throws an [ArgumentError] if the value is unknown. + static CujPersona fromDataValue(String dataValue) { + for (final persona in values) { + if (persona.dataValue == dataValue) { + return persona; + } + } + throw ArgumentError.value(dataValue, 'dataValue', 'Unknown CUJ persona'); + } + + /// The reader-facing name of this persona. + final String label; + + /// The persona value used in `cujs.yaml`. + final String dataValue; + + /// The color used for this persona's [Tag] badge. + final TagColor tagColor; +} diff --git a/sites/docs/src/content/ai/flutter-bench/cujs.md b/sites/docs/src/content/ai/flutter-bench/cujs.md new file mode 100644 index 00000000000..5d86c6c5de0 --- /dev/null +++ b/sites/docs/src/content/ai/flutter-bench/cujs.md @@ -0,0 +1,27 @@ +--- +title: Flutter critical user journeys +shortTitle: Flutter CUJs +description: >- + Browse the catalog of canonical Flutter and Dart critical user journeys that + the FlutterBench evaluations test. +bodyClass: wide-site-content +showToc: false +--- + +A _critical user journey_ (CUJ) is a goal that +a developer sets out to accomplish, +such as "make an application accessible to all users" or +"diagnose and resolve layout overflow errors". +Each CUJ is broken down into the concrete tasks required to complete it. + +Product teams at Google treat CUJs as a source of truth: they're how teams align +on priorities, shape roadmaps, measure product health, and more. The Flutter team uses +CUJs to derive evaluation tasks and prompts for FlutterBench. + +The following catalogue lists the Flutter team's CUJs. It's a claim about what matters in +Flutter development. If the way you build Flutter apps isn't represented here, +the list is incomplete, and we encourage [you to open an issue with your feedback][]. + + + +[you to open an issue with your feedback]: https://github.com/flutter/evals/issues diff --git a/sites/docs/src/content/ai/flutter-bench/index.md b/sites/docs/src/content/ai/flutter-bench/index.md new file mode 100644 index 00000000000..440c3398d04 --- /dev/null +++ b/sites/docs/src/content/ai/flutter-bench/index.md @@ -0,0 +1,10 @@ +--- +title: FlutterBench agent evaluations +shortTitle: FlutterBench +description: >- + How the Flutter team implements agent evals, and the results of those evals. +--- + +:::note +Evaluation tooling and benchmarks are coming soon. +::: \ No newline at end of file diff --git a/sites/docs/src/data/cujs.yaml b/sites/docs/src/data/cujs.yaml new file mode 100644 index 00000000000..47d304f961c --- /dev/null +++ b/sites/docs/src/data/cujs.yaml @@ -0,0 +1,1051 @@ +# List of all Flutter CUJs +# The source-of-truth is the tasks in the FlutterBench +# repository (which is currently private.) +# +# TODO(ewindmill): automate the generation of this file. +# +# CUJs should have: +# - id: The CUJs ID defined in the FlutterBench repository. +# - goal: The main CUJ text. +# - persona: The type of developer the CUJ speaks for from a list of ["The Tech Lead / Architect", "The App Developer", "The Plugin Developer", "Full-stack developer", "Hybrid (native + Flutter) developer"] +# - tasks: A list of tasks that a developer does to reach the CUJ goal +- id: 0 + goal: Evaluate and select the technical stack, folder structure, state management, + and routing architecture for a project + persona: The Tech Lead / Architect + tasks: + - id: 1 + name: research-existing-options-available-architecture + task: Evaluate available architectural patterns, routing libraries, and state + management frameworks, documenting the rationale for the selected technology + stack. + - id: 2 + name: use-workspace-monorepo-repo-structure + task: Configure a multi-package Dart workspace or monorepo repository structure + to separate core domain logic from application UI features. +- id: 1 + goal: Enforce consistent code formatting, linting, and architectural standards + persona: The Tech Lead / Architect + tasks: + - id: 3 + name: compose-analysis-options-style-guide + task: Author comprehensive static analysis rules in "analysis_options.yaml" + and document architectural standards in a project style guide. + - id: 4 + name: ensure-codebase-uses-only-selected + task: Enforce that the codebase adheres strictly to documented architectural + decisions and state management patterns, avoiding unapproved approaches. +- id: 2 + goal: Manage dependency risks and audit third-party packages + persona: The Tech Lead / Architect + tasks: + - id: 5 + name: audit-third-party-pub-dev + task: Audit third-party pub.dev packages for license compliance, maintenance + activity, and security vulnerabilities before adoption. + - id: 6 + name: ensure-dependencies-are-installed-cli + task: Add project dependencies using official command-line package managers + rather than manually modifying configuration files. +- id: 3 + goal: Establish repository governance, branching conventions, code review standards, + and CI quality gates + persona: The Tech Lead / Architect + tasks: + - id: 7 + name: establish-repository-governance-standardize-branching + task: Establish repository governance policies to standardize branching models, + enforce peer code reviews, and automate CI quality gates. +- id: 4 + goal: Develop custom Dart CLI developer utilities and automation tools + persona: The App Developer + tasks: + - id: 8 + name: write-dart-cli-tool-generate + task: Develop a standalone Dart command-line utility that parses database schema + specifications and generates required boilerplate data access code. + - id: 9 + name: write-cli-tool-optimise-csv + task: Develop a Dart command-line utility to automate the parsing, validation, + and compression of CSV datasets and application resources. +- id: 5 + goal: Optimize application release builds for minimal bundle size + persona: The Tech Lead / Architect + tasks: + - id: 10 + name: analyze-size-analyze-size-devtools + task: Analyze application bundle composition and asset weight using command-line + size analysis tools and Flutter DevTools. + - id: 11 + name: enable-tree-shaking-obfuscation-split + task: Configure production build flags to enable code tree shaking, symbol obfuscation, + and split debug information. + - id: 12 + name: audit-compress-codebase-assets + task: Audit application resources to remove unused assets and compress images + and fonts for reduced download size. +- id: 6 + goal: Maintain accurate, up-to-date repository documentation and README guides + persona: The Tech Lead / Architect + tasks: + - id: 13 + name: review-update-readme-other-documentation + task: Audit and update repository documentation, including README guides and + architectural overviews, to align with recent codebase modifications. +- id: 7 + goal: Make an application accessible to all users + persona: The App Developer + tasks: + - id: 14 + name: evaluate-how-app-accessibility-is + task: Audit the application using Flutter DevTools and automated accessibility + inspection tools to identify compliance gaps. + - id: 15 + name: modify-app-add-semantic-labels + task: Refactor UI widgets to include descriptive Semantics properties and screen + reader labels for visually impaired users. + - id: 16 + name: modify-app-make-tappable-areas + task: Enforce minimum interactive touch target dimensions across all interactive + components to meet mobile accessibility standards. + - id: 17 + name: remove-fixed-text-scaler + task: Refactor text components to support dynamic system font scaling and remove + hardcoded text scale restrictions. + - id: 18 + name: add-high-contrast-color-themes + task: Implement high-contrast visual themes and color palettes to support users + with visual impairments. +- id: 8 + goal: Achieve comprehensive test coverage with unit, widget, and integration test + suites + persona: The App Developer + tasks: + - id: 19 + name: check-existing-test-coverage-percentage + task: Analyze current test coverage to identify untested code sections and + determine which parts of the application require additional test coverage. + - id: 20 + name: add-app-benchmarking-uses-binding + task: Implement automated performance benchmarking using binding.traceAction + to measure frame timing and verify that the 90th percentile execution duration + remains below defined latency thresholds. + - id: 21 + name: add-flutter-integration-tests-mobile + task: Develop end-to-end integration test suites using "package:integration_test" + to validate complete user journeys across mobile and web environments. +- id: 9 + goal: Diagnose and resolve layout overflow errors in UI component trees + persona: The App Developer + tasks: + - id: 22 + name: find-real-cause-ui-overflow + task: Diagnose and identify the root cause of layout overflow errors in the + UI component tree. + - id: 23 + name: fix-overflow-bug-with-proper-widgets + task: Refactor the layout using flexible scrolling or bounding widgets to resolve + the overflow error. + - id: 24 + name: write-widget-tests-edge-cases + task: Implement automated widget tests covering boundary conditions and large + data values to prevent regression of layout overflows. +- id: 10 + goal: Implement a structured routing and navigation system + persona: The App Developer + tasks: + - id: 25 + name: set-up-go-router-named + task: Configure declarative application routing using "package:go_router", implementing + named routes and dynamic URL path parameters. + - id: 26 + name: set-up-go-router-builder + task: Integrate "package:go_router_builder" and code generation to manage type-safe + route navigation and arguments. + - id: 27 + name: implement-deep-linking-trigger-deep + task: Configure platform-specific deep linking schemas and verify that external + links navigate correctly to target application screens. + - id: 28 + name: guard-routes-based-auth-state + task: Implement redirection guards within the routing configuration to restrict + access to authenticated user sessions. + - id: 29 + name: use-navigator-v1-route-does + task: Implement imperative navigation using standard Navigator 1.0 APIs for + simple internal modal dialogs and screen transitions. +- id: 11 + goal: Add a new UI screen to an existing application following established design + and architectural patterns + persona: The App Developer + tasks: + - id: 30 + name: add-new-screen-design-system + task: Add a new UI screen to the application that integrates with the existing + design system, routing architecture, and standard page structure. +- id: 12 + goal: Design responsive UI layouts that reflow cleanly across all window sizes and + device orientations + persona: The App Developer + tasks: + - id: 31 + name: define-central-breakpoints-m3-window + task: Define layout breakpoints based on Material Design 3 window size classes, + such as using compact layouts for widths under 600 logical pixels. + - id: 32 + name: use-mediaquery-sizeof-window-sizing + task: Refactor responsive sizing logic to use MediaQuery.sizeOf for global window + dimensions and LayoutBuilder for local widget constraint sizing, removing + hardcoded device-type checks. + - id: 33 + name: apply-safearea-notches-insets + task: Wrap visual layouts in SafeArea widgets to prevent content from obscuring + system status bars, display notches, and physical screen bezels. + - id: 34 + name: don-t-portrait-lock-support + task: Configure the application to support both portrait and landscape orientations, + verifying smooth UI reflow during device rotation. + - id: 35 + name: cap-content-width-large-windows + task: Constrain maximum content width on wide desktop or tablet displays using + BoxConstraints or by dynamically transitioning from ListView to GridView layouts. + - id: 36 + name: handle-foldable-letterboxing-support-all + task: Optimize layouts for foldable devices and letterboxed display modes across + various screen postures and orientations. +- id: 13 + goal: Optimize application rendering and memory performance + persona: The App Developer + tasks: + - id: 37 + name: use-devtools-profile-rendering-performance + task: Profile application frame rendering times and rasterization metrics using + Flutter DevTools. + - id: 38 + name: hunt-down-memory-leaks + task: Diagnose and resolve application memory leaks and retained object graphs + using memory profiling tools. + - id: 39 + name: add-renderrepaintboundary-s-widget-tree + task: Refactor the widget hierarchy by inserting RenderRepaintBoundary widgets + around frequently animating components to isolate repaint regions. +- id: 14 + goal: Implement state restoration to preserve user state across application restarts + persona: The App Developer + tasks: + - id: 40 + name: add-state-restoration-functionality-app + task: Implement Flutter state restoration APIs using RestorationManager and + RestorationBucket to preserve interface navigation and scroll states across + process terminations. + - id: 41 + name: add-hydrated-versions-state-management + task: Integrate persistent state management libraries, such as "package:hydrated_bloc", + to automatically serialize and restore application state across application + restarts. +- id: 15 + goal: Implement offline-first data caching and synchronization + persona: The App Developer + tasks: + - id: 42 + name: add-local-caching-solution-be + task: Implement an offline-first repository pattern that caches remote server + data locally and synchronizes pending mutations when network connectivity + is restored. +- id: 16 + goal: Build interactive widget preview catalogs and isolated design system showcases + persona: The App Developer + tasks: + - id: 43 + name: create-interactive-website-every-widget + task: Develop a standalone interactive web catalog showcasing every UI component + and visual state available within the component library. + - id: 44 + name: add-widget-previews-for-components + task: Implement isolated widget preview configurations using the official Flutter + widget previewer tool for all UI components in the application. +- id: 17 + goal: Implement a customizable, reusable UI design system + persona: The App Developer + tasks: + - id: 45 + name: create-totally-custom-design-system + task: Build an independent UI design system from scratch without relying on + standard Material or Cupertino widget libraries. + - id: 46 + name: customise-material-design-system-fit + task: Customize and extend Material Design widgets and styling tokens to implement + a proprietary visual design system. + - id: 47 + name: customise-cupertino-design-system-fit + task: Customize and extend Cupertino widgets to implement a proprietary iOS-styled + visual design system. +- id: 18 + goal: Implement a consistent visual design theme and styling across an application + persona: The App Developer + tasks: + - id: 48 + name: create-theme-data-from-design-document + task: Implement application ThemeData configurations derived from specifications + in a design document. + - id: 49 + name: add-dark-mode-support + task: Implement dark mode theming and color scheme switching. + - id: 50 + name: change-dropdown-popup-buttons-styling + task: Customize visual styling for dropdown menus, popup dialogs, and interactive + buttons by extending central ThemeData configurations. +- id: 19 + goal: Implement custom gesture detection and pointer interactions + persona: The App Developer + tasks: + - id: 51 + name: create-custom-widget-detects-hover + task: Implement a custom interactive component that combines MouseRegion for + hover detection with GestureDetector or InkWell for touch and pointer interactions. +- id: 20 + goal: Implement custom widget animation states and transitions + persona: The App Developer + tasks: + - id: 52 + name: create-widget-uses-animationcontrollers-animate + task: Develop an explicit animated component using AnimationController to manage + custom state transitions and tween animations. + - id: 53 + name: add-tests-confirm-animation-logic + task: Implement automated widget tests to verify that animation state machines + and value transitions execute correctly. + - id: 54 + name: replace-static-widget-animated-version + task: Refactor static UI components to use implicit animation widgets, such + as AnimatedContainer and AnimatedOpacity, for smooth state transitions. + - id: 55 + name: use-hero-transition-animations-between + task: Implement shared element routing transitions across navigation boundaries + using Hero animation widgets. +- id: 21 + goal: Integrate rich animated graphics and shaders into an application + persona: The App Developer + tasks: + - id: 56 + name: use-rive-lottie-or-some + task: Integrate animation libraries, such as "package:rive" or "package:lottie", + to render rich vector animations within the application. + - id: 57 + name: use-shaders-animate-things-app + task: Implement fragment shaders using GLSL shader programs to render custom + GPU-accelerated visual effects and animations. +- id: 22 + goal: Integrate interactive data visualization and charting libraries + persona: The App Developer + tasks: + - id: 58 + name: find-list-available-libraries-charts + task: Research available charting libraries on pub.dev and evaluate which packages + support the required chart types and features for the use case. + - id: 59 + name: install-chart-library-supports-bar + task: Install a third-party charting package that supports interactive bar charts + using official command-line tools ("flutter pub add") rather than manually + editing configuration files. + - id: 60 + name: implement-library-application-show-chart + task: Integrate the charting library into the application dashboard to render + interactive data visualizations, implementing automated widget tests to verify + chart rendering. +- id: 23 + goal: Configure package dependency overrides using Git repositories or local filesystem + paths + persona: The App Developer + tasks: + - id: 61 + name: add-dependency-override-git + task: Configure package dependency overrides in "pubspec.yaml" to target a specific + remote Git repository and subdirectory path, verifying that all automated + tests pass. + - id: 62 + name: add-dependency-override-local + task: Configure package dependency overrides in "pubspec.yaml" to link against + a local filesystem package path, verifying that all automated tests pass. +- id: 24 + goal: Build an application that renders Material UI on Android and Cupertino UI + on iOS + persona: The App Developer + tasks: + - id: 63 + name: create-new-app-android-ios + task: Create a new cross-platform Flutter application targeting both Android + and iOS. + - id: 64 + name: add-adaptive-material-cupertino-layouts + task: Implement navigation layouts that adaptively render Material Design components + on Android and Cupertino components on iOS. +- id: 25 + goal: Implement performant scrolling layouts for long-form content + persona: The App Developer + tasks: + - id: 65 + name: identify-overflow-refactor-layout-use + task: Diagnose vertical layout overflow errors and refactor the component hierarchy + to use SingleChildScrollView. + - id: 66 + name: migrate-customscrollview-slivers-more-complex + task: Refactor standard scroll views to use CustomScrollView and Sliver components + for advanced scrolling effects and header animations. + - id: 67 + name: have-long-list-items-variate + task: Implement programmatic scroll-to-index functionality for variable-height + item lists using scroll controllers or item alignment libraries. +- id: 26 + goal: Build intuitive, validated user forms with polished input UX + persona: The App Developer + tasks: + - id: 68 + name: auto-focus-first-invalid-field + task: Implement form validation logic that automatically transfers focus to + the first invalid input field when a user submits an incomplete form. + - id: 69 + name: add-floating-label-behavior-textfields + task: Configure text input fields with floating label behavior and error messaging + using InputDecoration properties. +- id: 27 + goal: Build an application that communicates with a REST API + persona: The App Developer + tasks: + - id: 70 + name: fetch-parse-json-http-or + task: Implement network calls to fetch and parse JSON payloads from a REST API + using "package:http" or "package:dio". + - id: 71 + name: handle-errors-timeouts-loading-states + task: Implement robust error handling, network request timeout management, and + UI loading state indicators. + - id: 72 + name: get-rid-ui-jank-due + task: Offload JSON serialization and deserialization to background worker isolates + to prevent main thread stutter and UI frame drops. + - id: 73 + name: if-api-has-spec-use + task: Generate type-safe API client code and data models automatically from + an OpenAPI specification using code generation tools. +- id: 28 + goal: Implement and evaluate state management architectures + persona: The App Developer + tasks: + - id: 74 + name: use-setstate-manage-state + task: Implement application state management using standard StatefulWidget and + setState mechanisms. + - id: 75 + name: use-provider-manage-state + task: Implement application state management using "package:provider" for dependency + injection and reactive updates. + - id: 76 + name: use-riverpod-manage-state-maybe + task: Implement application state management using "package:riverpod", optionally + incorporating "package:flutter_hooks" and code generation. + - id: 77 + name: use-bloc-cubit-manage-state + task: Implement application state management using the Business Logic Component + (BLoC) and Cubit patterns from "package:flutter_bloc". + - id: 78 + name: use-hooks-manage-state + task: Implement application state management using "package:flutter_hooks" to + manage widget lifecycle and local state with composable hook functions. + - id: 79 + name: use-rxdart-manage-state + task: Implement stream-based application state management using reactive programming + primitives from "package:rxdart". +- id: 29 + goal: Implement local data persistence in an application + persona: The App Developer + tasks: + - id: 80 + name: set-up-shared-preferences-package + task: Implement local persistence for simple key-value data and user preferences + using "package:shared_preferences". + - id: 81 + name: set-up-sqlite-sqflite-or + task: Implement a structured local relational database using "package:sqflite" + or "package:drift". + - id: 82 + name: set-up-secure-storage-store + task: Implement encrypted local storage for sensitive user data and authentication + tokens using "package:flutter_secure_storage". + - id: 83 + name: use-path-provider-locate-application + task: Integrate "package:path_provider" to locate platform-specific filesystem + directories for application documents and temporary files. +- id: 30 + goal: Offload CPU-intensive computation to background isolates to prevent UI freezing + persona: The App Developer + tasks: + - id: 84 + name: offload-cpu-intensive-work-isolate + task: Offload computationally expensive synchronous operations to background + worker isolates using Isolate.run. + - id: 85 + name: use-compute-one-shot-tasks + task: Execute one-shot background computations using the top-level compute function + to prevent UI thread blocking. + - id: 86 + name: set-up-long-lived-isolate + task: Implement a long-lived background isolate communicating via ReceivePort + and SendPort message passing to handle continuous asynchronous processing. +- id: 31 + goal: Adopt code generation tools to reduce boilerplate for models and immutable + data classes + persona: The App Developer + tasks: + - id: 87 + name: set-up-build-runner-json + task: Configure "package:build_runner" and "package:json_serializable" to generate + type-safe JSON serialization code for data models. + - id: 88 + name: use-freezed-immutable-data-classes + task: Integrate "package:freezed" to generate immutable data classes, union + types, and value equality boilerplate. + - id: 89 + name: use-build-verify-ci-cd + task: Configure automated CI/CD pipelines using "package:build_verify" to ensure + generated source code is synchronized with existing data models. +- id: 32 + goal: Diagnose and resolve native platform interop bugs and channel communication + errors + persona: The Plugin Developer + tasks: + - id: 90 + name: analyze-codebase-try-find-cause + task: Analyze native platform channel implementations and Dart bindings to diagnose + the root cause of platform communication failures. + - id: 91 + name: add-debug-logs-perform-test + task: Instrument platform interop channels with diagnostic logging and execute + test runs to isolate native execution errors. + - id: 92 + name: fix-issue + task: Refactor native host code and Dart channel handlers to resolve platform + interop exceptions and restore reliable communication. +- id: 33 + goal: Design a unified, well-documented cross-platform API surface for plugin consumers + persona: The Plugin Developer + tasks: + - id: 93 + name: design-intuitive-well-documented-unified + task: Design a unified, documented Dart API that abstracts native iOS and Android + implementation differences for plugin consumers. + - id: 94 + name: encapsulate-platform-interface-code + task: Structure the plugin package to ensure public APIs encapsulate and hide + internal platform interface implementations. + - id: 95 + name: generate-html-api-documentation + task: Generate HTML API documentation from inline dartdoc comments using command-line + tools to verify public API presentation. +- id: 34 + goal: Implement automated native platform test suites (XCTest, Espresso, JUnit) + to prevent OS upgrade regressions + persona: The Plugin Developer + tasks: + - id: 96 + name: write-automated-tests-validate-both + task: Implement automated test suites validating Dart logic and native implementations + using XCTest for iOS and JUnit or Espresso for Android. +- id: 35 + goal: Maintain high-quality published packages with rigorous semantic versioning, + detailed changelogs, and responsiveness to SDK updates + persona: The Plugin Developer + tasks: + - id: 97 + name: manage-versions-write-changelogs-get + task: Manage semantic versioning, maintain changelogs, achieve high pub.dev + quality scores, and publish package releases compatible with current Flutter + SDK versions. +- id: 36 + goal: Adopt optimal native interop mechanisms (FFI, Pigeon, JS Interop) based on + performance and platform requirements + persona: The Plugin Developer + tasks: + - id: 98 + name: migrate-pigeon-based-platform-channels + task: Migrate Pigeon-based platform channels to Foreign Function Interface (FFI) + bindings for performance-critical or synchronous native calls. + - id: 99 + name: replace-manual-platform-channel-boilerplate + task: Replace manual platform channel boilerplate with type-safe message passing + code generated by "package:pigeon". + - id: 100 + name: create-type-safe-bindings-between + task: Create type-safe bindings between Dart and JavaScript using "dart:js_interop" + and extension types to integrate with browser APIs and external JavaScript + libraries. + - id: 101 + name: check-all-resources-used-native + task: Ensure native memory allocations (malloc, calloc, FFI structs, OpenGL + handles, file descriptors) are properly released using NativeFinalizer and + the Finalizable interface. +- id: 37 + goal: Extend an existing federated plugin architecture to support a new target platform + persona: The Plugin Developer + tasks: + - id: 102 + name: add-implementation-new-platform-app + task: Implement native platform support for an additional operating system within + an existing plugin, verifying that all application-facing integration tests + pass. + - id: 103 + name: test-new-plugin-through-app + task: Verify the new platform implementation by running automated test suites + against the application-facing package. + - id: 104 + name: map-c-language-types-integers + task: Map C language data types (integers, structs, and pointers) to "dart:ffi" + types, utilizing AbiSpecificInteger for platform-dependent type sizing. + - id: 105 + name: refactor-plugin-split-it-into + task: Refactor a monolithic plugin into a federated architecture consisting + of separate application-facing, platform interface, and platform implementation + packages. +- id: 38 + goal: Implement automated cross-platform integration tests for plugins using modern + testing frameworks + persona: The Plugin Developer + tasks: + - id: 106 + name: use-patrol-package-be-able + task: Implement automated cross-platform integration tests using "package:patrol" + to verify plugin functionality across native environments. + - id: 107 + name: write-ci-cd-pipeline-test + task: Configure an automated CI/CD pipeline to execute plugin integration tests + on every pull request and prior to release publication. + - id: 108 + name: write-integration-tests-has-100% + task: Develop comprehensive integration test suites that achieve full test coverage + of native platform plugin functionality. +- id: 39 + goal: Implement user authentication flows and UI + persona: The Full Stack Developer + tasks: + - id: 109 + name: plan-auth-provider-design-system + task: Define the architectural requirements, user experience flows, and edge-case + handling for application authentication. + - id: 110 + name: code-auth-flow + task: Implement secure user authentication and registration workflows connecting + the frontend UI to the authentication service. + - id: 111 + name: test-all-auth-flows + task: Implement automated unit, widget, and integration test suites to verify + all authentication and session management workflows. +- id: 40 + goal: Integrate push notification services into an application + persona: The Full Stack Developer + tasks: + - id: 112 + name: add-firebase-cloud-messaging + task: Integrate "package:firebase_messaging" to enable push notifications across + mobile and web platforms. + - id: 113 + name: handle-foreground-background-terminated-states + task: Implement notification event listeners and handlers for foreground, background, + and terminated application lifecycle states. +- id: 41 + goal: Integrate third-party authentication providers into an application + persona: The Full Stack Developer + tasks: + - id: 114 + name: add-google-sign + task: Integrate "package:google_sign_in" to enable single sign-on authentication. + - id: 115 + name: handle-sign-sign-out-flows + task: Implement complete authentication state machines managing sign-in, sign-out, + session persistence, and OAuth token refresh workflows. +- id: 42 + goal: Integrate cloud file storage into an application + persona: The Full Stack Developer + tasks: + - id: 116 + name: add-firebase-storage + task: Integrate "package:firebase_storage" into the application to enable cloud + storage capabilities. + - id: 117 + name: implement-upload-download-delete + task: Implement user flows and repository methods to upload, download, and delete + cloud storage files. +- id: 43 + goal: Integrate crash reporting, error tracking, and production telemetry + persona: The Full Stack Developer + tasks: + - id: 118 + name: add-crashlytics + task: Integrate Firebase Crashlytics ("package:firebase_crashlytics") to capture + and monitor real-time fatal exception reports. + - id: 119 + name: add-custom-log-events-non + task: Implement custom error logging and non-fatal exception tracking to record + application telemetry and diagnostic metadata. +- id: 44 + goal: Integrate AWS Amplify authentication, cloud storage, and backend APIs into + an application + persona: The Full Stack Developer + tasks: + - id: 120 + name: integrate-amplify-auth-+-storage + task: Integrate AWS Amplify authentication, cloud storage, and API services + into the application architecture. +- id: 45 + goal: Integrate Supabase authentication, database services, and real-time subscriptions + into an application + persona: The Full Stack Developer + tasks: + - id: 121 + name: integrate-supabase-auth-database + task: Integrate Supabase authentication, relational database services, and real-time + data subscriptions into the application architecture. +- id: 46 + goal: Build a full-stack Dart web server backend with shared data models between + frontend and backend + persona: The Full Stack Developer + tasks: + - id: 122 + name: create-web-server-shelf + task: Develop a backend web server and HTTP API routing layer using "package:shelf". + - id: 123 + name: create-cloud-function-upload-firebase + task: Implement server-side logic or Google Cloud Functions to process file + uploads and store metadata in Firebase. + - id: 124 + name: decide-best-repo-structure-according + task: Design and implement a shared monorepo workspace structure to allow seamless + data model reuse between Dart frontend and backend services. +- id: 47 + goal: Implement API versioning and backward compatibility checks between frontend + applications and backend services + persona: The Full Stack Developer + tasks: + - id: 125 + name: create-ci-cd-pipeline-confirm + task: Configure automated CI/CD deployment pipelines to verify that the target + backend API version is active prior to releasing client applications. + - id: 126 + name: create-screen-app-app-version + task: Implement a dedicated application deprecation screen that informs users + when their client version is no longer supported by backend API services. +- id: 48 + goal: Develop Google Cloud Functions in Dart using the Genkit SDK + persona: The Full Stack Developer + tasks: + - id: 127 + name: write-google-cloud-function-dart + task: Develop and deploy serverless Google Cloud Functions written in Dart using + the Genkit framework ("package:genkit"). +- id: 49 + goal: Add internationalization (i18n) and localization (l10n) support to an application + persona: The App Developer + tasks: + - id: 128 + name: add-required-languages-locales-app + task: Configure supported languages and regional locales within the application + localization settings. + - id: 129 + name: confirm-default-flutter-ways-i18n + task: Verify that the codebase implements standard Flutter internationalization + practices using ARB files and generated localization delegates. +- id: 50 + goal: Embed interactive Flutter applications and widgets within existing HTML or + React web pages + persona: The Hybrid (Native + Flutter) Developer + tasks: + - id: 130 + name: create-website-jaspr + task: Build a server-rendered or static website in Dart using the Jaspr web + framework ("package:jaspr"). + - id: 131 + name: add-flutter-app-react-app + task: Embed a compiled Flutter web application as an interactive component within + an existing React web application. + - id: 132 + name: add-many-flutter-widgets-across + task: Embed multiple interactive Flutter widgets across a standard HTML web + page, utilizing Flutter multi-view mode to optimize rendering performance + and resource consumption. +- id: 51 + goal: Integrate Flutter modules into existing native Android and iOS applications + using Add-to-app + persona: The Hybrid (Native + Flutter) Developer + tasks: + - id: 133 + name: add-flutter-engine-view-android + task: Integrate a cached FlutterEngine and FlutterActivity into an existing + native Android application using Flutter Add-to-app workflows. + - id: 134 + name: add-flutter-engine-view-ios + task: Integrate a cached FlutterEngine and FlutterViewController into an existing + native iOS application using Flutter Add-to-app workflows. +- id: 52 + goal: Implement seamless cross-layer navigation and state synchronization between + native host apps and embedded Flutter modules + persona: The Hybrid (Native + Flutter) Developer + tasks: + - id: 135 + name: cache-pre-warm-flutter-engine + task: Configure native host applications to pre-warm and cache the FlutterEngine + during application startup to eliminate initialization latency. + - id: 136 + name: manage-complex-navigation-stacks-where + task: Implement bidirectional navigation stacks where users transition between + native Swift screens and embedded Flutter modules, ensuring native gesture + back-swipes behave naturally. + - id: 137 + name: securely-pass-active-user-session + task: Synchronize active session tokens, visual theme preferences, and user + state from the host native application into embedded Flutter modules to provide + a seamless user experience. +- id: 53 + goal: Build platform-specific home screen widgets (for iOS WidgetKit and Android) + that share data with the host application + persona: The Hybrid (Native + Flutter) Developer + tasks: + - id: 138 + name: set-up-home-screen-widget + task: Scaffold and configure native home screen widget extensions for iOS using + WidgetKit and for Android using AppWidgets. + - id: 139 + name: share-data-between-flutter-app + task: Implement shared local storage using App Groups on iOS and SharedPreferences + on Android to synchronize data between the Flutter application and native + widgets. + - id: 140 + name: update-widget-data-flutter + task: Trigger programmatic background updates and timeline reloads for native + home screen widgets directly from Dart application logic. +- id: 54 + goal: Identify and refactor architectural anti-patterns in the codebase + persona: The App Developer + tasks: + - id: 141 + name: analyze-codebase-anti-patterns + task: Analyze the codebase to identify and refactor architectural anti-patterns, + such as building complex widget trees inside helper methods rather than separate + widget classes. +- id: 55 + goal: Audit and migrate codebases away from deprecated frameworks, libraries, and + SDK APIs + persona: The Tech Lead / Architect + tasks: + - id: 142 + name: analyze-codebase-deprecated-api + task: Analyze the codebase to identify and migrate deprecated API usage, including + Material Design 2 components, direct window references in "dart:ui", and legacy + ThemeData styling properties. +- id: 56 + goal: Automate multi-flavor application build, configuration, and distribution pipelines + persona: The Tech Lead / Architect + tasks: + - id: 143 + name: config-flavors-app-different-naming + task: Configure multi-flavor build schemes across iOS and Android to support + distinct application names, bundle identifiers, and launcher icons for staging + and production environments. + - id: 144 + name: write-ci-cd-pipeline-deploy + task: Implement an automated CI/CD distribution pipeline to build and deploy + application binaries to internal testing tracks or distribution services. +- id: 57 + goal: Upgrade and migrate legacy Flutter applications to the latest SDK version + persona: The Tech Lead / Architect + tasks: + - id: 145 + name: audit-and-upgrade-flutter-sdk + task: Audit the local Flutter SDK installation and upgrade safely using version + management tools ("fvm") or system package managers. + - id: 146 + name: run-dart-fix-migration-tool + task: Execute "dart fix" to update deprecated syntax and resolve breaking API + changes across the codebase. +- id: 58 + goal: Extend an existing application to support an additional target platform + persona: The Tech Lead / Architect + tasks: + - id: 147 + name: analyze-repo-check-which-features + task: Audit existing codebase capabilities and third-party plugins to determine + feature compatibility with the target platform. + - id: 148 + name: verify-proper-command-is-used + task: Execute official platform scaffolding commands to generate target platform + projects and verify dependency compatibility. +- id: 59 + goal: Implement new application features utilizing modern language capabilities + and defensive coding practices + persona: The App Developer + tasks: + - id: 149 + name: verify-assert-calls-all-passed + task: Enforce defensive coding by adding runtime assert statements to validate + constructor and function parameter boundaries. + - id: 150 + name: use-record-structure-type-function + task: Refactor function signatures to return structured, type-safe multiple + values using modern Dart Record types. +- id: 60 + goal: Implement custom canvas drawing and custom painters for specialized UI components + persona: The App Developer + tasks: + - id: 151 + name: create-widget-displays-custom-pattern + task: Implement a custom component utilizing CustomPaint and Canvas primitives + to render specialized graphics above or below child widget layers. +- id: 61 + goal: Design and develop a new cross-platform plugin from scratch, selecting the + appropriate native interop mechanism + persona: The Plugin Developer + tasks: + - id: 152 + name: investigate-should-ffi-or-methodchannels + task: Evaluate whether Foreign Function Interface (FFI) bindings or asynchronous + MethodChannels provide the optimal architectural foundation for a new cross-platform + plugin. +- id: 62 + goal: Set up and configure a complete cross-platform Flutter development environment + persona: The App Developer + tasks: + - id: 153 + name: install-configure-xcode-command-line + task: Install and configure Xcode, command-line tools, and CocoaPods on a macOS + development environment to compile for all supported Flutter target platforms. + - id: 154 + name: sets-up-windows-environment-flutter + task: Set up and configure a Windows development environment for Flutter with + necessary dependencies to compile for all supported non-Apple target platforms. + - id: 155 + name: sets-up-linux-environment-flutter + task: Set up and configure a Linux development environment for Flutter with + necessary dependencies to compile for all supported non-Apple target platforms. +- id: 63 + goal: Build a responsive Flutter Web frontend for an Enterprise Resource Planning + (ERP) system + persona: The App Developer + tasks: + - id: 156 + name: create-flutter-web-app-has + task: Develop a responsive Flutter web frontend that dynamically adapts between + desktop browser layouts and mobile web layouts. + - id: 157 + name: use-proper-url-path-strategy + task: Configure the web URL routing strategy (hash-based or path-based) according + to target web hosting platform requirements. + - id: 158 + name: use-wasm-if-possible + task: Configure the web build pipeline to compile to WebAssembly (Wasm) for + high-performance browser execution. + - id: 159 + name: use-pwa-web-app-if + task: Configure Progressive Web App (PWA) manifest and service worker features + to enable offline support and desktop installation. + - id: 160 + name: use-package:web-dart:js-interop-interact + task: Implement browser API integrations and JavaScript interop using "package:web" + and modern Dart type-safe JS interop mechanisms. +- id: 64 + goal: Build adaptive UI layouts that dynamically adjust to platform conventions + and input methods (touch, mouse, keyboard, stylus) + persona: The App Developer + tasks: + - id: 161 + name: switch-nav-window-size:-bottom + task: Implement adaptive navigation that transitions between a bottom navigation + bar on compact screens and a side NavigationRail on expanded displays, sharing + routing destinations. + - id: 162 + name: target-android-tier-3-mouse + task: Optimize the interface for mouse and stylus input by utilizing Material + Design 3 components with built-in hover and focus states. + - id: 163 + name: scroll-wheel-custom-scrollables-listener + task: Refactor custom scrollable components using Listener widgets to support + mouse scroll wheel and trackpad navigation. + - id: 164 + name: tab-traversal-+-visible-focus + task: Implement keyboard tab navigation and visible focus highlights on custom + interactive components using FocusableActionDetector and FocusTraversalGroup. + - id: 165 + name: keyboard-shortcuts-shortcuts-actions-disable + task: Configure application-wide keyboard shortcuts using Shortcuts and Actions + widgets, ensuring shortcuts are disabled during text input. + - id: 166 + name: visualdensity-switched-input-mode-hit + task: Adjust widget VisualDensity dynamically based on active input mode to + optimize touch target sizes versus mouse precision sizing. +- id: 65 + goal: Migrate application architecture between state management solutions + persona: The App Developer + tasks: + - id: 167 + name: replace-setstate-riverpod + task: Refactor the codebase to use "package:riverpod" rather than StatefulWidgets. + - id: 168 + name: replace-setstate-provider + task: Refactor the codebase to use "package:provider" rather than StatefulWidgets. + - id: 169 + name: replace-inheritedwidget-provider + task: Refactor the codebase to use "package:provider" rather than custom InheritedWidgets. + - id: 170 + name: replace-provider-riverpod + task: Migrate existing state management from "package:provider" to "package:riverpod". + - id: 171 + name: replace-provider-bloc + task: Migrate existing state management from "package:provider" to the BLoC + ("package:flutter_bloc") architecture. + - id: 172 + name: replace-setstate-rxdart + task: Refactor the codebase to manage reactive state using "package:rxdart" + rather than StatefulWidgets. +- id: 66 + goal: Diagnose, debug, and resolve runtime exceptions and network defects + persona: The App Developer + tasks: + - id: 173 + name: reproduce-reported-defect-failing-test + task: Reproduce a reported defect in a failing test, then trace the root cause + using the Dart debugger and Flutter DevTools. + - id: 174 + name: fix-common-runtime-exceptions + task: Diagnose and resolve common runtime exceptions (null errors, late init + failures, RangeErrors, invalid setState calls). + - id: 175 + name: diagnose-fix-failed-network-request + task: Diagnose and resolve failed HTTP requests (non-200 status codes, timeouts, + JSON deserialization failures). +- id: 67 + goal: Refactor application code to improve modularity, component reusability, and + architectural maintainability + persona: The App Developer + tasks: + - id: 176 + name: extract-repeated-widget-code-into + task: Extract repeated widget trees into reusable components and consolidate + shared colors, spacing, and text styles into central theme constants. + - id: 177 + name: split-large-dart-class-into + task: Refactor large Dart classes into smaller units, separating business logic + from widget presentation. + - id: 178 + name: extract-shared-ui-logic-into-mixins + task: Extract shared UI behavior and state logic into reusable Dart mixins. +- id: 68 + goal: Call native platform APIs directly using MethodChannel and EventChannel implementations + on Android and iOS + persona: The App Developer + tasks: + - id: 179 + name: call-one-shot-native-method + task: Implement one-shot communication between Dart and native platforms via + MethodChannel (e.g., reading battery level or triggering haptic feedback), + writing handlers in Kotlin for Android and Swift for iOS. + - id: 180 + name: stream-continuous-native-events-into + task: Stream continuous native events into Dart via EventChannel (e.g., sensor + data or network connectivity state).