What's under the hood?

Dart Basics“, telegram channel MADTeacher and Associate Professor of the Department of Applied Computer Science at the Saint Petersburg State University of Aerospace Instrumentation.

This time I translated an article from Medium that walks through the hot reload process in Flutter step by step – “Flutter Reload: What's Under the Hood” It is best to classify it as free, i.e. it is not verbatim and discards some of the author's text, shortening and transforming it in places where it is not critical to the meaning.


Introduction

Hot Reload in Flutter is one of the killer features of the framework, allowing you to make changes to the app you’re developing almost instantly in debug mode, without having to recompile and relaunch it. But have you ever wondered what actually happens when you hit the reload button? Let’s dive deeper into the inner workings of this process in Flutter and uncover the street magic behind this powerful feature of the framework.

Flutter hot reload

Flutter hot reload

Note: All code examples in this article are taken directly from the repositories. Flutter and its engine (Flutter Engine). For greater clarity of the described actions and a better understanding of what is happening in them, minor edits were made to the code.

*****

To understand the whole process, let's first imagine the stages that Hot Reload goes through in the form of the following diagram:

Hot Reload Stages

Hot Reload Stages

Now let's look at each stage of the presented process.

1. The user clicks on the “Hot Reboot” button

When you click the Hot Reload button or save changes in the IDE, with the app running in the emulator, the HotRunner class from the Flutter toolkit comes into play. It takes control and is responsible for organizing the subsequent stages of the process. In other words, HotRunner:

  • Checks whether the application is in a state that allows Hot Reboot.

  • Determines which files have changed since the last compilation.

  • communicates with the Dart virtual machine using the VM Service Protocol.

Future<OperationResult> restart({...}) async {
  final List<Uri> updatedFiles = await _getChangedFiles();
  final bool success = await _reloadSources(updatedFiles);
  if (!success) {
    return OperationResult(1, 'Hot reload failed');
  }
  // ...
}

Link: hot_runner.dart

2. Updating the source code (implementing Dart VM code)

The Dart VM receives the updated source code and injects it into the running application. At this point:

  • New versions of modified libraries are created.

  • To efficiently process updates, a copy-on-write mechanism is used.

  • Old versions of the code continue to be stored in memory, allowing for rollback if necessary.

Future<bool> _reloadSources(List<Uri> files) async {
  final vm_service.VM vm = await _vmService.getVM();
  final vm_service.IsolateRef isolateRef = vm.isolates.first;
  final vm_service.ReloadReport report = await _vmService.reloadSources(isolateRef.id);
  return report.success;
}

Link: app_snapshot.cc

3. Compiling code using JIT

The Dart VM's Just-In-Time (JIT) compiler quickly compiles new code (changed libraries) using tiered compilation. This approach allows for incremental optimization of the hottest parts of the code using techniques such as inline caching and type feedback.

At this stage, the JIT compiler tries to use as much of the previous compilation as possible, thereby speeding up the process.

class DartVM {
  Future<void> compileJIT(List<Library> modifiedLibraries) async {
    for (var library in modifiedLibraries) {
      await _jitCompiler.compile(library);
    }
  }
}

Link: compiler.cc

4. Comparison of old and new widget trees

The Flutter framework compares the old widget tree with the new one. At this stage, widgets that need to be updated are determined. This is done using the element tree, which is Flutter's matching layer between widgets and renderers, to which an efficient comparison algorithm is applied that can handle widget IDs and saved states.

It should be noted that the tree comparison is optimized to minimize the number of necessary rebuilds.

class Element {
  void update(Widget newWidget) {
    _widget = newWidget;
    markNeedsBuild();
  }

  void markNeedsBuild() {
    if (!_dirty) {
      _dirty = true;
      owner.scheduleBuildFor(this);
    }
  }
}

Link: widget_framework.dart

5. Identifying and rebuilding affected widgets

Based on the results of the previous comparison stage, Flutter determines which widgets need to be rebuilt and schedules their rebuilding. This is where the BuildOwner class shines. It manages the current stage of the hot reload process, maintains a list of “dirty” elements that need to be rebuilt, and triggers the rebuilding process itself using a depth-first traversal of the widget tree. During the rebuild, State objects are preserved where possible to maintain the current state of the application (remember the jokes with keys and StatefulWidget).

class BuildOwner {
  void buildScope(Element context, [VoidCallback callback]) {
    _dirtyElements.sort(Element._sort);
    _dirtyElements.reversed.forEach((Element element) {
      if (element._dirty)
        element.rebuild();
    });
  }
}

Link: widgets_framework.dart

6. Memory management and garbage collection

At this stage of the hot reload process, the Dart garbage collector runs and removes objects from the heap that are no longer needed. In the case of widgets, Flutter mechanisms are used to properly dispose of the resources that belong to them. Flutter also tries to reuse existing objects where possible. This approach minimizes the risk of memory overflow.

class State<T extends StatefulWidget> {
  @protected
  void dispose() {
    // Clean up resources here
  }
}

Link: dart_heap.cc

7. Real-time UI update

In the final step, Flutter, or more specifically the RendererBinding class, updates the UI to reflect the changes made during hot reload. Since Flutter uses a mode-preserving rendering system, this allows it to efficiently update only those parts of the UI that have changed.

class RendererBinding extends BindingBase with SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding {
  void drawFrame() {
    pipelineOwner.flushLayout();
    pipelineOwner.flushCompositingBits();
    pipelineOwner.flushPaint();
    renderView.compositeFrame();
  }
}

Link: rendering_binding.dart

Conclusion

Hot Reload in Flutter is a powerful feature that significantly speeds up development. It instantly applies code changes without losing the app state, allowing for rapid iteration and experimentation during development. Complex processes such as code injection, efficient widget tree comparison, and intelligent UI updates clearly show how much the Flutter team cares about developer productivity. Despite some limitations, Hot Reload shows how thoughtful engineering can significantly improve the app development workflow, resulting in higher quality and more polished apps.

The translation was prepared by a teacher who was fucked over the summer, the presenter telegram channel MADTeacher, dedicated to Dart/Flutter and the insanity of our higher education system

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *