diff --git a/pkgs/dartpad_ui/lib/app/genai_editing.dart b/pkgs/dartpad_ui/lib/app/genai_editing.dart index 921e79ec4..c61b8e341 100644 --- a/pkgs/dartpad_ui/lib/app/genai_editing.dart +++ b/pkgs/dartpad_ui/lib/app/genai_editing.dart @@ -27,6 +27,7 @@ class EditorWithButtons extends StatefulWidget { this.showCodeEditTool = true, required this.appModel, required this.appServices, + required this.onCopy, required this.onFormat, required this.onCompileAndRun, required this.onCompileAndReload, @@ -35,6 +36,7 @@ class EditorWithButtons extends StatefulWidget { final bool showCodeEditTool; final AppModel appModel; final AppServices appServices; + final VoidCallback onCopy; final VoidCallback onFormat; final VoidCallback onCompileAndRun; final VoidCallback onCompileAndReload; @@ -127,6 +129,7 @@ class _EditorWithButtonsState extends State { child: _EditingArea( widget.appModel, widget.appServices, + onCopy: widget.onCopy, onFormat: widget.onFormat, onCompileAndReload: widget.onCompileAndReload, onCompileAndRun: widget.onCompileAndRun, @@ -658,6 +661,7 @@ class _EditingArea extends StatelessWidget { const _EditingArea( this.appModel, this.appServices, { + required this.onCopy, required this.onFormat, required this.onCompileAndReload, required this.onCompileAndRun, @@ -665,6 +669,7 @@ class _EditingArea extends StatelessWidget { final AppModel appModel; final AppServices appServices; + final VoidCallback onCopy; final VoidCallback onFormat; final VoidCallback onCompileAndReload; final VoidCallback onCompileAndRun; @@ -704,6 +709,16 @@ class _EditingArea extends StatelessWidget { }, ), const SizedBox(width: denseSpacing), + // Copy button + PointerInterceptor( + child: MiniIconButton( + icon: const Icon(Icons.content_copy), + tooltip: 'Copy code to clipboard', + small: true, + onPressed: onCopy, + ), + ), + const SizedBox(width: denseSpacing), // Format button ValueListenableBuilder( valueListenable: appModel.formattingBusy, diff --git a/pkgs/dartpad_ui/lib/main.dart b/pkgs/dartpad_ui/lib/main.dart index 431d02075..e68355a4b 100644 --- a/pkgs/dartpad_ui/lib/main.dart +++ b/pkgs/dartpad_ui/lib/main.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'package:dartpad_shared/services.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -358,6 +359,7 @@ class DartPadMainPageState extends State final editor = EditorWithButtons( appModel: appModel, appServices: appServices, + onCopy: _handleCopy, onFormat: _handleFormatting, onCompileAndRun: appServices.performCompileAndRun, onCompileAndReload: appServices.performCompileAndReload, @@ -522,6 +524,18 @@ class DartPadMainPageState extends State ); } + // TODO: Check if platform permission specific error needs to be handled. + Future _handleCopy() async { + try { + final source = appModel.sourceCodeController.text; + await Clipboard.setData(ClipboardData(text: source)); + appModel.editorStatus.showToast('Code copied to clipboard'); + } catch (error) { + appModel.editorStatus.showToast('Error copying code'); + appModel.appendError('Copy issue: $error'); + } + } + Future _handleFormatting() async { try { final source = appModel.sourceCodeController.text;