When to use Material Ribbon
Material Ribbon brings the sleek, badge‑like ribbons from Material 3 into Flutter with a set of ready‑to‑use widgets that blend seamlessly into any layout. The package supplies a `Ribbon` widget, a `RibbonBanner` for overlaying content, and a `RibbonClipper` for custom shapes, all built on top of Flutter's `CustomPainter` and animation framework. By handling the heavy lifting of sizing, positioning, and theming, it lets developers focus on the story they want to tell rather than the pixel‑perfect details of a ribbon. The widgets respect the current `ThemeData`, automatically adapting colors for light and dark modes, and they expose a small API for custom colors, corner radius, and animation curves.
When to use Material Ribbon? Any UI that needs to highlight a status, promotion, or badge can benefit from a ribbon. Think of e‑commerce product cards that show "Sale", "New", or "Limited Offer" labels, news tiles that need a "Breaking" tag, or onboarding screens that draw attention to a new feature. Because the widgets are built to be responsive, they scale gracefully from a phone screen to a large desktop monitor, making them ideal for adaptive layouts where the same code runs on multiple platforms. The package also integrates well with `LayoutBuilder` and `MediaQuery`, allowing you to hide or reposition ribbons based on screen size without extra boilerplate.
From an architectural perspective, Material Ribbon is a pure UI layer that does not impose any state‑management or backend constraints. It can be dropped into a clean architecture, MVVM, or any other pattern without affecting the domain or data layers. The ribbon's visibility and text can be driven by any state manager—`Provider`, `Riverpod`, `Bloc`, or even a simple `setState`. Because the widget is lightweight and stateless by default, it can be rebuilt frequently without performance penalties, and the built‑in animation controller ensures smooth entry and exit transitions.
Getting started is straightforward. After adding the dependency with `flutter pub add material_ribbon`, import the package and wrap the target widget with `RibbonBanner` or place a `Ribbon` inside a `Stack`. The default ribbon follows the Material 3 spec, but you can override the shape, elevation, and animation duration to match your brand. For production use, be mindful of accessibility: provide a meaningful `semanticLabel` and ensure sufficient contrast between the ribbon background and its text. Also, test the ribbon on all target platforms because the underlying `Canvas` rendering may behave slightly differently on web versus native. The package includes a `debugPaint` flag that draws the ribbon's bounding box, helping you fine‑tune layout during development.
Beginners will appreciate the concise example that demonstrates a product card with a "SALE" ribbon that animates in when the card becomes visible. More advanced users can combine `RibbonClipper` with custom `Path` logic to create diagonal or curved ribbons that match unique branding guidelines. The documentation covers common pitfalls, such as avoiding overflow in tight constraints and handling RTL languages. Overall, Material Ribbon fills a gap in the Flutter ecosystem by delivering a polished, Material‑3‑compliant ribbon solution that works everywhere Flutter runs.
Pros
- lightweight and stateless by default
- full Material‑3 compliance
- adaptive to all platforms
- customizable shape and animation
Watch outs
- limited to visual decoration – no built‑in data handling
- requires manual RTL handling for custom text
Setup notes
Add the dependency with the exact command: ``` flutter pub add material_ribbon ``` Then run `flutter pub get` and import the package: ```dart import 'package:material_ribbon/material_ribbon.dart'; ```
Requires Flutter 3.0 or newer. Works on Android, iOS, macOS, Windows, Linux, and web. The package respects Material 3 theming and adapts to dark mode automatically. No platform‑specific native code is used, so no additional setup is needed for each target.
```dart
import 'package:flutter/material.dart';
import 'package:material_ribbon/material_ribbon.dart';
class RibbonDemo extends StatelessWidget {
const RibbonDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Material Ribbon Demo')),
body: Center(
child: Stack(
alignment: Alignment.topRight,
children: [
Card(
elevation: 4,
child: SizedBox(width: 200, height: 120, child: const Center(child: Text('Product'))),
),
RibbonBanner(
message: 'SALE',
location: RibbonLocation.topEnd,
color: Colors.redAccent,
textStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
],
),
),
);
}
}
```