Orkun Client

Environment Variable

Use envied from pub.dev

  • Add new enviroment variable in .env file

  • Run cmd dart run build_runner build

  • Add new field in env.dart

@EnviedField()
static String requestHeaderApiKey = _Env.requestHeaderApiKey;

REST Api

The class is responsible for managing HTTP requests, including setting headers, building URLs, and handling responses and this class is a good example of how to manage HTTP requests in a Flutter application

  • api_service.dart -> ApiService: User Authentication Management and many more API Services

  • apis.dart -> Apis: All the end-points Url resource should be adding here as constant variables

  • http_manager. dart

    • class CustomHttpManager extends HttpManager {
        
        /// Init class constructor
        CustomHttpManager() {}
        
        
        /// Use override function from based class HttpManager
        /// buildOptions function for add/customize httpHeaders
        @override
        Options buildOptions(String method, CachePolicy? policy) {
          httpHeaders['x-language'] = CoreConfig.LANG;
          
         ///Returns an Options object containing the headers and HTTP method. 
         return Options(headers: httpHeaders, method: method);
        }
      
      
        /// This method returns the base URL for the API by calling Apis.resolveHost(), 
        /// which likely determines the correct host based on the environment or configuration.
        @override
        String buildBaseUrl() => Apis.resolveHost();
        
        ///This method retrieves the authentication token 
        /// from the application instance.
        @override
        String? getToken() => Application.instance.getToken();
      
      
        /// Returning Parameters: 
        /// It returns a map containing all the constructed query parameters.
        @override
        Map<String, dynamic> buildQueryParameters(
          Map<String, dynamic>? queryParameters, String? url, {String? method}) {
          
          var params = <String, dynamic>{};
          params['os'] = deviceType;
          params['oss'] = _deviceId;
          params['t'] = t.toString();
          params['device_id'] = _deviceId;
          if (!TextUtil.isEmpty(getToken())) {
            params['token'] = getToken();
          }
          
          return map; 
        }
        
        
        ///Purpose: This method intercepts the result data returned from an HTTP request. 
        /// It allows you to handle specific response codes or 
        /// modify the response before it is returned to the caller.
        /// Handling Unauthorized Access (401):
        @override
        ResultData interceptResultData(ResultData data) {
        /// If the response code is 401 (Unauthorized), 
        /// it indicates that the user is not authenticated or the token has expired.
          return data;
        }
      
      
      }

Analysis

The class named BaseAnalysis that is responsible for managing and logging various user-related events and actions within the application, using the Firebase Analytics and Smartlook services

  1. FirebaseAnalytics Instance: The class has a private _analytics property that holds an instance of the FirebaseAnalytics class

  2. event(String name, [Map<String, Object?>? parameters]): This method is responsible for logging an event with the provided name and optional parameters.

  3. _convertSafeMap(Map<String, Object?> values): This private method is responsible for converting the provided parameters map to a safe format

  4. logAppOpen(): This method is responsible for logging the "app_open" event. It calls the logAppOpen() method of the _analytics instance, the trackEvent() method of the Smartlook.instance.trackEvent() instance, and sets the user ID and user properties in the _analytics instance if the user is logged in.

  5. setAnalyticsUserId(String userId): This method is responsible for setting the user ID in the _analytics instance.

  6. trackNavigationEnter(String name): This method is responsible for logging the screen view event. It calls the logScreenView() method of the _analytics instance and the trackNavigationEnter() method of the SmartLookManager.instance instance.

  7. trackNavigationExit(String name): This method is responsible for logging the screen exit event when the user exits a screen.

  8. logOut(): This method is responsible for logging the "log_out" event when the user logs out. It retrieves the user information from the Application.instance.userInfo object, calls the event() method to log the event, and calls the openNew().

  9. login(): This method is responsible for logging the "login" event when the user logs in. It retrieves the user information from the Application.instance.userInfo object, calls the setUser() method of the SmartLookManager.instance instance, and calls the event() method to log the event.

