import 'dart:async'; import 'package:fintracker_api/fintracker_api.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../portfolio/labels.dart' show assetClassLabel; import '../providers.dart'; /// Search-and-pick over `GET /instruments?q=`. Deliberately dumb: it shows what the search /// returned and nothing else — no "probably this one" pre-selection, no highlighted best /// guess. The binding is the user's statement, not the app's inference. class LinkInstrumentDialog extends ConsumerStatefulWidget { const LinkInstrumentDialog({required this.initialQuery, super.key}); /// Prefilled search text (the ISIN or ticker from the report). It only fills the search /// box — nothing is selected until the user taps a row. final String initialQuery; @override ConsumerState createState() => _LinkInstrumentDialogState(); } class _LinkInstrumentDialogState extends ConsumerState { late final TextEditingController _controller = TextEditingController( text: widget.initialQuery, ); String _query = ''; Timer? _debounce; @override void initState() { super.initState(); _query = widget.initialQuery; } @override void dispose() { _debounce?.cancel(); _controller.dispose(); super.dispose(); } void _onChanged(String value) { _debounce?.cancel(); _debounce = Timer(const Duration(milliseconds: 350), () { if (mounted) setState(() => _query = value); }); } @override Widget build(BuildContext context) { final results = ref.watch(instrumentSearchProvider(_query)); return AlertDialog( title: const Text('Привязать к инструменту'), content: SizedBox( width: 520, height: 420, child: Column( children: [ TextField( controller: _controller, autofocus: true, decoration: const InputDecoration( prefixIcon: Icon(Icons.search), hintText: 'Тикер, ISIN или название', border: OutlineInputBorder(), isDense: true, ), onChanged: _onChanged, ), const SizedBox(height: 12), Expanded( child: results.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center(child: Text('$e')), data: (rows) { if (_query.trim().length < 2) { return const Center( child: Text('Введите минимум 2 символа'), ); } if (rows.isEmpty) { return const Center(child: Text('Ничего не найдено')); } return ListView.builder( itemCount: rows.length, itemBuilder: (context, i) => _row(rows[i]), ); }, ), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Отмена'), ), ], ); } Widget _row(InstrumentOut instrument) { final subtitle = [ assetClassLabel(instrument.assetClass), if (instrument.isin != null) instrument.isin!, if (instrument.board != null) instrument.board!, instrument.currency, ].join(' · '); return ListTile( dense: true, title: Text( [ if (instrument.ticker != null) instrument.ticker!, instrument.name, ].join(' · '), ), subtitle: Text(subtitle), onTap: () => Navigator.of(context).pop(instrument.id), ); } }