You're Using Flutter's Clipboard Wrong (And Missing These 10 Powerful Features) (Part 1)

You're Using Flutter's Clipboard Wrong (And Missing These 10 Powerful Features) (Part 1)

FlutterPulse

This article was translated specially for the channel FlutterPulseYou'll find lots of interesting things related to Flutter on this channel. Don't hesitate to subscribe!πŸš€

Master Flutter's clipboard with 10 powerful patterns! Copy, paste, detect content, secure data, and build historyβ€Šβ€”β€Šall with Riverpod…

That "Copy to Clipboard" button in your app? It could do SO much more. Let me show you the hidden superpowers πŸ“‹

The Complete Guide to Copy, Paste, and Everything In Between

Skill Level: Intermediate

Hey friend! πŸ‘‹

So you've probably added a "Copy to Clipboard" button to your Flutter app at some point, right? You probably did something like:

Clipboard.setData(ClipboardData(text: 'Some text'));

And thought, "Cool, done!"

But here's the thing: The clipboard is WAY more powerful than you think. And most Flutter developers are only scratching the surface of what it can do.

Today, I'm gonna blow your mind with all the cool stuff you can do with Flutter's clipboard. We're talking:

  • πŸ“‹ Smart copy/paste with user feedback
  • 🎨 Rich text formatting
  • πŸ–ΌοΈ Images in clipboard
  • πŸ” Clipboard monitoring (yes, really!)
  • 🎯 Context-aware paste detection
  • πŸ” Sensitive data handling
  • ⚑ And a bunch of UX tricks that'll make your app feel premium

All with clean Riverpod state management. No messy code, just smooth, professional implementations.

Ready to level up? Let's go! πŸš€

Why Clipboard Matters More Than You Think

Before we dive into code, let me show you why this matters:

Scenario 1: Password Manager App

BAD UX:
User clicks "Copy Password" β†’ No feedback β†’ Did it work?
User pastes β†’ Wrong thing copied β†’ Security risk!

GOOD UX:
User clicks "Copy Password" β†’ "Copied! βœ“" appears
After 30 seconds β†’ Clipboard auto-clears β†’ Secure!

Scenario 2: Social Media App

BAD UX:
User copies post link β†’ Just a boring link
User shares β†’ Looks unprofessional

GOOD UX:
User copies post link β†’ Includes preview text
User shares β†’ Looks great everywhere

Scenario 3: Note-Taking App

BAD UX:
User pastes β†’ Loses all formatting
User has to re-format β†’ Waste of time

GOOD UX:
User pastes β†’ Keeps formatting
User is happy β†’ Saves time

See the difference? Good clipboard UX = Happy users = Better ratings.

Setting Up (The Right Way)

First, let's get the imports and permissions sorted:

# pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^2.5.1

Good news: Flutter's clipboard works out of the box! No extra packages needed for basic functionality. But we'll use Riverpod to make it clean and powerful.

Pattern 1: Basic Copy/Paste (But Make It Professional)

Let's start simple but do it RIGHT:

// providers/clipboard_provider.dart
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// State to track what was just copied
final lastCopiedTextProvider = StateProvider<String?>((ref) => null);

// State to show "Copied!" feedback
final showCopiedFeedbackProvider = StateProvider<bool>((ref) => false);

// The clipboard service provider
final clipboardServiceProvider = Provider<ClipboardService>((ref) {
return ClipboardService(ref);
});

class ClipboardService {
ClipboardService(this.ref);

final Ref ref;

// Copy text with feedback
Future<bool> copyText(String text) async {
try {
await Clipboard.setData(ClipboardData(text: text));

// Update state
ref.read(lastCopiedTextProvider.notifier).state = text;
ref.read(showCopiedFeedbackProvider.notifier).state = true;

// Hide feedback after 2 seconds
Future.delayed(Duration(seconds: 2), () {
ref.read(showCopiedFeedbackProvider.notifier).state = false;
});

return true;
} catch (e) {
print('Failed to copy: $e');
return false;
}
}

// Paste text
Future<String?> pasteText() async {
try {
final data = await Clipboard.getData(Clipboard.kTextPlain);
return data?.text;
} catch (e) {
print('Failed to paste: $e');
return null;
}
}

// Check if clipboard has data
Future<bool> hasData() async {
try {
final data = await Clipboard.getData(Clipboard.kTextPlain);
return data?.text?.isNotEmpty ?? false;
} catch (e) {
return false;
}
}
}