import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter_smartlook/flutter_smartlook.dart';
import 'package:orkun/common/analysis/smartlook_manager.dart';
import 'package:orkun/common/application.dart';

class BaseAnalysis {
  final FirebaseAnalytics _analytics = FirebaseAnalytics.instance;

  ///Report Event
  void event(String name, [Map<String, Object?>? parameters]) {
    if (parameters != null) {
      var map = Map<String, Object?>.from(parameters);
      _convertSafeMap(map);
      _analytics.logEvent(name: name, parameters: map);
    } else {
      _analytics.logEvent(name: name);
    }
    SmartLookManager.instance.trackEvent(name, parameters: parameters);
  }

  void _convertSafeMap(Map<String, Object?> values) {
    var removeKeys = <String>[];
    for (final key in values.keys) {
      var value = values[key];
      if (value == null) {
        removeKeys.add(key);
        continue;
      }
      if (value is String || value is num) {
        continue;
      }
      values[key] = value.toString();
    }
    for (final element in removeKeys) {
      values.remove(element);
    }
  }

  void logAppOpen() {
    _analytics.logAppOpen();
    Smartlook.instance.trackEvent('app_open');
    if (Application.instance.isLogin) {
      var user = Application.instance.userInfo!;
      _analytics.setUserId(id: '${user.userId}');
      _analytics.setUserProperty(name: '${user.clientName}', value: '${user.userId}');
      SmartLookManager.instance.setUser(user);
    }
  }

  void setAnalyticsUserId(String userId) => _analytics.setUserId(id: userId);

  void trackNavigationEnter(String name) {
    _analytics.logScreenView(screenName: name);
    SmartLookManager.instance.trackNavigationEnter(name);
  }

  void trackNavigationExit(String name) {
    SmartLookManager.instance.trackNavigationExit(name);
  }

  void logOut() {
    var user = Application.instance.userInfo!;
    event('log_out', {
      'name': '${user.clientName}',
      'id': '${user.userId}',
      'phone number': '+${user.phoneCountry} ${user.phoneCountry}',
    });
    Smartlook.instance.user.openNew();
  }

  void login() {
    var user = Application.instance.userInfo!;
    SmartLookManager.instance.setUser(user);
    event('login', {
      'login method': 'Phone Login',
      'id': '${user.userId}',
      'phone number': '${user.phoneCountry} ${user.phoneCountry}',
    });
  }
}

The Dart class named AnalysisManager that extends the BaseAnalysis class. This class is responsible for managing various user-related events and actions within an application.

  1. Singleton Pattern: The class uses the Singleton pattern to ensure that there is only one instance of AnalysisManager available throughout the application. The _instance variable is used to store the single instance, and the _getInstance() method is used to retrieve it.

  2. Factory Constructor: The factory AnalysisManager() constructor is used to create a new instance of AnalysisManager.

  3. editProfile(): This method is responsible for handling the "edit_profile" event.

  4. clickBottomTab(int index): This method is responsible for handling the "click_[tab_name]" event when the user clicks on a bottom tab. It calls the event() method with the appropriate event name based on the tab index.

  5. registerUser(Map<String, Object?>? parameters): This method is responsible for handling the "register_user" event. It calls the trackEvent() method of the SmartLookManager instance.

  6. continueToBookingClicked(Map<String, Object?>? parameters): This method is responsible for handling the "continue_to_booking" event. It calls the trackEvent() method of the SmartLookManager instance.

  7. createOrder(Map<String, Object?>? parameters): This method is responsible for handling the "create_order" event. It calls the trackEvent() method of the SmartLookManager instance to log the event.

  8. checkout(Map<String, Object?>? parameters): This method is responsible for handling the "checkout" event.

import 'package:orkun/common/analysis/base_analysis.dart';
import 'package:orkun/common/analysis/smartlook_manager.dart';
import 'package:orkun/common/application.dart';

List _tabs = ['home_tab', 'request_tab', 'favorite_tab', 'message_tab', 'profile_tab'];

class AnalysisManager extends BaseAnalysis {
  static AnalysisManager? _instance;

