# How to Implement a Dynamic Sorting Algorithm for Search Results in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 25-35 min
- Compatibility: FlutterFlow Free+
- Last updated: March 2026

## TL;DR

Implement dynamic search result sorting by building a Custom Function that scores results based on multiple weighted factors: text match quality, recency, popularity via view count, and user personalization from past interactions. Fetch a candidate set from Firestore with basic filtering, run the scoring function to rank results, and display them in a ListView with highlighted match terms. Optionally integrate Algolia for full-text search with typo tolerance and faceted filtering.

## Implementing Dynamic Search Result Sorting in FlutterFlow

Basic search shows results in whatever order the database returns them. Dynamic sorting ranks results by relevance using multiple factors so the best match appears first. This tutorial builds a scoring algorithm that weights text match quality, content recency, popularity, and user preferences, then displays ranked results with highlighted match terms.

## Before you start

- A FlutterFlow project with Firestore containing searchable content
- A collection with fields like title, description, viewCount, and timestamp
- Basic familiarity with Custom Functions in FlutterFlow
- An Algolia account (optional, for advanced full-text search)

## Step-by-step guide

### 1. Set up the search data model and candidate fetching

Ensure your searchable collection (e.g., articles, products, or listings) has fields: title (String), description (String), category (String), viewCount (Integer), timestamp (Timestamp), tags (String Array). Add a titleLowerCase field that stores the lowercase version of the title for case-insensitive searching. On the SearchPage, add a TextField for the search query. When the user types and presses search, execute a Backend Query on the collection. Use a whereIn or arrayContainsAny filter on tags if applicable, or use a range query on titleLowerCase with startAt and endAt for prefix matching. Limit results to 100 candidates to keep client-side scoring fast.

**Expected result:** A search query fetches up to 100 candidate results from Firestore for client-side ranking.

### 2. Build the multi-factor scoring Custom Function

Create a Custom Function named scoreAndRankResults that takes the search query string and the list of candidate documents as inputs. For each candidate, calculate a relevance score based on weighted factors. Title contains the exact query: add 10 points. Title starts with the query: add 5 bonus points. Description contains the query: add 3 points. Recency: calculate days since timestamp, score = max(0, 5 minus days divided by 30) so newer content scores higher up to 5 points. Popularity: score = min(5, viewCount divided by 100) capping at 5 points. Personalization: if the item's category matches any category the user has viewed before (stored in App State recentCategories), add 3 points. Sum all factors into a total score. Sort the list by score descending and return it.

```
// Custom Function: scoreAndRankResults
double scoreItem(Map<String, dynamic> item, String query, List<String> userCategories) {
  double score = 0;
  final title = (item['title'] as String).toLowerCase();
  final desc = (item['description'] as String).toLowerCase();
  final q = query.toLowerCase();
  
  // Text match scoring
  if (title.contains(q)) score += 10;
  if (title.startsWith(q)) score += 5;
  if (desc.contains(q)) score += 3;
  
  // Recency scoring (max 5 points)
  final days = DateTime.now().difference(item['timestamp'].toDate()).inDays;
  score += (5 - (days / 30)).clamp(0, 5);
  
  // Popularity scoring (max 5 points)
  score += ((item['viewCount'] ?? 0) / 100).clamp(0, 5);
  
  // Personalization scoring
  if (userCategories.contains(item['category'])) score += 3;
  
  return score;
}

List<Map<String, dynamic>> scoreAndRankResults(
  List<Map<String, dynamic>> items,
  String query,
  List<String> userCategories,
) {
  for (var item in items) {
    item['_score'] = scoreItem(item, query, userCategories);
  }
  items.sort((a, b) => (b['_score'] as double).compareTo(a['_score'] as double));
  return items;
}
```

**Expected result:** A Custom Function that scores each result on multiple factors and returns the list sorted by relevance score.

### 3. Display ranked results with highlighted match terms

