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 'dart:async';
|
||||||
import 'package:e_scan/backend/objectbox/entities/stock_picking/stock_picking_record_entity.dart';
|
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/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/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:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:e_scan/themes/app_theme.dart';
|
import 'package:e_scan/themes/app_theme.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -16,14 +18,17 @@ class ReceptionSearchPage extends ConsumerStatefulWidget {
|
|||||||
|
|
||||||
class _ReceptionSearchPageState extends ConsumerState<ReceptionSearchPage> {
|
class _ReceptionSearchPageState extends ConsumerState<ReceptionSearchPage> {
|
||||||
Timer? _debounce;
|
Timer? _debounce;
|
||||||
|
String _query = '';
|
||||||
|
|
||||||
void _onSearchChanged(String value) {
|
void _onSearchChanged(String value) {
|
||||||
_debounce?.cancel();
|
_debounce?.cancel();
|
||||||
|
_query = value;
|
||||||
_debounce = Timer(const Duration(milliseconds: 500), () {
|
_debounce = Timer(const Duration(milliseconds: 500), () {
|
||||||
ref
|
ref
|
||||||
.read(receptionSearchPageModelProvider.notifier)
|
.read(receptionSearchPageModelProvider.notifier)
|
||||||
.allByName(name: value);
|
.allByName(name: value);
|
||||||
});
|
});
|
||||||
|
setState(() {}); // pour rafraîchir le highlight immédiatement
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -32,6 +37,47 @@ class _ReceptionSearchPageState extends ConsumerState<ReceptionSearchPage> {
|
|||||||
super.dispose();
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = ref.watch(receptionSearchPageModelProvider);
|
final state = ref.watch(receptionSearchPageModelProvider);
|
||||||
@ -68,12 +114,26 @@ class _ReceptionSearchPageState extends ConsumerState<ReceptionSearchPage> {
|
|||||||
? const Center(child: Text('Aucun résultat trouvé'))
|
? const Center(child: Text('Aucun résultat trouvé'))
|
||||||
: SingleChildScrollView(
|
: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: receptions
|
children: receptions.map((reception) {
|
||||||
.map(
|
final name = reception.name ?? '';
|
||||||
(reception) =>
|
return ListTile(
|
||||||
ListTile(title: Text(reception.name ?? '')),
|
onTap: () {
|
||||||
)
|
final id = reception.id;
|
||||||
.toList(),
|
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