├── data ├── providers │ └── api_provider.dart └── services │ └── image_services.dart ├── views ├── dialogs │ └── error_dialog.dart ├── widgets │ └── button.dart ├── pages │ ├── 01_intro │ │ └── intro_screen.dart │ └── 02_login │ │ └── login_screen.dart ├── .DS_Store └── themes │ ├── text_theme.dart │ └── theme.dart ├── controllers ├── auth │ └── auth_controller.dart └── settings │ └── settings_controller.dart ├── .DS_Store ├── screenshot.png ├── constants ├── app_colors.dart ├── app_sizes.dart ├── app_defaults.dart └── app_images.dart ├── models └── user.dart ├── utils └── ui_helper.dart └── README.md /data/providers/api_provider.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/services/image_services.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/dialogs/error_dialog.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/widgets/button.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /controllers/auth/auth_controller.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/pages/01_intro/intro_screen.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/pages/02_login/login_screen.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /controllers/settings/settings_controller.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdulmominsakib/getx_flutter_structure/HEAD/.DS_Store -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdulmominsakib/getx_flutter_structure/HEAD/screenshot.png -------------------------------------------------------------------------------- /views/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdulmominsakib/getx_flutter_structure/HEAD/views/.DS_Store -------------------------------------------------------------------------------- /constants/app_colors.dart: -------------------------------------------------------------------------------- 1 | // import 'dart:ui'; 2 | 3 | // class AppColors { 4 | // static const Color PRIMARY_COLOR = Color(0xFFFB5943); 5 | // static const Color ACCENT_COLOR = Color(0xFFFFD001); 6 | // static const Color DARK_COLOR = Color(0xFF2A2A2A); 7 | // static const Color PLACEHOLDER_COLOR = Color(0xFFC1BDCA); 8 | // static const Color DARK_PLACEHOLDER = Color(0xFF373737); 9 | // static const Color BLACK = Color(0xFF3F3D56); 10 | // } 11 | -------------------------------------------------------------------------------- /models/user.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class User { 4 | String name; 5 | String email; 6 | String pictureLink; 7 | int phone; 8 | bool isVerified; 9 | User({ 10 | required this.name, 11 | required this.email, 12 | required this.pictureLink, 13 | required this.phone, 14 | required this.isVerified, 15 | }); 16 | 17 | Map toMap() { 18 | return { 19 | 'name': name, 20 | 'email': email, 21 | 'pictureLink': pictureLink, 22 | 'phone': phone, 23 | 'isVerified': isVerified, 24 | }; 25 | } 26 | 27 | factory User.fromMap(Map map) { 28 | return User( 29 | name: map['name'], 30 | email: map['email'], 31 | pictureLink: map['pictureLink'], 32 | phone: map['phone'], 33 | isVerified: map['isVerified'], 34 | ); 35 | } 36 | 37 | String toJson() => json.encode(toMap()); 38 | 39 | factory User.fromJson(String source) => User.fromMap(json.decode(source)); 40 | } 41 | -------------------------------------------------------------------------------- /constants/app_sizes.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter/material.dart'; 2 | 3 | // /// Contains Default Values With Sizedbox Static 4 | // class AppSizes { 5 | // static const double DEFAULT_RADIUS = 16.00; 6 | // static const double DEFAULT_PADDING = 16.00; 7 | // static const double DEFAULT_MARGIN = 16.00; 8 | // /* <----------- Height Gap ------------> */ 9 | // static const hGap5 = SizedBox(height: 5); 10 | // static const hGap10 = SizedBox(height: 10); 11 | // static const hGap15 = SizedBox(height: 15); 12 | // static const hGap20 = SizedBox(height: 20); 13 | // static const hGap30 = SizedBox(height: 30); 14 | // static const hGap40 = SizedBox(height: 40); 15 | // /* <----------- Weight Gap ------------> */ 16 | // static const wGap5 = SizedBox(width: 5); 17 | // static const wGap10 = SizedBox(width: 10); 18 | // static const wGap15 = SizedBox(width: 15); 19 | // static const wGap20 = SizedBox(width: 20); 20 | 21 | // /// Used For Cover 22 | // static const double DEFAULT_ASPECT_RATIO = 2 / 2; 23 | // } 24 | -------------------------------------------------------------------------------- /views/themes/text_theme.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter/material.dart'; 2 | // import 'package:get/route_manager.dart'; 3 | 4 | // /// To Make a clean look at code. These are all theme specific style 5 | // class AppText { 6 | // // For easy access on the app 7 | // static TextStyle get b1 => Get.textTheme.bodyText1!; 8 | // static TextStyle get b2 => Get.textTheme.bodyText2!; 9 | 10 | // static TextStyle get h1 => Get.textTheme.headline1!; 11 | // static TextStyle get h2 => Get.textTheme.headline2!; 12 | // static TextStyle get h3 => Get.textTheme.headline3!; 13 | // static TextStyle get h4 => Get.textTheme.headline4!; 14 | // static TextStyle get h5 => Get.textTheme.headline5!; 15 | // static TextStyle get h6 => Get.textTheme.headline6!; 16 | 17 | // static TextStyle get caption => Get.textTheme.caption!; 18 | // static TextStyle get subtitle1 => Get.textTheme.subtitle1!; 19 | // static TextStyle get subtitle2 => Get.textTheme.subtitle2!; 20 | 21 | // /* <---- Extra ----> */ 22 | // static TextStyle get bLight => 23 | // Get.textTheme.bodyText1!.copyWith(fontWeight: FontWeight.w100); 24 | // } 25 | -------------------------------------------------------------------------------- /constants/app_defaults.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter/material.dart'; 2 | // import 'app_sizes.dart'; 3 | 4 | // class AppDefaults { 5 | // /// Used For Border Radius 6 | // static BorderRadius defaulBorderRadius = 7 | // BorderRadius.circular(AppSizes.DEFAULT_RADIUS); 8 | 9 | // /// Used For Bottom Sheet 10 | // static BorderRadius defaultBottomSheetRadius = BorderRadius.only( 11 | // topLeft: Radius.circular(AppSizes.DEFAULT_RADIUS), 12 | // topRight: Radius.circular(AppSizes.DEFAULT_RADIUS), 13 | // ); 14 | 15 | // /// Used For Top Sheet 16 | // static BorderRadius defaultTopSheetRadius = BorderRadius.only( 17 | // bottomLeft: Radius.circular(AppSizes.DEFAULT_RADIUS), 18 | // bottomRight: Radius.circular(AppSizes.DEFAULT_RADIUS), 19 | // ); 20 | 21 | // /// Default Box Shadow used for containers 22 | // static List defaultBoxShadow = [ 23 | // BoxShadow( 24 | // blurRadius: 25, 25 | // spreadRadius: 0, 26 | // offset: Offset(0, 2), 27 | // color: Colors.black.withOpacity(0.08), 28 | // ), 29 | // ]; 30 | 31 | // /// Default Animation Duration used for the entire app 32 | // static Duration defaultDuration = Duration(milliseconds: 300); 33 | // } 34 | -------------------------------------------------------------------------------- /constants/app_images.dart: -------------------------------------------------------------------------------- 1 | class AppImages { 2 | static const String LOGO = 'assets/images/logo.png'; 3 | static const String LOGIN_HERO = 'assets/images/login_hero_image.png'; 4 | static const String GOOGLE_LOGO = 'assets/images/google_logo.png'; 5 | 6 | /// Intro Images 7 | static const String INTRO_0 = 'assets/images/intro_images/intro_0.png'; 8 | static const String INTRO_1 = 'assets/images/intro_images/intro_1.png'; 9 | static const String INTRO_2 = 'assets/images/intro_images/intro_2.png'; 10 | 11 | // User Photo 12 | static const String USER_IMAGE = 13 | 'https://images.unsplash.com/photo-1527980965255-d3b416303d12?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1160&q=80'; 14 | 15 | /* <---- Cover Photo Link ----> */ 16 | static const List dummyImages = [ 17 | 'https://vip-go.shutterstock.com/blog/wp-content/uploads/sites/5/2020/03/Guide-to-Podcast-Art-Images.jpg', 18 | 'https://images.unsplash.com/photo-1524678606370-a47ad25cb82a?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1738&q=80', 19 | 'https://images.unsplash.com/photo-1590602846989-e99596d2a6ee?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1740&q=80', 20 | 'https://images.unsplash.com/photo-1487537023671-8dce1a785863?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NHx8cG9kY2FzdHxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=900&q=60', 21 | 'https://images.unsplash.com/photo-1508700115892-45ecd05ae2ad?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1738&q=80', 22 | 'https://images.unsplash.com/photo-1559523161-0fc0d8b38a7a?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1760&q=80', 23 | ]; 24 | } 25 | -------------------------------------------------------------------------------- /utils/ui_helper.dart: -------------------------------------------------------------------------------- 1 | // import 'dart:math'; 2 | 3 | // import 'package:flutter/material.dart'; 4 | // import 'package:flutter/services.dart'; 5 | 6 | // class AppUiHelper { 7 | // /// Dismissises Keyboard From Anywhere 8 | // static void dismissKeyboard({required BuildContext context}) { 9 | // FocusScopeNode currentFocus = FocusScope.of(context); 10 | // if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 11 | // FocusManager.instance.primaryFocus!.unfocus(); 12 | // } 13 | // } 14 | 15 | // /// Set The Portrait as Default Orientation 16 | // static void dontAutoRotate() async { 17 | // await SystemChrome.setPreferredOrientations( 18 | // [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); 19 | // } 20 | 21 | // /* <---- Generate Material Color for the whole app ----> */ 22 | // static MaterialColor generateMaterialColor(Color color) { 23 | // return MaterialColor(color.value, { 24 | // 50: tintColor(color, 0.9), 25 | // 100: tintColor(color, 0.8), 26 | // 200: tintColor(color, 0.6), 27 | // 300: tintColor(color, 0.4), 28 | // 400: tintColor(color, 0.2), 29 | // 500: color, 30 | // 600: shadeColor(color, 0.1), 31 | // 700: shadeColor(color, 0.2), 32 | // 800: shadeColor(color, 0.3), 33 | // 900: shadeColor(color, 0.4), 34 | // }); 35 | // } 36 | 37 | // static int tintValue(int value, double factor) => 38 | // max(0, min((value + ((255 - value) * factor)).round(), 255)); 39 | 40 | // static Color tintColor(Color color, double factor) => Color.fromRGBO( 41 | // tintValue(color.red, factor), 42 | // tintValue(color.green, factor), 43 | // tintValue(color.blue, factor), 44 | // 1); 45 | 46 | // static int shadeValue(int value, double factor) => 47 | // max(0, min(value - (value * factor).round(), 255)); 48 | 49 | // static Color shadeColor(Color color, double factor) => Color.fromRGBO( 50 | // shadeValue(color.red, factor), 51 | // shadeValue(color.green, factor), 52 | // shadeValue(color.blue, factor), 53 | // 1); 54 | // } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Folder Structure With GetX 🔥 2 | 3 | Basically This is the structure what I used for almost my all production projects. With Some tweaks here and there and from this video of CodeX, I think it is efficient enough now. You can watch the video to grasp about how this structure will work for your project. 4 | 5 | Developers don't have to repeat themselves, and they shouldn't be. That's why I created this repo. You can use it with bloc or provider or riverpod as well. 6 | 7 | Download the structure here. ⬇️ 8 | 9 | * After downloading the release uncomment the files, you will find some helpful function 10 | 11 | 12 | 13 | # Why ? 🤔 14 | 15 | This structure follows MVC pattern. This is what you see on Web frameworks mostly. Flutter uses dart for it's UI and LOGIC. That's why it is quite easy to mess up a project. And this is the reason you need a structure, which every developer will understand when he looks at it. 🚀 😁 16 | 17 | # About 📖 18 | 19 | ## - Consts 20 | 21 | -- All of the unchanged value will be here, 22 | Which are not going to be changed very much and same accross the apps own universe. Like 23 | 24 | const String APPLOGO = "assets/logo.png"; 25 | 26 |
27 | 28 | ## - Controllers 29 | 30 | -- The heart of your app 💙 should be cleaned with a folder with each controller. Because as your app grows you will be in need of more controllers, and folder keep things organized. 31 | 32 |
33 | 34 | ## - Models 35 | 36 | -- How your data will be presented in a structural way. You can keep your model class here. 37 | 38 |
39 | 40 | ## - Data 41 | 42 | #### ___ Provider 43 | -- This will communicate with your database. All of the API, Local connection will be made here. You can use this for your controller 44 | 45 | #### ___Services 46 | 47 | --- Which aims to organize the services. Like Image_Cropping, Play Audio 48 | 49 |
50 | 51 | ## - Utils 52 | 53 | -- You can create your small function which doesn't involve any object. And which only accepts normal Type like String, integar etc. Like formatting a phone number with a simple function. 54 | 55 |
56 | 57 | ## - Views 58 | 59 | -- Dialogs 60 | --- All of you dialogs and pop up will be here. You can also seperate it by folder per feature. 61 | 62 | 63 | -- Pages 64 | --- All of your UI pages will be here. You can rename them like "signup-page.dart". And Organize them in their respective folder. 65 | 66 | 67 | -- Themes 68 | --- You can make theme file seperate for each theme like "light_theme.dart". 69 | 70 | 71 | -- Widgets 72 | --- All of your custom Button, Text Fields, Dropdowns will be here. 73 | 74 |
75 | 76 | This project is open for contribution. You can make changes that suits your needs and effective for you and which everybody will understand. The structure and pattern are made for make developer life easier. Peace. 😊 77 | -------------------------------------------------------------------------------- /views/themes/theme.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter/material.dart'; 2 | // import 'package:flutter_podcast_app/consts/app_colors.dart'; 3 | // import 'package:flutter_podcast_app/consts/app_defaults.dart'; 4 | // import 'package:flutter_podcast_app/consts/app_sizes.dart'; 5 | // import 'package:flutter_podcast_app/utils/ui_helper.dart'; 6 | // import 'package:flutter_podcast_app/views/themes/text.dart'; 7 | // import 'package:google_fonts/google_fonts.dart'; 8 | 9 | // class AppThemes { 10 | // /* <-----------------------> 11 | // Light Theme 12 | // <-----------------------> */ 13 | // static ThemeData lightTheme = ThemeData( 14 | // primaryColor: AppColors.PRIMARY_COLOR, 15 | // brightness: Brightness.light, 16 | // textTheme: GoogleFonts.poppinsTextTheme(), 17 | // scaffoldBackgroundColor: Colors.white, 18 | // appBarTheme: AppBarTheme( 19 | // actionsIconTheme: IconThemeData( 20 | // color: AppColors.DARK_COLOR, 21 | // ), 22 | // titleTextStyle: TextStyle( 23 | // color: AppColors.DARK_COLOR, 24 | // fontWeight: FontWeight.bold, 25 | // ), 26 | // centerTitle: true, 27 | // backgroundColor: Colors.white, 28 | // iconTheme: IconThemeData(color: AppColors.DARK_COLOR), 29 | // toolbarTextStyle: AppText.b1, 30 | // elevation: 0, 31 | // ), 32 | // inputDecorationTheme: InputDecorationTheme( 33 | // enabledBorder: OutlineInputBorder( 34 | // borderRadius: AppDefaults.defaulBorderRadius, 35 | // borderSide: BorderSide.none, 36 | // ), 37 | // focusedBorder: OutlineInputBorder( 38 | // borderRadius: AppDefaults.defaulBorderRadius, 39 | // borderSide: BorderSide( 40 | // color: AppColors.PLACEHOLDER_COLOR, 41 | // ), 42 | // ), 43 | // fillColor: AppColors.PLACEHOLDER_COLOR.withOpacity(0.2), 44 | // filled: true, 45 | // ), 46 | // iconTheme: IconThemeData( 47 | // color: AppColors.PRIMARY_COLOR, 48 | // ), 49 | // elevatedButtonTheme: ElevatedButtonThemeData( 50 | // style: ElevatedButton.styleFrom( 51 | // padding: EdgeInsets.all(AppSizes.DEFAULT_PADDING), 52 | // ), 53 | // ), 54 | // outlinedButtonTheme: OutlinedButtonThemeData( 55 | // style: OutlinedButton.styleFrom( 56 | // padding: EdgeInsets.all(AppSizes.DEFAULT_PADDING), 57 | // ), 58 | // ), 59 | // colorScheme: ColorScheme.fromSwatch( 60 | // primarySwatch: 61 | // AppUiHelper.generateMaterialColor(AppColors.PRIMARY_COLOR)) 62 | // .copyWith(secondary: AppColors.ACCENT_COLOR), 63 | // ); 64 | 65 | // /* <-----------------------> 66 | // Dark Themes For this app 67 | // <-----------------------> */ 68 | 69 | // static ThemeData darkTheme = ThemeData( 70 | // primaryColor: AppColors.PRIMARY_COLOR, 71 | // scaffoldBackgroundColor: AppColors.DARK_COLOR, 72 | // brightness: Brightness.dark, 73 | // cardColor: AppColors.DARK_COLOR.withOpacity(0.7), 74 | // textTheme: GoogleFonts.poppinsTextTheme().apply( 75 | // bodyColor: Colors.white, 76 | // displayColor: Colors.white, 77 | // ), 78 | // appBarTheme: AppBarTheme( 79 | // actionsIconTheme: IconThemeData( 80 | // color: AppColors.PRIMARY_COLOR, 81 | // ), 82 | // titleTextStyle: TextStyle( 83 | // color: Colors.white, 84 | // fontWeight: FontWeight.bold, 85 | // ), 86 | // centerTitle: true, 87 | // backgroundColor: AppColors.DARK_COLOR, 88 | // iconTheme: IconThemeData(color: AppColors.PRIMARY_COLOR), 89 | // toolbarTextStyle: AppText.b1, 90 | // elevation: 0, 91 | // ), 92 | // inputDecorationTheme: InputDecorationTheme( 93 | // // enabledBorder: OutlineInputBorder( 94 | // // borderRadius: AppDefault.defaulBorderRadius, 95 | // // borderSide: BorderSide( 96 | // // color: AppColors.PLACEHOLDER_COLOR, 97 | // // ), 98 | // // ), 99 | // // focusedBorder: OutlineInputBorder( 100 | // // borderRadius: AppDefault.defaulBorderRadius, 101 | // // borderSide: BorderSide( 102 | // // color: AppColors.ACCENT_COLOR, 103 | // // ), 104 | // // ), 105 | // ), 106 | // iconTheme: IconThemeData( 107 | // color: AppColors.PRIMARY_COLOR, 108 | // ), 109 | // elevatedButtonTheme: ElevatedButtonThemeData( 110 | // style: ElevatedButton.styleFrom( 111 | // padding: EdgeInsets.all(AppSizes.DEFAULT_PADDING), 112 | // ), 113 | // ), 114 | // outlinedButtonTheme: OutlinedButtonThemeData( 115 | // style: OutlinedButton.styleFrom( 116 | // padding: EdgeInsets.all(AppSizes.DEFAULT_PADDING), 117 | // side: BorderSide( 118 | // color: AppColors.PRIMARY_COLOR, 119 | // ), 120 | // ), 121 | // ), 122 | // colorScheme: ColorScheme.fromSwatch( 123 | // primarySwatch: 124 | // AppUiHelper.generateMaterialColor(AppColors.PRIMARY_COLOR)) 125 | // .copyWith(secondary: AppColors.ACCENT_COLOR), 126 | // ); 127 | // } 128 | --------------------------------------------------------------------------------