// widgets/copy_button.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class CopyButton extends ConsumerWidget {
final String textToCopy;
final String? label;
final IconData? icon;

const CopyButton({
super.key,
required this.textToCopy,
this.label,
this.icon,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
final showFeedback = ref.watch(showCopiedFeedbackProvider);

return ElevatedButton.icon(
onPressed: () async {
await ref.read(clipboardServiceProvider).copyText(textToCopy);
},
icon: Icon(
showFeedback ? Icons.check : (icon ?? Icons.copy),
size: 18,
),
label: Text(showFeedback ? 'Copied!' : (label ?? 'Copy')),
style: ElevatedButton.styleFrom(
backgroundColor: showFeedback ? Colors.green : null,
),
);
}
}

// Usage:
CopyButton(
textToCopy: 'Hello, World!',
label: 'Copy Text',
)

What makes this professional?

  • βœ… Visual feedback ("Copied!" message)
  • βœ… Icon changes to checkmark
  • βœ… Color changes to green
  • βœ… Feedback auto-hides after 2 seconds
  • βœ… Error handling
  • βœ… Clean Riverpod state management

Pattern 2: Smart Copy with Snackbar Feedback

Want even better UX? Show a snackbar:

// widgets/smart_copy_button.dart
class SmartCopyButton extends ConsumerWidget {
final String textToCopy;
final String? successMessage;

const SmartCopyButton({
super.key,
required this.textToCopy,
this.successMessage,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
return IconButton(
icon: Icon(Icons.copy),
tooltip: 'Copy to clipboard',
onPressed: () async {
final success = await ref
.read(clipboardServiceProvider)
.copyText(textToCopy);

if (success && context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(Icons.check_circle, color: Colors.white),
SizedBox(width: 12),
Expanded(
child: Text(
successMessage ?? 'Copied to clipboard!',
),
),
],
),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
duration: Duration(seconds: 2),
action: SnackBarAction(
label: 'OK',
textColor: Colors.white,
onPressed: () {},
),
),
);
}
},
);
}
}

// Usage:
SmartCopyButton(
textToCopy: userEmail,
successMessage: 'Email copied!',
)

Pattern 3: Copy with Auto-Clear (For Sensitive Data)

Perfect for passwords, API keys, etc.:

// providers/secure_clipboard_provider.dart
final secureClipboardProvider = Provider<SecureClipboardService>((ref) {
return SecureClipboardService(ref);
});

class SecureClipboardService {
SecureClipboardService(this.ref);

final Ref ref;
Timer? _clearTimer;

// Copy with auto-clear after duration
Future<bool> copySecure(
String text, {
Duration clearAfter = const Duration(seconds: 30),
}) async {
try {
await Clipboard.setData(ClipboardData(text: text));

// Cancel existing timer
_clearTimer?.cancel();

// Set new timer to clear clipboard
_clearTimer = Timer(clearAfter, () {
_clearClipboard();
});

ref.read(lastCopiedTextProvider.notifier).state = text;
ref.read(showCopiedFeedbackProvider.notifier).state = true;

Future.delayed(Duration(seconds: 2), () {
ref.read(showCopiedFeedbackProvider.notifier).state = false;
});

return true;
} catch (e) {
print('Failed to copy: $e');
return false;
}
}

Future<void> _clearClipboard() async {
try {
await Clipboard.setData(ClipboardData(text: ''));
print('πŸ” Clipboard cleared for security');
} catch (e) {
print('Failed to clear clipboard: $e');
}
}

void dispose() {
_clearTimer?.cancel();
}
}

