Slider
Themed value slider — lavender track, optional value bubble, discrete steps & disabled
View component →A single, reusable Flutter star rating that works both ways — tap a star to set a whole-number rating, or render a read-only bar that shows half stars for fractional averages. Filled stars use the house lavender, empty stars a hairline grey, and the star size is a single parameter. No external packages, fully DartPad-ready.
What's Included
Use Cases
PRO TIP
Let users tap to set whole stars, but display fractional averages with half stars so a 4.3 average never rounds away its nuance. Keep the interactive size at least 40px wide per star for a comfortable tap target, and shrink to a compact read-only size when the rating is just being shown alongside other content.
Copy this into your Flutter project. No external packages required.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double _rating = 3;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: const Color(0xFFFAFAFB),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AppStarRating(
value: _rating,
onChanged: (v) => setState(() => _rating = v.toDouble()),
),
const SizedBox(height: 24),
const AppStarRating(value: 4.5, size: 20, readOnly: true),
],
),
),
),
);
}
}
/// FlutterKit Star Rating — house design system (Linear lavender, light).
/// Tap to rate, or render read-only with half stars for averages.
/// rohansurve.in/flutterkit/star-rating
class AppStarRating extends StatelessWidget {
const AppStarRating({
super.key,
required this.value,
this.onChanged,
this.count = 5,
this.size = 28,
this.readOnly = false,
});
/// Current rating (supports halves in read-only mode).
final double value;
/// Called with the tapped star (1..count). Null = read-only.
final ValueChanged<int>? onChanged;
/// Number of stars.
final int count;
/// Star size in logical pixels.
final double size;
/// Force a non-interactive bar.
final bool readOnly;
// ── House tokens — docs/flutterkit-design.md §1 ──
static const Color _accent = Color(0xFF5E6AD2);
static const Color _empty = Color(0xFFD5D8DD);
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(count, (i) {
final pos = i + 1;
late final IconData icon;
if (value >= pos) {
icon = Icons.star_rounded;
} else if (value >= pos - 0.5) {
icon = Icons.star_half_rounded;
} else {
icon = Icons.star_outline_rounded;
}
final filled = value >= pos - 0.5;
final star = Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: Icon(icon, size: size, color: filled ? _accent : _empty),
);
if (readOnly || onChanged == null) return star;
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => onChanged!(pos),
child: star,
);
}),
);
}
}
// ── Usage ──
// AppStarRating(value: v, onChanged: (n) {}) // tap to rate
// const AppStarRating(value: 4.5, readOnly: true) // read-only
// const AppStarRating(value: 3, count: 5, size: 20) // compactThemed value slider — lavender track, optional value bubble, discrete steps & disabled
View component →Minus / value / plus stepper — bounded min & max, disabled ends, touch-sized buttons
View component →Single-select chip group — lavender selected chip, pill shape, wraps across lines
View component →