  factory AnalysisManager() => _getInstance();

  static AnalysisManager get instance => _getInstance();

  AnalysisManager._internal();

  static AnalysisManager _getInstance() => _instance ??= AnalysisManager._internal();

  void editProfile() {
    var user = Application.instance.userInfo;
    event('edit_profile', {'name': '${user?.clientName}', 'avatar': '${user?.clientImage}'});
    SmartLookManager.instance.setUser(user!);
  }

  void clickBottomTab(int index) => event('click_${_tabs[index]}');

  void registerUser({Map<String, Object?>? parameters}) {
    const registerUser = 'register_user';
    SmartLookManager.instance.trackEvent(registerUser, parameters: parameters);
    event(registerUser, parameters);
  }

  void continueToBookingClicked({Map<String, Object?>? parameters}) {
    const continueToBooking = 'continue_to_booking';
    SmartLookManager.instance.trackEvent(continueToBooking, parameters: parameters);
    event(continueToBooking, parameters);
  }

  void createOrder({Map<String, Object?>? parameters}) {
    const createOrder = 'create_order';
    SmartLookManager.instance.trackEvent(createOrder, parameters: parameters);
    event(createOrder, parameters);
  }

  void checkout({Map<String, Object?>? parameters}) {
    const checkout = 'checkout';
    SmartLookManager.instance.trackEvent(checkout, parameters: parameters);
    event(checkout, parameters);
  }
}

Configuration

The Dart class named AppImageConfig that implements the ImageLoaderConfigInterface interface from the infinity_core package

  1. getErrorBuilder(double? width, double? height, double? border, Color? borderColor, double? radius): This method returns a LoadingErrorWidgetBuilder function that is used to display an error widget.

  2. getPlaceBuilder(double? width, double? height, double? border, Color? borderColor, double? radius): This method returns a PlaceholderWidgetBuilder function that is used to display a placeholder widget.

  3. getCircleErrorBuilder(double? radius, double? border, Color? borderColor): This method returns a LoadingErrorWidgetBuilder function that is used to display an error widget when a circular image fails to load.

  4. getCirclePlaceBuilder(double? radius, double? border, Color? borderColor): This method returns a PlaceholderWidgetBuilder function that is used to display a placeholder widget for a circular image while it is loading.

import 'package:flutter/material.dart';
import 'package:infinity_core/core.dart';
import 'package:orkun/res/colours.dart';
import 'package:orkun/res/r.dart';
import 'package:shimmer/shimmer.dart';

class AppImageConfig extends ImageLoaderConfigInterface {
  @override
  LoadingErrorWidgetBuilder getErrorBuilder(
    double? width,
    double? height,
    double? border,
    Color? borderColor,
    double? radius,
  ) {
    return (BuildContext context, String url, _) {
      return const SizedBox();
    };
  }

  @override
  PlaceholderWidgetBuilder getPlaceBuilder(
    double? width,
    double? height,
    double? border,
    Color? borderColor,
    double? radius,
  ) {
    return (BuildContext context, String url) {
      return Shimmer.fromColors(
        baseColor: Colors.grey.shade300,
        highlightColor: Colors.grey.shade100,
        child: Container(color: Colors.grey.shade300),
      );
    };
  }

  @override
  LoadingErrorWidgetBuilder getCircleErrorBuilder(double? radius, double? border, Color? borderColor) {
    return (BuildContext context, String url, _) {
      return ClipOval(
        child: Container(
          decoration: BoxDecoration(
            shape: BoxShape.circle,
            border: Border.all(color: borderColor ?? Colours.transparent, width: border!),
          ),
          width: radius! * 2,
          height: radius * 2,
          alignment: Alignment.center,
          child: Image.asset(R.avatarProfile, width: radius * 2, height: radius * 2, fit: BoxFit.fitWidth),
        ),
      );
    };
  }

