enhance: Enhances reception search with highlighting and navigation
Adds a text highlighting feature to search results, making it easier for users to identify matching terms within reception names. Enables direct navigation from a selected search result to its corresponding reception details page, improving workflow efficiency.
This commit is contained in:
parent
099334942a
commit
d7587bab32
@ -1,7 +1,9 @@
|
||||
import 'dart:async';
|
||||
import 'package:e_scan/backend/objectbox/entities/stock_picking/stock_picking_record_entity.dart';
|
||||
import 'package:e_scan/components/components.dart';
|
||||
import 'package:e_scan/pages/operation/reception/reception_page_model.dart';
|
||||
import 'package:e_scan/pages/operation/reception/reception_search_page_model.dart';
|
||||
import 'package:e_scan/router/go_secure_router_builder.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:e_scan/themes/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -16,14 +18,17 @@ class ReceptionSearchPage extends ConsumerStatefulWidget {
|
||||
|
||||
class _ReceptionSearchPageState extends ConsumerState<ReceptionSearchPage> {
|
||||
Timer? _debounce;
|
||||
String _query = '';
|
||||
|
||||
void _onSearchChanged(String value) {
|
||||
_debounce?.cancel();
|
||||
_query = value;
|
||||
_debounce = Timer(const Duration(milliseconds: 500), () {
|
||||
ref
|
||||
.read(receptionSearchPageModelProvider.notifier)
|
||||
.allByName(name: value);
|
||||
});
|
||||
setState(() {}); // pour rafraîchir le highlight immédiatement
|
||||
}
|
||||
|
||||
@override
|
||||
@ -32,6 +37,47 @@ class _ReceptionSearchPageState extends ConsumerState<ReceptionSearchPage> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// Crée un TextSpan avec le texte où les parties correspondant à la requête sont surlignées.
|
||||
TextSpan _buildHighlightedText(
|
||||
String? text,
|
||||
String query,
|
||||
TextStyle defaultStyle,
|
||||
) {
|
||||
if (text == null || query.isEmpty) {
|
||||
return TextSpan(text: text ?? '', style: defaultStyle);
|
||||
}
|
||||
|
||||
final lcText = text.toLowerCase();
|
||||
final lcQuery = query.toLowerCase();
|
||||
final spans = <TextSpan>[];
|
||||
int start = 0;
|
||||
|
||||
while (true) {
|
||||
final index = lcText.indexOf(lcQuery, start);
|
||||
if (index < 0) {
|
||||
spans.add(TextSpan(text: text.substring(start), style: defaultStyle));
|
||||
break;
|
||||
}
|
||||
if (index > start) {
|
||||
spans.add(
|
||||
TextSpan(text: text.substring(start, index), style: defaultStyle),
|
||||
);
|
||||
}
|
||||
spans.add(
|
||||
TextSpan(
|
||||
text: text.substring(index, index + query.length),
|
||||
style: defaultStyle.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppTheme.of(context).primaryText,
|
||||
),
|
||||
),
|
||||
);
|
||||
start = index + query.length;
|
||||
}
|
||||
|
||||
return TextSpan(children: spans);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final state = ref.watch(receptionSearchPageModelProvider);
|
||||
@ -68,12 +114,26 @@ class _ReceptionSearchPageState extends ConsumerState<ReceptionSearchPage> {
|
||||
? const Center(child: Text('Aucun résultat trouvé'))
|
||||
: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: receptions
|
||||
.map(
|
||||
(reception) =>
|
||||
ListTile(title: Text(reception.name ?? '')),
|
||||
)
|
||||
.toList(),
|
||||
children: receptions.map((reception) {
|
||||
final name = reception.name ?? '';
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
final id = reception.id;
|
||||
ReceptionDetailsRoute(
|
||||
receptionId: id,
|
||||
).pushReplacement(context);
|
||||
},
|
||||
title: RichText(
|
||||
text: _buildHighlightedText(
|
||||
name,
|
||||
_query,
|
||||
AppTheme.of(context).bodyLarge.override(
|
||||
color: AppTheme.of(context).secondaryText,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
Loading…
x
Reference in New Issue
Block a user