Notes: 04. Understand the Call Stack
The student materials have been reviewed and are updated as of SEPTEMBER 2022.
The call stack is used to show whats happening behind the scene in Flutter when our code hits a breakpoint. It shows a hierachy of active function calls in a stack order. So when a function calls another function, the new function is added to the top of the call stack.
And when it is done, that new function is first popped or removed from the top of the stack before the function that called it is then removed.
Let’s hit the increment button. If you look at the call stack, the incrementCounter method is at the top of the stack because that is the current function being executed. Let’s keep step over the setState call. The next statement is the call to the displayMessage method. Step into it.
Now you can see this method is at the top of the stack because that is the current method we’re in. One benefit of the call stack is that we can navigate up and down the stack to see what values our app stores at different levels. So if i click on incrementCounter method, we can see that the message variable is not yet stored in our app.
But when i click on the displayMessage, we can see our app now holds that value in memory. So we can use this to travel to different stages of our application to check its different states.
There are more items in the call stack but they are hidden since these are the code run by Flutter behind the scene. Click on “Show More Stack Frames” to make them visible. And you can see that they’re all greyed out and this is simply because we did not write all these functions. They are dart and flutter specific functions.
To analyze this clearly, right click on the method at the top of the call stack and select “Copy Call Stack.” Create a new file by pressing Ctrl + N or Cmd + N on mac. And then paste it in.
This shows the complete call stack for the current operation being executed. This might look intimidating at first but let’s break it down.
We code our Flutter apps using the Dart programming language. So behind the scene, there are some Dart code being executed. So when we click on the increment button which calls the incrementCounter method, Flutter first calls some Dart code.
These Dart code executes the required action that leads up to point where the incrementCounter method is called. You can see them at the bottom of the Stack.
Remember, we pressed a button first and that’s why you see the _dispatchPointerDataPacket which is the first Dart function executed by Flutter when you pressed the button. And this function calls the one above it and it goes on and on in that order. Next, we have Flutter code.
We’re are beginning to see some familiar functions here. We can see GestureBinding.dispatchEvent and inside it, it calls the GestureBinding.handleEvent and that’s why you have it on top of the calling method. If we go up the stack, we can see the _InkResponseState._handleTap.
And this points to the ink_well file from the Flutter framework.
Under the hood, a FAB is an InkWell widget. In Flutter, an InkWell widget is used to add interaction to a widget and adds a Material design ripple effect. We can see that when we click the button. And this _handleTap method is the one responsible for calling the incrementCounter method.
And this brings us to the method we declared which are the incrementCounter and the displayMessage methods.
We have the incrementCounter and inside it we call the displayMessage method. So you see the flow: When we perform an action, the underlying Dart code is first called, then the Flutter code and finally; our app’s code. Let’s head back to main.dart file.
And then step into the build method. And watch that the displayMessage method is first popped from the call stack, then the incrementCounter method follows.
Right click on it once more and copy the call stack. Then paste below the previous call stack. Now, the build method is currently on top of the stack and the call stack is entirely different. Both the incrementCounter and the displayMessage methods have been popped off from the call stack because they have been completed. So the current point of exection moves to the build method and thats why you have it at top the stack.
So you can see that the call stack is not a complete log of all the whole functions that were called. Instead it shows the path leading to the current execution point. And that’s why the call stack at the increment counter point differs from the build method point. But if you notice, you can see we have a similar order.
First, the underlying Dart code, then the Flutter code and finally the code created in our app. Now you might be thinking why this call stack is different. The first quetion i’ll ask you is: “What does a build method do?”
You got it right: It simply draws a frame in our app. And thats the first Dart function to be executed; the _drawFrame function. Whenever we rebuild a widget, we want to draw a frame so the function executes and calls the next function which and this calls other successive functions.
If we go up to the Flutter section in the call stack, we can see the Element.rebuild method which eventually calls the _MyHomePageState.build method which is the method we defined. And as a side note: In Flutter, the Widget is just the configuration of the UI which we write in our code while the Element is the actual item in the Flutter tree. Flutter creates these elements behind the scene. So just note that.
Now, once the build method is done, it pops from the stack and its calling method follows in that order. Last In First Out. The call stack simply helps Flutter know where to return to when a method is done.
Wheww!!! That was quite long. So you might be asking: What role does the call stack play when debugging your app? Well, the call stack window simply shows you the functions that were called leading up to the current point of execution. This can be used to see the call that caused an error.
Plus, you can step back and forth the stack to see what values your app stores at different points like we demonstrated previously. There are definately different ways to debug your program and the call stack is just one of the many toolset at your disposal.