Flutter Best Practices

Chapter 2 · Practice 1 of 5

Keep build() free of costly and repeated work

From Widgets and build methods

Why it matters

The Flutter docs say to avoid repetitive and costly work in build() methods, since build() can be called frequently when ancestor widgets rebuild. A parent animation, a theme change or a keyboard opening can all trigger it.

Things that do not belong in build():

  • network requests, file reads and database queries,
  • parsing JSON or sorting large lists,
  • creating controllers, streams or futures that should live as long as the widget.

Compute data in your view model or repository, and hand build() a ready-to-render value. A FutureBuilder whose future is created inside build() is a classic bug: every rebuild starts a new request.

Do

  • Create futures and streams in initState() or a view model, then pass them in
  • Pre-compute sorted or filtered lists outside build()

Avoid

  • Calling http.get or jsonDecode inside build()

Example

Dart
// Bad: a new request on every rebuild
Widget build(BuildContext context) {
  return FutureBuilder(future: api.fetchOrders(), builder: _render);
}

// Good: the future is created once
late final Future<List<Order>> _orders = api.fetchOrders();

Widget build(BuildContext context) {
  return FutureBuilder(future: _orders, builder: _render);
}