layer_kit 1.1.0 copy "layer_kit: ^1.1.0" to clipboard
layer_kit: ^1.1.0 copied to clipboard

A Flutter architecture package that combines Clean Architecture and MVVM patterns using Provider for state management.

LayerKit #

Where MVVM meets Clean Architecture

LayerKit is a powerful Flutter framework that combines the best aspects of the MVVM (Model-View-ViewModel) pattern with Clean Architecture principles to create a robust, maintainable, and scalable application structure.

pub package License: MIT

[Layer Kit]

🌟 Features #

  • Clean Project Structure - Organized folder structure that follows both MVVM and Clean Architecture principles
  • Feature Generation - CLI tools to quickly scaffold new features with all necessary components
  • Project Generation - Quickly bootstrap an entire project with best practices built-in
  • Error Handling - Standardized exceptions and failures for consistent error management
  • Configuration System - Flexible configuration for different environments
  • Extension Methods - Utility extensions for common types to reduce boilerplate code
  • UI Utilities - Helper methods for common UI tasks such as decorations
  • Logging - Development-friendly logging system

πŸ“‹ Table of Contents #

πŸš€ Installation #

Add LayerKit to your pubspec.yaml:

dependencies:
  layer_kit:

Run:

flutter pub get

🏁 Getting Started #

Generating a New Project #

Create a new Flutter project, then run:

dart run layer_kit --project

This will generate a properly structured project with all necessary files and configurations.

Generating a New Feature #

To add a new feature to your project:

dart run layer_kit --feature feature_name

This command will:

  1. Create a feature directory in the src folder
  2. Generate all necessary files following MVVM and Clean Architecture principles
  3. Update routes to include the new feature

πŸ“ Project Structure #

LayerKit organizes your project into a well-defined structure:

lib/
β”œβ”€β”€ config/                         # Configuration files
β”‚   β”œβ”€β”€ data/                       # Data-related configuration
β”‚   β”œβ”€β”€ lang/                       # Localization
β”‚   β”œβ”€β”€ routes/                     # Routing
β”‚   └── theme/                      # Theme configuration
β”œβ”€β”€ core/                           # Core components
β”‚   β”œβ”€β”€ callbacks/                  # Callback interfaces
β”‚   β”œβ”€β”€ common/                     # Common utilities
β”‚   β”‚   └── widgets/                # Reusable widgets
β”‚   β”œβ”€β”€ constants/                  # Constants
β”‚   β”œβ”€β”€ extensions/                 # Extension methods
β”‚   β”œβ”€β”€ helper/                     # Helper methods 
β”‚   β”œβ”€β”€ network/                    # Network service 
β”‚   └── utils/                      # Utilities
β”œβ”€β”€ src/                            # Features
β”‚   β”œβ”€β”€ feature1/                   # Feature module
β”‚   β”‚   β”œβ”€β”€ datasource/             # Data sources
β”‚   β”‚   β”‚   β”œβ”€β”€ models/             # Data models
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ body_models/    # UI display models (used in screens)
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ requests/       # API request models
β”‚   β”‚   β”‚   β”‚   └── response/       # API response models
β”‚   β”‚   β”‚   └── repo/               # Repository implementation
β”‚   β”‚   β”œβ”€β”€ providers/              # Feature providers
β”‚   β”‚   └── screens/                # UI screens
β”‚   └── feature2/                   # Another feature module
β”œβ”€β”€ defaults.dart                   # Default values used across the app
β”œβ”€β”€ di_container.dart               # Dependency Injection imports (part of di main)
β”œβ”€β”€ di_container.main.dart          # DI main file - injection of all dependencies (using 'Get It')
└── main.dart                       # Entry point

βš™οΈ Configuration #

LayerKit provides a flexible configuration system:

// main.dart
  await AppLocalization.init();
  await AppTheme.init();

  runApp(AppLocalization(
    child: AppTheme(
      child: MyApp(),
    ),
  ));
  
/// ...code....  

