# How to Set Up a Music Recognition Feature in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 25-35 min
- Compatibility: FlutterFlow Pro+ (Custom Actions and Cloud Functions required)
- Last updated: March 2026

## TL;DR

Build a Shazam-like music identification feature in FlutterFlow. You will record a short audio clip using a Custom Action with the record package, send the audio bytes to the AudD music recognition API via a Cloud Function, display the result as a card with album art, track title, artist name, and a Listen on Spotify button. A Lottie animation plays pulsing concentric circles during the recognition process. All recognized tracks are saved to a recognized_tracks Firestore collection so users can browse their listening history.

## Identify songs playing around you with audio recording and the AudD API

This tutorial builds a music recognition feature similar to Shazam. When the user taps a button, the app records 5 seconds of ambient audio, sends it to a music recognition API, and displays the identified song with album art, title, artist, and a link to listen on Spotify. The recording uses a Custom Action wrapping the record Flutter package. The API call goes through a Cloud Function to keep your API key secure. A Lottie animation provides visual feedback during the listening phase. Every successful recognition is saved to Firestore, giving users a browseable history of songs they have identified.

## Before you start

- A FlutterFlow project with Firebase/Firestore connected
- Firebase Blaze plan for Cloud Functions
- An AudD API key (free tier allows 300 requests/month at audd.io)
- FlutterFlow Pro plan for Custom Actions and Custom Widgets
- A Lottie animation file for the listening pulse effect (freely available on lottiefiles.com)

## Step-by-step guide

### 1. Set up the Firestore collection and AudD API credentials

Create a Firestore collection called recognized_tracks with fields: userId (String), title (String), artist (String), album (String), albumArtUrl (String), spotifyUrl (String), recognizedAt (Timestamp). Next, sign up at audd.io and copy your API token. In your Firebase project, store the AudD token as an environment variable for Cloud Functions using firebase functions:config:set audd.token='your_token_here'. Never put the API key in FlutterFlow client code — it must stay server-side in the Cloud Function. Create a composite index on recognized_tracks for userId (ascending) and recognizedAt (descending) to support the history query.

**Expected result:** The recognized_tracks collection exists in Firestore. The AudD API token is stored securely as a Cloud Function environment variable.

### 2. Build the audio recording Custom Action

Add the record package to your FlutterFlow project dependencies (record: ^5.0.0 in Custom Code settings). Create a Custom Action called recordAudioClip that returns a String (the base64-encoded audio data). Inside the action, initialize a Record instance, check and request microphone permission using Record.hasPermission, then start recording with await record.start(encoder: AudioEncoder.aacLc, samplingRate: 44100). Use a Future.delayed of 5 seconds, then stop the recording with await record.stop(). Read the recorded file bytes and convert to base64 with base64Encode. Return the base64 string. On the main recognition page, wire this Custom Action to the Recognize button's On Tap Action Flow. Store the returned base64 string in a Page State variable called audioData.

```
// Custom Action: recordAudioClip
// Return Type: String (base64-encoded audio)

import 'dart:convert';
import 'dart:io';
import 'package:record/record.dart';

Future<String> recordAudioClip() async {
  final record = AudioRecorder();
  
  if (!await record.hasPermission()) {
    throw Exception('Microphone permission denied');
  }
  
  final dir = await getTemporaryDirectory();
  final path = '${dir.path}/recognition_clip.m4a';
  
  await record.start(
    const RecordConfig(
      encoder: AudioEncoder.aacLc,
      sampleRate: 44100,
      bitRate: 128000,
    ),
    path: path,
  );
  
  await Future.delayed(const Duration(seconds: 5));
  final result = await record.stop();
  
  if (result == null) return '';
  final bytes = await File(result).readAsBytes();
  return base64Encode(bytes);
}
```

**Expected result:** Tapping the record button captures 5 seconds of audio and stores the base64-encoded data in Page State. Microphone permission is requested on first use.

