# Common Dart & Flutter Issues A collection of common issues to cover whenever reviewing Dart or Flutter code. These comments follow the [Conventional Comments](https://conventionalcomments.org/) structure. ## Issues _Problems that must be addressed._ ### `initState` first **issue**: Avoid implementing this method before calling `super.initState()`. **suggestion**: Call `super.initState` before any other logic in your Stateful Widget. Implementations of this method should start with a call to the inherited method, as in `super.initState()`. ### `dispose` last **issue**: Avoid calling `super.dispose()` before implementing this method. **suggestion**: Call `super.dispose` after any other logic in your Stateful Widget. Implementations of this method should end with a call to the inherited method, as in `super.dispose()`. ### Strong typing **issue**: Avoid using dynamic type where possible. **suggestion**: Consider using stronger typing rather than relying on dynamic This language feature offers the following benefits: - Revealing type-related bugs at compile time. A sound type system forces code to be unambiguous about its types, so type-related bugs that might be tricky to find at runtime are revealed at compile time. - More readable code. Code is easier to read because you can rely on a value actually having the specified type. In sound Dart, types can’t lie. - More maintainable code. With a sound type system, when you change one piece of code, the type system can warn you about the other pieces of code that just broke. - Better ahead of time (AOT) compilation. While AOT compilation is possible without types, the generated code is much less efficient. See also: https://dart.dev/guides/language/type-system ### Const keyword **issue**: Avoid using var/final when possible. **suggestion**: Consider using const over var/final. The const keyword serves as an indicator that tells the compiler that, “for this variable, it will never change in the lifetime of this code, so create just one copy of it and where ever it is mentioned, just reference back to the one copy you already created and don’t create a new one.” See also: https://www.youtube.com/watch?v=DCKaFaU4jdk https://medium.com/flutter-community/the-flutter-const-keyword-demystified-c8d2a2609a80 https://resocoder.com/2020/01/06/dart-const-tutorial-all-you-need-to-know-const-expressions-canonical-instances-and-more/ ### Functions vs. Widgets **issue**: Avoid returning widgets from helper methods. **suggestion**: Prefer to either inline widgets or extract them into another widget. Extracting widgets to a method is considered a Flutter anti-pattern, because when Flutter rebuilds the widget tree, it calls the function all the time, making more processor time for the operations, and potentially short-circuiting hot reload. There are a number of advantages to extracting UI as a widget rather than using a helper method: - Allows the framework to do performance optimizations (const constructor, more granular rebuild, etc.) - Ensures that layout switching properly disposes of resources where functions may reuse previous state. - Ensures that hot-reload works properly - Integrates with the DevTools widget inspector - Better error messaging, key definitions, and use of context. It is also a good practice to create private widgets for UI that you would not like to expose in other places in the application. Or you could expose the widget for tests using the `@visibleForTesting` decorator. That way you can test the component but if you try to use it in the application, you will see a warning. See also: https://flutter.dev/docs/perf/rendering/best-practices#controlling-build-cost https://www.youtube.com/watch?v=IOyq-eTRhvo ## Suggestions _Non-blocking improvements._ ## DI/IOC **suggestion**: Consider passing dependencies in constructor rather than instantiating them in the class. Example: ```dart= class NetworkHelper { NetworkHelper( this.url, { http.Client? client, }) { this.client = client ?? http.Client(); } late final http.Client client; final Uri url; Future getData() async { http.Response response = await client.get(url); if (response.statusCode == 200) { String res = response.body; final data = jsonDecode(res).cast<Map<String, dynamic>>(); return data; } else { print(response.statusCode); } } ///...rest of class } ``` Passing in the client as a dependency will allow you to easily mock the client when writing tests. See also: https://flutter.dev/docs/cookbook/testing/unit/mocking ## Print calls **suggestion**: Consider avoiding `print` calls in production. See also: https://dart-lang.github.io/linter/lints/avoid_print.html ## Environment configuration **suggestion**: Consider setting environment variables (such as URLs) using an environment configuration. See also: https://stacksecrets.com/flutter/environment-configuration-in-flutter-app#Introduction_To_Environment_Configuration ## Public vs. Private **suggestion**: Consider making variables private using an underscore and improve readability. A public member can be accessed from outside the class. That means that if something goes wrong with a public field, the culprit could be anywhere. Additionally, using underscores for private variables improves the readability of your code by allowing users to differentiate private class variables from function variables. See also: https://softwareengineering.stackexchange.com/questions/143736/why-do-we-need-private-variables ## Using shrinkwrap **suggestion**: Avoid using `ListView.shrinkWrap`. Consider instead using a `CustomScrollView` + `SliverList` Example: ```dart= CustomScrollView( slivers: [ SliverList( delegate: SliverChildBuilderDelegate( (context, index) => Container(), childCount: someObject.length, ), ), ], ), ``` According to the Flutter documentation, using shrinkWrap in lists is expensive performance-wise and should be avoided, since using slivers is significantly cheaper and achieves the same or even better results. See also: https://github.com/dart-lang/linter/issues/3496 ## Using Border.all **suggestion**: Prefer using `Border.fromBorderSide` over `Border.all` for performance, since the former is a `const` constructor Example: ```dart= class BorderWidget extends StatelessWidget { final Widget child; const BorderWidget({ Key? key, required this.child, }) : super(key: key); @override Widget build(BuildContext context) { return Container( border: const Border.fromBorderSide(BorderSide( color: const Color(0xFF000000), width: 1.0, style: BorderStyle.solid, )), child: child, ); } } ```