@override
Widget build(BuildContext context) {
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
    // DeviceOrientation.landscapeRight,
    // DeviceOrientation.landscapeLeft,
  ]);

  /// FOR THEME 
  ThemeConfig.init(context);
  /// FOR ROUTING (SEE https://pub.flutter-io.cn/packages/flutter_easy_routing)
  RouteConfig.setDefaultTransition(TransitionType.fade);
  
  return ToastificationWrapper(
    child: MaterialApp(
      title: AppConsts.appName,
      navigatorKey: AppRouter.navigatorKey,
      debugShowCheckedModeBanner: false,
      onGenerateRoute: (s) => AppRouter.generateRoute(s, SplashScreen()),
      scrollBehavior: const StretchScrollBehavior(),
      initialRoute: Routes.splash.path,
      localizationsDelegates: context.localizationDelegates,
      supportedLocales: context.supportedLocales,
      locale: context.locale,
      theme: context.theme.currentTheme,
      builder: (context, child) {
        return MediaQuery(
          data: MediaQuery.of(context).copyWith(textScaler: const TextScaler.linear(1.0)),
          child: child ?? SizedBox(),
        );
      },
    ),
  );
}
/// ...code....

🧠 Core Concepts #

MVVM + Clean Architecture #

LayerKit combines the MVVM pattern with Clean Architecture principles:

  • View - Flutter UI components (screens, widgets)
  • ViewModel - Business logic and state management
  • Model - Data models and repository interfaces

With additional Clean Architecture layers:

  • Repositories - Abstract data source interactions and implementations that access data models
  • Data Sources - Concrete implementations of data access including models, requests, and responses

ViewModels #

ViewModels in LayerKit extend the BaseViewModel class:

class MovieProvider extends BaseViewModel {
  final MovieRepo _movieRepo;
  final NetworkService _networkService;

  MovieProvider({required MovieRepo movieRepo, required NetworkService networkService})
          : _movieRepo = movieRepo,
            _networkService = networkService;

  Future<bool> getData({
    required String data1,
    required String data2,
    bool listen = true,
  }) async {
    setLoading();
    final isNetwork = await _networkService.isConnected;
    final isSuccess = await apiCallback(
      name: "getData",
      isNetwork: isNetwork,
      doWhenOnline: () async {
        final req = MovieReq(data1: data1, data2: data2);
        final res = await _movieRepo.getData(req);
        showSnackbar(res.message);
        setLoaded();
        return res.status;
      },
      errorListener: (e) {
        setError(e.toString());
      },
    );
    if (listen) notifyListeners();
    return isSuccess;
  }
}

Repositories #

Repositories abstract data sources:

abstract interface class MovieRepo {
  Future<MovieResponse> getData(MovieReq req);
}

class MovieRepoImpl implements MovieRepo {
  final DioClient _dioClient;

  MovieRepoImpl({required DioClient dioClient}) : _dioClient = dioClient;

  @override
  Future<MovieResponse> getData(MovieReq req) async {
    return await repoCallback<MovieResponse>(
      name: "getData",
      callback: () async {
        final res = await _dioClient.post(Apis.getData, data: req.toJson());
        return MovieResponse.fromJson(res.data ?? {});
      },
    );
  }
}

Theme Configuration #

  • All theme configuration is located under the path: lib/config/theme
  • LayerKit includes AppColors where you can define your app colors and themeColors (of type ThemeColorsModel()) where you can define your dynamic theme colors
  • By default, LayerKit loads the user's device system theme
  • There is also a ThemeSelectorDropdown widget that provides a UI to change theme colors (not dark/light mode; this is used for multiple themes. For dark and light mode, use context.toggleThemeMode(); theme extensions)

Size Configuration #

  • Dynamic width, height, and radius values
  • These are all extensions on num that return double values
  • .h -> dynamic height
    • 10.h = 10% of screen height
  • .w -> dynamic width
    • 10.w = 10% of screen width
  • .t -> dynamic value for text sizing
  • .r -> dynamic radius used for border radius, equals height and width (like for square images)

Environment Types (EnvType) #

  • This enum helps you change variables by environment
  • Set EnvType in the Defaults [D] class and add conditions based on envType
  • For example:
enum EnvType {
  development, // normal debug and development
  developmentWithPrint, // useful to print logs in running release app
  production, // set this for play store deploy bundle
  ;

  bool get isDevelopment => this == EnvType.development;
  bool get isDevelopmentWithPrint => this == EnvType.developmentWithPrint;
  bool get isProduction => this == EnvType.production;
}
  • Now you can use the envType variable like:
  static const stagingBaseUrl = "https://staging-example.com";
  static const liveBaseUrl = "https://example.com";

  static String get baseUrl => D.envType.isProduction ? liveBaseUrl : stagingBaseUrl;

Theme Atoms #

See folder: lib/config/theme/atoms
Useful for maintaining consistent layouts
Customize as per your needs

  • Text: lib/config/theme/atoms/text.dart
