Adaptive Palette
Immersive fluid animated backgrounds from images with intelligent color extraction.
Create stunning, music app-style backgrounds that adapt to your images with smooth animations and vibrant colors.
Features
β¨ Fluid Animated Backgrounds - Layered image shaders with orbital motion and heavy blur π¨ Intelligent Color Extraction - Weighted k-means clustering for accurate palette generation π Smooth Transitions - Cross-fade between images with palette color tweening π« Corner Accent Glows - Radial gradients using extracted colors π Matte Treatment - Prevents harsh whites, ensures rich vibrant colors β‘ Performance Optimized - Instant fallback, background extraction, 60fps animations
Installation
dependencies:
adaptive_palette: ^3.0.0
Quick Start
Option 1: FluidBackground Widget (Recommended)
The easiest way - widget handles everything automatically:
import 'package:adaptive_palette/adaptive_palette.dart';
import 'package:flutter/material.dart';
class MusicPlayer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FluidBackground(
imageProvider: NetworkImage('https://example.com/album.jpg'),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text('Now Playing'),
),
body: YourContent(),
),
);
}
}
Option 2: Manual Color Extraction
For custom implementations where you need direct access to colors:
import 'dart:ui' as ui;
import 'package:adaptive_palette/adaptive_palette.dart';
// Load and extract
final ui.Image image = await loadImageFromProvider(
NetworkImage('https://example.com/album.jpg'),
);
final palette = await FluidPaletteExtractor.extract(image);
// Use colors
Container(color: palette.baseDark); // Dark base
Container(color: palette.accent1); // Top-left glow
Container(color: palette.accent2); // Top-right glow
Container(color: palette.accent3); // Bottom-left glow
Container(color: palette.accent4); // Bottom-right glow
FluidBackground Widget
Basic Usage
FluidBackground(
imageProvider: NetworkImage(albumUrl),
child: YourContent(),
)
Full Configuration
FluidBackground(
// Optional - shows matte fallback if null
imageProvider: imageUrl == null ? null : NetworkImage(imageUrl),
// Blur intensity (60-120 recommended)
blurSigma: 80,
// Dark overlay for text legibility (0.05-0.20 recommended)
overlayDarken: 0.10,
// Enable slow orbital motion
animate: true,
// Transition duration for palette changes
transitionDuration: Duration(milliseconds: 1400),
child: YourContent(),
)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
imageProvider |
ImageProvider? |
null |
Optional image to extract colors from |
child |
Widget |
required | Content to display on top |
blurSigma |
double |
80 |
Blur intensity (higher = softer) |
overlayDarken |
double |
0.10 |
Dark overlay opacity for legibility |
animate |
bool |
true |
Enable orbital motion animation |
transitionDuration |
Duration |
1400ms |
Palette transition duration |
FluidPalette Model
class FluidPalette {
final Color baseDark; // Dark base (lightness β€ 0.22)
final Color accent1; // Top-left glow
final Color accent2; // Top-right glow
final Color accent3; // Bottom-left glow
final Color accent4; // Bottom-right glow
}
Methods
// Fallback palette
FluidPalette.fallback()
// Interpolate between palettes
FluidPalette.lerp(paletteA, paletteB, 0.5)
// Copy with changes
palette.copyWith(accent1: newColor)
Complete Example
import 'package:adaptive_palette/adaptive_palette.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Music Player',
theme: ThemeData.dark(),
home: MusicPlayerPage(),
);
}
}
class MusicPlayerPage extends StatefulWidget {
@override
State<MusicPlayerPage> createState() => _MusicPlayerPageState();
}
class _MusicPlayerPageState extends State<MusicPlayerPage> {
int currentIndex = 0;
final albums = [
'https://picsum.photos/seed/album1/800/800',
'https://picsum.photos/seed/album2/800/800',
'https://picsum.photos/seed/album3/800/800',
];
void nextSong() => setState(() =>
currentIndex = (currentIndex + 1) % albums.length
);
@override
Widget build(BuildContext context) {
return FluidBackground(
imageProvider: NetworkImage(albums[currentIndex]),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text('Now Playing'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
albums[currentIndex],
width: 300,
height: 300,
),
),
SizedBox(height: 32),
Text('Song Title', style: TextStyle(fontSize: 28)),
SizedBox(height: 8),
Text('Artist Name', style: TextStyle(fontSize: 18)),
SizedBox(height: 32),
IconButton(
icon: Icon(Icons.skip_next),
iconSize: 48,
onPressed: nextSong,
),
],
),
),
),
);
}
}
How It Works
The FluidBackground widget creates immersive backgrounds through these steps:
- Instant Display - Shows matte gradient fallback immediately
- Background Extraction - Loads image and extracts FluidPalette
- Smooth Reveal - Cross-fades from fallback to extracted colors (1400ms)
- Fluid Shaders - Renders 4 layered ImageShaders with transforms:
- Layer 1: scale 1.35, rotation -0.08β0.10, opacity 0.55
- Layer 2: scale 0.95, rotation 0.12β-0.06, opacity 0.45
- Layer 3: scale 0.70, rotation -0.18β0.04, opacity 0.35
- Layer 4: scale 1.85, rotation 0.05β-0.12, opacity 0.25
- Heavy Blur - Applies 80Ο Gaussian blur for atmospheric effect
- Corner Glows - Adds 4 radial gradients (360Γ360px, 90Ο blur)
- Dark Overlay - Subtle dark layer ensures white text legibility
Color Extraction Algorithm
FluidPaletteExtractor uses advanced color science:
- Smart Sampling - Samples every 10th pixel, filters extremes
- Vibrancy Weighting - Weights by
(saturation Γ 0.75 + lightness Γ 0.25) - Center Proximity - Increases weight for pixels near center
- Weighted K-Means - Clusters into 6 centers (10 iterations)
- Perceptual Scoring - Ranks by
(vivid Γ 1.2 + mid-tone Γ 0.8) - Matte Treatment - Blends near-whites with gray (14% mix)
- Brightness Clamping - Limits lightness to 0.78 max
- Dark Base Enforcement - Ensures base β€ 0.22 lightness
Use Cases
Perfect for:
- π΅ Music player screens
- π¬ Video player backgrounds
- πΌοΈ Gallery detail pages
- π± App hero screens
- π¨ Media browsing UIs
- β¨ Immersive experiences
Tips & Best Practices
Visual Quality
- Blur intensity: 60-80 for subtle, 90-120 for dramatic
- Overlay darkness: 0.05-0.10 for vibrant, 0.15-0.20 for dramatic
- Animation: Disable (
animate: false) for battery savings - Transition speed: 1000-1500ms feels smooth
Performance
- Fast startup: Matte fallback shows instantly (0ms)
- Background loading: Image extraction doesn't block UI
- Smooth animations: 60fps orbital motion with GPU acceleration
- Memory efficient: Downsampled color sampling, shader caching
Common Patterns
No image available:
FluidBackground(
imageProvider: null, // Shows matte fallback only
child: YourContent(),
)
Conditional image:
FluidBackground(
imageProvider: imageUrl == null ? null : NetworkImage(imageUrl),
child: YourContent(),
)
Disable animation:
FluidBackground(
imageProvider: NetworkImage(url),
animate: false, // Save battery
child: YourContent(),
)
Examples
Check out the example directory for complete, runnable examples:
- fluid_background_example.dart - Full music player with playlist
Run the example:
cd example
flutter run -t lib/fluid_background_example.dart
Requirements
- Flutter SDK: >=3.0.0
- Dart SDK: >=3.0.0 <4.0.0
Migration from v2.x
Version 3.0 deprecates old APIs in favor of FluidBackground. All v2.x code continues to work but shows deprecation warnings.
Old (v2.x)
// Old Material Design theming API
final colors = await AdaptivePalette.fromImage(NetworkImage(url));
MaterialApp(theme: colors.toThemeData());
New (v3.0)
// New FluidBackground widget
FluidBackground(
imageProvider: NetworkImage(url),
child: Scaffold(
backgroundColor: Colors.transparent,
body: YourContent(),
),
)
// Or manual extraction
final image = await loadImageFromProvider(NetworkImage(url));
final palette = await FluidPaletteExtractor.extract(image);
Old widgets will be removed in v4.0.0:
- β
AdaptivePaletteβ βFluidPaletteExtractor - β
PaletteScopeβ βFluidBackground - β
AdaptiveImageOverlayβ βFluidBackground - β
AdaptiveGlowFrameβ βFluidBackground - β
AdaptiveGradientScaffoldβ βFluidBackground
Troubleshooting
Colors look washed out
- Reduce
overlayDarken(try 0.05) - Check source image quality
Background is too bright for white text
- Increase
overlayDarken(try 0.15-0.20)
Animation is choppy
- Reduce
blurSigma(try 60) - Disable animation:
animate: false
Image doesn't load
- Check network connectivity
- Verify image URL
- FluidBackground will show fallback automatically
License
MIT License - see LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Credits
Created by Hardik Jain
Built with advanced color extraction using weighted k-means clustering, perceptual scoring, and fluid shader layers.
Libraries
- adaptive_palette
- Immersive fluid animated backgrounds from images with intelligent color extraction.
- main