Recursion in programming: advantages, disadvantages, and a real-world example in Flutter

- Andrés Cruz - ES En español

Video thumbnail

I want to talk about a topic that I find truly fascinating: recursion, or recursive programming. I don't see it mentioned very often nowadays—and honestly, I think that's a shame. It's a concept I brought with me from college, and ever since, I've barely seen it in modern courses, books, or tutorials.

That's why I felt it was valuable to bring it here, because it can be a very powerful tool in certain situations. If you are already familiar with it, you'll know that it has its pros and cons; the key, as always, is knowing when and how to apply it. So I want to show you a practical, real-world example of how I am using it in a Flutter project.

The Practical Case: Converting HTML to Native Widgets

The starting point is an HTML file. All the content you see in the viewer—images, lists (ul, ol), paragraphs (p), headings (h1, h2), div, etc.—is a standard HTML page. What I wanted to achieve was to translate this HTML into native Flutter widgets, instead of rendering it inside a WebView. This brings noticeable advantages in performance, accessibility, and control over the visual style, although that deserves its own article.

What does matter here is that recursion turned out to be the key piece of this implementation. You'll see why.

The Problem of Nested div Elements

I have a function called htmlToWidget, which is responsible for processing the HTML document. Once I get the document tree, I iterate over its nodes and check the type of each one: whether it's an h1, h2, p, img, ul, etc., and based on that, I generate the corresponding widget in Flutter.

Everything works fine... until div elements appear.

The problem is that a div can contain any number of HTML elements inside, and those elements in turn can contain others. If we only iterate over the direct children of the first level, we lose all content that is more deeply nested. For example, in a structure like this:

<div>
  <div>
    <section>
      <p>Text</p>
    </section>
  </div>
</div>

If we only iterate over the direct children of the root div, we will only find the inner div, and we will never reach the <p>Text</p>. This is where flat iteration simply falls short.

The Solution Using Recursion

When I encounter a div or a section during processing, I don't ignore it nor do I try to flatten it manually. Instead, I call a helper function called _searchContent, which in turn invokes _processHTML again: the exact same main function. That is precisely what makes a function recursive: it calls itself to solve subproblems of the same type.

switch (element.localName) {
  case 'h1':
  case 'h2':
  case 'h3':
  case 'h4':
  case 'h5':
  case 'h6':
    return [Text(...)];
  case 'ul':
  case 'ol':
    return [Text(...)];
  case 'p':
    return [Text(...)];
  case 'img':
    return [processImage(element)];
  case 'div':
  case 'section':
    // If it is a container, we search its internal content recursively
    return _searchContent(element, context);
}

List<Widget> _searchContent(html_dom.Element element, BuildContext context) {
  if (element.children.isEmpty) return [];

  List<Widget> ws = [];
  element.children.forEach((e) {
    _processHTML(e, context).forEach((w) {
      ws.add(w);
    });
  });

  return ws;
}

The flow is elegant: _processHTML finds a div, delegates to _searchContent, which iterates over the children and calls _processHTML again for each one. Thus, the function "descends" through the HTML tree level by level until it reaches the real content nodes—h2, p, img, etc.—and turns them into their corresponding widgets.

This is where recursion shines. Without it, I would have to write multiple nested forEach loops, duplicate logic across different blocks, or set an arbitrary depth limit. The iterative alternative would not only be longer, but considerably more fragile when faced with dynamic and unpredictable HTML structures.

Advantages and Disadvantages of Recursion

Recursion is not a silver bullet. It is best to understand its strengths and weaknesses well before deciding to use it:

Advantages

  • Cleaner and more expressive code: for hierarchical or tree-like problems, the recursive solution tends to be much more readable than the equivalent iterative one.
  • Handles arbitrary depth: it doesn't matter how many levels of nesting the HTML has; the function traverses them all without you needing to know the depth beforehand.
  • Less duplicated code: a single well-defined function replaces several blocks of repeated logic.

Disadvantages

  • Higher memory consumption: each recursive call takes up a frame in the call stack (call stack). With very deep trees, this can accumulate quickly.
  • Risk of stack overflow (StackOverflow): if there is no well-defined exit condition—or if the tree is excessively deep—the function can call itself indefinitely until available memory is exhausted.
  • Harder to debug: tracing nested calls in a debugger can be more complex than tracing a flat for or forEach loop.
  • Lower efficiency in simple cases: for flat or shallow structures, a direct forEach will be faster and have lower overhead.

I understand why recursion has taken a back seat in modern development. However, as you can see in this example, there are scenarios—especially when working with tree structures like the DOM of an HTML document—where it is, without a doubt, the right tool.

Discover what recursive programming is, its advantages and disadvantages, and how to apply it to a real-world scenario: converting HTML into native widgets in Flutter.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

I agree to receive announcements of interest about this Blog.