/// How to use text instead of Text widget
"This is text".regular16.build(
          textAlign: TextAlign.start,
          maxLines: 20,
          fontSize: 1.4.t,
          color: textColor ?? Colors.white,
          fontWeight: FontWeight.w500,
        )

/// Available types:
// titleX
// title32
// title24
// title18
// regular16
// regular14
// small13
// tiny12
// appbar
// button
  • Gap: lib/config/theme/atoms/padding.dart
  • Also includes padding extensions for ease of coding
Gap.size16.paddingHorizontal

(4.w, 1.h).paddingHV
  • VGap/HGap Spacing widgets: lib/config/theme/atoms/spacing.dart
  • Use these instead of SizedBox for spacing in columns and rows (UI)
VGap.default1,

VGap(5), // This equals SizedBox(height: 5.h); here .h is an extension used for dynamic height (5% of screen height)
  • Theme Extensions - Some theme extensions:
// Basically used to toggle theme mode (light to dark and vice versa)
context.toggleThemeMode();

// Reset theme mode to system mode
context.resetThemeMode();

// This theme colors index is used for multiple themes in the app
// There is a list of themes, users can select their favorite theme
// We store the index of this theme and save it
final index = themeColorsOptions.indexOf(value);
final i = index >= 0 ? index : null;
context.setThemeColorsIndex(i);

Localizations (Multiple Languages in App) #

  • Wrap the MainApp widget with AppLocalization
  • Localization configuration is located under the path: lib/config/lang
  • There is also a LangSelectorDropdown widget that provides a UI to change language

Local JSON Files #

  • Requires <lang-code>.json files
  • Folder path: lib/config/lang/jsons (if you change this path, also change the path in pubspec.yaml file)
  • Add a list of LanguageModel in the languages.dart file
  • If you don't have an API for localization and the app depends on local JSON files, then remove the lang_from_api folder and related errors (as we set up for API now, they might throw errors)
// languages.dart 
List<LanguageModel> languages = [
  LanguageModel(code: "en", language: "English", displayName: "English", image: "πŸ‡¬πŸ‡§"),
  LanguageModel(code: "de", language: "German", displayName: "Deutsch", image: "πŸ‡©πŸ‡ͺ"),
];

Localizations from API (for Dynamic Changes) #

  • Requires TranslationApiModel type of response (see the toJson method of this class; however, you can change it as per your response)
  • Folder path: lib/config/lang/src/lang_from_api (this is a dummy API explanation; modify as per your requirements)
  • For temporary checking, enable/disable the fetchLanguagesFromApi variable in the DummyLangApi class
  • If you don't have an API for localization and the app depends on local JSON files, then remove the lang_from_api folder and related errors (as we set up for API now, they might throw errors)

Routing #

Callbacks #

  • LayerKit contains callbacks that make lengthy functions shorter and improve logging

  • Examples of callbacks:

widgetBinding((_) {
  //code
})
onDebugOnly((){
      // code
    });
onTap: () async => await safeRun(
   name: "Feature Button",
   isEnabled: true,
   logEnabled: true,
   tryBlock: () {
     /// TODO: implement onClick logic
     showSnackbar("Feature Button Clicked");
     devlog("Feature Button Clicked");
   },
   errorHandler: (e) {
     /// TODO: handle error
     devlogError("Error occurred on Feature Button click - $e");
   },
 )),
  • Other callbacks used in API calling in repo repoCallback and provider apiCallback (main purpose is logging and identifying which API has errors)

πŸ› οΈ Extensions #

LayerKit includes several useful extensions:

// Border radius extension
D.defaultRadius.borderRadiusCircular
20.borderRadiusTop
20.borderRadiusBottom
20.borderRadiusRight

// String casing
"hello world".toTitleCase      // "Hello World"
"example".firstUpperCased      // "Example"

// Time formatting
120.toMMSS                     // "02 : 00"
3725.toHHMMSS                  // "01 : 02 : 05"

See extensions under the core folder for more.

πŸ“„ License #

LayerKit is available under the MIT license. See the LICENSE file for more info.


Created by Nayan Parmar Β© 2025

3
likes
0
points
54
downloads

Publisher

verified publishertcircle.kesug.com

Weekly Downloads

A Flutter architecture package that combines Clean Architecture and MVVM patterns using Provider for state management.

License

unknown (license)

Dependencies

args, flutter, flutter_easy_routing, path, recase

More

Packages that depend on layer_kit