  @override
  PlaceholderWidgetBuilder getCirclePlaceBuilder(double? radius, double? border, Color? borderColor) {
    return (BuildContext context, String url) {
      return ClipOval(
        child: Container(
          decoration: BoxDecoration(
            shape: BoxShape.circle,
            border: Border.all(color: borderColor ?? Colours.transparent, width: border ?? 0),
          ),
          width: radius! * 2,
          height: radius * 2,
          alignment: Alignment.center,
          child: Image.asset(R.avatarProfile, width: radius * 2, height: radius * 2, fit: BoxFit.fitWidth),
        ),
      );
    };
  }
}

The Dart class named AppUiConfig that extends the AppUI class from the infinity_core package

  1. refreshHeader(...): This method returns a Widget that represents the header for the pull-to-refresh functionality.

  2. loadMoreFooter(...): This method returns a Widget that represents the footer for the load-more functionality. It creates a CustomFooter widget that contains a RefreshFooter widget.

  3. loadingPage(): This method returns a Widget that represents the loading page. It returns a CircularProgressIndicatorWidget that displays a circular progress indicator.

  4. emptyPage(VoidCallback refreshCallback, String? tip): This method returns a Widget that represents the empty page.

  5. errorPage(VoidCallback refreshCallback, String? msg): This method returns a Widget that represents the error page.

import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:infinity_core/core.dart';
import 'package:infinity_core/widget/refresh/refresh_footer.dart';
import 'package:orkun/res/colours.dart';
import 'package:orkun/res/r.dart';
import 'package:orkun/res/strings.dart';
import 'package:orkun/res/tailwind_ext.dart' hide TextFontSizeFeatureExt;
import 'package:orkun/widgets/pin_mark_widget.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';

class AppUiConfig extends AppUI {
  @override
  Widget refreshHeader({String? releaseText, String? refreshingText, String? completeText, TextStyle? style}) {
    return const MaterialClassicHeader();
  }

  @override
  Widget loadMoreFooter({
    String? noDataText,
    String? loadingText,
    String? canLoadingText,
    String? idleText,
    TextStyle style = const TextStyle(color: Colors.grey),
  }) {
    return CustomFooter(
      height: 100,
      builder: (context, mode) {
        return RefreshFooter(
          status: mode,
          loadFailHint: idleText ?? Ids.loadingCompleted.trn,
          loadingHint: loadingText ?? Ids.dataIsLoading.trn,
          loadMoreHint: loadingText ?? Ids.dataIsLoading.trn,
          noDataHint: noDataText ?? '',
        );
      },
    );
  }

  @override
  Widget loadingPage() {
    return const CircularProgressIndicatorWidget();
  }

  @override
  Widget emptyPage(VoidCallback refreshCallback, String? tip) {
    return Column();
  }

  @override
  Widget errorPage(VoidCallback refreshCallback, String? msg) {
    return const SizedBox();
  }
}

class CircularProgressIndicatorWidget extends StatelessWidget {
  const CircularProgressIndicatorWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Platform.isIOS
          ? const CupertinoActivityIndicator(
              color: Colours.primaryColor,
            )
          : const CircularProgressIndicator(
              color: Colours.primaryColor,
            ),
    );
  }
}

Middleware

The Dart class named LoginMiddleware that extends the GetMiddleware class from the infinity_core package.

  1. MiddleWareRedirectArgs: This is a data class that holds the information needed for the redirection, including the route name and any arguments.

  2. LoginMiddleware: This is the main class that extends GetMiddleware.

    • redirect(String? route): This is the main method that is called by the GetMiddleware class to determine if a route should be redirected.

    • Inside the method, it first checks if the user is not logged in (!Application.instance.isLogin).

    • If the user is not logged in, it retrieves the previous route name (previousRouteName) and any arguments (args) that were passed with the route.

    • It then returns a new RouteSettings object with the LoginPage.routeName as the route name and the MiddleWareRedirectArgs object as the arguments.

    • If the user is logged in, it returns null, which means that the route should not be redirected.

import 'package:flutter/material.dart';
import 'package:infinity_core/core.dart';
import 'package:orkun/common/application.dart';
import 'package:orkun/ui/auth_v2/login_page_v2.dart';

