Widgets
Everything in Flutter is a widget. Composition over inheritance: small widgets compose into bigger ones. StatelessWidget renders props; StatefulWidget holds local state.
Stateless, Stateful, composition
EXAMPLE
import 'package:flutter/material.dart';
// 1) Stateless — pure function of props
class Badge extends StatelessWidget {
final String label;
final Color color;
const Badge({super.key, required this.label, this.color = Colors.grey});
@@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
border: Border.all(color: color),
borderRadius: BorderRadius.circular(12),
),
child: Text(label, style: TextStyle(color: color, fontSize: 12)),
);
}
}
// 2) Stateful — holds mutable local state
class Counter extends StatefulWidget {
const Counter({super.key});
@@override State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _n = 0;
@@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Count: $_n', style: Theme.of(context).textTheme.titleLarge),
Row(children: [
OutlinedButton(onPressed: () => setState(() => _n--), child: const Text('-')),
const SizedBox(width: 8),
FilledButton(onPressed: () => setState(() => _n++), child: const Text('+')),
]),
],
);
}
}
// 3) Composition — build big widgets from small ones
class ProductCard extends StatelessWidget {
final String name;
final double price;
final String image;
final VoidCallback? onTap;
const ProductCard({super.key, required this.name, required this.price, required this.image, this.onTap});
@@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AspectRatio(aspectRatio: 4/3, child: Image.network(image, fit: BoxFit.cover)),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: Theme.of(context).textTheme.titleMedium, maxLines: 1, overflow: TextOverflow.ellipsis),
const SizedBox(height: 4),
Text('$${price.toStringAsFixed(2)}', style: TextStyle(color: cs.primary)),
],
),
),
],
),
),
);
}
}
// 4) Lifecycle methods (State<T>)
// initState : once, before first build — set up subscriptions, controllers
// didChangeDependencies : whenever inherited widgets change
// build : on every rebuild
// didUpdateWidget: on parent rebuild with new props
// deactivate : removed from tree
// dispose : final cleanup — controllers, streams
class Timer1 extends StatefulWidget {
@@override State<Timer1> createState() => _Timer1State();
}
class _Timer1State extends State<Timer1> {
late final Timer _t;
int _s = 0;
@@override void initState() {
super.initState();
_t = Timer.periodic(const Duration(seconds: 1), (_) => setState(() => _s++));
}
@@override void dispose() { _t.cancel(); super.dispose(); }
@@override Widget build(BuildContext context) => Text('Elapsed: $_s');
}
// 5) const widgets — perf win
// Mark constructors const where possible. Const widgets are compile-time constants,
// reused across rebuilds without allocating new objects.
// 6) Common pitfalls
// • Don't put expensive work in build() — it runs on every frame
// • Don't access InheritedWidget (Theme, MediaQuery) in initState — use didChangeDependencies
// • Always dispose controllers, animations, streams
// • Use Key for items in lists that may reorder
Why it matters
Composition + small const widgets is the Flutter performance secret. const ProductCard(...) is free across rebuilds; non-const allocates every frame. Mark constructors const aggressively.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Everything is a Widget — even padding, alignment, themes.
Text('hi')
Icon(Icons.star)
Padding(padding: EdgeInsets.all(8), child: Text('x'))
Try it Yourself »
Discussion
Loading…