### 3. Deploy the Cloud Function for AudD API recognition

Create a Cloud Function called recognizeSong that accepts an HTTP POST request with the base64 audio data in the request body. The function decodes the base64 string back to bytes, sends a multipart POST request to the AudD API endpoint (https://api.audd.io/) with the audio file and your API token. Parse the JSON response to extract the song title, artist, album, release date, and any available Spotify or Apple Music links. Return the parsed result as a JSON response to the FlutterFlow client. Handle error cases: if the API returns no match, return a structured response with a recognized field set to false. If the API returns an error (invalid token, rate limit), return an appropriate error message. In FlutterFlow, create an API Call definition pointing to your Cloud Function URL, with the request body containing the audioData base64 string.

```
// Cloud Function: recognizeSong
const functions = require('firebase-functions');
const fetch = require('node-fetch');
const FormData = require('form-data');

exports.recognizeSong = functions.https.onCall(async (data) => {
  const { audioBase64 } = data;
  const token = functions.config().audd.token;
  const audioBuffer = Buffer.from(audioBase64, 'base64');

  const form = new FormData();
  form.append('api_token', token);
  form.append('file', audioBuffer, {
    filename: 'clip.m4a',
    contentType: 'audio/mp4',
  });
  form.append('return', 'spotify');

  const response = await fetch('https://api.audd.io/', {
    method: 'POST',
    body: form,
  });
  const result = await response.json();

  if (result.status === 'success' && result.result) {
    return {
      recognized: true,
      title: result.result.title,
      artist: result.result.artist,
      album: result.result.album,
      albumArtUrl: result.result.spotify?.album?.images?.[0]?.url || '',
      spotifyUrl: result.result.spotify?.external_urls?.spotify || '',
    };
  }
  return { recognized: false };
});
```

**Expected result:** The Cloud Function accepts base64 audio, calls the AudD API, and returns structured song data including title, artist, album art URL, and Spotify link.

### 4. Design the recognition page with Lottie listening animation and result card

Create a page called MusicRecognition. Center the layout with a Column, mainAxisAlignment center. First child: a Lottie Animation widget loaded from a pulse/ripple animation JSON file (upload to your FlutterFlow assets or use a URL from lottiefiles.com). Set the Lottie widget size to 200x200 and wrap it in a Conditional Visibility container that shows only when a Page State variable isListening is true. Below the Lottie, add a large circular Container (width 120, height 120, borderRadius 60) with a solid primary color background, containing an Icon (Icons.mic, size 48, white). On Tap Action Flow: set isListening to true, call the recordAudioClip Custom Action, store result in audioData Page State, call the recognizeSong API with audioData, store the API response in Page State variables (title, artist, albumArtUrl, spotifyUrl, recognized), set isListening to false. Below the button, add a result card wrapped in Conditional Visibility (show when recognized is true): a Container with rounded corners holding a Row with an Image widget (Network Image bound to albumArtUrl, 80x80, borderRadius 8) and a Column with title in titleMedium bold, artist in bodyMedium secondary, and album in bodySmall. Below the card, add a Button labeled 'Listen on Spotify' that triggers a Launch URL action with the spotifyUrl. Add another Conditional Visibility container for the not-recognized state showing 'Song not recognized — try again' with a Retry button.

**Expected result:** The page shows a large mic button. During recognition, a Lottie pulse animation plays. On success, a result card displays album art, song title, artist, and a Spotify link. On failure, a retry message appears.

### 5. Save recognized tracks and build the listening history page

After a successful recognition (recognized is true), add an action in the Action Flow to create a Firestore document in recognized_tracks with userId set to currentUserUid, title, artist, album, albumArtUrl, spotifyUrl from the Page State variables, and recognizedAt set to the server timestamp. Create a second page called ListeningHistory. Add a Backend Query on the page: query recognized_tracks where userId equals currentUserUid, ordered by recognizedAt descending. Display results in a ListView. Each list item is a Row containing an Image (albumArtUrl, 56x56, rounded), a Column with title in titleSmall and artist in bodySmall, and a trailing Text showing the relative time (use a Custom Function timeAgo that returns strings like '2 min ago', '3 hours ago', 'Yesterday'). On tap of any list item, trigger a Launch URL action with the spotifyUrl. Add a BottomNavigationBar or AppBar action to switch between the MusicRecognition and ListeningHistory pages. Add an empty state widget for users with no history yet: a centered Column with an Icon (Icons.music_off), a Text saying 'No songs recognized yet', and a Button navigating to the recognition page.

```
// Custom Function: timeAgo
// Return Type: String
// Parameters: timestamp (DateTime)

String timeAgo(DateTime timestamp) {
  final now = DateTime.now();
  final diff = now.difference(timestamp);
  if (diff.inMinutes < 1) return 'Just now';
  if (diff.inMinutes < 60) return '${diff.inMinutes} min ago';
  if (diff.inHours < 24) return '${diff.inHours} hours ago';
  if (diff.inDays < 7) return '${diff.inDays} days ago';
  return '${(diff.inDays / 7).floor()} weeks ago';
}
```

**Expected result:** Every recognized song is saved to Firestore. The ListeningHistory page shows a scrollable list of past recognitions with album art, song details, and relative timestamps.

## Complete code example

File: `Music Recognition Feature Architecture`

```text
Firestore Data Model:
└── recognized_tracks/{auto-id}
    ├── userId: String
    ├── title: String (Shape of You)
    ├── artist: String (Ed Sheeran)
    ├── album: String (Divide)
    ├── albumArtUrl: String (https://i.scdn.co/...)
    ├── spotifyUrl: String (https://open.spotify.com/track/...)
    └── recognizedAt: Timestamp

Cloud Function:
  recognizeSong — HTTP callable, accepts base64 audio,
    calls AudD API, returns {recognized, title, artist, album,
    albumArtUrl, spotifyUrl}

Custom Action:
  recordAudioClip — record 5s audio via record package,
    return base64-encoded string

Custom Functions:
  timeAgo(timestamp) → relative time string

Page: MusicRecognition
├── Column (center)
│   ├── LottieAnimation (pulse ripple, 200x200)
│   │   └── Conditional Visibility: isListening == true
│   ├── Container (circular, 120x120, primary color)
│   │   └── Icon (mic, 48, white)
│   │   └── On Tap:
│   │       1. Set isListening = true
│   │       2. recordAudioClip → audioData
│   │       3. API Call: recognizeSong(audioData)
│   │       4. Store response in Page State
│   │       5. Set isListening = false
│   │       6. If recognized → Create recognized_tracks doc
│   ├── Result Card (Conditional: recognized == true)
│   │   └── Row
│   │       ├── Image (albumArtUrl, 80x80)
│   │       └── Column: title + artist + album
│   ├── Button (Listen on Spotify → Launch URL)
│   └── Not Recognized (Conditional: recognized == false)
│       └── Text + Retry Button

Page: ListeningHistory
├── Backend Query: recognized_tracks, userId == currentUser,
│   orderBy recognizedAt DESC
├── ListView
│   └── Row: Image (56x56) + Column (title, artist) + timeAgo
│       └── On Tap → Launch URL (spotifyUrl)
└── Empty State: Icon + Text + Navigate to Recognition
```

## Common mistakes

- **Recording more than 10 seconds of audio for recognition** — Music recognition APIs like AudD and ACRCloud need only 3-10 seconds of audio to identify a song. Recording longer clips wastes mobile bandwidth on upload, increases Cloud Function processing time, and adds unnecessary latency. Users expect near-instant results like Shazam. Fix: Record exactly 5 seconds for the optimal balance of recognition accuracy and speed. The AudD API documentation recommends 3-10 seconds, and 5 seconds consistently produces accurate matches.
- **Calling the AudD API directly from FlutterFlow client code** — Placing your API token in a FlutterFlow API Call exposes it in the app binary. Anyone who decompiles the app or inspects network traffic can extract and abuse your token, burning through your API quota or incurring unexpected charges. Fix: Always route the API call through a Cloud Function. The Cloud Function stores the API token as a server-side environment variable that never reaches the client. FlutterFlow calls your Cloud Function, which calls AudD.
- **Not handling the microphone permission denial gracefully** — If the user denies microphone access, the record package throws an exception. Without error handling, the app shows a generic error or crashes. Users do not understand why the feature is broken. Fix: Check Record.hasPermission before starting. If denied, show a friendly SnackBar explaining that microphone access is required and provide a button that opens the device settings using a Launch URL action with the app settings deep link.
- **Not showing any feedback during the 5-second recording and API call** — The total round-trip (5s recording + 2-3s API call) takes 7-8 seconds. Without visual feedback, users think the app is frozen and tap the button repeatedly, creating duplicate requests. Fix: Use the isListening Page State variable to show the Lottie pulse animation during recording and disable the mic button. Add a Text widget below the animation showing 'Listening...' to make the state explicit.

## Best practices

- Record exactly 5 seconds of audio for the optimal speed/accuracy tradeoff with music recognition APIs
- Always route API calls through a Cloud Function to keep your AudD or ACRCloud API token server-side
- Use a Lottie animation for the listening state — it provides clear visual feedback and looks professional
- Save every successful recognition to Firestore immediately so the user never loses a result
- Handle the not-recognized case with a friendly message and prominent retry button instead of a generic error
- Add an empty state to the listening history page for new users who have not recognized any songs yet
- Use the timeAgo Custom Function for human-readable timestamps instead of raw date-time values
- Test with real ambient music, not silence — the API returns no match for quiet recordings

## Frequently asked questions

### How much does the AudD API cost?

AudD offers a free tier with 300 requests per month, which is sufficient for development and small apps. Paid plans start at $7/month for 3,000 requests. ACRCloud is an alternative with a similar free tier. For most consumer apps, the free tier covers testing and the first paid tier handles moderate production traffic.

### Can I use ACRCloud instead of AudD?

Yes. ACRCloud works similarly — you send audio bytes and receive song metadata. The Cloud Function would call ACRCloud's identify endpoint instead of AudD's. The response format differs, so you would adjust the JSON parsing. ACRCloud generally has slightly better recognition accuracy for non-English music.

### Does the recording work on both iOS and Android?

Yes, the record package supports both platforms. On iOS, you need to add the NSMicrophoneUsageDescription key to your Info.plist (FlutterFlow handles this in Settings). On Android, the RECORD_AUDIO permission must be declared in AndroidManifest.xml. FlutterFlow adds this automatically when you use audio-related packages.

### What happens if the environment is too noisy for recognition?

Music recognition APIs are designed to work in noisy environments — they use audio fingerprinting that is robust to background noise. However, if the music is too quiet relative to ambient noise (e.g., a loud cafe with soft background music), recognition may fail. Show the not-recognized state with a tip suggesting the user move closer to the audio source.

### Can I add a preview playback of the recognized song?

Yes, if the API returns a preview URL (Spotify provides 30-second preview URLs for most tracks). Add an AudioPlayer Custom Widget or use FlutterFlow's built-in audio playback action to play the preview URL. Display a play/pause button on the result card.

### Can RapidDev help build a music app with recognition features?

Yes. A full music app with recognition, playlist management, social sharing, and streaming integration requires custom audio processing, multiple API integrations, and real-time features. RapidDev can architect and build the complete audio pipeline and backend infrastructure beyond what FlutterFlow's visual builder handles alone.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-set-up-a-music-recognition-feature-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-set-up-a-music-recognition-feature-in-flutterflow