class MiddleWareRedirectArgs {
  String? routeName;
  Object? arguments;

  MiddleWareRedirectArgs({this.routeName, this.arguments});
}

class LoginMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    // If user not login yet

    if (!Application.instance.isLogin) {
      var previousRouteName = route;
      Object? args = Get.arguments;

      return RouteSettings(
        name: LoginPageV2.routeName,
        arguments: MiddleWareRedirectArgs(arguments: args, routeName: previousRouteName),
      );
    }
    return null;
  }
}

Local storage

The Application class is a central point of access for managing the application's state and data, providing a consistent and organized way to interact with the application's data throughout the codebase.

  1. Singleton Pattern: The class uses the Singleton pattern to ensure that there is only one instance of Application available throughout the application.

  2. User Management: The class provides methods for managing the user's information, such as saveUser(), getUser(), and getUid(). It also checks the user's login status using the isLogin getter.

  3. Currency Management: The class provides methods for managing the application's currency, such as saveCurrency(), getCurrency(), saveExchangeRate(), and getExchangeRate().

  4. Country Management: The class provides methods for managing the user's country, such as getCountryCode(), saveCountryCode(), getCountryId(), and saveCountryId()

  5. Clearing Data: The clear() method is responsible for clearing the user's data, such as the userInfo, exchangeRate, and currency.

import 'package:infinity_core/core.dart';
import 'package:orkun/common/analysis/analysis_manager.dart';
import 'package:orkun/ui/auth/model/user_info.dart';
import 'package:orkun/ui/request/model/exchange_rate_model.dart';
import 'package:orkun/ui/splash/model/currencyModel.dart';

class Application {
  factory Application() => _getInstance();

  Application._internal() {
    userInfo = SpUtil.getObj<UserInfo>(_userInfo, UserInfo.fromJson);
    currency = SpUtil.getObj<CurrencyModel>(_currency, CurrencyModel.fromJson);
    exchangeRate = SpUtil.getObj<ExchangeRate>(_exchangeRate, ExchangeRate.fromJson);
  }

  static Application? _instance;

  static Application _getInstance() {
    _instance ??= Application._internal();
    return _instance!;
  }

  static const String _currency = 'currency';

  static const String _exchangeRate = 'ExchangeRate';

  static const String _userInfo = '_userInfo';

  static Application get instance => _getInstance();

  UserInfo? userInfo;

  ExchangeRate? exchangeRate;

  CurrencyModel? currency;

  String? getUid() => userInfo?.uuid;

  String? getUserId() => userInfo?.userId ?? '';

  String? getToken() => "Bearer ${userInfo?.userToken ?? ""}";

  bool get isLogin => userInfo?.userId?.isNotEmpty ?? false;

  void saveUser(UserInfo user) {
    if (userInfo == null) {
      userInfo = user;
    } else {
      userInfo?.mergerUser(user);
    }
    if (userInfo != null) {
      Monitor.instance.putsConsole(['UserToken:', userInfo?.userToken ?? '']);
      SpUtil.putObject(_userInfo, userInfo!);
    }
    AnalysisManager.instance.setAnalyticsUserId('${user.userId}');
  }

  UserInfo getUser() {
    var user = SpUtil.getObj(_userInfo, UserInfo.fromJson);
    return user ?? UserInfo();
  }

  CurrencyModel? getCurrency() => currency = SpUtil.getObj(_currency, CurrencyModel.fromJson);

  void saveCurrency(CurrencyModel currency) {
    this.currency = currency;
    SpUtil.putObject(_currency, currency);
  }

  ExchangeRate? getExchangeRate() {
    return exchangeRate ??= SpUtil.getObj(_exchangeRate, ExchangeRate.fromJson);
  }

  void saveExchangeRate(ExchangeRate exchangeRate) {
    this.exchangeRate = exchangeRate;
    SpUtil.putObject(_exchangeRate, exchangeRate);
  }