// widgets/secure_copy_button.dart
class SecureCopyButton extends ConsumerWidget {
final String sensitiveData;
final String label;
final Duration? clearAfter;

const SecureCopyButton({
super.key,
required this.sensitiveData,
required this.label,
this.clearAfter,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton.icon(
onPressed: () async {
final clearDuration = clearAfter ?? Duration(seconds: 30);

final success = await ref
.read(secureClipboardProvider)
.copySecure(sensitiveData, clearAfter: clearDuration);

if (success && context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'$label copied! Will auto-clear in ${clearDuration.inSeconds}s',
),
backgroundColor: Colors.orange,
duration: Duration(seconds: 3),
),
);
}
},
icon: Icon(Icons.lock),
label: Text('Copy $label'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
),
);
}
}

// Usage:
SecureCopyButton(
sensitiveData: apiKey,
label: 'API Key',
clearAfter: Duration(seconds: 30),
)

Security features:

  • βœ… Auto-clears after 30 seconds (configurable)
  • βœ… Visual warning (orange color)
  • βœ… Clear notification to user
  • βœ… Perfect for passwords, tokens, keys

Pattern 4: Paste Detection (Know What's in the Clipboard!)

This is SUPER useful β€” detect if there's paste-able content:

// providers/paste_detection_provider.dart
final clipboardContentProvider = FutureProvider.autoDispose<ClipboardContent>(
(ref) async {
final service = ref.watch(clipboardServiceProvider);

// Check if clipboard has text
final hasText = await service.hasData();

if (!hasText) {
return ClipboardContent.empty();
}

// Get the text
final text = await service.pasteText();

// Analyze the content
return ClipboardContent.analyze(text ?? '');
},
);

class ClipboardContent {
final String text;
final ClipboardContentType type;
final bool isEmpty;

ClipboardContent({
required this.text,
required this.type,
required this.isEmpty,
});

factory ClipboardContent.empty() {
return ClipboardContent(
text: '',
type: ClipboardContentType.unknown,
isEmpty: true,
);
}

factory ClipboardContent.analyze(String text) {
if (text.isEmpty) {
return ClipboardContent.empty();
}

// Detect content type
ClipboardContentType type;

if (_isUrl(text)) {
type = ClipboardContentType.url;
} else if (_isEmail(text)) {
type = ClipboardContentType.email;
} else if (_isPhoneNumber(text)) {
type = ClipboardContentType.phoneNumber;
} else if (_isColor(text)) {
type = ClipboardContentType.color;
} else {
type = ClipboardContentType.text;
}

return ClipboardContent(
text: text,
type: type,
isEmpty: false,
);
}

static bool _isUrl(String text) {
return Uri.tryParse(text)?.hasAbsolutePath ?? false;
}

static bool _isEmail(String text) {
return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(text);
}

static bool _isPhoneNumber(String text) {
return RegExp(r'^\+?[\d\s\-\(\)]+$').hasMatch(text.trim()) &&
text.replaceAll(RegExp(r'[\s\-\(\)]'), '').length >= 10;
}

static bool _isColor(String text) {
return RegExp(r'^#?([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})$').hasMatch(text);
}
}

enum ClipboardContentType {
text,
url,
email,
phoneNumber,
color,
unknown,
}

// widgets/smart_paste_button.dart
class SmartPasteButton extends ConsumerWidget {
final Function(String text, ClipboardContentType type) onPaste;

const SmartPasteButton({
super.key,
required this.onPaste,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
final clipboardAsync = ref.watch(clipboardContentProvider);

return clipboardAsync.when(
data: (content) {
if (content.isEmpty) {
return ElevatedButton.icon(
onPressed: null, // Disabled
icon: Icon(Icons.paste),
label: Text('Nothing to paste'),
);
}

// Show different icon based on content type
IconData icon;
String label;

switch (content.type) {
case ClipboardContentType.url:
icon = Icons.link;
label = 'Paste URL';
break;
case ClipboardContentType.email:
icon = Icons.email;
label = 'Paste Email';
break;
case ClipboardContentType.phoneNumber:
icon = Icons.phone;
label = 'Paste Phone';
break;
case ClipboardContentType.color:
icon = Icons.palette;
label = 'Paste Color';
break;
default:
icon = Icons.paste;
label = 'Paste Text';
}

return ElevatedButton.icon(
onPressed: () {
onPaste(content.text, content.type);
},
icon: Icon(icon),
label: Text(label),
);
},
loading: () => ElevatedButton.icon(
onPressed: null,
icon: SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
),
label: Text('Checking...'),
),
error: (_, __) => ElevatedButton.icon(
onPressed: null,
icon: Icon(Icons.error),
label: Text('Error'),
),
);
}
}

// Usage:
SmartPasteButton(
onPaste: (text, type) {
print('Pasted $type: $text');
// Handle based on type
switch (type) {
case ClipboardContentType.url:
_openUrl(text);
break;
case ClipboardContentType.email:
_composeEmail(text);
break;
// etc.
}
},
)

Smart features:

  • βœ… Detects content type (URL, email, phone, color, text)
  • βœ… Shows appropriate icon
  • βœ… Disables button if clipboard is empty
  • βœ… Context-aware actions

Pattern 5: Clipboard History (Power User Feature!)

Let users see what they've copied recently:

// models/clipboard_item.dart
class ClipboardItem {
final String id;
final String text;
final DateTime copiedAt;
final ClipboardContentType type;

ClipboardItem({
required this.id,
required this.text,
required this.copiedAt,
required this.type,
});

String get preview {
if (text.length <= 50) return text;
return '${text.substring(0, 50)}...';
}

String get timeAgo {
final diff = DateTime.now().difference(copiedAt);
if (diff.inSeconds < 60) return 'Just now';
if (diff.inMinutes < 60) return '${diff.inMinutes}m ago';
if (diff.inHours < 24) return '${diff.inHours}h ago';
return '${diff.inDays}d ago';
}
}

// providers/clipboard_history_provider.dart
final clipboardHistoryProvider = StateNotifierProvider<
ClipboardHistoryNotifier,
List<ClipboardItem>
>(
(ref) => ClipboardHistoryNotifier(),
);

class ClipboardHistoryNotifier extends StateNotifier<List<ClipboardItem>> {
ClipboardHistoryNotifier() : super([]) {
_loadHistory();
}

static const _maxHistorySize = 20;

Future<void> _loadHistory() async {
// Load from SharedPreferences
// Simplified for example
state = [];
}

void addItem(String text, ClipboardContentType type) {
final item = ClipboardItem(
id: DateTime.now().millisecondsSinceEpoch.toString(),
text: text,
copiedAt: DateTime.now(),
type: type,
);

// Add to beginning, remove duplicates, limit size
state = [
item,
...state.where((i) => i.text != text).take(_maxHistorySize - 1),
];

_saveHistory();
}

void removeItem(String id) {
state = state.where((item) => item.id != id).toList();
_saveHistory();
}

void clearHistory() {
state = [];
_saveHistory();
}

Future<void> _saveHistory() async {
// Save to SharedPreferences
// Simplified for example
}
}

// screens/clipboard_history_screen.dart
class ClipboardHistoryScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final history = ref.watch(clipboardHistoryProvider);

return Scaffold(
appBar: AppBar(
title: Text('Clipboard History'),
actions: [
if (history.isNotEmpty)
IconButton(
icon: Icon(Icons.delete_sweep),
tooltip: 'Clear history',
onPressed: () {
ref.read(clipboardHistoryProvider.notifier).clearHistory();
},
),
],
),
body: history.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.content_paste, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text(
'No clipboard history yet',
style: TextStyle(fontSize: 18, color: Colors.grey),
),
SizedBox(height: 8),
Text(
'Items you copy will appear here',
style: TextStyle(color: Colors.grey),
),
],
),
)
: ListView.builder(
itemCount: history.length,
itemBuilder: (context, index) {
final item = history[index];
return ClipboardHistoryTile(item: item);
},
),
);
}
}

