import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; /// A dialog for searching text within a PDF using SfPdfViewer's built-in search. class PdfTextSearchDialog extends StatefulWidget { final PdfViewerController viewerController; const PdfTextSearchDialog({super.key, required this.viewerController}); @override State createState() => _PdfTextSearchDialogState(); } class _PdfTextSearchDialogState extends State { final TextEditingController _queryController = TextEditingController(); PdfTextSearchResult? _searchResult; String _statusText = ''; @override void dispose() { _queryController.dispose(); _searchResult?.clear(); super.dispose(); } void _search() { final query = _queryController.text.trim(); if (query.isEmpty) return; final result = widget.viewerController.searchText(query); setState(() { _searchResult = result; _updateStatus(); }); } void _nextMatch() { _searchResult?.nextInstance(); _updateStatus(); } void _previousMatch() { _searchResult?.previousInstance(); _updateStatus(); } void _updateStatus() { final result = _searchResult; if (result == null || result.totalInstanceCount == 0) { setState(() => _statusText = 'No matches'); } else { setState(() { _statusText = '${result.currentInstanceIndex} of ${result.totalInstanceCount} matches'; }); } } @override Widget build(BuildContext context) { return Dialog( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Expanded( child: TextField( controller: _queryController, autofocus: true, decoration: const InputDecoration( hintText: 'Search in PDF...', prefixIcon: Icon(Icons.search), isDense: true, border: OutlineInputBorder(), ), onSubmitted: (_) => _search(), ), ), const SizedBox(width: 8), IconButton( icon: const Icon(Icons.search), tooltip: 'Search', onPressed: _search, ), ], ), const SizedBox(height: 12), Row( children: [ Text( _statusText, style: Theme.of(context).textTheme.bodySmall, ), const Spacer(), IconButton( icon: const Icon(Icons.keyboard_arrow_up), tooltip: 'Previous match', onPressed: _searchResult != null && _searchResult!.totalInstanceCount > 0 ? _previousMatch : null, iconSize: 20, ), IconButton( icon: const Icon(Icons.keyboard_arrow_down), tooltip: 'Next match', onPressed: _searchResult != null && _searchResult!.totalInstanceCount > 0 ? _nextMatch : null, iconSize: 20, ), IconButton( icon: const Icon(Icons.close), tooltip: 'Clear search', onPressed: () { _searchResult?.clear(); setState(() { _queryController.clear(); _searchResult = null; _statusText = ''; }); }, iconSize: 20, ), ], ), ], ), ), ), ); } }