  void clear() {
    SpUtil.remove(_userInfo);
    userInfo = null;
    userInfo?.userToken = '';
    exchangeRate = null;
    currency = null;
  }
}

Routes

The AppPages class is responsible for defining the application's navigation structure and the configuration of each route. It uses the GetX package to manage the navigation and routing, which provides a flexible way to handle navigation in Flutter.

The _page() method is a helper function that simplifies the creation of GetPage objects with common configuration options, such as the system UI overlay style, transparent routes, and logging.

  1. initial: This constant defines the initial route of the application, which is set to SplashPage.routeName.

  2. routes: This is a list of GetPage objects that define the application's routes. Each GetPage object represents a single route and contains the following properties:

    • name: The name of the route.

    • page: A function that returns the widget to be displayed for the route.

    • binding: A Bindings object that defines the dependencies to be injected into the route's controllers.

    • transition: The transition animation to be used when navigating to the route.

  3. _page(): This is a private method that creates a GetPage object with the provided configuration. It handles the following cases:

    • If transparentRoute is true, it creates a TransparentRoute object instead of a GetPage object.

    • If the platform is Android, it sets the system UI overlay style to a custom dark theme with a black system navigation bar color.

    • It sets the opaque property of the GetPage object based on the provided value.

    • It sets the fullscreenDialog property of the GetPage object based on the provided value.

    • It sets the binding, customTransition, transition, and middlewares properties of the GetPage object based on the provided values.

    • It calls the Monitor.instance.putPage() method to log the page name and transition.

class AppPages {

  static const initial = SplashPage.routeName;

  /// Define all routes here.
  /// Binding the controller for specific Page
  /// Inject all the dependencies when route to
  static final List<GetPage> routes = [
    _page(
      name: '/',
      page: SplashPage.new,
      binding: BindingsBuilder(() {
        Get.lazyPut(SplashPageController.new);
        Get.lazyPut(HomeController.new);
        Get.lazyPut(PageRequestController.new);
      }),
      transition: Transition.rightToLeft,
    ),
];


  static GetPage _page({
    required String name,
    required GetPageBuilder page,
    bool fullscreenDialog = false,
    bool transparentRoute = false,
    bool opaque = true,
    Bindings? binding,
    Transition? transition,
    CustomTransition? customTransition,
    List<GetMiddleware>? middlewares,
  }) {
    if (transparentRoute) {
      return TransparentRoute(
        name: name,
        binding: binding,
        transition: transition ?? Transition.downToUp,
        middlewares: [PageMiddleware(), ...?middlewares],
        page: () {
          Monitor.instance.putPage(name);
          return page();
        },
      );
    }

    var mySystemTheme = SystemUiOverlayStyle.dark.copyWith(systemNavigationBarColor: Colors.black);
    SystemChrome.setSystemUIOverlayStyle(mySystemTheme);
  
    return GetPage(
      name: name,
      opaque: opaque,
      fullscreenDialog: fullscreenDialog,
      binding: binding,
      customTransition: customTransition,
      transition: transition ?? Transition.rightToLeft,
      page: () {
        Monitor.instance.putPage('PageName: $name Transition: $transition');
        return page();
      },
      middlewares: [PageMiddleware(), ...?middlewares],
    );
  }
}

Resources

Colours

Define color name and hex code

class Colours {
  static const Color secondaryColor = Color(0xFF8D8D8D);
}

Tailwind Style

To generate Tailwind CSS .g file follow instruction below

Generate Tailwind extension file

Dart code, which appears to be a custom extension library for styling and sizing elements using a Tailwind-like approach and similar to Tailwind CSS's utility-first.

// Allows easy setting of height, width, and size using method chaining
extension SizeExt on T {
  T get h200 => this..height = 200.h;
  T get w200 => this..width = 200.w;
  T get s200 => this..size = 200.r;
}

Font Family Extension:

// Quickly set font family to Nunito
extension TextFeatureExt on T {
  T get fontNunito => this..fontFamily = 'Nunito';
}

Font Size Extensions:

