DEV Community

Edu Deza
Edu Deza

Posted on

Tip to reduce network calls rendering Flutter widgets

If our app is getting data through network request from our backend usually we handle the response with FutureBuilder or StreamBuilder widgets (state management approaches apart).

We can have something like this (this example use a FutureBuilder but of course we can do it with a StreamBuilder):

class MyHomePage extends StatelessWidget {
  final String title;
  final PostProvider postProvider = PostProvider();

  const MyHomePage({@required this.title});

  Future<List<Post>> getFuturePostList() async {
    return await postProvider.getPostList();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: getFuturePostList(),
        builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
          Widget child;
          if (snapshot.hasData) {
            // Show widget with list of Post
          } else if (snapshot.hasError) {
            child = Center(child: Text("Error: ${snapshot.error.toString()}"), );
          } else {
            child = Center(child: CircularProgressIndicator());
          }

          return child;
        });
  }
}
Enter fullscreen mode Exit fullscreen mode

The example above, is a very simple app fetching a list of Post objects trough our PostProvider (this class makes a request to our backend using for example http package), and render through a FutureBuilder widget.

It's not a bad approach and of course is running ok, but doing at this way, we are making a new request every time our widget is build.

A simple tip that can reduce drastically these number of calls, is extending our widget from a StatefulWidget and next instead of pass a function to our FutureBuilder, override initState method, and store the result in a variable.

Let's go to see:

class MyHomePage extends StatefulWidget {
 const MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final String title;
  final PostProvider postProvider = PostProvider();
  Future<List<Post>> futurePostList;

  Future<List<Post>> getFuturePostList() async {
    return await postProvider.getPostList();
  }

  @override
  void initState() {
     futurePostList = getFuturePostList();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: futurePostList,
        builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
          Widget child;
          if (snapshot.hasData) {
            // Show widget with list of Post
          } else if (snapshot.hasError) {
            child = Center(child: Text("Error: ${snapshot.error.toString()}"), );
          } else {
            child = Center(child: CircularProgressIndicator());
          }

          return child;
        });
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, our widget only do the request at initState, and every time we render (build) our widget we are avoiding to extra requests.

Tip: Avoid calling functions (doing network calls) at build state, and try to find another approach.

Top comments (1)

Collapse
 
raulurtecho profile image
RaulUrtecho

That is the reason why I chose Bloc over provider :D less spaghetti code in the UI