From 393d71a8bc5ee24061a627337f369b4fd7874feb Mon Sep 17 00:00:00 2001 From: Garrett Watson Date: Fri, 10 Nov 2023 18:34:17 -0500 Subject: [PATCH 1/5] feat: add copying message text via long press --- lib/widgets/messages/message_text.dart | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/widgets/messages/message_text.dart b/lib/widgets/messages/message_text.dart index 23dbafb..7a85e33 100644 --- a/lib/widgets/messages/message_text.dart +++ b/lib/widgets/messages/message_text.dart @@ -1,7 +1,8 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:recon/models/message.dart'; import 'package:recon/widgets/formatted_text.dart'; import 'package:recon/widgets/messages/message_state_indicator.dart'; -import 'package:flutter/material.dart'; class MessageText extends StatelessWidget { const MessageText({required this.message, this.foregroundColor, super.key}); @@ -11,7 +12,14 @@ class MessageText extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( + return GestureDetector( + onLongPress: () async { + await Clipboard.setData(ClipboardData(text: message.content)).then((_) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Copied to clipboard"))); + }); + }, + child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Container( @@ -21,8 +29,7 @@ class MessageText extends StatelessWidget { message.formattedContent, softWrap: true, maxLines: null, - style: Theme - .of(context) + style: Theme.of(context) .textTheme .bodyLarge ?.copyWith(color: foregroundColor), @@ -32,10 +39,14 @@ class MessageText extends StatelessWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ - MessageStateIndicator(message: message, foregroundColor: foregroundColor,), + MessageStateIndicator( + message: message, + foregroundColor: foregroundColor, + ), ], ), ], + ), ); } -} \ No newline at end of file +} From 7ff98649e9767e037592dc38f04a48e6fb4283e2 Mon Sep 17 00:00:00 2001 From: Nutcake Date: Sat, 11 Nov 2023 10:20:55 +0100 Subject: [PATCH 2/5] Fix unchecked use of BuildContext after await When using the BuildContext after an asynchronus gap (like when using await), you always need to check if the context is still mounted before using it to retrieve objects from the widget tree like with `ScaffoldMessenger.of(context)`. Additionally you don't need to use `.then((value) {})` on a future, if that future is already awaited, you can simply put any following statements on a new line for better readability of program flow. --- lib/widgets/messages/message_text.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/widgets/messages/message_text.dart b/lib/widgets/messages/message_text.dart index 7a85e33..1fa6ad6 100644 --- a/lib/widgets/messages/message_text.dart +++ b/lib/widgets/messages/message_text.dart @@ -14,10 +14,10 @@ class MessageText extends StatelessWidget { Widget build(BuildContext context) { return GestureDetector( onLongPress: () async { - await Clipboard.setData(ClipboardData(text: message.content)).then((_) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Copied to clipboard"))); - }); + await Clipboard.setData(ClipboardData(text: message.content)); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard"))); + } }, child: Column( crossAxisAlignment: CrossAxisAlignment.end, From 44e94555f24e0bd067e2a3a85af9d6e2d2be4983 Mon Sep 17 00:00:00 2001 From: Nutcake Date: Sat, 11 Nov 2023 10:51:24 +0100 Subject: [PATCH 3/5] Add ripple effect to text-copy action Generally the use of GestureDetector is discouraged in favor of InkWell unless you have a very specific reason to use it. InkWells automatically generate ripple effects if there is a parent material widget in the tree to indicate to a user that a given widget is interactible. --- lib/widgets/messages/message_text.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/messages/message_text.dart b/lib/widgets/messages/message_text.dart index 1fa6ad6..c085df5 100644 --- a/lib/widgets/messages/message_text.dart +++ b/lib/widgets/messages/message_text.dart @@ -12,7 +12,8 @@ class MessageText extends StatelessWidget { @override Widget build(BuildContext context) { - return GestureDetector( + return InkWell( + borderRadius: BorderRadius.circular(16), onLongPress: () async { await Clipboard.setData(ClipboardData(text: message.content)); if (context.mounted) { From e97e0a52af0d8bd95d9501f52788550a4f643125 Mon Sep 17 00:00:00 2001 From: Nutcake Date: Sat, 11 Nov 2023 10:54:47 +0100 Subject: [PATCH 4/5] Move copy message snackbar to top of view On Android 13 or newer there is an operating system integrated message bubble that pops up and shows you any automated clipboard changes at the bottom left of the screen. This bubble obscures the message text of the snackbar so I've moved it to the top of the screen when on Android. It might make sense to do more accurate version checking in the future to only show the snackbar if on Android < 13 or iOS but we'd need to pull in the device_info_plus package or similar for that. --- lib/widgets/messages/message_text.dart | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/widgets/messages/message_text.dart b/lib/widgets/messages/message_text.dart index c085df5..f9d7bcd 100644 --- a/lib/widgets/messages/message_text.dart +++ b/lib/widgets/messages/message_text.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:recon/models/message.dart'; @@ -17,7 +19,24 @@ class MessageText extends StatelessWidget { onLongPress: () async { await Clipboard.setData(ClipboardData(text: message.content)); if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard"))); + const content = Text("Copied to clipboard"); + ScaffoldMessenger.of(context).showSnackBar( + Platform.isIOS + ? const SnackBar(content: content) + : SnackBar( + content: content, + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + dismissDirection: DismissDirection.none, + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).size.height - 170, + right: 20, + left: 20, + ), + ), + ); } }, child: Column( From ec4d276cc844ab78438516ddb30e44effe8a7bbb Mon Sep 17 00:00:00 2001 From: Nutcake Date: Sat, 11 Nov 2023 11:03:14 +0100 Subject: [PATCH 5/5] Fix code formatting This project uses 120 character line width instead of the default 80. Make sure to change your IDE preferences to auto-format lines to 120 character. --- lib/widgets/messages/message_text.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/widgets/messages/message_text.dart b/lib/widgets/messages/message_text.dart index f9d7bcd..5587505 100644 --- a/lib/widgets/messages/message_text.dart +++ b/lib/widgets/messages/message_text.dart @@ -49,10 +49,7 @@ class MessageText extends StatelessWidget { message.formattedContent, softWrap: true, maxLines: null, - style: Theme.of(context) - .textTheme - .bodyLarge - ?.copyWith(color: foregroundColor), + style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: foregroundColor), ), ), Row(