// widgets/clipboard_history_tile.dart
class ClipboardHistoryTile extends ConsumerWidget {
final ClipboardItem item;

const ClipboardHistoryTile({required this.item});

@override
Widget build(BuildContext context, WidgetRef ref) {
IconData icon;
Color iconColor;

switch (item.type) {
case ClipboardContentType.url:
icon = Icons.link;
iconColor = Colors.blue;
break;
case ClipboardContentType.email:
icon = Icons.email;
iconColor = Colors.orange;
break;
case ClipboardContentType.phoneNumber:
icon = Icons.phone;
iconColor = Colors.green;
break;
case ClipboardContentType.color:
icon = Icons.palette;
iconColor = Colors.purple;
break;
default:
icon = Icons.text_fields;
iconColor = Colors.grey;
}

return ListTile(
leading: CircleAvatar(
backgroundColor: iconColor.withOpacity(0.1),
child: Icon(icon, color: iconColor),
),
title: Text(
item.preview,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(item.timeAgo),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Copy again button
IconButton(
icon: Icon(Icons.copy, size: 20),
tooltip: 'Copy again',
onPressed: () async {
await ref
.read(clipboardServiceProvider)
.copyText(item.text);
},
),
// Delete button
IconButton(
icon: Icon(Icons.delete, size: 20),
tooltip: 'Remove',
onPressed: () {
ref
.read(clipboardHistoryProvider.notifier)
.removeItem(item.id);
},
),
],
),
onTap: () {
// Show full text in dialog
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Clipboard Item'),
content: SelectableText(item.text),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Close'),
),
ElevatedButton(
onPressed: () async {
await ref
.read(clipboardServiceProvider)
.copyText(item.text);
if (context.mounted) {
Navigator.pop(context);
}
},
child: Text('Copy'),
),
],
),
);
},
);
}
}

