flare_rating 0.0.2
flare_rating: ^0.0.2 copied to clipboard
Animated star rating widget for Flutter with wave fill and particle burst effects. Supports half-star, haptics, and read-only mode.
import 'package:flare_rating/flare_rating.dart';
import 'package:flutter/material.dart';
void main() => runApp(const FlareRatingExampleApp());
class FlareRatingExampleApp extends StatelessWidget {
const FlareRatingExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'flare_rating demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.amber,
brightness: Brightness.dark,
),
home: const _HomePage(),
);
}
}
class _HomePage extends StatefulWidget {
const _HomePage();
@override
State<_HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<_HomePage> {
double _basicRating = 3.0;
double _halfRating = 2.5;
double _customRating = 0.0;
double _largeRating = 4.0;
@override
Widget build(BuildContext context) {
final tt = Theme.of(context).textTheme;
return Scaffold(
backgroundColor: const Color(0xFF121212),
appBar: AppBar(
backgroundColor: Colors.transparent,
title: const Text(
'flare_rating',
style: TextStyle(
fontWeight: FontWeight.w700,
letterSpacing: 1.2,
),
),
),
body: ListView(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
children: [
// ── Basic ──────────────────────────────────────────────
_SectionCard(
title: 'Basic Rating',
subtitle: 'Tap or drag to rate',
child: Column(
children: [
FlareRating(
rating: _basicRating,
onRatingUpdate: (v) => setState(() => _basicRating = v),
),
const SizedBox(height: 8),
Text(
'${_basicRating.toStringAsFixed(1)} / 5',
style: tt.titleMedium?.copyWith(color: Colors.amber),
),
],
),
),
const SizedBox(height: 16),
// ── Half Star ──────────────────────────────────────────
_SectionCard(
title: 'Half-Star Support',
subtitle: 'Tap the left half for 0.5 steps',
child: Column(
children: [
FlareRating(
rating: _halfRating,
allowHalfRating: true,
filledColor: Colors.deepOrange,
onRatingUpdate: (v) => setState(() => _halfRating = v),
),
const SizedBox(height: 8),
Text(
'${_halfRating.toStringAsFixed(1)} / 5',
style: tt.titleMedium?.copyWith(color: Colors.deepOrange),
),
],
),
),
const SizedBox(height: 16),
// ── Custom ─────────────────────────────────────────────
_SectionCard(
title: 'Custom Colour & Count',
subtitle: '7 stars • teal theme',
child: Column(
children: [
FlareRating(
rating: _customRating,
itemCount: 7,
filledColor: Colors.tealAccent,
unfilledColor: Colors.teal.shade900,
onRatingUpdate: (v) => setState(() => _customRating = v),
),
const SizedBox(height: 8),
Text(
'${_customRating.toStringAsFixed(1)} / 7',
style: tt.titleMedium?.copyWith(color: Colors.tealAccent),
),
],
),
),
const SizedBox(height: 16),
// ── Large ──────────────────────────────────────────────
_SectionCard(
title: 'Large Stars',
subtitle: 'itemSize: 64 • particles on',
child: FlareRating(
rating: _largeRating,
itemSize: 64,
itemPadding: 8,
filledColor: Colors.pinkAccent,
unfilledColor: Colors.pink.shade900,
onRatingUpdate: (v) => setState(() => _largeRating = v),
),
),
const SizedBox(height: 16),
// ── Read-only ──────────────────────────────────────────
_SectionCard(
title: 'Read-only Display',
subtitle: 'No onRatingUpdate → not interactive',
child: FlareRating(
rating: 4.0,
filledColor: Colors.purpleAccent,
unfilledColor: Colors.purple.shade900,
),
),
const SizedBox(height: 16),
// ── No Particles ───────────────────────────────────────
_SectionCard(
title: 'Particles Off',
subtitle: 'showParticles: false',
child: FlareRating(
rating: _basicRating,
showParticles: false,
filledColor: Colors.lime,
unfilledColor: Colors.green.shade900,
onRatingUpdate: (v) => setState(() => _basicRating = v),
),
),
const SizedBox(height: 40),
],
),
);
}
}
// ── Reusable card ────────────────────────────────────────────────────────────
class _SectionCard extends StatelessWidget {
const _SectionCard({
required this.title,
required this.subtitle,
required this.child,
});
final String title;
final String subtitle;
final Widget child;
@override
Widget build(BuildContext context) {
final tt = Theme.of(context).textTheme;
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFF1E1E1E),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: tt.titleMedium?.copyWith(
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
const SizedBox(height: 2),
Text(
subtitle,
style: tt.bodySmall?.copyWith(color: Colors.white38),
),
const SizedBox(height: 16),
Center(child: child),
],
),
);
}
}