This episode measures and accelerates your app: rendering performance and jank reduction, profiling with DevTools, the widget inspector, and timeline, techniques to reduce rebuilds with widget caching and image optimization, and memory usage and app startup time.

A slow app drives users away as fast as a broken feature. Episode 15 teaches you how to measure first, then optimize: rendering performance and jank reduction, profiling with DevTools and the widget inspector, techniques to reduce rebuilds and optimize images, and memory usage and startup time.
The golden rule before reading this episode: optimization without measurement is a guess. Always profile first, then change code.
Flutter targets 60 frames per second (or 120 on capable devices). Each frame only has about 16 milliseconds for layout, paint, and compositing. When a frame exceeds that limit, jank appears — stutter you feel while scrolling.
build method (file I/O, network, parsing).class ItemList extends StatelessWidget {
const ItemList({super.key});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) {
return const ItemTile(index: index);
},
);
}
}Notice the const on ListView.builder and ItemTile. const tells Flutter the widget is immutable — the framework can skip unnecessary rebuilds, one of the easiest performance savings.
Wrap areas that rarely change with RepaintBoundary so they aren't repainted when other areas change:
RepaintBoundary(
child: CustomPaint(painter: PetaPainter()),
)RepaintBoundary makes CustomPaint render to a separate layer. When surrounding widgets change, the map doesn't need to be redrawn — effective for elements like maps, animated backgrounds, and previews.
Run the app in profile mode for realistic measurements:
flutter run --profileflutter run --profile enables AOT while still keeping DevTools available — this is the profile used for measuring, not debug mode. Click the Open DevTools icon in the terminal to open the tool suite.
flutter run --profile --devtoolsflutter run --profile --devtools directly opens DevTools in the browser. Filter the timeline for slow frames, check which widgets are built most often, then optimize that point — not some random place.
Unnecessary rebuilds are usually the biggest source of waste. Three high-impact fixes:
const for immutable widgets.const widgets.itemExtent on long lists to make layout cheaper.Images are the biggest source of memory and bandwidth. Use cached_network_image for caching and correct resolution:
flutter pub add cached_network_imageCachedNetworkImage(
imageUrl: 'https://example.com/logo.png',
placeholder: (context, url) => const SizedBox(
width: 48,
height: 48,
child: CircularProgressIndicator(strokeWidth: 2),
),
errorWidget: (context, url, error) => const Icon(Icons.broken_image),
)CachedNetworkImage stores images on disk so repeated requests don't happen, and placeholder prevents layout jumps while loading. Resize images on the server side when possible — downloading a 4K image for a 48px thumbnail is wasteful.
Use the DevTools Memory tab and watch for patterns:
disposed are a common cause.class SearchScreen extends StatefulWidget {
const SearchScreen({super.key});
@override
State<SearchScreen> createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
final TextEditingController _controller = TextEditingController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return TextField(controller: _controller);
}
}_controller.dispose() in dispose prevents leaks. The rule: every resource opened in initState must be closed in dispose.
Slow startup is usually caused by heavy work in main() and the initial build. Common fixes:
main() before runApp.flutter build apk --release --obfuscate to shrink the binary size in releases.Key takeaways:
const on immutable widgets to skip rebuilds.RepaintBoundary isolates areas that rarely change.cached_network_image and the right size.In the next episode 16 we discuss animations and UI polish — the difference between implicit and explicit animations, AnimationController, Tween, and AnimatedBuilder, Hero animations and transitions for motion design, and CustomPaint and advanced UI effects. Your app starts to feel alive and premium.