diff --git a/packages/devtools_app/lib/src/shared/analytics/constants/_property_editor_sidebar_constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants/_property_editor_sidebar_constants.dart index c38bdd8e07a..b17bda01718 100644 --- a/packages/devtools_app/lib/src/shared/analytics/constants/_property_editor_sidebar_constants.dart +++ b/packages/devtools_app/lib/src/shared/analytics/constants/_property_editor_sidebar_constants.dart @@ -9,6 +9,9 @@ class PropertyEditorSidebar { /// Analytics id to track events that come from the DTD editor sidebar. static String get id => 'propertyEditorSidebar'; + /// Analytics id for opening the documentation. + static String get documentationLink => 'propertyEditorDocumentation'; + /// Analytics event that is sent when the property editor is updated with new /// properties. static String widgetPropertiesUpdate({String? name}) => diff --git a/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_panel.dart b/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_panel.dart index 7cb8ff3b5f1..368e6a4160a 100644 --- a/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_panel.dart +++ b/packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_panel.dart @@ -9,9 +9,11 @@ import 'package:devtools_app_shared/utils.dart'; import 'package:dtd/dtd.dart'; import 'package:flutter/material.dart'; +import '../../../framework/scaffold/report_feedback_button.dart'; import '../../../shared/analytics/analytics.dart' as ga; import '../../../shared/analytics/constants.dart' as gac; import '../../../shared/editor/editor_client.dart'; +import '../../../shared/primitives/query_parameters.dart'; import '../../../shared/ui/common_widgets.dart'; import 'property_editor_controller.dart'; import 'property_editor_view.dart'; @@ -115,22 +117,29 @@ class _PropertyEditorConnectedPanelState Scrollbar( controller: scrollController, thumbVisibility: true, - child: SingleChildScrollView( - controller: scrollController, - child: Padding( - padding: const EdgeInsets.fromLTRB( - denseSpacing, - defaultSpacing, - defaultSpacing, // Additional right padding for scroll bar. - defaultSpacing, + child: Column( + children: [ + Expanded( + child: SingleChildScrollView( + controller: scrollController, + child: Padding( + padding: const EdgeInsets.fromLTRB( + denseSpacing, + defaultSpacing, + defaultSpacing, // Additional right padding for scroll bar. + defaultSpacing, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PropertyEditorView(controller: widget.controller), + ], + ), + ), + ), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - PropertyEditorView(controller: widget.controller), - ], - ), - ), + const _PropertyEditorFooter(), + ], ), ), if (shouldReconnect) const ReconnectingOverlay(), @@ -140,3 +149,69 @@ class _PropertyEditorConnectedPanelState ); } } + +class _PropertyEditorFooter extends StatelessWidget { + const _PropertyEditorFooter(); + + static const _footerHeight = 25.0; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + final documentationLink = _documentationLink(); + return Container( + decoration: BoxDecoration( + color: colorScheme.surface, + border: Border(top: BorderSide(color: Theme.of(context).focusColor)), + ), + height: _footerHeight, + padding: const EdgeInsets.symmetric(vertical: densePadding), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + if (documentationLink != null) + Padding( + padding: const EdgeInsets.only(left: denseSpacing), + child: _DocsLink( + documentationLink: documentationLink, + color: colorScheme.onSurface, + ), + ), + const Spacer(), + ReportFeedbackButton(color: colorScheme.onSurface), + ], + ), + ); + } + + String? _documentationLink() { + final queryParams = DevToolsQueryParams.load(); + final isEmbedded = queryParams.embedMode.embedded; + if (!isEmbedded) return null; + const uriPrefix = 'https://docs.flutter.dev/tools/'; + const uriHash = '#property-editor'; + return '$uriPrefix${queryParams.ide == 'VSCode' ? 'vs-code' : 'android-studio'}$uriHash'; + } +} + +class _DocsLink extends StatelessWidget { + const _DocsLink({required this.documentationLink, required this.color}); + + final Color color; + final String documentationLink; + + @override + Widget build(BuildContext context) { + return LinkIconLabel( + icon: Icons.library_books_outlined, + link: GaLink( + display: 'Docs', + url: documentationLink, + gaScreenName: gac.PropertyEditorSidebar.id, + gaSelectedItemDescription: gac.PropertyEditorSidebar.documentationLink, + ), + color: color, + ); + } +}