As a Tech Lead for my day job, I had the opportunity to see and help to overcome several mistakes that beginners in Flutter can encounter.  

This article aims at helping you identify those mistakes to help you avoid them as fast as possible.

Of course, this is purely subjective, and you might not always find yourself making this kind of error. Let me know if you think something is missing from this article!

1. Using SizedBox to constraints the size

One thing I often find in the PR I review is using a SizedBox to prevent the content from being too big or expanding it artificially.

While it may work fine, it may hide that your constraints are not correctly set. For instance, you might want to constraint a column to a specific size (the column might be very far in the Widget Tree).

SizedBox(
	height: 20,
	child: Column(
		children: [Text('Hey'), Text('You')],
	),
)

Even though you have constrained the size, if you add any widget to the list of children, you will need to change the height of the Column. Here the fix would be to remove the SizedBox and use the MainAxisSize:

Column(
      mainAxisSize: MainAxisSize.min,
      children: [Text('Hey'), Text('You')],
);

You don't need to specify height now and make your Widget more scalable.

Bottom line: if you need to constraint the size of something that should be able to determine its size by itself, make sure that every child has the right size constraints. It shouldn't be the responsibility of the last parent to set the height.

2. Losing track of Flex widgets

Flex widgets like Column and Row make it really start composing your interface. But by the time you write your last Widget, you might have coded several Columns widgets nested between them. Even if it works perfectly fine for now, you might end up later losing track of which Column Widget is overflowing.

Column(
  children: [
    Text('Hey'),
    if (condition)
      Column(
        children: [
          Text('Hello?'),
          Text('Someone here?')
        ],
      )
  ],
);

To simplify everything, you might want to spread the contents of your nested Column and apply padding or other layouts on a per-component basis. Or extract to another component that you will be able to test individually.

Column(
  children: [
    Text('Hey'),
    if (condition) ...[
      Text('Hello?'),
      Text('Someone here?'),
    ],
  ],
);

3. Not extracting to widgets

The thing that can make you improve as a Flutter developer the fastest is extracting your components to separate Widgets early in the development. I often see components going to 10 or more widget depth. It makes debugging the layout really hard. Moreover, reusing the components is nearly impossible since a 10 depth widget is very specific.

You can always extract a Column or a Row of widgets to another class and make your components more easily reusable and testable.

4. Not separating UI and Business Logic

A standard error is to bind all the Widgets to the business logic. It means that even though this Widget could be reused somewhere else, you make it usable only in the specified situation.

For instance:

class MyComponent extends StatelessWidget {
  const MyComponent({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final data = context.read<MyData>();
    return Container(
      child: Text(data.text),
    );
  }
}

This component is perfectly fine, but you need to have a MyData higher in the widget tree to render it. Whereas

class MyComponent extends StatelessWidget {
  const MyComponent({Key? key, required this.text}) : super(key: key);
  final String text;

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(text),
    );
  }
}

Both components are similar, but this one can be reused anywhere and tested without needing to provide the MyData class!

5. Reimplementing default widgets

One common pitfall is to reimplement default widgets. I've seen countless projects where a dev had reimplemented the Button widget, for instance, because he felt like it was easier. While it can be true initially, you might end up missing features of the default button. For example, if your project is ported to the web, you might not have thought about MouseRegion for your custom button while the default Flutter button has everything.

The most effective way, in my opinion, to deal with this is to spend more time learning about how to style default Flutter widgets to prevent rework later.

6. Forgetting error handling

Error handling can be tricky for beginners. What kind of errors should I be catching? Is this one important or not? But even tho error handling is complex, debugging an app with unmanaged error handling is even more challenging.

One common mistake is to wrap everything asynchronous in try/catch. It can be neat initially, but once you start to have multiple layers of try/catch, detecting the error might go silent without you even noticing it.

To prevent this, I would recommend avoiding layers of try/catch and keeping error handling at the highest level possible.

7. Not using IDE refactor

VSCode and IntelliJ have great support for Dart and Flutter, and it can make you gain a lot of time.

You should try to use the refactoring offered by the extensions as much as possible since it can make you gain a lot of time.

For instance:

Automatically change to bloc body.
Remove this Widget

8. Forgetting about BuildContext

One common mistake is to take the BuildContext for granted and think that all contexts are the same. BuildContext is one of the things that Flutter Developers tend to overlook, but that is very important.

When adding things to the context thanks to Provider or anything else, you should picture your Widgets as a tree to see which widgets have access to the content provided and which widgets cannot.

9. Not knowing about lifecycles

Lifecycles are often boring to learn, but that can save you a lot of time debugging. If you're using a specific controller and not disposing of them properly in your StatefulWidgets, for instance, you will get a lot of performance issues down the line.

The Flutter documentation is an excellent source to learn about what can be done at each lifecycle step.

10. Not reloading when a native plugin has been added

Sometimes, you can get stuck with errors about PlatformChannels and methods not existing while you are sure to have installed everything.

Even if Flutter has one of the best hot reload out there, it cannot hot reload the native parts of your app (the plugins interacting with the underlying platform). Sometimes, just stopping and restarting your app fully can unblock you!

And finally, as a bonus ...

11. Not asking enough questions

You should ask questions!! Don't get stuck with a problem! There is an excellent Flutter community out there, and people have been really helpful toward each other!

Conclusion

Thanks for reading! I've been trying to compile some of my projects' common mistakes during junior onboarding. I hope it can help some of you. I consider myself always learning, so if you have some tips to help people learn Flutter, don't hesitate to drop a message on Twitter. I'll be happy to update the article!

See you for the next one! Don't forget to subscribe below to be alerted when my next article drops!