// Provides quick methods to set font sizes
extension TextFontSizeFeatureExt on T {
  T get f21 => this..font(21);
  T get f31 => this..font(31);
  T get f25 => this..font(25);
  T get f45 => this..font(45);
}

Text Style Extensions:

// Defines multiple text styles

extension TextStyleExt on T {
  T get styleMain => this..style = ts.redAccent.f16.bold.underline.mk;
  T get styleAccent => this..style = ts.greenAccent.f20.bold.underline.mk;
  T get styleTradition => this
    ..style = TextStyle(
      color: Colors.greenAccent,
      fontSize: 20.sp,
      fontWeight: FontWeight.bold,
      decoration: TextDecoration.underline,
    );
}

Shadow Extension:

// Provides a predefined box shadow with specific properties
extension ShadowExt on T {
  T get customShadow => this
    ..boxShadow = const [
      BoxShadow(
        color: Color(0x78000000),
        offset: Offset(0, 4),
        blurRadius: 4.0,
        spreadRadius: 0.0,
      )
    ];
}

Decoration Extensions:

// Defines box decorations using both Tailwind-like and traditional Flutter approaches
extension DecorationExt on T {
  T get decorMain => this..decoration = bd.greenAccent.circle.borderBrown.rounded8.customShadow.border5.mk;
  T get decorTradition => this
    ..decoration = BoxDecoration(
      color: Colors.greenAccent,
      border: Border.all(color: Colors.brown, width: 5.r),
      borderRadius: BorderRadius.circular(8.r),
      boxShadow: const [
        BoxShadow(
          color: Color(0x78000000),
          offset: Offset(0, 4),
          blurRadius: 4.0,
          spreadRadius: 0.0,
        ),
      ]
    );
}

The code provides a flexible, chainable way to apply styles and sizes in Flutter

Icon

This is a utility class typically used for organizing and centralizing resource paths in a Flutter/Dart application. The 'R' likely stands for 'Resources'

class R {
  static const String _assets = 'assets';

  ///------------------------ assets ------------------------
  static const String icEdit = '$_assets/ic_edit.svg';
  static const String icPerm = '$_assets/ic_perm.svg';
  static const String icWarn = '$_assets/ic_warn.svg';
  static const String icLangu = '$_assets/ic_langu.svg';
  static const String icSmart = '$_assets/ic_smart.png';
  
}

Add asset resrouce in the Assets Folder then generate the code

Usage

Image.asset(R.icFacebook)

Translation

GetX localization configuration in Flutter:

Global Initialization Method:

Global.init(
  () async {
    runApp(const MyApp()); // Initialize
  },
  languages: [ ... ]
);
  • Calls a global initialization method

  • Takes an asynchronous callback function and a list of supported languages

  • Directly runs the main app without additional setup (unlike the previous example)

Language Configuration:

languages: [
  Languages('繁体中文', 'zh-HK', 'HK'),  // Traditional Chinese (Hong Kong)
  Languages('English', 'en', 'US'),    // English (United States)
  Languages('ខ្មែរ', 'km', 'KH'),        // Khmer (Cambodia)
  Languages('简体中文', 'zh-Hans', 'CH'), // Simplified Chinese (China)
]

Each Languages entry contains three parameters:

  • Display name of the language

  • Language code

  • Country/region code

Supported Languages:

  • Traditional Chinese (Hong Kong)

  • English (United States)

  • Khmer (Cambodia)

  • Simplified Chinese (China)

This setup provides a straightforward way to:

  • Initialize the app

  • Configure multilingual support

  • Set up language options for user selection

  1. Supported Locales:

supportedLocales: const [
  Locale('zh', 'CH'),   // Chinese (China)
  Locale('en', 'US'),   // English (United States)
  Locale('km', 'KH'),   // Khmer (Cambodia)
],
  • Defines the languages and specific regional variants that the app supports

  • Each Locale has two parameters:

    • First parameter: language code (zh = Chinese, en = English, km = Khmer)

    • Second parameter: country/region code (CH = China, US = United States, KH = Cambodia)

  1. Localization Delegates:

