A Simple way to organize your code in Flutter

Leonidas Kanellopoulos
6 min readApr 17, 2024

--

On my journey to make Flutter my everyday tool for developing apps, I was looking for a way to organize my code in a way that will not come back to bite me in the ass in the future. Maintaining multiple projects is never easy and the cleaner the code is, the easiest it gets. With that in mind, I used prior experience with technologies like native Android, Apache Flex even PHP to create an organized project structure and I hope it helps you too.

The Skeleton Application

This is the structure I use in all my projects which means that jumping from a project to another is easy and I don’t have to go digging to figure out where things are. The team I work with is also used to this structure which helped minimizing our production to the point of making apps within days.

Screenshot 1: Typical structure organize of my projects

Let’s discuss the structure a bit further:

Screens: This is the folder where all the screens of an app usually goes. As it can be seen on the screenshot, each screen has its own folder. In this specific example the app has a Homescreen which uses a bottom navigation bar and 4 major screens (war, news, major_orders and game). I will discuss this part more later.

Widgets: This is the folder where all the reusable widgets of the app usually goes. In this folder I save files like my custom_scaffold and all the “recyclers” or list item widgets.

Services: This is basically all the external services used in the apps I make which is also known as my DIO folder. Classes here usually include connection to a REST API that feeds data to the app.

Models: Following the Services folder I always keep my Models (object classes) in a separated folder named Models. For people who used Flex or AIR in the past this used to be my ValueObjects folder. An example of a Model for dictionary terms:

class TermVO {
int? id;
String? title;
String? description;
String? language;


TermVO(
{this.id, this.title, this.description, this.language});

TermVO.fromJson(Map<String, dynamic> json) {
id = json['id'];
title = json['title'];
description = json['description'];
language = json['language'];

}
}

Utils: This folder is the home of all “helper” files for external APIs or local libraries. Here I will keep things like firebase helper class, OneSignal helper class or even my own StringHelper class which includes functions to format Strings in our projects.

Styles: This is straight forward as it includes the themes and styles for the apps.

Commons.dart: This is a file we use as a main “import” for all the things used in the app. This way you just import a single file in your files which can also help with quick refactoring, changing or adding libraries.

Example of commons.dart file used in a social media app.

With this structure, you can keep even big projects fairly organized making your life much easier.

The separation of UI and Code

As we all know Flutter is not opinionated and you are free to do whatever you want with your code. One of the things I was missing in Flutter was the separation of code and UI the way it is done in Native Android (xml + kt file) or even before that in my beloved old technology Apache Flex (MXML for UI and AS for code). While discussing this with a fellow developer and good friend, he came up with an idea to achieve this so here we are (thank you Roipeker).
The idea behind this is that on every screen is separated in two files, one containing only the UI part / widgets and the other file containing all the functions, variables and functioning code of the screen.
Let’s see an example of a screen containing a list of news coming from a REST API.

Example of screen displaying news

As you can see the “News” screen is seperated in the *_screen.dart file which contains the UI and the *_controller.dart file which is all the functioning part. To achieve this we use the power of “ part ‘news_controller.dart’ ” for the screen.dart and “ part of ‘news_screen.dart’ “ in the controller.dart file. This way the two files will act as one.

news_screen.dart

You can see that the first file is simple and only contains the UI part.

news_controller.dart

This is the controller file which is used to load the news and sort them. As you can see the newsList is loaded with content in the controller.dart and used in the screen.dart

Although this is a simple example you can see the value of having the two files keeping the code organized and clean.

To take this even further and with the goal to minimize development time, we had these integrated as File and Code Templates in our Android Studios. This way we can create them fast for each new screen.

Make the Template for the 2 files screen structure

To add this in your templates you will have to follow the next steps:

  1. File — Settings — Code Style — File and Code Templates
  2. Add a new entry
  3. Name it anything (I use View State as the name)
  1. File name should be ${NAME}_screen
  2. Extension should be dart
  3. Use the following code for the main file (screen.dart)
#set($class = ${NAME})
#set($class_start = $class.substring(0,1).toUpperCase())
#set($class_rest = $class.substring(1).toLowerCase())
#set($class = $class_start + $class_rest)

part '${NAME}_controller.dart';

class ${class}Screen extends StatefulWidget {
const ${class}Screen({Key? key}) : super(key: key);

@override
createState() => _${class}Screen();
}

class _${class}Screen extends ${class}Controller {
@override
Widget build(BuildContext context) {
return Container();
}
}

7. Create a Child Template File under the main file with the following code

#set($class = ${NAME})
#set($class_start = $class.substring(0,1).toUpperCase())
#set($class_rest = $class.substring(1).toLowerCase())
#set($class = $class_start + $class_rest)

part of '${NAME}_screen.dart';

abstract class ${class}Controller extends State<${class}Screen> {

@override
void initState() {
// TODO: implement initState
super.initState();
}

@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
}

8. Name the Child Template File ${NAME}_controller and the extension as before should be dart.

9. Apply and OK to finish up

After this you can go to your folders create a new test folder (screens-test) then right click on it — new — View State — name it.
This will create 2 files with name_screen and name_controller.

As I mentioned before, this is just the way my team and I organize our code and there are many ways to do it but I wanted to share for a while as this has helped me going from the absurd chaos of the first Flutter project in 2019 to something well organized and quick to maintain.

If you like this method, integrate it in your projects, share it with fellow flutter developers and don’t forget to applaud.

--

--

Leonidas Kanellopoulos

💻 Tech Enthusiast | 🕹️ Gamer |📱 Mobile Developer | Fortune 40Under40, ✌️ GDG Athens, WTM Ambassador | 🎯 PTIEP Mentor