# Why using a StatefulWidget to implement the first challenge solution in Chapter 5?

**URL:** <https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595>\
**Category:** Flutter Apprentice\
**Created:** [October 24, 2021, 3:39pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595 "2021-10-24T15:39:33Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![alilosoft](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/alilosoft/32/99601_2.png) [@alilosoft](https://forums.kodeco.com/u/alilosoft)\
**Post date:** [October 24, 2021, 3:39pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/1 "2021-10-24T15:39:33Z")

</div>

Is there any reason to use a `StatefullWidget` to implement the solution for the first challenge in **Chapter 5**?

I have simply used a local variable for `ScrollController` in the `build()` method of the `ExploreScreen` `StatelessWidget` and it worked perfectly, but as I’m new to **Flutter** I’m not sure of the tradeoffs of this solution compared to the book solution.

Could anyone give an explanation, please?

this is how my solution look:

```dart
class ExploreScreen extends StatelessWidget {

  final fooderlichService = MockFooderlichService();

  ExploreScreen({Key? key}) : super(key: key);

  @override

  Widget build(BuildContext context) {
    final scrollController = ScrollController();
    scrollController.addListener(() {
      final minPos = scrollController.position.minScrollExtent;
      final maxPos = scrollController.position.maxScrollExtent;
      if (scrollController.offset == minPos) {
        print("I'm at the top");
      }
      if (scrollController.offset == maxPos) {
        print("I'm at the bottom");
      }
    });

    return FutureBuilder(
      future: fooderlichService.getExploreData(),
      builder: (context, AsyncSnapshot<ExploreData> snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          final recipes = snapshot.data?.todayRecipes ?? [];
          final posts = snapshot.data?.friendPosts ?? [];
          return ListView(
            controller: scrollController,
            children: [
              TodayRecipeListView(recipes: recipes),
              FriendPostListView(posts: posts),
            ],
          );
        } else {
          return const Center(
            child: CircularProgressIndicator(),
          );
        }
      },
    );
  }
}

```

---

<div class="post-metadata">

**Author:** ![jomoka](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/jomoka/32/109828_2.png) [@jomoka](https://forums.kodeco.com/u/jomoka)\
**Post date:** [October 24, 2021, 4:05pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/2 "2021-10-24T16:05:40Z")

</div>

@alilosoft if you do this make sure you dispose your scrollController.

Placing the scrollController within the build method itself will create a new instance, and add a brand new listener to the scroll controller. So you will have many scroll controllers.

Good idea to only have one, and dispose the controller when the widget is destroyed.

---

<div class="post-metadata">

**Author:** ![alilosoft](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/alilosoft/32/99601_2.png) [@alilosoft](https://forums.kodeco.com/u/alilosoft)\
**Post date:** [October 24, 2021, 6:22pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/3 "2021-10-24T18:22:00Z")

</div>

I’m coming from a Java background, and as far as I know in Java the Garbage collector will take care of unrefrenced objects, I don’t know if there is such mechanism in Fluttet/Dart?

---

<div class="post-metadata">

**Author:** ![alilosoft](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/alilosoft/32/99601_2.png) [@alilosoft](https://forums.kodeco.com/u/alilosoft)\
**Post date:** [October 24, 2021, 6:26pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/4 "2021-10-24T18:26:21Z")

</div>

How I can destroy the scrollController object manually?

Does the scrollController object persiste even after its declaration scope (the build() method) ends?

---

<div class="post-metadata">

**Author:** ![funkyboy](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/funkyboy/32/109099_2.png) [@funkyboy](https://forums.kodeco.com/u/funkyboy)\
**Post date:** [October 24, 2021, 6:47pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/5 "2021-10-24T18:47:10Z")

</div>

Yes there is.  
Dart VM has a garbage collector and you can even check memory usage. See [Using the Memory view | Flutter](https://flutter.dev/docs/development/tools/devtools/memory)

---

<div class="post-metadata">

**Author:** ![alilosoft](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/alilosoft/32/99601_2.png) [@alilosoft](https://forums.kodeco.com/u/alilosoft)\
**Post date:** [October 24, 2021, 8:21pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/6 "2021-10-24T20:21:05Z")

</div>

> [@jomoka](#):
>
> Good idea to only have one, and dispose the controller when the widget is destroyed.

if this is the case, why I do need to destroy `scrollController` object manually? what I’m missing?

my reasoning is as follow:

- the `scrollController` is created and attached a `listener` in `build()` method.
- as the widget is stateless the `build()` method is only called once.
- `scrollController` lifecycle is related to the widget.
- when Flutter destroy the widget `scrollController` will not be referenced.
- the GC will take care of scrollController and its listeners.

---

<div class="post-metadata">

**Author:** ![jomoka](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/jomoka/32/109828_2.png) [@jomoka](https://forums.kodeco.com/u/jomoka)\
**Post date:** [October 25, 2021, 2:48am UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/7 "2021-10-25T02:48:42Z")

</div>

You might want to think about whether you need to use a Stateful Widget vs a Stateless Widget for whatever you are building.

If you are using a statefulWidget, then keeping the scrollController in the state would make sense, and you should use the dispose() method to clean up any listeners of the scroll controller. This is recommended practice.

If you use a `StatelessWidget` you will lose the scroll controller every time the UI gets rebuilt. Stateful widgets keep the state across rebuilds, so it’s also more efficient.

Here’s an exercise for you 🙂

> **[Using the Memory view](https://docs.flutter.dev/development/tools/devtools/memory)**
>
> Learn how to use the DevTools memory view.

Play with the memory view on devtools, maybe you can find out more information about how many scrollController instances you see!

---

<div class="post-metadata">

**Author:** ![alilosoft](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/alilosoft/32/99601_2.png) [@alilosoft](https://forums.kodeco.com/u/alilosoft)\
**Post date:** [October 25, 2021, 12:41pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/8 "2021-10-25T12:41:42Z")

</div>

I found this article about Dart Garbage Collector interesting: [Flutter: Don’t Fear the Garbage Collector | by Matt Sullivan | Flutter | Medium](https://medium.com/flutter/flutter-dont-fear-the-garbage-collector-d69b3ff1ca30)

My takeaway (I may be wrong), is that Flutter/Dart GC is optimized for short lived objects, this is why Widgets are created and destroyed frequently without impacting the performance.  
In the context of my question, I still prefer using a Stateless Widget to solve the problem of Scroll Controller listener, as it’s a very simple object and let the GC clean up the memory instead of using the arguably complex solution of Stateful Widget and override Lifecyle methods (`initState()` and `destroy()`).

One more optimization I did for the shared solution above, is that I moved the `ScrollController` instantiation out of the `build()` method, so it wont be created every time the widget get rebuilt.

---

<div class="post-metadata">

**Author:** ![coulson59](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/coulson59/32/81494_2.png) [@coulson59](https://forums.kodeco.com/u/coulson59)\
**Post date:** [October 26, 2021, 6:41am UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/9 "2021-10-26T06:41:09Z")

</div>

> [@funkyboy](#):
>
> Yes there is.  
> Dart VM has a garbage collector and you can even check memory usage. See [Using the Memory view | Flutter](https://flutter.dev/docs/development/tools/devtools/memory/) [indigocard](https://www.indigocard.review/)

Thanks for sharing, very helpful for me.

---

<div class="post-metadata">

**Author:** ![mih0vil](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/mih0vil/32/102075_2.png) [@mih0vil](https://forums.kodeco.com/u/mih0vil)\
**Post date:** [October 27, 2021, 12:26pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/10 "2021-10-27T12:26:22Z")

</div>

i was reading the documentation and it seemed logical to just use a notification listener instead of a controller

```auto
if (snapshot.connectionState == ConnectionState.done) {
          // 5
          return NotificationListener<ScrollNotification>(
            onNotification: (ScrollNotification scrollNotification) {
              if (scrollNotification.metrics.atEdge) {
                print(scrollNotification.metrics.pixels ==
                        scrollNotification.metrics.minScrollExtent
                    ? 'On top'
                    : 'At the end');
              }
              return true;
            },
            child: ListView(

```

---

<div class="post-metadata">

**Author:** ![jbadda](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/jbadda/32/81494_2.png) [@jbadda](https://forums.kodeco.com/u/jbadda)\
**Post date:** [November 8, 2021, 9:10am UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/11 "2021-11-08T09:10:48Z")

</div>

A widget build method might be called dozens of times a second. Perhaps this isn’t well understood… 60 fps?

Given this, it isn’t correct to needlessly construct and wire up complex objects (and then make them available for destruction) within that method. Incorporating a widget instead allows the Framework to cache and reuse that which doesn’t change between those (potentially many) method calls.

Imagine if your approach was used throughout a widget tree containing thousands of widgets?

---

<div class="post-metadata">

**Author:** ![alilosoft](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/alilosoft/32/99601_2.png) [@alilosoft](https://forums.kodeco.com/u/alilosoft)\
**Post date:** [November 8, 2021, 9:41am UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/12 "2021-11-08T09:41:07Z")

</div>

Many thanks for the replay @jbadda  
Indeed, I wasn’t aware of this fact when I first asked the question, but thankfully I realized my mistake and understood how the `build()` method works, I changed my solution to something else (hopefully better),

I didn’t want to use a `StatefulWidget` because I don’t understand why we need to `dispose()` the controller manually, as it should be cleaned by the GC when the widget is disposed, in the code bellow `_controller = MyScrollController();` will not be referenced after `ExploreScreen` is disposed.

Maybe I miss something, I really want an answer for that question 🤔

here is the current solution code:

```auto
class MyScrollController extends ScrollController {
  MyScrollController() {
    addListener(createListener);
  }

  void createListener() {
    if (offset == position.minScrollExtent) {
      print('top reached!');
    }
    if (offset == position.maxScrollExtent) {
      print('bottom reached');
    }
  }
}

class ExploreScreen extends StatelessWidget {
  final mockService = MockFooderlichService();
  final _controller = MyScrollController();

  ExploreScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    //print('ExploreScreen build() / ${_controller}');
    //_controller = MyScrollController();
    return FutureBuilder(
      future: mockService.getExploreData(),
      builder: (context, AsyncSnapshot<ExploreData> snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return ListView(
            scrollDirection: Axis.vertical,
            controller: _controller,
            children: [
              TodayRecipeListView(recipes: snapshot.data?.todayRecipes ?? []),
              const SizedBox(height: 16),
              FriendPostListView(
                friendPosts: snapshot.data?.friendPosts ?? [],
              )
            ],
          );
        } else {
          return const Center(child: CircularProgressIndicator());
        }
      },
    );
  }
}

```

---

<div class="post-metadata">

**Author:** ![jbadda](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/jbadda/32/81494_2.png) [@jbadda](https://forums.kodeco.com/u/jbadda)\
**Post date:** [November 8, 2021, 11:09am UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/13 "2021-11-08T11:09:27Z")

</div>

Well perhaps consider that if you add no listeners to your controller (or create some other custom long lived object referenced to it), you need no dispose method offering you the opportunity to override the default and tidy up things you’ve used or created.

However you do. You add a listener. Where is that listener removed so that the underlying ChangeNotifier might have no listeners attached so it can be garbage collected?

In other word, how can your controller be garbage collected with a listener referenced to it? How about the widget that owns the controllers?

Hope that makes sense.

---

<div class="post-metadata">

**Author:** ![alilosoft](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/alilosoft/32/99601_2.png) [@alilosoft](https://forums.kodeco.com/u/alilosoft)\
**Post date:** [November 8, 2021, 3:00pm UTC](https://forums.kodeco.com/t/why-using-a-statefulwidget-to-implement-the-first-challenge-solution-in-chapter-5/154595/14 "2021-11-08T15:00:09Z")

</div>

Thanks for the insights, I’m exploring and I hope all this will make sense to me soon 🙂

> [@jbadda](#):
>
> However you do. You add a listener. Where is that listener removed so that the underlying ChangeNotifier might have no listeners attached so it can be garbage collected?

I have just checked the code, and found that `class ScrollController extends ChangeNotifier` so basically A `ScrollController` object is a `ChangeNotifier` object, that have `_listeners` as member field defined as `List<VoidCallback?> _listeners = List<VoidCallback?>.filled(0, null);`  
As I understand, the `ChangeNotifier` object (i.e the `ScrollController`) holds a reference to the list of listeners, and not the inverse, so the listeners will not prevent the object from being garbage collected? am I wrong? is there any thing special about `ChangeNotifier` objects? like being referenced from the framework?
