DEV Community

David Adewoyin
David Adewoyin

Posted on • Edited on

How to Structure a Flutter Project

A common problem faced by most beginners when attempting to write a program or an app beyond the simple hello world or TODO app is how to properly structure their code in other to accommodate ease of development and evolvement.

Another factor that comes into play when designing how to properly structure your project is the architectural style you use, An application built using bloc or provider tends to have different project structure.

Though there is no one size fit all, the best approach i have found is to sample a wide array of common project structure and to evolve to the one that best suit your need.

How to Structure A Flutter Project

In this article we will be covering how to structure our flutter application.

Flutter Project Directory
The Project structure consist of four major sub-folders containing different kind of files.

  1. Models
  2. Views
  3. Services
  4. Commands

Additional folders include components which contains components which are widgets such as buttons, toasts that are share between pages and screens.

Models

the Models folder

The Models folder contains the models of the application each in it own dart file.Some models extends ChangeNotifier and are use to propagate changes down the app such as to add the user name to the screen if the user log in. Example of a user model is shown below.

class User {
  String? id;
  String? firstname;
  String? lastname;
  String? mobile;
  String? email;

  User(
      {this.id,
      this.firstname,
      this.lastname,
     });
  User.fromJson(Map<String, dynamic> json)
      : id = json["id"],
        firstname = json["firstname"],
        email = json["email"],
        mobile = json["phone"],
        lastname = json["lastname"],

Enter fullscreen mode Exit fullscreen mode

An app model class is also created which is used with provider in other to provide change notification to various listeners in the application as the app state changes.

class AppModel extends ChangeNotifier {
  String? _accessToken;
  bool _isFreshInstall = true;
  User? _user;

  String? get accessToken => _accessToken;
  bool get isFreshInstall => _isFreshInstall;
  User? get user => _user;

  set setUser(User user) {
    _user = user;
    notifyListeners();
  }

  set isFreshInstall(bool fresh) {
    _isFreshInstall = fresh;
    notifyListeners();
  }

  set accessToken(String? token) {
    _accessToken = token;
    notifyListeners();
  }

  @override
  void notifyListeners() {
    super.notifyListeners();
  }
}
Enter fullscreen mode Exit fullscreen mode

Views

Views folder

Views contains the various pages or screens of your application. The Views can also contain sub-folders that contains related views together.

Services

The third folder is the Services which contains files that makes Apis calls or interact with external network such as HTTP or background location services.Files in this folder are strictly concerned with making requests.

Here is how a service look like

class UserService{

User getUser() async {
// Makes http calls here
}

}
Enter fullscreen mode Exit fullscreen mode

Commands

Commands is how you primarily interact with the Services.The Views invoke a command which in turn can call upon a service to fulfill a task. A base command is created in which other command class simply extends which makes it easy to share common models and services between various commands.

Here is how the BaseCommand class looks like:

BuildContext? _mainContext;
BuildContext get mainContext => _mainContext!;
bool get hasContext => _mainContext != null;

void setContext(BuildContext c) {
  _mainContext = c;
}

class BaseAppCommand {
  UserService get userService => getProvided();
  AppService get appService => getProvided();

  AppModel get appModel => getProvided();

  T getProvided<T>() {
    assert(_mainContext != null,
        "You must call setcontext(buildcontext before call commands");
    return _mainContext!.read<T>();
  }
}
Enter fullscreen mode Exit fullscreen mode

A bootstrap command is also used in other to configure or check app requirement such as is the user logged or initiate state or app configuration.

class BootStrapCommand extends BaseAppCommand {
  Future<void> run(BuildContext context) async {
    if (Commands.hasContext == false) {
      Commands.setContext(context);
    }
    print("Bootstrapping app....");
    await appService.init();
    appModel.accessToken = await appService.accessToken() ?? null;
    var futures = await Future.wait<dynamic>([
      appService.accessToken(),
      appService.init(),
    ]);
    appModel.accessToken = futures.first as String? ?? null;

    if (appModel.accessToken != null) {
      appService.setAccessToken(appModel.accessToken!);
    }
    appModel.isFreshInstall = appService.isAppFreshInstall;
    appModel.setUser = appService.user();
  }
}

Enter fullscreen mode Exit fullscreen mode

In this article i have tried to distill down how to structure a flutter application.

Top comments (0)