On the SearchPage, bind the ListView to the output of the scoreAndRankResults function. Each result card is a Container showing the title, description snippet, category badge, viewCount, and timestamp. For highlighting matched terms in the title and description, create a Custom Widget that takes the text and query as parameters and renders a RichText widget. The widget splits the text at query match positions and applies bold styling with a yellow background to the matched segments while keeping the rest in normal styling. This visual highlight helps users see why each result matched.

```
// Custom Widget: HighlightedText
import 'package:flutter/material.dart';

class HighlightedText extends StatelessWidget {
  final String text;
  final String highlight;
  final TextStyle baseStyle;

  const HighlightedText({
    required this.text,
    required this.highlight,
    this.baseStyle = const TextStyle(fontSize: 14),
  });

  @override
  Widget build(BuildContext context) {
    if (highlight.isEmpty) return Text(text, style: baseStyle);
    final lower = text.toLowerCase();
    final queryLower = highlight.toLowerCase();
    final List<TextSpan> spans = [];
    int start = 0;
    int idx;
    while ((idx = lower.indexOf(queryLower, start)) != -1) {
      if (idx > start) spans.add(TextSpan(text: text.substring(start, idx)));
      spans.add(TextSpan(
        text: text.substring(idx, idx + highlight.length),
        style: baseStyle.copyWith(
          fontWeight: FontWeight.bold,
          backgroundColor: Colors.yellow.withOpacity(0.3),
        ),
      ));
      start = idx + highlight.length;
    }
    if (start < text.length) spans.add(TextSpan(text: text.substring(start)));
    return RichText(text: TextSpan(style: baseStyle, children: spans));
  }
}
```

**Expected result:** Search results display in relevance order with matched query terms visually highlighted in bold with a yellow background.

### 4. Add Algolia integration for full-text search

For apps needing real full-text search with typo tolerance, synonyms, and faceted filtering, integrate Algolia. In FlutterFlow, go to Settings and configure the Algolia integration with your Application ID and Search API Key. Sync your Firestore collection to an Algolia index using the Firebase Extensions Algolia Search extension. On the SearchPage, switch the Backend Query to Algolia Search Query instead of Firestore. Algolia handles relevance ranking, typo tolerance (finding 'restaurnt' when searching 'restaurant'), and instant results. Configure searchable attributes in the Algolia dashboard, setting title as the primary searchable attribute and description as secondary. Add faceted filtering for category using Algolia facets.

**Expected result:** Algolia powers the search with instant full-text matching, typo tolerance, and server-side relevance ranking.

### 5. Add manual sort override and search analytics

Below the search TextField, add a DropDown for manual sort override with options: Most Relevant (default, uses the scoring algorithm), Newest First (sort by timestamp descending), Most Popular (sort by viewCount descending), and Alphabetical (sort by title ascending). When a user selects a non-relevance sort, bypass the scoring function and apply a simple sort on the selected field. Track search behavior: on each search, log the query, results count, and which result the user tapped to an analytics collection. Use this data to improve scoring weights over time. If certain queries consistently lead to clicks on lower-ranked results, adjust the factor weights.

**Expected result:** Users can override the relevance sort with manual options, and search analytics track behavior for continuous improvement.

## Complete code example

File: `FlutterFlow Dynamic Search Sorting Setup`

