How to Perform Text Search in a PDF Document Using Flutter PDF Viewer
Tags: flutter, dart, mobile, webdev
Published: 2021-04-21 11:55:55

---
title: How to Perform Text Search in a PDF Document Using Flutter PDF Viewer
published: true
date: 2021-04-21 10:38:11 UTC
tags: flutter, dart, mobile, webdev
canonical_url: https://www.syncfusion.com/blogs/post/perform-text-search-in-a-pdf-document-using-flutter-pdf-viewer.aspx
cover_image: https://www.syncfusion.com/blogs/wp-content/uploads/2021/04/How-to-perform-text-search-over-the-PDF-document-using-Flutter-PDF-Viewer-672x372.png
---

The Syncfusion [Flutter PDF Viewer](https://www.syncfusion.com/flutter-widgets/flutter-pdf-viewer "Link to the Syncfusion Flutter PDF Viewer") allows you to view PDF documents seamlessly and efficiently. When working with PDF documents, you’ll often need to search for text. In this blog, you will learn how to create a custom search toolbar and perform text search in a PDF document using the Syncfusion Flutter PDF Viewer.

## Configuring the Flutter PDF Viewer widget

Please see our [Flutter PDF Viewer Getting Started documentation](https://help.syncfusion.com/flutter/pdf-viewer/getting-started "Link to the getting started documentation of Flutter PDF Viewer") to create a Flutter application and configure the Syncfusion Flutter PDF Viewer ( **SfPdfViewer** ) widget in it.

## Create custom search toolbar and perform text search

**Step 1** : Create a **SearchToolbar** widget class to display the toolbar items for search operations and a typedef for the **onTap** callback. The **SearchToolbar** widget class has the following properties:

- **controller** —An object used to control the SfPdfViewer.
- **onTap** —Called when we select or tap on any search toolbar item.
- **showTooltip** —Indicates whether to display the tooltip for the search toolbar items or not.

```dart
/// Signature for the [SearchToolbar.onTap] callback.
typedef SearchTapCallback = void Function(Object item);

/// SearchToolbar widget.
class SearchToolbar extends StatefulWidget {
  ///Describes the search toolbar constructor.
  SearchToolbar({
    this.controller,
    this.onTap,
    this.showTooltip = true,  
    Key key,
  }) : super(key: key);

  /// Indicates whether the tooltip for the search toolbar items should be shown or not.
  final bool showTooltip;

  /// An object that is used to control the [SfPdfViewer].
  final PdfViewerController controller;

  /// Called when the search toolbar item is selected.
  final SearchTapCallback onTap;

  @override
  SearchToolbarState createState() => SearchToolbarState();
}
```

**Step 2** : Create a **SearchToolbarState** class to build and display the search toolbar and its items in the user interface. We will define the functionalities for each toolbar item in this class. Our example includes the following toolbar items:

- **Back button** closes the search toolbar.
- **Text form field entry** gets the text to be searched in the document.
- **Close button** cancels the search progress.
- **Instances information text** displays the current instance index and total instances count of the searched text.
- **Previous instance search navigation button** navigates to the previous match instance.
- **Next instance search navigation button** navigates to the next match instance.

We can perform the text search operation in Flutter PDF Viewer using the [searchText](https://pub.dev/documentation/syncfusion_flutter_pdfviewer/latest/pdfviewer/PdfViewerController/searchText.html "Link to the searchText method in Flutter PDF Viewer") controller method. It takes the text to be searched and [TextSearchOption](https://pub.dev/documentation/syncfusion_flutter_pdf/latest/pdf/TextSearchOption-class.html "Link to the TextSearchOption enum") as parameters. This method searches for the text and highlights all the instances of it in the document. It returns the [PdfTextSearchResult](https://pub.dev/documentation/syncfusion_flutter_pdfviewer/latest/pdfviewer/PdfTextSearchResult-class.html "Link to the PdfTextSearchResult class in Flutter PDF Viewer") object holding the result values such as total instance count, current highlighted instance index, etc.

The [PdfTextSearchResult](https://pub.dev/documentation/syncfusion_flutter_pdfviewer/latest/pdfviewer/PdfTextSearchResult-class.html "Link to the PdfTextSearchResult class in Flutter PDF Viewer") object will also help you navigate to the different instances of the searched-for text available and cancel the search operation.

Now, we are going to call the [searchText](https://pub.dev/documentation/syncfusion_flutter_pdfviewer/latest/pdfviewer/PdfViewerController/searchText.html "Link to the searchText method in Flutter PDF Viewer") method within the text form field entry’s **onFieldSubmitted** callback function.  Also, we will implement the logic to navigate to the different text instances with the **onPressed** callback function of the previous and next search navigation buttons.

```dart
/// State for the SearchToolbar widget.
class SearchToolbarState extends State<SearchToolbar> {
  bool _showSearchResultItems = false;
  int _textLength = 0;

  /// Define the focus node. To manage the life cycle, create the FocusNode in the initState method, and clean it up in the dispose method.
  FocusNode _focusNode;

  /// An object that is used to control the Text Form Field.
  final TextEditingController _editingController = TextEditingController();

  /// An object that is used to retrieve the text search result.
  PdfTextSearchResult _pdfTextSearchResult = PdfTextSearchResult();

  @override
  void initState() {
    super.initState();
    _focusNode = FocusNode();
    _focusNode?.requestFocus();
  }

  ///Clear the text search result.
  void clearSearch() {
    _pdfTextSearchResult.clear();
  }

  @override
  void dispose() {
    // Clean up the focus node when the Form is disposed.
    _focusNode?.dispose();
    super.dispose();
  }

  ///Display the Alert dialog to search from the beginning.
  void _showSearchAlertDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          insetPadding: EdgeInsets.all(0),
          title: Text('Search Result'),
          content: Container(
              width: 328.0,
              child: Text(
                  'No more occurrences found. Would you like to continue to search from the beginning?')),
          actions: <Widget>[
            FlatButton(
              child: Text('YES'),
              onPressed: () {
                _pdfTextSearchResult?.nextInstance();
                Navigator.of(context).pop();
              },
            ),
            FlatButton(
              child: Text('NO'),
              onPressed: () {
                _pdfTextSearchResult?.clear();
                _editingController.clear();
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.center, 
      children: <Widget>[
        Material(
          color: Colors.transparent,
          child: IconButton(
            icon: Icon(
              Icons.arrow_back,
              color: Color(0x000000).withOpacity(0.54),
              size: 24,
            ),
            onPressed: () {
              widget.onTap?.call('Cancel Search');
              _editingController.clear();
              _pdfTextSearchResult?.clear();
            },
          ),
        ),
        Flexible(
          child: TextFormField(
            style: TextStyle(
                color: Color(0x000000).withOpacity(0.87), fontSize: 16),
            enableInteractiveSelection: false,
            focusNode: _focusNode,
            keyboardType: TextInputType.text,
            textInputAction: TextInputAction.search,
            controller: _editingController,
            decoration: InputDecoration(
              border: InputBorder.none,
              hintText: 'Find...',
              hintStyle: TextStyle(color: Color(0x000000).withOpacity(0.34)),
            ),
            onChanged: (text) {
              if (_textLength < _editingController.value.text.length) {
                _textLength = _editingController.value.text.length;
              }
              if (_editingController.value.text.length < _textLength) {
                setState(() {
                  _showSearchResultItems = false;
                });
              }
            },
            onFieldSubmitted: (String value) async {
              _pdfTextSearchResult =
                  await widget.controller.searchText(_editingController.text);
              if (_pdfTextSearchResult.totalInstanceCount == 0) {
                widget.onTap?.call('onSubmit');
              } else {
                setState(() {
                  _showSearchResultItems = true;
                });
              }
            },
          ),
        ),
        Visibility(
          visible: _editingController.text.isNotEmpty,
          child: Material(
            color: Colors.transparent,
            child: IconButton(
              icon: Icon(
                Icons.clear,
                color: Color.fromRGBO(0, 0, 0, 0.54),
                size: 24,
              ),
              onPressed: () {
                setState(() {
                  _editingController.clear();
                  _pdfTextSearchResult?.clear();
                  widget.controller.clearSelection();
                  _showSearchResultItems = false;
                  _focusNode.requestFocus();
                });
                widget.onTap?.call('Clear Text');
              },
              tooltip: widget.showTooltip ? 'Clear Text' : null,
            ),
          ),
        ),
        Visibility(
          visible: _showSearchResultItems,
          child: Row(
            children: [
              Text(
                '${_pdfTextSearchResult?.currentInstanceIndex}',
                style: TextStyle(
                    color: Color.fromRGBO(0, 0, 0, 0.54).withOpacity(0.87),
                    fontSize: 16),
              ),
              Text(
                ' of ',
                style: TextStyle(
                    color: Color.fromRGBO(0, 0, 0, 0.54).withOpacity(0.87),
                    fontSize: 16),
              ),
              Text(
                '${_pdfTextSearchResult?.totalInstanceCount}',
                style: TextStyle(
                    color: Color.fromRGBO(0, 0, 0, 0.54).withOpacity(0.87),
                    fontSize: 16),
              ),
              Material(
                color: Colors.transparent,
                child: IconButton(
                  icon: Icon(
                    Icons.navigate_before,
                    color: Color.fromRGBO(0, 0, 0, 0.54),
                    size: 24,
                  ),
                  onPressed: () {
                    setState(() {
                      _pdfTextSearchResult?.previousInstance();
                    });
                    widget.onTap?.call('Previous Instance');
                  },
                  tooltip: widget.showTooltip ? 'Previous' : null,
                ),
              ),
              Material(
                color: Colors.transparent,
                child: IconButton(
                  icon: Icon(
                    Icons.navigate_next,
                    color: Color.fromRGBO(0, 0, 0, 0.54),
                    size: 24,
                  ),
                  onPressed: () {
                    setState(() {
                      if (_pdfTextSearchResult?.currentInstanceIndex ==
                              _pdfTextSearchResult?.totalInstanceCount &&
                          _pdfTextSearchResult?.currentInstanceIndex != 0 &&
                          _pdfTextSearchResult?.totalInstanceCount != 0) {
                        _showSearchAlertDialog(context);
                      } else {
                        widget.controller.clearSelection();
                        _pdfTextSearchResult?.nextInstance();
                      }
                    });
                    widget.onTap?.call('Next Instance');
                  },
                  tooltip: widget.showTooltip ? 'Next' : null,
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }
}
```

In the previous code example, the **\_showSearchAlertDialog** method displays the alert dialog when the full cycle of text search is completed and there exists no more occurrences of the searched text in the document.

The following are the uses of the other properties and variables in that code example:

- **\_showSearchResultItems** represents whether to display the search result toolbar items (instances information text, Previous instance search navigation button, and Next instance search navigation button) or not. It will display these items only when there is a matching text found in the document.
- **\_textLength** represents the text length entered in the text form field. This will help to decide whether the search result toolbar items should be displayed or not. When we edit the text after the search process, then the search result toolbar items will be hidden based on this property’s value.
- **\_focusNode** represents the widget to be focused. Here, we are using it to control the focus of the **TextFormField ** widget.
- **\_editingController** object controls the text form field.
- **\_pdfTextSearchResult** object holds the text search result.

Step 3: Create a** CustomSearchPdfViewer **widget class and set that as the default root route of the application in the** main **function. Then, create a** CustomSearchPdfViewerState**class to build and display the main toolbar (with title and search icon) and the**SfPdfViewer**.

Here, we are going to load the **SfPdfViewer**  with the PDF document from the network or URL and display the search toolbar when we select the search icon in the main toolbar.

```dart
void main() {
  runApp(MaterialApp(
    debugShowCheckedModeBanner: false,
    home: CustomSearchPdfViewer(),
  ));
}

/// Represents the Homepage for navigation.
class CustomSearchPdfViewer extends StatefulWidget {
  @override
  CustomSearchPdfViewerState createState() => CustomSearchPdfViewerState();
}

class CustomSearchPdfViewerState extends State<CustomSearchPdfViewer> {
  final PdfViewerController _pdfViewerController = PdfViewerController();
  final GlobalKey<SearchToolbarState> _textSearchKey = GlobalKey();

  bool _showToast;
  bool _showScrollHead;
  bool _showSearchToolbar;

  // Ensure the entry history of text search.
  LocalHistoryEntry _localHistoryEntry;

  @override
  void initState() {
    _showToast = false;
    _showScrollHead = true;
    _showSearchToolbar = false;
    super.initState();
  }

  void _ensureHistoryEntry() {
    if (_localHistoryEntry == null) {
      final ModalRoute<dynamic> route = ModalRoute.of(context);
      if (route != null) {
        _localHistoryEntry =
            LocalHistoryEntry(onRemove: _handleHistoryEntryRemoved);
        route.addLocalHistoryEntry(_localHistoryEntry);
      }
    }
  }

  void _handleHistoryEntryRemoved() {
    _textSearchKey?.currentState?.clearSearch();
    setState(() {
      _showSearchToolbar = false; 
      _localHistoryEntry = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _showSearchToolbar
          ? AppBar(
              flexibleSpace: SafeArea(
                child: SearchToolbar(
                  key: _textSearchKey,
                  showTooltip: true,
                  controller: _pdfViewerController,
                  onTap: (Object toolbarItem) async {
                    if (toolbarItem.toString() == 'Cancel Search') {
                      setState(() {
                        _showSearchToolbar = false;
                        _showScrollHead = true;
                        if (Navigator.canPop(context)) {
                          Navigator.maybePop(context);
                        }
                      });
                    }
                    if (toolbarItem.toString() == 'onSubmit') {
                      setState(() {
                        _showToast = true;
                      });
                      await Future.delayed(Duration(seconds: 2));
                      setState(() {
                        _showToast = false;
                      });
                    }
                  },
                ),
              ),
              automaticallyImplyLeading: false,
              backgroundColor: Color(0xFFFAFAFA),
            )
          : AppBar(
              title: Text(
                'Syncfusion Flutter PDF Viewer',
                style: TextStyle(color: Colors.black87),
              ),
              actions: [
                IconButton(
                  icon: Icon(
                    Icons.search,
                    color: Colors.black87,
                  ),
                  onPressed: () {
                    setState(() {
                      _showScrollHead = false;
                      _showSearchToolbar = true;
                      _ensureHistoryEntry();
                    });
                  },
                ),
              ],
              automaticallyImplyLeading: false,
              backgroundColor: Color(0xFFFAFAFA),
            ),
      body: Stack(
        children: [
          SfPdfViewer.network(
            'https://cdn.syncfusion.com/content/PDFViewer/flutter-succinctly.pdf',
            controller: _pdfViewerController,
            canShowScrollHead: _showScrollHead,
          ),
          Visibility(
            visible: _showToast,
            child: Align(
              alignment: Alignment.center,
              child: Flex(
                direction: Axis.horizontal,
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Container(
                    padding:
                        EdgeInsets.only(left: 15, top: 7, right: 15, bottom: 7),
                    decoration: BoxDecoration(
                      color: Colors.grey[600],
                      borderRadius: BorderRadius.all(
                        Radius.circular(16.0),
                      ),
                    ),
                    child: Text(
                      'No matches found',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                          fontFamily: 'Roboto',
                          fontSize: 16,
                          color: Colors.white),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}
```

The following are the uses of other properties and variables in the previous code example:

- **\_showToast ** displays a toast message when any matches are found or not.
- **\_showScrollHead** displays the scroll head when text search is in progress. In this example, we have hidden the scroll head when a text search is in progress.
- **\_showSearchToolbar** displays the search toolbar. By default, the search toolbar will remain hidden. It will appear when we select the search icon in the main toolbar.
- **\_localHistoryEntry** keeps the history of the text search.
- **\_pdfViewerController** controls the instance of the **SfPdfViewer**.
- **\_textSearchKey** provides access to the objects of the **SearchToolbar ** widget.

Execute the application with the previous code example,  and you will get the output like in the following screenshot.

![Main Page of Flutter PDF Viewer](https://www.syncfusion.com/blogs/wp-content/uploads/2021/03/Main-Page.png)<figcaption>Main Page of Flutter PDF Viewer</figcaption>

![Search Toolbar in Flutter PDF Viewer](https://www.syncfusion.com/blogs/wp-content/uploads/2021/03/Search-Toolbar.png)<figcaption>Search Toolbar in Flutter PDF Viewer</figcaption>

## Resource

For the complete working project, refer to the [How to perform text search in Flutter PDF Viewer](https://github.com/ramkumarsync/How-to-perform-text-search-using-flutter-pdf-viewer "Link to How to perform text search in Flutter PDF Viewer demo on GitHub") demo.

## Conclusion

I hope you now know how to easily perform text search in a PDF file using the Syncfusion [Flutter PDF Viewer](https://www.syncfusion.com/flutter-widgets/flutter-pdf-viewer "Link to the Syncfuison Flutter PDF Viewer"). Please try out the example in this blog post and let us know your feedback in the comments section below.

If you are a current Syncfusion user, you can download the latest Essential Studio version from the [License and Downloads](https://www.syncfusion.com/account/downloads "Link to license and download page of Essential Studio") page and try these features for yourself. If you aren’t a customer yet, you can try our 30-day [free trial](https://www.syncfusion.com/downloads "Link to the free evaluation of the Syncfusion Essential Studio") to check them out. Our other samples are available on [GitHub](https://github.com/syncfusion/flutter-examples "Link to the Syncfusion Flutter examples on GitHub"). Also, please don’t miss our demo app in [Google Play](https://play.google.com/store/apps/details?id=com.syncfusion.flutter.examples&hl=en "Link to the Syncfusion Flutter demo in Google Play store") and the [App Store](https://apps.apple.com/in/app/syncfusion-flutter-ui-widgets/id1475231341 "Link to the Syncfusion Flutter demo in App Store").

If you wish to send us feedback or would like to submit any questions, please feel free to post them in the comments section below. Contact us through our [support forum](https://www.syncfusion.com/forums "Link to the Syncfusion support forum"), [Direct-Trac,](https://www.syncfusion.com/support/directtrac/ "Link to the Syncfusion support system Direct Trac") or [feedback portal](https://www.syncfusion.com/feedback/ "Link to Syncfusion Feedback Portal"). We are happy to assist you!

If you like this article, we think you will also like the following:

- [5 Easy Ways to Find Text in PDF Documents in Flutter](https://dev.to/syncfusion/5-easy-ways-to-find-text-in-pdf-documents-in-flutter-4k8h "Link to the article 5 Easy Ways to Find Text in PDF Documents in Flutter") [Blog]
- [How to Create PDF/A Standard Files in Flutter](https://dev.to/syncfusion/how-to-create-pdf-a-standard-files-in-flutter-1ic5 "Link to the article How to Create PDF/A Standard Files in Flutter") [Blog]
- [How to Encrypt and Decrypt PDF Files in Flutter](https://dev.to/syncfusion/how-to-encrypt-and-decrypt-pdf-files-in-flutter-5hgp "Link to the article How to Encrypt and Decrypt PDF Files in Flutter") [Blog]
- [7 Best Flutter Charts for Visualizing Income and Expenditure](https://dev.to/syncfusion/7-best-flutter-charts-for-visualizing-income-and-expenditure-2ld7 "Link to the article 7 Best Flutter Charts for Visualizing Income and Expenditure") [Blog]
- [_Flutter Succinctly_](https://www.syncfusion.com/ebooks/flutter-succinctly "Link to the eBook Flutter Succinctly") [ebook]