A production-quality Flutter app with homescreen widgets that displays an old-school sound wave visualizer for your currently-playing Spotify tracks.
- Spotify OAuth Integration: Secure PKCE authentication flow
- Real-time Audio Analysis: Visualizes audio features and analysis from Spotify Web API
- Multiple Visualizer Styles: Choose from oscilloscope, bars, waveform, and spectrum
- Home Screen Widgets: Android App Widget and iOS WidgetKit support
- Customizable Themes: Multiple color presets and dark/light themes
- Battery Efficient: Smart update intervals and optimized rendering
| Main App | Widget (Android) | Widget (iOS) |
|---|---|---|
- Go to the Spotify Dashboard
- Create a new app
- Note your Client ID
- In app settings, add these redirect URIs:
com.example.spotifyvisualizer://callbackhttp://localhost:8888/callback(for testing)
- Open
lib/src/core/services/spotify_service.dart - Replace
YOUR_SPOTIFY_CLIENT_IDwith your actual Spotify Client ID:
static const String clientId = 'your_actual_client_id_here';- Update
android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.spotify_visualizer">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application>
<!-- Main Activity -->
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.example.spotifyvisualizer" />
</intent-filter>
</activity>
<!-- Widget Provider -->
<receiver android:name=".SpotifyVisualizerWidgetProvider"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/spotify_visualizer_widget_info" />
</receiver>
</application>
</manifest>- Add URL scheme to
ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>spotify-auth</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.example.spotifyvisualizer</string>
</array>
</dict>
</array>- Configure App Groups in Xcode:
- Select your main app target
- Go to "Signing & Capabilities"
- Add "App Groups" capability
- Create group:
group.com.example.spotify_visualizer - Repeat for the widget extension target
flutter pub getflutter packages pub run build_runner buildflutter runflutter build appbundle --releaseflutter build ios --release- Long press on home screen
- Select "Widgets"
- Find "Spotify Visualizer"
- Drag to home screen
- Long press on home screen
- Tap the "+" button
- Search for "Spotify Visualizer"
- Select widget size and add
- SpotifyService: Handles OAuth, API calls, and token management
- VisualizerService: Processes audio analysis and generates waveforms
- StorageService: Secure token storage and app preferences
- WidgetService: Cross-platform widget updates and data sharing
- Oscilloscope: Retro CRT-style waveform with scan line
- Bars: Classic frequency bars with reflections
- Waveform: Filled waveform with progress indicator
- Spectrum: Frequency spectrum with color-coded bands
- Provider pattern for reactive state updates
- Automatic token refresh and error handling
- Efficient caching of audio analysis data
- Update Frequency: Limited by iOS WidgetKit (15-30 min intervals)
- Real-time Animation: Not supported; uses precomputed timeline entries
- Background Execution: Restricted; updates when app is active
- Battery Optimization: Updates limited on Android 12+
- Update Frequency: 15-30 minutes recommended for battery life
- Memory Usage: Simplified visualizations to reduce memory footprint
- Spotify Web API: 100 requests per minute per user
- Current Track Polling: Every 2 seconds when app is active
- Audio Analysis Caching: Cached per track to avoid re-fetching
- Token Storage: Uses platform secure storage (Keychain/EncryptedSharedPreferences)
- PKCE Flow: No client secret embedded in app
- Data Transmission: All API calls over HTTPS
- Local Data: Audio analysis cached locally, cleared on logout
Run unit tests:
flutter testRun widget tests:
flutter test test/widget/Run integration tests:
flutter drive --target=test_driver/app.dart-
Authentication Failed
- Verify Client ID is correct
- Check redirect URI matches Spotify app settings
- Ensure app has required scopes
-
No Audio Data
- Confirm music is playing in Spotify
- Check network connectivity
- Verify Spotify Premium subscription (required for some API endpoints)
-
Widget Not Updating
- Check background app refresh permissions
- Verify widget is added to home screen
- Restart device if widget appears frozen
-
iOS Widget Issues
- Ensure App Groups are configured correctly
- Check widget timeline generation in logs
- Verify shared UserDefaults access
Enable debug logging:
flutter run --debugCheck widget data (Android):
adb shell dumpsys appwidget- Spotify Developer Agreement compliance
- Privacy policy mentioning Spotify data usage
- App Store review guidelines compliance
- Widget functionality clearly described
- Test on multiple device sizes
- Verify widget installation flow
- Test offline behavior
- Validate authentication flow
- Performance testing with large playlists
- Memory usage optimization
- Battery usage testing
- Requires Spotify Premium for real-time playback state
- Audio analysis limited to tracks in Spotify catalog
- Widget updates subject to platform restrictions
- No offline visualization (requires network connectivity)
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
MIT License - see LICENSE file for details
For issues and questions:
- Check the troubleshooting section
- Search existing GitHub issues
- Create a new issue with detailed description and logs
Built with ❤️ using Flutter and the Spotify Web API