A toast message in Flutter is a brief, non-modal notification that appears temporarily on the screen to provide simple feedback about an operation without interrupting the user experience. These messages automatically fade away after a short duration, making them ideal for displaying confirmation messages, status updates, or other transient information.
Unlike dialogs or snackbars that may require user interaction or occupy significant screen space, toast messages are designed to be minimally intrusive while still conveying important information. They typically appear as small, rounded rectangles containing text, although they can be extensively customized in Flutter.
Toast messages play a crucial role in creating intuitive and responsive user interfaces. Here's why they're important:
Implementing effective toast messages in your Flutter application can significantly enhance user satisfaction by keeping them informed without disrupting their workflow.
Flutter does not include a built-in toast widget in its core library. However, several options are available for implementing toast messages in your Flutter applications:
The most popular solution is the fluttertoast package, which provides a simple, customizable toast implementation that works across platforms.
To get started with the fluttertoast package, add it to your pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
fluttertoast: ^8.2.1 # Use the latest version
After adding the dependency, run:
flutter pub get
Now you can use the Fluttertoast.showToast()
method to display toast messages:
import 'package:fluttertoast/fluttertoast.dart';
// Basic usage
Fluttertoast.showToast(
msg: \"This is a Flutter Toast\",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1,
backgroundColor: Colors.black,
textColor: Colors.white,
fontSize: 16.0
);
Another option is the [toast](https://pub.dev/packages oast) package, which offers similar functionality:
dependencies:
flutter:
sdk: flutter
toast: ^0.3.0 # Use the latest version
Alternatively, you can create toast-like behavior using Flutter's built-in SnackBar
widget:
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('This is a toast-like message'),
duration: Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.all(20),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
);
For maximum flexibility, you can create a completely custom toast widget:
void showCustomToast(BuildContext context, String message) {
OverlayState? overlayState = Overlay.of(context);
OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(
builder: (context) => Positioned(
bottom: 50.0,
left: 10.0,
right: 10.0,
child: Material(
elevation: 10.0,
borderRadius: BorderRadius.circular(10),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(10),
),
child: Text(
message,
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
),
),
),
);
overlayState?.insert(overlayEntry);
Future.delayed(Duration(seconds: 2), () {
overlayEntry.remove();
});
}
One of the strengths of using Flutter toast messages is the extensive customization options available. Let's explore these options in detail:
The fluttertoast package allows you to control where your toast appears on the screen using the gravity
parameter:
// Top position
Fluttertoast.showToast(
msg: \"Toast at the top\",
gravity: ToastGravity.TOP
);
// Center position
Fluttertoast.showToast(
msg: \"Toast in the center\",
gravity: ToastGravity.CENTER
);
// Bottom position (default)
Fluttertoast.showToast(
msg: \"Toast at the bottom\",
gravity: ToastGravity.BOTTOM
);
You can also set custom offsets for more precise positioning:
Fluttertoast.showToast(
msg: \"Custom positioned toast\",
gravity: ToastGravity.TOP,
positionedToastBuilder: (context, child) {
return Positioned(
top: 100.0,
left: 20.0,
right: 20.0,
child: child,
);
}
);
Control how long your toast message appears on screen:
// Short duration
Fluttertoast.showToast(
msg: \"Quick toast\",
toastLength: Toast.LENGTH_SHORT // ~2 seconds
);
// Long duration
Fluttertoast.showToast(
msg: \"Longer toast\",
toastLength: Toast.LENGTH_LONG // ~3.5 seconds
);
// Custom duration (iOS and web only)
Fluttertoast.showToast(
msg: \"Custom duration toast\",
timeInSecForIosWeb: 4 // 4 seconds for iOS and web
);
Customize the appearance of your toast messages:
Fluttertoast.showToast(
msg: \"Styled toast message\",
backgroundColor: Colors.purple,
textColor: Colors.white,
fontSize: 18.0,
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.BOTTOM
);
For even more advanced styling, you can use a custom toast widget implementation as shown earlier.
While basic toast messages typically contain only text, custom implementations can include more complex content:
void showRichToast(BuildContext context) {
OverlayState? overlayState = Overlay.of(context);
OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(
builder: (context) => Positioned(
bottom: 50.0,
left: 20.0,
right: 20.0,
child: Material(
elevation: 10.0,
borderRadius: BorderRadius.circular(15),
child: Container(
padding: EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
border: Border.all(color: Colors.grey.shade300),
),
child: Row(
children: [
Icon(Icons.check_circle, color: Colors.green, size: 30),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
\"Success!\",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
Text(
\"Your item has been added to the cart.\",
style: TextStyle(color: Colors.grey.shade700),
),
],
),
),
],
),
),
),
),
);
overlayState?.insert(overlayEntry);
Future.delayed(Duration(seconds: 3), () {
overlayEntry.remove();
});
}
When implementing toast messages in Flutter, it's important to be aware of platform-specific behaviors and considerations:
On Android, the fluttertoast package uses the native Android Toast API, which has certain limitations:
For Android, ensure you've added the necessary permissions in your AndroidManifest.xml
:
<uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\" />
On iOS, the fluttertoast package implements a custom overlay system since iOS doesn't have a native toast equivalent:
timeInSecForIosWeb
parameterFor web deployments:
timeInSecForIosWeb
parameter to control durationWhen multiple toast messages need to be displayed in succession, implementing a queue system prevents overlapping:
class ToastService {
static final ToastService _instance = ToastService._internal();
factory ToastService() => _instance;
ToastService._internal();
final List<String> _toastQueue = [];
bool _isShowingToast = false;
void showToast(String message) {
_toastQueue.add(message);
if (!_isShowingToast) {
_processQueue();
}
}
Future<void> _processQueue() async {
if (_toastQueue.isEmpty) {
_isShowingToast = false;
return;
}
_isShowingToast = true;
String message = _toastQueue.removeAt(0);
Fluttertoast.showToast(msg: message);
// Wait for toast to finish displaying
await Future.delayed(Duration(seconds: 3));
// Process next toast in queue
_processQueue();
}
}
For scenarios where user interaction with toast messages is required:
void showInteractiveToast(BuildContext context) {
OverlayState? overlayState = Overlay.of(context);
OverlayEntry? overlayEntry;
overlayEntry = OverlayEntry(
builder: (context) => Positioned(
bottom: 50.0,
left: 20.0,
right: 20.0,
child: Material(
elevation: 10.0,
borderRadius: BorderRadius.circular(15),
child: Container(
padding: EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
),
child: Row(
children: [
Expanded(
child: Text(\"Item deleted\"),
),
TextButton(
onPressed: () {
// Undo action
overlayEntry?.remove();
overlayEntry = null;
},
child: Text(\"UNDO\", style: TextStyle(color: Colors.blue)),
),
],
),
),
),
),
);
overlayState?.insert(overlayEntry);
Future.delayed(Duration(seconds: 5), () {
if (overlayEntry != null) {
overlayEntry?.remove();
overlayEntry = null;
}
});
}
Enhance user experience with smooth animations:
void showAnimatedToast(BuildContext context, String message) {
OverlayState? overlayState = Overlay.of(context);
OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(
builder: (context) => Positioned(
bottom: 50.0,
left: 20.0,
right: 20.0,
child: AnimatedOpacity(
duration: Duration(milliseconds: 300),
opacity: 1.0,
curve: Curves.easeIn,
child: SlideTransition(
position: Tween<Offset>(
begin: Offset(0, 1),
end: Offset(0, 0),
).animate(CurvedAnimation(
parent: AnimationController(
duration: Duration(milliseconds: 400),
vsync: Navigator.of(context),
)..forward(),
curve: Curves.fastOutSlowIn,
)),
child: Material(
elevation: 10.0,
borderRadius: BorderRadius.circular(15),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(15),
),
child: Text(
message,
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
),
),
),
),
),
);
overlayState?.insert(overlayEntry);
Future.delayed(Duration(seconds: 3), () {
overlayEntry.remove();
});
}
Different state management approaches can affect how you implement toast messages:
class NotificationService with ChangeNotifier {
void showToast(String message) {
Fluttertoast.showToast(msg: message);
}
}
// Usage
final notificationService = Provider.of<NotificationService>(context, listen: false);
notificationService.showToast(\"Item saved successfully\");
// Event
abstract class NotificationEvent {}
class ShowToastEvent extends NotificationEvent {
final String message;
ShowToastEvent(this.message);
}
// Bloc
class NotificationBloc extends Bloc<NotificationEvent, NotificationState> {
NotificationBloc() : super(NotificationInitial()) {
on<ShowToastEvent>((event, emit) {
Fluttertoast.showToast(msg: event.message);
emit(ToastShownState(event.message));
});
}
}
// Usage
BlocProvider.of<NotificationBloc>(context).add(ShowToastEvent(\"Operation completed\"));
class ToastController extends GetxController {
void showToast(String message) {
Fluttertoast.showToast(msg: message);
}
}
// Usage
final controller = Get.find<ToastController>();
controller.showToast(\"Profile updated\");
void submitForm() async {
try {
await apiService.submitData(formData);
Fluttertoast.showToast(
msg: \"Form submitted successfully\",
backgroundColor: Colors.green,
textColor: Colors.white
);
} catch (e) {
Fluttertoast.showToast(
msg: \"Error submitting form: ${e.toString()}\",
backgroundColor: Colors.red,
textColor: Colors.white
);
}
}
void monitorNetworkStatus() {
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
if (result == ConnectivityResult.none) {
Fluttertoast.showToast(
msg: \"No internet connection\",
backgroundColor: Colors.orange,
textColor: Colors.white
);
} else {
Fluttertoast.showToast(
msg: \"Back online\",
backgroundColor: Colors.green,
textColor: Colors.white
);
}
});
}
void showFeatureTip(String feature) {
if (!prefs.getBool('tip_shown_$feature') ?? false) {
Fluttertoast.showToast(
msg: \"Try our new $feature feature!\",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER
);
prefs.setBool('tip_shown_$feature', true);
}
}