```text
FIRESTORE DATA MODEL:
  articles/{articleId} (or any searchable collection)
    title: String
    titleLowerCase: String
    description: String
    category: String
    viewCount: Integer
    tags: [String]
    timestamp: Timestamp

  search_analytics/{logId} (optional)
    query: String
    resultsCount: Integer
    clickedResultId: String
    timestamp: Timestamp

APP STATE:
  recentCategories: [String] (persisted, tracks user's viewed categories)

CUSTOM FUNCTION: scoreAndRankResults(items, query, userCategories)
  Scoring per item:
    +10: title contains query
    +5: title starts with query
    +3: description contains query
    +0-5: recency (newer = higher)
    +0-5: popularity (viewCount / 100, capped)
    +3: category matches user preference
  Sort by total score descending

CUSTOM WIDGET: HighlightedText(text, highlight)
  Renders RichText with bold+yellow on matched segments

PAGE: SearchPage
WIDGET TREE:
  Column
    ├── Row
    │     ├── TextField (search query)
    │     └── IconButton (search)
    ├── Row
    │     └── DropDown (sort: Relevant | Newest | Popular | A-Z)
    ├── Text ("X results" count)
    └── ListView (ranked results)
          └── Container (result card)
                ├── HighlightedText (title, query)
                ├── HighlightedText (description snippet, query)
                ├── Row
                │     ├── Badge (category)
                │     ├── Text (viewCount + eye icon)
                │     └── Text (relative timestamp)
                └── On Tap: Navigate + log search analytics

ALGOLIA INTEGRATION (optional):
  Settings → Algolia → Application ID + Search API Key
  Sync: Firebase Extension → Algolia Search
  Searchable attributes: title (rank 1), description (rank 2)
  Facets: category
```

## Common mistakes

- **Running the scoring algorithm on all documents in the collection client-side** — Fetching and scoring 10,000 or more documents freezes the UI and consumes excessive Firestore reads. The app becomes unresponsive during search. Fix: Limit the Firestore fetch to the top 100 candidates using basic filters (prefix match, category filter). Score and rank only those 100 on the client.
- **Using case-sensitive string matching for search queries** — Searching 'Pizza' won't find 'pizza' or 'PIZZA', making the search feel broken to users who don't match capitalization exactly. Fix: Store a lowercase version of searchable fields (titleLowerCase) and convert the query to lowercase before comparison. All matching happens in lowercase.
- **Not providing a fallback when Algolia is unavailable** — If the Algolia service is down or the API key expires, the search returns nothing and users cannot find content at all. Fix: Implement a fallback Firestore-based search that activates when Algolia returns an error. It won't have typo tolerance but will still return basic results.

## Best practices

- Limit candidate fetch to 100 results for client-side scoring to maintain UI responsiveness
- Store lowercase versions of searchable fields for case-insensitive matching
- Weight scoring factors based on what matters most to your users (text match usually highest)
- Highlight matched terms in results so users can see why each result appeared
- Track which results users click to validate and improve scoring weights over time
- Provide manual sort options so power users can override the algorithm
- Use Algolia for production apps needing typo tolerance and instant full-text search

## Frequently asked questions

### Does Firestore support full-text search natively?

No. Firestore supports exact match, prefix match, and array-contains queries, but not fuzzy text search or typo tolerance. For full-text search, use Algolia, Typesense, or Meilisearch integrated via Cloud Functions or FlutterFlow's built-in Algolia support.

### How do I handle search queries with multiple words?

Split the query into individual words and score each word match separately. A result matching all words scores higher than one matching only some. For multi-word exact phrase matching, compare the full phrase against the text.

### Can I adjust scoring weights after launch?

Yes. Store scoring weights in a Firestore config document (textMatchWeight, recencyWeight, popularityWeight). Read these in the scoring function. Admins can adjust weights without code changes and see results immediately.

### How do I boost certain results like featured or sponsored items?

Add a boostScore field to documents that should be promoted. In the scoring function, add this boost to the total score. Featured items will rank higher naturally without a separate promoted section.

### What is the cost of Algolia for a small app?

Algolia offers a free tier with 10,000 search requests per month and 10,000 records. The Build plan starts at $0 with pay-as-you-go pricing. For most small apps, the free tier is sufficient.

### Can RapidDev help implement advanced search?

Yes. RapidDev can implement full-text search with Algolia or Elasticsearch, custom ranking algorithms, AI-powered semantic search, autocomplete suggestions, faceted navigation, and search analytics dashboards.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-implement-a-dynamic-sorting-algorithm-for-search-results-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-implement-a-dynamic-sorting-algorithm-for-search-results-in-flutterflow