Pattern 6: Rich Text Formatting (Advanced!)

Copy text with formatting preserved:

// For rich text, you'll need html package
// pubspec.yaml:
// html: ^0.15.4

import 'package:html/parser.dart' as html_parser;

class RichTextClipboard {
// Copy text with HTML formatting
static Future<void> copyRichText({
required String plainText,
required String htmlText,
}) async {
// Flutter's clipboard only supports plain text natively
// But we can store HTML in a special format

final data = {
'text/plain': plainText,
'text/html': htmlText,
};

// For now, we'll just copy plain text
// On web, this would work with HTML
await Clipboard.setData(ClipboardData(text: plainText));
}

// Parse HTML from clipboard (if available)
static String parseHtmlToPlain(String html) {
final document = html_parser.parse(html);
return document.body?.text ?? html;
}
}

Pattern 7: Image to Clipboard (iOS/Android specific)

// Note: Images in clipboard require platform-specific code
// This is a simplified example

import 'dart:typed_data';

class ImageClipboard {
// Copy image (platform-specific)
static Future<bool> copyImage(Uint8List imageBytes) async {
try {
// On mobile platforms, you'd use platform channels
// This is a placeholder
print('Image clipboard requires platform-specific implementation');
return false;
} catch (e) {
print('Error copying image: $e');
return false;
}
}
}

Pattern 8: Clipboard Listener (Monitor Changes)

Want to know when clipboard changes? Here's how:

// providers/clipboard_monitor_provider.dart
final clipboardMonitorProvider = StreamProvider.autoDispose<String?>((ref) {
return Stream.periodic(Duration(seconds: 1), (_) async {
try {
final data = await Clipboard.getData(Clipboard.kTextPlain);
return data?.text;
} catch (e) {
return null;
}
}).asyncMap((future) => future);
});

