import 'package:flutter/material.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:url_launcher/url_launcher.dart'; import '../l10n/l10n.dart'; import '../utils/platform_info.dart'; import '../helpers/snack_bar_builder.dart'; class LinkHandler { static TextStyle defaultLinkStyle(BuildContext context, TextStyle base) { final brightness = Theme.of(context).brightness; final orange = brightness == Brightness.dark ? const Color(0xFFFFB74D) : const Color(0xFFE65100); return base.copyWith(color: orange, decoration: TextDecoration.underline); } /// Returns a [SelectableLinkify] on desktop or a [Linkify] on mobile. static Widget buildLinkifyText({ required BuildContext context, required String text, required TextStyle style, TextStyle? linkStyle, }) { final effectiveLinkStyle = linkStyle ?? defaultLinkStyle(context, style); const options = LinkifyOptions(humanize: false, defaultToHttps: false); const linkifiers = [UrlLinkifier(), EmailLinkifier()]; void onOpen(LinkableElement link) => handleLinkTap(context, link.url); if (PlatformInfo.isDesktop) { return SelectableLinkify( text: text, style: style, linkStyle: effectiveLinkStyle, options: options, linkifiers: linkifiers, onOpen: onOpen, ); } return Linkify( text: text, style: style, linkStyle: effectiveLinkStyle, options: options, linkifiers: linkifiers, onOpen: onOpen, ); } static Future handleLinkTap(BuildContext context, String url) async { // Show confirmation dialog final shouldOpen = await showDialog( context: context, builder: (context) => AlertDialog( title: Text(context.l10n.chat_openLink), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( context.l10n.chat_openLinkConfirmation, style: const TextStyle(fontSize: 14), ), const SizedBox(height: 16), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), ), child: SelectableText( url, style: const TextStyle(fontSize: 12, fontFamily: 'monospace'), ), ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text(context.l10n.common_cancel), ), FilledButton( onPressed: () => Navigator.pop(context, true), child: Text(context.l10n.chat_open), ), ], ), ); if (shouldOpen != true) return; // Launch URL try { final uri = Uri.parse(url); if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) { if (context.mounted) { showDismissibleSnackBar( context, content: Text(context.l10n.chat_couldNotOpenLink(url)), backgroundColor: Colors.red, ); } } } catch (e) { if (context.mounted) { showDismissibleSnackBar( context, content: Text(context.l10n.chat_invalidLink), backgroundColor: Colors.red, ); } } } }