Skip to content

Commit

Permalink
Add a feature to copy from clipboard in the search page
Browse files Browse the repository at this point in the history
  • Loading branch information
g123k committed Jan 29, 2024
1 parent acbe694 commit 5327131
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 38 deletions.
8 changes: 8 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,14 @@
"@copy_to_clipboard": {
"description": "Copy to clipboard button description"
},
"copy_from_clipboard": "Copy from clipboard",
"@copy_from_clipboard": {
"description": "Copy the content of the clipboard"
},
"no_data_available_in_clipboard": "No data available in your clipboard",
"@no_data_available_in_clipboard": {
"description": "No data available in your clipboard"
},
"clipboard_barcode_copy": "Copy barcode to clipboard",
"@clipboard_barcode_copied": {
"description": "Snackbar label after clipboard copy",
Expand Down
145 changes: 107 additions & 38 deletions packages/smooth_app/lib/pages/scan/search_history_view.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/database/dao_string_list.dart';
Expand Down Expand Up @@ -36,21 +37,83 @@ class _SearchHistoryViewState extends State<SearchHistoryView> {

@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _queries.length,
itemBuilder: (BuildContext context, int i) =>
_buildSearchHistoryTile(context, _queries[i]),
return ListTileTheme(
data: const ListTileThemeData(
titleTextStyle: TextStyle(fontSize: 20.0),
minLeadingWidth: 18.0,
),
child: ListView.builder(
itemBuilder: (BuildContext context, int i) {
if (i == 0) {
return _SearchItemCopyFromClipboard(
onData: (String data) => widget.onTap?.call(data),
);
}

final String query = _queries[i - 1];

return _SearchHistoryTile(
query: query,
onTap: () => widget.onTap?.call(query),
onEditItem: () => _onEditItem(query),
onDismissItem: () async {
// we need an immediate action for the display refresh
_queries.remove(query);
// and we need to impact the database too
final LocalDatabase localDatabase = context.read<LocalDatabase>();
await DaoStringList(localDatabase)
.remove(DaoStringList.keySearchHistory, query);
setState(() {});
},
);
},
itemCount: _queries.length + 1, // +1 for the "Copy to clipboard"
),
);
}

void _onEditItem(String query) {
final TextEditingController controller = Provider.of<TextEditingController>(
context,
listen: false,
);

controller.text = query;
controller.selection =
TextSelection.fromPosition(TextPosition(offset: query.length));

// If the keyboard is hidden, show it.
if (View.of(context).viewInsets.bottom == 0) {
widget.focusNode?.unfocus();

WidgetsBinding.instance.addPostFrameCallback((_) {
FocusScope.of(context).requestFocus(widget.focusNode);
});
}
}
}

class _SearchHistoryTile extends StatelessWidget {
const _SearchHistoryTile({
required this.query,
required this.onTap,
required this.onEditItem,
required this.onDismissItem,
});

final String query;
final VoidCallback onTap;
final VoidCallback onEditItem;
final VoidCallback onDismissItem;

Widget _buildSearchHistoryTile(BuildContext context, String query) {
@override
Widget build(BuildContext context) {
final AppLocalizations localizations = AppLocalizations.of(context);

return Dismissible(
key: Key(query),
direction: DismissDirection.endToStart,
onDismissed: (DismissDirection direction) async =>
_handleDismissed(context, query),
onDismissed: (DismissDirection direction) async => onDismissItem(),
background: Container(
color: RED_COLOR,
alignment: AlignmentDirectional.centerEnd,
Expand All @@ -61,39 +124,19 @@ class _SearchHistoryViewState extends State<SearchHistoryView> {
),
),
child: InkWell(
onTap: () => widget.onTap?.call(query),
onTap: () => onTap,
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 18.0, end: 13.0),
child: ListTile(
leading: const Padding(
padding: EdgeInsetsDirectional.only(top: VERY_SMALL_SPACE),
child: Icon(
Icons.search,
size: 18.0,
),
),
trailing: InkWell(
customBorder: const CircleBorder(),
onTap: () {
final TextEditingController controller =
Provider.of<TextEditingController>(
context,
listen: false,
);

controller.text = query;
controller.selection = TextSelection.fromPosition(
TextPosition(offset: query.length));

// If the keyboard is hidden, show it.
if (View.of(context).viewInsets.bottom == 0) {
widget.focusNode?.unfocus();

WidgetsBinding.instance.addPostFrameCallback((_) {
FocusScope.of(context).requestFocus(widget.focusNode);
});
}
},
onTap: onEditItem,
child: Tooltip(
message: localizations.search_history_item_edit_tooltip,
enableFeedback: true,
Expand All @@ -104,20 +147,46 @@ class _SearchHistoryViewState extends State<SearchHistoryView> {
),
),
minLeadingWidth: 10.0,
title: Text(query, style: const TextStyle(fontSize: 20.0)),
title: Text(query),
),
),
),
);
}
}

class _SearchItemCopyFromClipboard extends StatelessWidget {
const _SearchItemCopyFromClipboard({
required this.onData,
});

final Function(String) onData;

@override
Widget build(BuildContext context) {
final AppLocalizations localizations = AppLocalizations.of(context);

Future<void> _handleDismissed(BuildContext context, String query) async {
// we need an immediate action for the display refresh
_queries.remove(query);
// and we need to impact the database too
final LocalDatabase localDatabase = context.read<LocalDatabase>();
await DaoStringList(localDatabase)
.remove(DaoStringList.keySearchHistory, query);
setState(() {});
return InkWell(
onTap: () async {
final ClipboardData? data = await Clipboard.getData('text/plain');
if (data?.text?.isNotEmpty == true) {
onData(data!.text!);
} else if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(localizations.no_data_available_in_clipboard),
),
);
}
},
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 18.0, end: 13.0),
child: ListTile(
title: Text(localizations.copy_from_clipboard),
leading: const Icon(Icons.copy),
minLeadingWidth: 10.0,
),
),
);
}
}

0 comments on commit 5327131

Please sign in to comment.