Flutter interactions that feel right.
Crafted by
IldebertoLatest Discoveries
Auto-Blur Privacy Shield for App Switcher
Infinite 2D Gallery
Hux UI: State of the Art Design System
Swipe Actions with Dismissible
Animated Like Button
Dynamic Moving Backgrounds
Dynamic Color Extraction (Spotify Style)
Interactive Gallery with Pinch & Zoom
Thinking Orbs Animation
Interactive 3D Globe with GoodMap
Expandable FAB
Easy Wave Animations with `wave`
Add Custom Actions to Keyboard
No Material? Go with Forui.
Regretted it? Undo the action
Liquid Swipe Onboarding
Animated Emojis
Biometric Privacy Shield 🛡️
When building banking, crypto, or messaging apps, security is paramount. A common leak of private data happens when the user swipes up to enter the OS Multitasking/App Switcher. The OS takes a “screenshot” of the app to display in the thumbnail, potentially exposing account balances or private messages to anyone looking over the user’s shoulder.
In this tutorial, we fix this by injecting a frosted glass blur the millisecond the app goes into the background.
The Secret: WidgetsBindingObserver
We don’t need any external packages to achieve this. Flutter provides a native way to listen to OS lifecycle events through the WidgetsBindingObserver mixin.
By observing the didChangeAppLifecycleState, we can detect exactly when the OS triggers the inactive or paused state.
AppLifecycleState.inactive: The app is still visible but cannot receive input (e.g. the user just started swiping up to enter the App Switcher on iOS).AppLifecycleState.paused: The app is fully in the background.
The Implementation
When the state changes to inactive, we instantly set a boolean _isBackgrounded to true.
In our build method, we wrap the entire screen in a Stack. If _isBackgrounded is true, we render a Positioned.fill overlay with a BackdropFilter set to ImageFilter.blur(sigmaX: 15, sigmaY: 15).
This guarantees that the OS screenshot taken for the app switcher will only capture the beautiful, frosted privacy shield!
Adaptive Design with Dynamic Colors 🎨
One of the most luxurious details of modern music apps (like Apple Music and Spotify) is how the entire interface morphs to embrace the album art you are listening to. The background, the gradients, and even the play buttons magically change color to match.
In this tutorial, we replicate this premium architecture by building a reactive Album View using the palette_generator_plus package.
Why “Plus”?
The original Google package (palette_generator) has been discontinued. We updated our architecture to use the modern, community-maintained successor, palette_generator_plus, which features optimized performance (Isolates) and color extraction perfectly tuned for Material 3.
The Extraction Flow
The magic happens in 3 simple steps inside our _extractColors() function:
- Loading: We create a
NetworkImagepointing to our album cover (a high-quality neon image from Unsplash). - Analytical Clustering: We pass this image to
PaletteGenerator.fromImageProvider. The package scans the pixels and clusters the colors, returning a Palette. - Color Injection: We grab the strongest, most vibrant color using
paletteGenerator.dominantColor?.color.
UX Bonus (Accessibility)
What if the extracted color is a very light white, causing our text to become unreadable? The paletteGenerator handles the mathematical accessibility for us! We simply read the titleTextColor property it provides to guarantee our track title has perfect, readable contrast against the dynamically generated background gradient.
All of this is wrapped inside an AnimatedContainer and an AnimatedDefaultTextStyle, making the UI transition like silk the moment the image finishes loading.
Native Keyboard Mastery ⌨️
One of the biggest differentiators of premium apps is how they handle typing interactions. Relying on the standard OS keyboard covers the basics, but elite developers inject custom logic directly into the keyboard space.
Using the latest version of the keyboard_actions package (v5+), we didn’t just solve the classic iOS problem (the missing “Done” button on numeric keypads), we went further by building Quick Actions.
What We Built
By wrapping the screen in the KeyboardActions widget, we inject an overlay perfectly aligned above the system keyboard:
-
Numeric Field (iOS Fix): We added the famous blue “Done” button to dismiss the numeric keypad (something Apple doesn’t provide natively on the iPhone numeric keyboard). We also enabled
displayArrows, allowing users to jump between inputs without tapping the screen. -
Generic Text Field (Quick Actions): Here is the magic! If the user opens the keyboard on the notes field, the overlay now displays dynamic buttons:
- A Hashtag (#) button that instantly injects the ‘#flutter’ tag into the TextEditingController’s string.
- A Mention (@) button to tag the ‘@equipa’.
- A red Clear button aligned to the far right (using a
Spacer()) to wipe all text in the blink of an eye.
This UX approach saves users dozens of taps in complex forms and note-taking apps.
Infinite 2D Gallery Engine 🗺️
Sometimes GridView and InteractiveViewer just aren’t enough when you want to build a truly unbounded canvas (like a map or an infinite photo grid) that feels completely native and handles natural scrolling momentum.
In this advanced tutorial, we build a Custom 2D Scrolling Engine from scratch, featuring a staggered honeycomb layout!
1. Natural Momentum Physics
To simulate realistic scrolling (flinging), we capture the velocity when the user lifts their finger in onScaleEnd. We then calculate a stopping distance and animate our offset using a curved animation (Curves.easeOutCubic) to mimic friction!
void _onScaleEnd(ScaleEndDetails details) {
final velocity = details.velocity.pixelsPerSecond;
final distance = velocity.distance * 0.15;
if (distance > 10) {
final direction = velocity / velocity.distance;
final endOffset = _offset + (direction * distance);
_animation = Tween<Offset>(begin: _offset, end: endOffset).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic)
);
_controller.forward(from: 0);
}
}
2. Pinch-to-Zoom centered on the Fingers
When scaling, if we simply multiply our items by _scale, the zoom will center on the top-left of the screen. To make it zoom exactly where the user is pinching (the focal point), we apply this vector math trick:
final newScale = (_baseScale * details.scale).clamp(0.3, 3.0);
final scaleRatio = newScale / _scale;
// Keep the focal point locked by shifting the offset
_offset = details.localFocalPoint - (details.localFocalPoint - _offset) * scaleRatio;
_scale = newScale;
3. The Staggered Math (Honeycomb Layout)
To make the gallery look organic rather than a rigid chess board, we shift every odd column downwards by exactly half a tile size (tileSize / 2).
// If the column index is an odd number, push the tile down by half its height
final double staggeredOffsetY = (col.abs() % 2 == 1) ? (tileSize / 2) : 0.0;
final double x = col * tileSize + _offset.dx;
final double y = row * tileSize + _offset.dy + staggeredOffsetY;
4. High Performance Rendering
A truly infinite grid would crash the app if we rendered every item. Instead, we calculate exactly how many columns and rows fit on the screen based on the current scale, and only generate the Positioned widgets for the visible tiles. This makes the gallery incredibly smooth, even if it spans millions of theoretical pixels!
Interactive Image Gallery 🖼️
A common requirement for modern apps is a photo gallery where users can click an image to view it full-screen, swipe between photos, and zoom in to see details.
In the past, developers had to rely on heavy third-party packages to achieve this. But Flutter now has a powerful native widget for this exact purpose: InteractiveViewer.
In this tutorial, we combine three powerful Flutter mechanics:
- 2D Infinite Canvas: We wrap a massive 20x20 image grid inside an
InteractiveViewerwithconstrained: false, allowing the user to pan in any direction (X and Y) and zoom out to see the whole “map” of images. HeroAnimations: For the seamless transition from the 2D Canvas to Full Screen.- Double-tap to Zoom: Custom math to zoom into a specific point in full screen.
The InteractiveViewer
The InteractiveViewer widget handles multi-touch gestures out of the box.
InteractiveViewer(
panEnabled: true,
scaleEnabled: true,
minScale: 1.0, // Prevent zooming out smaller than the screen
maxScale: 4.0, // Max zoom limit
child: Image.network(url),
)
Adding “Double-Tap to Zoom”
While pinch-to-zoom is automatic, users also expect to double-tap to zoom in and out. To achieve this, we capture the tap position using GestureDetector and use a TransformationController mixed with an AnimationController to smoothly animate the matrix scale!
// 1. Capture the exact coordinate the user tapped
void _handleDoubleTapDown(TapDownDetails details) {
_doubleTapDetails = details;
}
// 2. Animate the matrix to scale into that point
void _handleDoubleTap() {
final position = _doubleTapDetails!.localPosition;
final isZoomed = _transformationController.value.getMaxScaleOnAxis() > 1.0;
final endMatrix = isZoomed
? Matrix4.identity()
: Matrix4.identity()
..translate(-position.dx * 2, -position.dy * 2)
..scale(3.0);
_animation = Matrix4Tween(
begin: _transformationController.value,
end: endMatrix,
).animate(_animationController);
_animationController.forward(from: 0);
}
This ensures your app feels exactly like a native iOS or Android photo gallery, without adding a single external dependency!
The Blank Canvas: Flutter Beyond Material 🎨
Since recent Flutter updates, the framework has increasingly distanced itself from forcing Material Design down every developer’s throat. The core architecture has been modularized, proving once and for all: Flutter is not a Google App builder, it is a pure painting canvas.
If you are tired of the default Android-like aesthetic and want a sleek, modern, web-first look (heavily inspired by the legendary shadcn/ui), Forui is your best friend.
1. Dropping the Material Dependency
Take a look at the top of your dart file. Instead of this:
import 'package:flutter/material.dart'; // ❌ We don't need this anymore!
You can now strictly use the core widgets and your new design system:
import 'package:flutter/widgets.dart'; // ✅ Pure Flutter Core
import 'package:forui/forui.dart'; // ✅ Forui Design System
2. The F-Widgets
Forui replaces standard Material components with F-prefixed components that follow a strict, minimalistic design language.
FScaffoldinstead ofScaffoldFButtoninstead ofElevatedButtonFTextFieldinstead ofTextField
FButton(
onPress: () => print('Hello Forui!'),
child: const Text('Click me'),
)
3. The Power of Custom Themes
By wrapping your app in FTheme(data: FTheme.neutral.dark.touch, ...) inside a raw WidgetsApp, every single component instantly adapts to a perfectly crafted, high-contrast dark mode with beautiful rounded corners and subtle borders. No more fighting with ThemeData or unexpected semantic errors from MaterialApp!
4. More Information
To explore more about this incredible design system, check out the official links:
- Pub.dev: https://pub.dev/packages/forui
- GitHub: https://github.com/duobaseio/forui
Embrace the Blank Canvas era. Your apps are about to look much more premium.
Hux UI: The Future of Flutter Interfaces 🚀
If you’re looking for a state-of-the-art UI library that brings highly polished, animated, and consistent components to Flutter, Hux UI is exactly what you need.
Designed by the brilliant folks at LofiDesigner, Hux UI offers components that feel incredibly premium right out of the box, reducing the amount of time you spend tweaking padding, shadows, and hover states.
1. Why Hux UI?
Hux UI excels in bringing a modern and clean design language to your applications. It provides:
- Multiple variants for components out-of-the-box (e.g., primary, secondary, outline, ghost).
- Data visualization charts.
- Advanced Tab and Navigation structures.
- Beautiful dark-mode-first aesthetic.
2. Using Hux Components
Integrating Hux is straightforward. Replace standard Material components with their Hux equivalents.
// A beautifully styled button with variants
HuxButton(
variant: HuxButtonVariant.primary,
onPressed: () => print('Hello Hux!'),
child: const Text('Get Started'),
)
// A clean and modern input field
HuxInput(
hint: 'name@example.com',
)
3. The Power of Cards
HuxCard is one of the most versatile components in the system, automatically applying the correct background colors, borders, and shadows to ensure your content stands out without looking cluttered.
4. More Information
To explore the entire ecosystem of Hux UI, including their live web playground, check out the official links:
- Pub.dev: https://pub.dev/packages/hux
- Website/Live Demo: https://ui.thehuxdesign.com
- GitHub: https://github.com/lofidesigner/hux
If you want your app to look like a premium tool designed by a top-tier studio, Hux UI is your toolkit.
Orb Animations with orb_animations
Building dynamic, organic status indicators (like “Siri” or ChatGPT’s voice mode) from scratch using CustomPainter and custom Shaders can be complex and prone to performance issues. Thankfully, there is an incredibly lightweight package that solves exactly this: orb_animations.
This package is perfect for loading indicators, live visual states (like “recording audio”), and expressive UI feedback.
1. Installation
Simply add the package to your pubspec.yaml:
dependencies:
orb_animations: ^1.0.0
And run flutter pub get.
2. The Magic of ThinkingOrb
The main widget is called ThinkingOrb. It has no heavy external dependencies and uses a single internal Ticker to guarantee a constant 60fps (or 120fps) with minimal CPU usage.
Basic Example:
import 'package:orb_animations/orb_animations.dart';
ThinkingOrb(
state: OrbState.working, // The animation type
size: OrbSize.size64, // The preset size (64px or 20px)
speed: 1.0, // Speed multiplier
)
3. The Available States
The best part of this package is that it comes with several hand-tuned animation states right out of the box:
OrbState.working(Processing information)OrbState.searching(Looking for something)OrbState.solving(Logically solving a problem)OrbState.listening(Listening to user audio)OrbState.composing(Generating text/response)OrbState.shaping(Drawing or morphing shapes)
Accessibility Tip ♿️
This package is incredibly detailed: if the user has the “Reduced Motion” option enabled in their phone settings (iOS or Android), ThinkingOrb automatically detects this (via MediaQuery.disableAnimations) and draws only a static, deterministic frame without animation. This natively respects user accessibility settings!
Time Travel in Flutter ⏳
“It’s like taking a snapshot of the state of something you want to remember later… The great thing is that this ‘photo’ or ‘record’ doesn’t spoil ANYTHING or interfere with what’s happening at that moment.”
The Memento Pattern is a behavioral design pattern that allows you to save and restore the state of an object without exposing its internal details.
There is no better way to demonstrate this than a Drawing Application. Imagine trying to mathematically erase a specific brush stroke that intersects with 5 other strokes. It’s a nightmare. Instead, using the Memento pattern, we just take a “snapshot” of the canvas before drawing. If the user hits undo, we replace the entire canvas with the snapshot!
The Three Pillars of Memento
The pattern requires three specific components:
- Originator: The object whose state needs to be preserved (Our Canvas Controller).
- Memento: The “snapshot” object containing the state (The list of strokes).
- Caretaker: The manager that stores the history of Mementos (Using Undo/Redo Stacks).
Modern Dart Implementation
In modern Flutter applications, the best practice is to capture the entire state into an immutable Memento using List.unmodifiable().
1. The Immutable Memento
class CanvasMemento {
final List<DrawingStroke> strokes;
// ⚠️ CRITICAL: We use List.unmodifiable() to create a deep copy.
// If we just assigned the list, the Memento would point to the
// same memory address as the Canvas, breaking the pattern!
CanvasMemento(List<DrawingStroke> currentStrokes)
: strokes = List.unmodifiable(currentStrokes);
}
2. The Caretaker & Originator
We combine these into a ChangeNotifier to make it reactive. We use two Stacks: _undoStack and _redoStack.
Before any action (starting a new brush stroke, or clearing the board), we take a snapshot:
void _saveSnapshot() {
// Push current state to undo history
_undoStack.add(CanvasMemento(_strokes));
// Any new action invalidates the redo history
_redoStack.clear();
notifyListeners();
}
3. Restoring State (Undo)
To undo, we push our current state to the Redo stack, pop the last state from the Undo stack, and apply it.
void undo() {
if (_undoStack.isEmpty) return;
_redoStack.add(CanvasMemento(_strokes));
final memento = _undoStack.removeLast();
// Restore the snapshot!
_strokes = List.from(memento.strokes);
notifyListeners();
}
By applying this pattern, your drawing app can gracefully recover from bad strokes, accidental clears, or massive changes with a single tap!
Smooth Swipe Interactions 👈👉
Swipe gestures have become a core part of modern mobile navigation. Whether you are archiving an email, deleting a notification, or completing a task, the swipe interaction provides an intuitive, fluid experience.
Flutter makes this incredibly easy with a built-in widget: Dismissible. It handles all the gesture recognition, animations, and threshold logic out of the box!
1. The Core Concepts
To make any widget swipeable, you simply wrap it in a Dismissible. It requires two crucial properties:
key: This is absolutely mandatory. Flutter needs a unique key to know which widget was removed from the tree after the animation finishes.onDismissed: A callback where you must remove the underlying data from your list.
Dismissible(
key: Key(item.id),
onDismissed: (direction) {
// ⚠️ CRITICAL: Remove the item from your data source!
setState(() {
items.remove(item);
});
},
child: YourListItemWidget(),
)
2. Adding Colorful Backgrounds 🎨
A blank swipe isn’t very helpful. Users need visual feedback indicating what action will occur. You can configure the background (when swiping left-to-right) and the secondaryBackground (when swiping right-to-left).
Dismissible(
key: Key(item.id),
// Revealed when swiping Left -> Right
background: Container(
color: Colors.green,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 20),
child: const Icon(Icons.archive, color: Colors.white),
),
// Revealed when swiping Right -> Left
secondaryBackground: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
child: const Icon(Icons.delete, color: Colors.white),
),
// ...
)
3. Controlling the Direction
By default, the Dismissible allows swiping in both horizontal directions. You can restrict it using the direction property:
DismissDirection.endToStart(Only swipe left - useful for pure delete actions)DismissDirection.startToEnd(Only swipe right)DismissDirection.vertical(Swipe up/down)
Pro Tip 💡: Confirming Actions
Accidental swipes happen. You can prevent immediate deletion by using the confirmDismiss callback. If you return Future.value(true), the item is dismissed. If you return false (e.g. the user tapped “Cancel” on a dialog), the widget smoothly animates back into place!
The World at Your Fingertips 🌍
Standard 2D maps are functional, but when you want to show global connections, flight paths, or worldwide data distribution, nothing beats a 3D interactive globe.
The flutter_goodmap package (created specifically for advanced geospatial visualizations) allows you to render a highly performant 3D globe directly in Flutter.
1. Setting up GoodMapGlobe
The core widget for 3D rendering in this package is GoodMapGlobe. It provides a massive set of features right out of the box.
import 'package:goodmap/goodmap.dart';
GoodMapGlobe(
initialCenter: LatLng(-8.84, 13.23), // Centered on Luanda
initialZoom: 1.0,
// Show a beautiful dotted grid for a digital look
showDottedGrid: true,
dottedGridColor: Colors.white.withOpacity(0.3),
)
2. Drawing Connections (Arcs) ✈️
One of the most powerful features of GoodMapGlobe is the ability to draw 3D arcs between coordinates. This is perfect for showing routes, supply chains, or network connections.
You define GlobeArc objects and pass them to the arcs property:
final arcs = [
GlobeArc(
from: LatLng(-8.84, 13.23), // Luanda
to: LatLng(40.71, -74.01), // New York
dashed: true,
drawProgress: 1.0, // Animate this to make the arc grow!
),
];
3. Data Visualization (Heatmaps & Markers)
You can easily overlay data on the globe’s surface:
- Markers: Use
MarkerOptionsto place glowing, pulsing dots on specific coordinates (e.g.,pulse: true,pulseMaxRadius: 22). - Heatmaps: Pass
HeatmapOptionsto render density data directly on the curvature of the earth! You provide thepoints,weights, andradius.
4. Day / Night Cycle & Atmosphere 🌙
Want to make the globe photorealistic? GoodMapGlobe supports atmospheric scattering and real-time solar positioning!
GoodMapGlobe(
// ...
atmosphere: true, // Adds a glowing atmospheric rim
dateTime: DateTime.now().toUtc(), // Accurately renders day/night shadows based on the sun's real position!
)
By putting all these together, you get a breathtaking, interactive 3D globe that responds to user gestures and beautifully displays complex geospatial data!
Stunning Transitions with liquid_swipe 💧
Static carousels or standard swipe transitions for onboarding screens can sometimes feel boring. If you want to impress your users right from the first screen, the liquid_swipe package provides a gorgeous, interactive, and fluid transition effect.
In this tutorial, we will create a multi-page liquid swipe onboarding flow!
1. Installation
As verified on pub.dev, add the package to your pubspec.yaml:
dependencies:
liquid_swipe: ^3.1.0
(Always check for the latest version on pub.dev)
Then, run flutter pub get.
2. Using the LiquidSwipe Widget
Implementing it is incredibly straightforward. Instead of a PageView, we use LiquidSwipe and pass it a list of our full-screen containers (pages).
Here is the code setup for our stunning liquid screens:
import 'package:flutter/material.dart';
import 'package:liquid_swipe/liquid_swipe.dart';
class LiquidSwipeTutorialWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: LiquidSwipe(
// enableLoop allows swiping from the last page back to the first!
enableLoop: true,
// The type of liquid reveal animation (circularReveal or liquidReveal)
waveType: WaveType.liquidReveal,
pages: [
Container(
color: Colors.white,
child: Center(child: Text('Page 1', style: TextStyle(fontSize: 30))),
),
Container(
color: Colors.blueAccent.shade700,
child: Center(child: Text('Page 2', style: TextStyle(fontSize: 30, color: Colors.white))),
),
Container(
color: Colors.black87,
child: Center(child: Text('Page 3', style: TextStyle(fontSize: 30, color: Colors.white))),
),
],
),
);
}
}
3. Important Properties 🛠️
pages: A list ofWidgets that represent the pages. For the liquid effect to work best, these widgets should be full screen (like aContainerwith a solid color).enableLoop: A boolean that, when set totrue, allows the user to swipe from the final page directly to the first page in a continuous loop.waveType: Defines the style of the swipe wave. We usedWaveType.circularRevealwhich creates a perfect circle stretching outwards. You can also useWaveType.liquidReveal.
UX Tip ✨
Make sure that your pages have highly contrasting background colors (e.g., White -> Dark Blue -> Black). The liquid swipe effect thrives on stark color contrasts, making the transition look satisfying and dramatic!
Heartfelt Micro-Interactions 💙
Micro-interactions are the secret sauce to a great User Experience. The iconic “Twitter Heart” animation when liking a post provides immediate, joyful feedback to the user.
Thanks to the like_button package from fluttercandies, you can implement this exact animation with just a few lines of code, and fully customize the icons, colors, and particles!
1. Installation
As verified on pub.dev, add the dependency to your pubspec.yaml:
dependencies:
like_button: ^2.0.5 # Check pub.dev for the latest version
Run flutter pub get to install it.
2. Basic Usage
The package exposes the LikeButton widget. You can wrap any icon you want and it will automatically handle the tap gestures and the animation.
import 'package:flutter/material.dart';
import 'package:like_button/like_button.dart';
// Inside your widget's build method:
LikeButton(
size: 80,
likeBuilder: (bool isLiked) {
return Icon(
Icons.favorite,
color: isLiked ? Colors.lightBlueAccent : Colors.grey,
size: 80,
);
},
)
3. Customizing the Particles and Bubbles ✨
The real magic happens when you customize the expanding circle and the exploding bubbles to match your brand’s colors!
You can provide CircleColor and BubblesColor to tweak the exact hex values of the explosion:
LikeButton(
size: 80,
// Define the expanding ring's gradient
circleColor: const CircleColor(
start: Color(0xff00ddff),
end: Color(0xff0099cc)
),
// Define the exploding dots' colors
bubblesColor: const BubblesColor(
dotPrimaryColor: Color(0xff33b5e5),
dotSecondaryColor: Color(0xff0099cc),
),
likeBuilder: (bool isLiked) {
return Icon(
Icons.flutter_dash, // You can use ANY icon!
color: isLiked ? Colors.lightBlueAccent : Colors.grey,
size: 80,
);
},
)
Pro Tip 💡
The LikeButton has an asynchronous onTap callback. You can use it to make an API request to your backend. If the request fails, you can return false from the onTap function, and the button will gracefully revert its state without needing complex state management!
More Actions, Less Clutter ➕
A standard Floating Action Button (FAB) is great for single primary actions (like “Compose Email” or “Add Item”). But what if you have multiple important actions that the user might want to take?
Instead of crowding your UI, you can use a Speed Dial FAB. The flutter_expandable_fab package allows you to effortlessly create a FAB that animates into a list of multiple actions when tapped.
1. Installation
As recommended on pub.dev, add the package to your pubspec.yaml:
dependencies:
flutter_expandable_fab: ^2.1.0 # Ensure you check pub.dev for the latest version
Run flutter pub get in your terminal.
2. Setting up the ExpandableFab
Setting it up is simple. Inside your Scaffold, you replace your standard FAB with ExpandableFab.
Crucial Step: You must set the floatingActionButtonLocation to ExpandableFab.location for the package to properly measure and position the button.
import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
// Inside your Scaffold:
Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
type: ExpandableFabType.up, // The direction the children will pop out
children: [
FloatingActionButton(
onPressed: () {},
child: Icon(Icons.email),
),
FloatingActionButton(
onPressed: () {},
child: Icon(Icons.image),
),
],
),
body: Container(),
)
3. Customizing the Toggle Buttons 🛠️
By default, the package handles the open/close state. However, you can deeply customize what the button looks like when it’s closed and when it’s open.
In our example, we used DefaultFloatingActionButtonBuilder to display a menu icon (Icons.menu) when closed, and an “X” close icon (Icons.close) when open:
ExpandableFab(
openButtonBuilder: DefaultFloatingActionButtonBuilder(
fabSize: ExpandableFabSize.regular,
child: const Icon(Icons.menu),
),
closeButtonBuilder: DefaultFloatingActionButtonBuilder(
fabSize: ExpandableFabSize.regular,
child: const Icon(Icons.close),
),
children: [ ... ],
)
UI Tip ✨
You aren’t limited to ExpandableFabType.up! You can use ExpandableFabType.side to push the buttons horizontally, or even create a fan shape! Experiment with different layouts depending on where your FAB is positioned on the screen.
Express Yourself with animated_emoji 🎉
Static text emojis are standard, but if you want to add a layer of delight and polish to your app, animated emojis are the way to go!
The animated_emoji package allows you to effortlessly render Noto Animated Emojis in Flutter.
1. Installation
As verified in the official pub.dev documentation, add this to your pubspec.yaml:
dependencies:
animated_emoji: ^3.0.0 # Make sure to check pub.dev for the latest version
Then run flutter pub get.
2. Displaying Emojis
Using the package is incredibly simple. It behaves just like a standard widget but provides a smooth, looping animation by default.
Here is how you can render a grid of different animated emojis:
import 'package:flutter/material.dart';
import 'package:animated_emoji/animated_emoji.dart';
class AnimatedEmojiDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 16,
children: const [
AnimatedEmoji(AnimatedEmojis.rocket, size: 65),
AnimatedEmoji(AnimatedEmojis.partyingFace, size: 65),
AnimatedEmoji(AnimatedEmojis.joy, size: 65),
],
);
}
}
3. Customization & Skin Tones 🎨
The animated_emoji package supports a lot of flexibility!
- Skin Tones: You can easily change the skin tone of supported emojis using modifiers like
.dark,.medium, or.light.AnimatedEmoji(AnimatedEmojis.wave.mediumDark, size: 50) - Animation Control: You don’t want it to loop endlessly? Just pass
repeat: false.AnimatedEmoji(AnimatedEmojis.clown, repeat: false) - Controllers: You can even pass an
AnimationControllerto trigger the emoji animation only when a user taps or hovers over it!
Use this to add micro-interactions to empty states, chat apps, or success dialogs!
Breathe Life into Your App 🌌
A solid color background is functional, but an animated background can make your app feel alive. Whether it’s a calm flowing wave for a meditation app, or a cyberpunk aesthetic for a gaming dashboard, dynamic backgrounds set the mood.
The flutter_moving_background package is a highly optimized library that gives you exactly that, maintaining 60fps+ without draining the battery.
1. Installation
As verified on pub.dev, simply add this to your pubspec.yaml:
dependencies:
flutter_moving_background: ^0.2.1 # Always check pub.dev for the latest version
Then, run flutter pub get.
2. The Power of Presets
The package comes with several stunning presets that you can use instantly with zero configuration. You just wrap your content with it!
import 'package:flutter_moving_background/flutter_moving_background.dart';
// Sunset preset
MovingBackground.sunset(
child: YourContentWidget(),
);
// Cyberpunk preset
MovingBackground.cyberpunk(
child: YourContentWidget(),
);
3. Specialized Backgrounds 🌧️
Beyond the presets, the package exposes incredibly powerful specialized widgets.
WaveBackground: Creates overlapping, flowing waves. You can customize thewaveCount,amplitude, andfrequency.RainBackground: A parallax rain effect. You can controlnumberOfDrops,fallSpeed, and even add trails!BubbleBackground: Bouncing, blurred bubbles that float around. Perfect for modern, soft UI designs.ConstellationBackground: Connecting particles that form a constellation effect.
Example: Custom Waves
WaveBackground(
waveCount: 4,
speed: 1.2,
amplitude: 30.0,
colors: const [
Colors.blue,
Colors.teal,
Colors.cyan,
Colors.lightBlueAccent
],
backgroundColor: Colors.lightBlue.shade50,
child: Scaffold(
backgroundColor: Colors.transparent, // Let the waves show through!
body: Center(child: Text("Flowing...")),
),
)
Performance Tip ⚡️
The package is designed to be highly performant (it uses a single Ticker and CustomPainter). However, if you are rendering very complex screens on top of the background, ensure you use const constructors where possible so Flutter doesn’t unnecessarily rebuild your UI tree while the background repaints underneath!
Beautiful Waves without the Math 🌊
Building complex wave animations using CustomPainters and Math formulas can be time-consuming. If you want an incredible and fast result for backgrounds, headers, or visual effects, the wave package is the best choice!
In this mini-tutorial, we’ll create a multi-layered wave animation in just a few steps.
1. Installation
First, add the wave package to your pubspec.yaml file:
dependencies:
wave: ^0.2.2 # check for the latest version on pub.dev
Then, run flutter pub get in your terminal.
2. Configuring the WaveWidget
The secret of this package lies in the WaveWidget. It requires a configuration (config) where we define how each layer of the wave will behave.
In the code below, we use CustomConfig to create 4 layers of waves with different colors and speeds:
import 'package:flutter/material.dart';
import 'package:wave/wave.dart';
class WaveBackground extends StatelessWidget {
@override
Widget build(BuildContext context) {
return WaveWidget(
config: CustomConfig(
// Colors for each wave layer (from back to front)
colors: [
Colors.blue.shade100,
Colors.blueAccent.shade200,
Colors.lightBlueAccent.shade200,
Colors.black54,
],
// Speed of the wave in milliseconds. Less time = faster
durations: [3000, 4000, 5000, 6500],
// The base height of the wave relative to the container (0.0 to 1.0)
heightPercentages: [0.64, 0.66, 0.68, 0.70],
),
// The size the widget will occupy
size: const Size(double.infinity, double.infinity),
);
}
}
3. Understanding the Parameters 🛠️
colors: A list of colors. Each color represents one wave layer.durations: Controls the speed of each wave. The first wave (light blue) takes 3000ms to complete a cycle, while the last one (black) is slower, taking 6500ms. This contrast in speeds creates a realistic water effect!heightPercentages: Sets the base height for each wave.0.64means the wave starts at 64% of the screen height (from top to bottom).
UI Tip ✨
Try using gradients (gradients property instead of colors) in CustomConfig, or use colors with opacity (Colors.blue.withOpacity(0.5)) so the waves overlap smoothly and look beautifully translucent!