// widgets/clipboard_monitor_widget.dart
class ClipboardMonitorWidget extends ConsumerWidget {
final Function(String text) onClipboardChange;

const ClipboardMonitorWidget({
super.key,
required this.onClipboardChange,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
final clipboardStream = ref.watch(clipboardMonitorProvider);

String? previousText;

ref.listen(clipboardMonitorProvider, (previous, next) {
next.whenData((text) {
if (text != null && text != previousText && text.isNotEmpty) {
previousText = text;
onClipboardChange(text);
}
});
});

return SizedBox.shrink(); // Invisible widget
}
}

// Usage:
ClipboardMonitorWidget(
onClipboardChange: (text) {
print('Clipboard changed: $text');
// Maybe show a floating action button to paste?
},
)

Warning: Be careful with clipboard monitoring for privacy reasons!

Pattern 9: Context Menu with Copy/Paste

Add copy/paste to context menus:

// widgets/selectable_text_with_context.dart
class SelectableTextWithContext extends StatelessWidget {
final String text;

const SelectableTextWithContext({required this.text});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Clipboard Mastery'),
actions: [
IconButton(
icon: Icon(Icons.history),
tooltip: 'Clipboard History',
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ClipboardHistoryScreen(),
),
);
},
),
],
),
body: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Input section
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'πŸ“ Enter Text',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16),
TextField(
controller: _textController,
decoration: InputDecoration(
hintText: 'Type something to copy...',
border: OutlineInputBorder(),
),
maxLines: 3,
),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: CopyButton(
textToCopy: _textController.text,
label: 'Copy',
),
),
SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
_textController.clear();
},
icon: Icon(Icons.clear),
label: Text('Clear'),
),
),
],
),
],
),
),
),

SizedBox(height: 16),

// Quick copy examples
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'⚑ Quick Copy Examples',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16),
_buildQuickCopyItem(
'Email',
'hello@example.com',
Icons.email,
Colors.blue,
),
_buildQuickCopyItem(
'Phone',
'+1 (555) 123-4567',
Icons.phone,
Colors.green,
),
_buildQuickCopyItem(
'URL',
'https://flutter.dev',
Icons.link,
Colors.purple,
),
_buildQuickCopyItem(
'Color',
'#FF5733',
Icons.palette,
Colors.orange,
),
],
),
),
),

SizedBox(height: 16),

// Secure copy section
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'πŸ” Secure Copy (Auto-clears)',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'Perfect for passwords and sensitive data',
style: TextStyle(color: Colors.grey[600]),
),
SizedBox(height: 16),
SecureCopyButton(
sensitiveData: 'MySecretPassword123!',
label: 'Password',
clearAfter: Duration(seconds: 30),
),
],
),
),
),

SizedBox(height: 16),

// Smart paste section
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'πŸ“‹ Smart Paste',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'Detects what\'s in your clipboard',
style: TextStyle(color: Colors.grey[600]),
),
SizedBox(height: 16),
SmartPasteButton(
onPaste: (text, type) {
_textController.text = text;

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Pasted ${type.name}: $text'),
),
);
},
),
],
),
),
),
],
),
),
);
}

Widget _buildQuickCopyItem(
String label,
String value,
IconData icon,
Color color,
) {
return Padding(
padding: EdgeInsets.only(bottom: 12),
child: Row(
children: [
CircleAvatar(
backgroundColor: color.withOpacity(0.1),
child: Icon(icon, color: color, size: 20),
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
Text(
value,
style: TextStyle(fontWeight: FontWeight.w500),
),
],
),
),
SmartCopyButton(
textToCopy: value,
successMessage: '$label copied!',
),
],
),
);
}
}

Best Practices & Tips

1. Always Provide Feedback

// ❌ BAD - Silent copy
await Clipboard.setData(ClipboardData(text: text));

// βœ… GOOD - Visual feedback
await Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Copied!')),
);

2. Handle Errors Gracefully

try {
await Clipboard.setData(ClipboardData(text: text));
return true;
} catch (e) {
print('Clipboard error: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to copy')),
);
return false;
}

3. Clear Sensitive Data

// For passwords, API keys, etc.
Timer(Duration(seconds: 30), () {
Clipboard.setData(ClipboardData(text: ''));
});

4. Check Context Before Showing UI

if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(

Report Page