{"id":26578071,"url":"https://github.com/emadbeltaje/flutter_getx_template","last_synced_at":"2025-04-06T06:06:39.113Z","repository":{"id":37265829,"uuid":"502160287","full_name":"EmadBeltaje/flutter_getx_template","owner":"EmadBeltaje","description":"Create flutter project with all needed configuration in two minutes (theme, localization, connect to firebase, FCM, local notifications, safe API call, error handling, animation..etc)","archived":false,"fork":false,"pushed_at":"2024-09-01T22:29:11.000Z","size":6750,"stargazers_count":451,"open_issues_count":1,"forks_count":121,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-03-30T05:03:34.337Z","etag":null,"topics":["android","api","app","dart","error-handle","fcm","firebase","flutter","flutter-example","get-cli","getx","getx-template","ios","localization","notifications","patterns","template","theme","ui"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EmadBeltaje.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-10T19:35:31.000Z","updated_at":"2025-03-26T15:43:45.000Z","dependencies_parsed_at":"2024-08-08T17:21:46.809Z","dependency_job_id":null,"html_url":"https://github.com/EmadBeltaje/flutter_getx_template","commit_stats":null,"previous_names":[],"tags_count":1,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EmadBeltaje%2Fflutter_getx_template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EmadBeltaje%2Fflutter_getx_template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EmadBeltaje%2Fflutter_getx_template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EmadBeltaje%2Fflutter_getx_template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EmadBeltaje","download_url":"https://codeload.github.com/EmadBeltaje/flutter_getx_template/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247441042,"owners_count":20939239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","api","app","dart","error-handle","fcm","firebase","flutter","flutter-example","get-cli","getx","getx-template","ios","localization","notifications","patterns","template","theme","ui"],"created_at":"2025-03-23T04:19:48.697Z","updated_at":"2025-04-06T06:06:39.090Z","avatar_url":"https://github.com/EmadBeltaje.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\n# Flutter GetX Template\n\nFlutter Getx template: Starting up new project with all needed configuration has never been easier.\n\n\u003cimg src=\"preview_images/github.png\" width=\"100%\"\u003e\n\n## Used By\n- [Accessline Company](https://accessline.ps/)\n- [Smart Angle Group](https://www.smartanglegroup.com)\n- [Squarement](https://www.squarement.sa/)\n- GACA (+10k) ( [Google Play](https://play.google.com/store/apps/details?id=com.talapps.hrApp\u0026hl=en) - [Apple Store](https://apps.apple.com/il/app/%D8%A8%D9%88%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%B8%D9%81/id1616229306) )\n- Hr App ( [Google Play](https://play.google.com/store/apps/details?id=com.talapps.hrApp) - [Apple Store]() )\n- Jumpers ( [Google Play](https://play.google.com/store/apps/details?id=com.jumpers.ksa) - [Apple Store](https://apps.apple.com/us/app/jumpers-app/id1662183546) )\n- Open Source : [Ecommerce Ui Kit](https://github.com/AbdQader/flutter_ecommerce_app)\n- Open Source : [Weather App](https://github.com/AbdQader/flutter_weather_app)\n- thunder_cli : [package](https://pub.dev/packages/thunder_cli)\n\n## Introduction\n\nWe all face the same problem when we want to start a new project we have to take care of some repeatable things such as\n- Theme (light/dark) \u0026 store current theme in shared pref 🌒\n- Localization \u0026 store the current locale in shared pref 🅰️\n- Firebase Messaging 📨\n- Notifications setup 🔔\n- Safe api requests \u0026 error handling 🔏\n- Changing between widgets during api call (loading,success,failed..etc) 🔥\n- Snackbar,Toasts \u0026 in app notifications 🪖\n- Making app more responsive and stop font scaling 🚀\n- Change app Icon, Name and package id 🖋️\n\nThis project will take care of all this repeatable things so you can start your project in few steps and you will have all the mentioned points set up and ready to use 😎\n\n## Latest Updates ([Updated Branch](https://github.com/EmadBeltaje/flutter_getx_template/tree/upgrade_to_latest_flutter_and_getx_version)) 🚀\n- [x] **Upgraded** project to work with the **latest Flutter versions** 🔥\n- [x] **Updated** to **GetX 5** 🚀\n- [x] **Fixed** deprecated code and ensured compatibility 🛠️\n- [x] **Refactored** code and resolved all Dart lints 🛠️\n\n\n## What is new 🌟\n- [x] **GetX 5** support \n- [x] Separate Local Notification helper from fcm helper 🛠️\n- [x] Integration Test for BaseClient 🧪\n- [x] Integration Test for Awesome Notifications Helper 🧪\n- [x] Integration Test for Widget Animator 🧪\n- [x] Unit Test also for BaseClient 🧪\n- [x] Unit Test for MySharedPreference 🧪\n- [x] Unit Test for MyHive 🧪\n- [x] Unit Test for Localization Service 🧪\n- [x] Replace get_storage with SharedPref (unsolved testing problems with get_storage) 📦️\n\n[//]: # (## Acknowledgment)\n\n[//]: # (Project was created using [get_cli]\u0026#40;https://pub.dev/packages/get_cli\u0026#41; which is a great tool helping you to \u0026#40;start project,create screens/controllers, handling DI\u0026#41;..etc and we will list other packages that helped to create this skeleton)\n\n[//]: # (- [GetX]\u0026#40;https://pub.dev/packages/get\u0026#41; for state management,navigation,managing dependencies..etc)\n\n[//]: # (- [flutter_screenutil]\u0026#40;https://pub.dev/packages/flutter_screenutil\u0026#41; to make app more responsive)\n\n[//]: # (- [hive]\u0026#40;https://pub.dev/packages/hive\u0026#41; as local database)\n\n[//]: # (- [shared_preferences]\u0026#40;https://pub.dev/packages/shared_preferences\u0026#41; store data persistently as key/value)\n\n[//]: # (- [awesome_notifications]\u0026#40;https://pub.dev/packages/awesome_notifications\u0026#41; for local notification)\n\n## Clone and start project\n- To make your app responsive and look exactly as your (xd,figma..etc) design you need to set artbord size for flutter_ScreenUtil in main.dart\n    ```dart\n    ScreenUtilInit(\n      designSize: const Size(375, 812), // change this to your xd artboard size\n    ```\n\n\n- FCM \u0026 Awesome Notifications are initialized in main.dart so when ever you connect your app to firebase your app will be ready to receive notifications you don't need to do anything, if you want to send token to api you can find this function in FcmHelper class 😎\n    ```dart\n    static _sendFcmTokenToServer(){\n        var token = MySharedPref.getFcmToken();\n        // TODO SEND FCM TOKEN TO SERVER\n    }\n    ```\n\n- Change app package name\n    ```\n    flutter pub run change_app_package_name:main com.new.package.name\n    ```\n\n- Change app name\n    ```\n    flutter pub run rename_app:main all=\"My App Name\"\n    ```\n\n- Change app launch icon (replace assets/images/app_icon.png with your app icon) then run this command\n    ```\n    flutter pub run flutter_launcher_icons:main\n    ```\n\n- FCM: firebase has recently added (add flutter app) to your firebase which will make adding our flutter(android/ios) app to firebase take only 2 steps 🔥 but first you need to download [Firebase CLI](https://firebase.google.com/docs/cli?authuser=0\u0026hl=en#install_the_firebase_cli) and in the terminal execute:\n    ```\n    dart pub global activate flutterfire_cli\n    ```\n  then follow the firebase guid you will get command similar to this one\n    ```\n    flutterfire configure --project=flutter-firebase-YOUR_PROJECT_ID\n    ```\n  and that's it! your project is now connected to firebase and fcm is up and ready to get notifications\n  ##### Important Note\n  IOS require few more steps from your side to recive fcm notifications follow the [Dcos](https://firebase.flutter.dev/docs/messaging/apple-integration/) steps and after that everything should be working fine from flutter side\n\n## Quick Start\n- Responsive app: to make your app responsive you need to get advantage of using flutter_ScreenUtil so instead of using normal double values for height,width,radius..etc you need to use it like this\n```dart\n200.w // adapted to screen width\n100.h // /Adapted to screen height\n25.sp // adapted font size\n10.r // adapted radius\n// Example\nContainer(\n    height: 100.h,\n    width: 200.w,\n    child: Text(\"Hello\",style: TextStyle(fontSize: 20.sp,))\n)\n```\n\n- Theme\n  - Change theme\n\n      ```dart\n      MyTheme.changeTheme();\n      ```\n\n  - Check current theme\n\n      ```dart\n      bool isThemeLight = MyTheme.getThemeIsLight();\n      ```\n\n- Localization\n  - Change app locale\n\n      ```dart\n      LocalizationService.updateLanguage('en');\n      ```\n\n  - Get current locale\n\n      ```dart\n      LocalizationService.getCurrentLocal();\n      ```\n\n  - Use translation\n\n      ```dart\n      Text(Strings.hello.tr)\n      ```\n\n- Safe api call\n  - logic code (in controller)\n      ```dart\n        // hold data coming from api\n        List\u003cdynamic\u003e data;\n    \n        // api call status\n        ApiCallStatus apiCallStatus = ApiCallStatus.holding;\n\n        // getting data from api\n        getData() async {\n          // *) perform api call\n          await BaseClient.safeApiCall(\n            Constants.todosApiUrl, // url\n            RequestType.get, // request type (get,post,delete,put),\n            onLoading: () {\n              // *) indicate loading state\n              apiCallStatus = ApiCallStatus.loading;\n              update();\n            },\n            onSuccess: (response){ // api done successfully\n              data = List.from(response.data);\n              // -) indicate success state\n              apiCallStatus = ApiCallStatus.success;\n              update(); // update ui\n            },\n            // if you don't pass this method base client\n            // will automatically handle error and show error message to user\n            onError: (error){\n              // show error message to user\n              BaseClient.handleApiError(error);\n              // -) indicate error status\n              apiCallStatus = ApiCallStatus.error;\n              update(); // update ui\n            },\n          );\n        }\n      ```\n  - UI: MyWidgetsAnimator will animate between widgets depending on current api call status\n\n      ```dart\n      GetBuilder\u003cHomeController\u003e(\n      builder: (controller){\n        return MyWidgetsAnimator(\n            apiCallStatus: controller.apiCallStatus,\n            loadingWidget: () =\u003e const Center(child: CircularProgressIndicator(),),\n            errorWidget: ()=\u003e const Center(child: Text('Something went wrong!'),),\n            successWidget: () =\u003e\n               ListView.separated(\n                itemCount: controller.data!.length,\n                separatorBuilder: (_,__) =\u003e SizedBox(height: 10.h,),\n                itemBuilder: (ctx,index) =\u003e ListTile(\n                    title: Text(controller.data![index]['userId'].toString()),\n                    subtitle: Text(controller.data![index]['title']),\n                  ),\n              ),\n\n        );\n      },\n    )\n      ```\n\n- Snackbars (in app notify):\n\n    ```dart\n    CustomSnackBar.showCustomSnackBar(title: 'Done successfully!', message: 'item added to wishlist');\n    CustomSnackBar.showCustomErrorSnackBar(title: 'Failed!', message: 'failed to load data');\n    CustomSnackBar.showCustomToast(message: 'added to card');\n    CustomSnackBar.showCustomErrorToast(message: 'added to card');\n    ```\n\n  \u003cimg src=\"preview_images/success_snackbar.jpg\" width=\"170px\"\u003e\u0026nbsp;\u0026nbsp;\u003cimg src=\"preview_images/fail_snackbar.jpg\" width=\"170px\"\u003e\u0026nbsp;\u0026nbsp;\u003cimg src=\"preview_images/success_toast.jpg\" width=\"170px\"\u003e\u0026nbsp;\u0026nbsp;\u003cimg src=\"preview_images/fail_toast.jpg\" width=\"170px\"\u003e\n\n## Discovering Project\nAfter setting up all the needed thing now lets talk about folder structure which is mainly based on Getx Pattern and there are some personal opinions, if you open your lib folder you will find those folders\n\n```\n.\n└── lib\n    ├── app\n    │   ├── components\n    │   ├── data\n    │   │   ├── local\n    │   │   └── models\n    │   ├── modules\n    │   │   └── home\n    │   ├── routes\n    │   └── services\n    ├── config\n    │   ├── theme\n    │   └── translation\n    └── utils\n```\n\n- app: will contain all our core app logic\n  - components: will contain all the shared UI widgets\n  - data: will contain our models and local data sources (local db \u0026 shared pref)\n  - modules: app screens\n  - routes: generated by get_cli and it will contain our navigation routes\n  - services: contain all logic for making safe \u0026 clean api calls\n- config: will contain app config such as themes, localization services\n- utils: for our helper classes\n## Features\n- Theme: if you opened theme package you will see those files\n\n    ```\n    └── theme\n        ├── dark_theme_colors.dart\n        ├── light_theme_colors.dart\n        ├── my_fonts.dart\n        ├── my_styles.dart\n        └── my_theme.dart\n   \n    ```\n\n  you only need to change app colors (light/dark_theme_colors) and if you want to change app fonts sizes and family just modify my_fonts.dart and that is it you don't need to worry about styles and theme you only need to edit my_syles.dart if you want to change some element theme data (padding,border..etc) and if you want to change theme just use this code\n\n    ```dart\n    // change theme and save current theme state to shared pref\n    MyTheme.changeTheme();\n    ```\n\n  and if you want to check if the theme is dark/light just use\n    ```dart\n    bool themeIsLight = MyTheme.getThemeIsLight();\n    // OR\n    bool themeIsLight = MySharedPref.getThemeIsLight();\n    ```\n- Localization/translation we will use getx localization system which in the normal case code would look something like this\n\n    ```dart\n    class LocalizationService extends Translations {\n        @override\n        Map\u003cString, Map\u003cString, String\u003e\u003e get keys =\u003e {\n            'en_US': { 'hello' : 'Hello' },\n            'ar_AR': { 'hello' : 'مرحباً' },\n        };\n    }\n\n    Text('hello'.tr); // translated text \n  ```\n\n  but because we have so many words to translate we will separate keys file (strings_enum.dart) and languages map into different classes so code will become like this\n\n  ```dart\n  class LocalizationService extends Translations {\n        @override\n        Map\u003cString, Map\u003cString, String\u003e\u003e get keys =\u003e {\n            'en_US': enUs,\n            'ar_AR': arAR,\n        };\n    }\n  // keys\n  class Strings {\n      static const String hello = 'hello';\n  }\n  // english words\n  const Map\u003cString, String\u003e enUs = {\n      Strings.hello : 'Hello',\n  }\n  // arabic translate\n  final Map\u003cString, String\u003e arAR = {\n      Strings.hello : 'مرحبا',\n  }\n  //result\n  Text(Strings.hello.tr)\n  ```\n\n  and that explain why we have this file structure inside our translation package\n\n     ```\n        └── translations\n            ├── ar_Ar\n            │   └── ar_ar_translation.dart\n            ├── en_US\n            │   └── en_us_translation.dart\n            ├── localization_service.dart\n            └── strings_enum.dart\n     ```\n\n  to change language you will use\n\n    ```dart\n    LocalizationService.updateLanguage('en');\n    ```\n\n  and to get the current locale/language you can use\n\n    ```dart\n    LocalizationService.getCurrentLocal();\n    // OR\n    MySharedPref.getCurrentLocal();\n    ```\n\n- Safe api call: under if you opened lib/app/services package you will find 3 files\n  - api_call_status.dart: which contain all possible stages of our api call (loading,success,error..etc)\n  - api_exception.dart: custom exception class to make error handling more informative\n  - base_client.dart: contain our safe api call functions\n    to perform api request the right way you would do this\n\n```dart\nclass HomeController extends GetxController {\n  // hold data\n  List\u003cdynamic\u003e? data;\n  // api call status\n  ApiCallStatus apiCallStatus = ApiCallStatus.holding;\n\n  // getting data from api simulating\n  getData() async {\n    // *) indicate loading state\n    apiCallStatus = ApiCallStatus.loading;\n    update();\n    // *) perform api call\n    await BaseClient.safeApiCall(\n      Constants.todosApiUrl, // url\n      RequestType.get,\n      onSuccess: (response){ // api done successfully\n        data = List.from(response.data);\n        // -) indicate success state\n        apiCallStatus = ApiCallStatus.success;\n        update(); // update ui\n      },\n      // if you don't pass this method base client\n      // will automatically handle error and show message\n      onError: (error){\n        // show error message to user\n        BaseClient.handleApiError(error);\n        // -) indicate error status\n        apiCallStatus = ApiCallStatus.error;\n        update(); // update ui\n      }, // error while performing request\n    );\n  }\n\n  @override\n  void onInit() {\n    getData();\n    super.onInit();\n  }\n}\n```\n\nbase client will catch all the possible errors and if you didn't pass onError function it will automatically catch the error in UI side code will be\n\n```dart\nGetBuilder\u003cHomeController\u003e(\n    builder: (_){\n        return MyWidgetsAnimator(\n            apiCallStatus: controller.apiCallStatus,\n            loadingWidget: () =\u003e const Center(child: CircularProgressIndicator(),),\n            errorWidget: ()=\u003e const Center(child: Text('Something went wrong!'),),\n            successWidget: () =\u003e\n            ListView.separated(\n            itemCount: controller.data!.length,\n            separatorBuilder: (_,__) =\u003e SizedBox(height: 10.h,),\n            itemBuilder: (ctx,index) =\u003e ListTile(\n            title: Text(controller.data![index]['userId'].toString()),\n            subtitle: Text(controller.data![index]['title']),\n            ),\n        ),\n        \n        );\n    },\n)\n```\n**NOTE:** MyWidgetsAnimator will take care of ui changing with animation you will pass the ApiCallStatus and success,failed,loading..etc widgets and it will take care of transition\n\n## Thanks To Contributors 🧡\n\n[![Contributors](https://contrib.rocks/image?repo=EmadBeltaje/flutter_getx_template)](https://github.com/EmadBeltaje/flutter_getx_template/graphs/contributors)\n\nThanks to all the amazing contributors who have helped improve this project! 😇\n\n## Support\n\nFor support, email emadbeltaje@gmail.com or Facebook [Emad Beltaje](https://www.facebook.com/EmadBeltaje/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femadbeltaje%2Fflutter_getx_template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femadbeltaje%2Fflutter_getx_template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femadbeltaje%2Fflutter_getx_template/lists"}