localizationsDelegates: const [
  GlobalMaterialLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
  GlobalCupertinoLocalizations.delegate,
],
  • Provides localization support for different parts of the app

  • GlobalMaterialLocalizations.delegate: Adds localization for Material Design widgets

  • GlobalWidgetsLocalizations.delegate: Provides localization for basic widgets

  • GlobalCupertinoLocalizations.delegate: Adds localization for Cupertino (iOS-style) widgets

  1. Locale Setting:

locale: LanguageUtil.getCurrentLanguage().toLocale(),
  • Dynamically sets the app's current language

  • Uses a LanguageUtil helper to get the current language

  • Converts the language to a Locale object

  1. Translations:

translations: TranslationService(),
  • Uses a custom TranslationService() for managing translations

  • Likely implements the GetX Translations interface to provide translation maps for different languages

Overall, this configuration sets up a multilingual app with support for Chinese, English, and Khmer, using GetX for internationalization. It allows the app to:

  • Support multiple languages

  • Dynamically change the app's language

  • Provide localized strings and widget translations

  • Handle different regional variants of languages

This approach provides a flexible and comprehensive way to internationalize a Flutter application.

Let me break down this code for fetching and managing languages:

  1. getLanguages() Method:

getLanguages() async {
  LoadingUtil.show();
  Global.languages.clear(); // Clear existing languages
  
  // Fetch languages from API
  final repository = await ApiService().getLanguages();
  
  if (repository.isSuccess()) {
    LoadingUtil.dismiss();
    
    repository.data.forEach((v) {
      Global.languages.add(LanguageModel(v['name'], v['code'], ''));
      
      // Check if this language matches current language
      if (v['code'] == LanguageUtil.getCurrentLanguage().languageCode) {
        LanguageUtil.setLocalModel(context: context, model: LanguageModel(v['name'], v['code'], ''));
      }
      
      // Get translation for each language
      String locale = v['code'];
      debugPrint('_i18nLanguages: $locale');
      ApiService.of.getTranslation(locale);
    });

    setState(() {});
    LoadingUtil.dismiss();
  } else {
    showToast('${repository.msg}');
  }
  LoadingUtil.dismiss();
}
  1. ApiService Class:

class ApiService extends BaseRepository {
  // Singleton-like access to ApiService
  static ApiService get of => getx.Get.find<ApiService>();
  
  final dio.Dio _dio = dio.Dio();

  /// Translation request method
  Future<void> getTranslation(String lang) async {
    // Only fetch translations for non-English languages
    if (lang != 'en') {
      // Construct URL with environment variables
      final url = Env.translatesProds;
      final appId = Env.appId;
      final version = Env.version;
      final name = Env.appName;

      try {
        final response = await _dio.get('$url?lang=$lang&appId=$appId&version=$version&name=$name');
        
        if (response.statusCode == 200) {
          final data = Map<String, String>.from(response.data);
          
          // Add translations to GetX translation service
          getx.Get.addTranslations({lang: data});
        } else {
          log('Error fetching data: ${response.statusCode}');
        }
      } on dio.DioException catch (e) {
        log('DioError: $e URL --> $url');
      }
    }
  }
}

Key Functionality:

  • Fetches available languages from an API

  • Clears and repopulates the global languages list

  • Adds translations for each non-English language

  • Uses GetX for state management and translations

  • Dynamically loads translations based on language code

Workflow:

  1. Clear existing languages

  2. Fetch languages from API

  3. For each language:

    • Add to global languages list

    • Set current language if matched

    • Fetch translations

  4. Update UI

  5. Handle any errors

The code is designed to:

  • Dynamically update app languages

  • Fetch translations from a backend service

  • Provide a flexible multilingual approach

  • Handle different language variants (zh-HK, zh-Hans, km, etc.)

Utils

Apns Manager

  • Crashlytics Manager

  • Location Permission

  • Message Services

  • Nofitifcation Manager

  • Pusher Manager

Widget

Last updated