Skip to content

essasabbagh/dentix

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dentix

Description

A brief overview of Dentix, its purpose, and key features. Explain what makes this app unique or valuable.

Table of Contents

Features

Features of Template app:


Prerequisites

Before you begin, ensure you have met the following requirements:

  • Flutter SDK 3.35.7
  • Dart SDK 3.9.2
  • FVM - Flutter Version Management
  • Android Studio or VS Code with Flutter extensions
  • Simulator/Emulator or physical device for testing

Recommended Versions

  • Flutter: 3.35.7
  • Dart: 3.9.2
fvm use 3.35.7

Recommended VsCode Extensions

Flutter Riverpod Snippets

Dart Data Class Generator

Dart Import Sorter

Flutter Print Tools

Dart Barrel File Generator

Better Comments

Flutter Applog


Installation

Provide instructions on how to install your app. Include any prerequisites and step-by-step installation guide.

Clone the Repository

Github link

git clone https://github.com/essasabbagh/dentix.git
cd dentix

Install Dependencies

fvm flutter pub get

Assets

Use Flutter assets vscode extension Flutter assets

flutter_assets:
  assets_path: assets/
  output_path: lib/core/constants/
  filename: images.dart
  classname: AppImages
  ignore_comments: true
  field_prefix:

Compress Flutter assets squoosh - tinypng

Run

# Run on Mac
fvm flutter run -d macos 

# Run on Windows
fvm flutter run -d windows

Change app name

  • Run this command
fvm dart run rename_app:main all="Dentix"
  • lib/configs/app_configs.dart
static const String appName = 'Dentix';
  • lib/locale/intl_XX.arb
"appName": "Dentix",

Generates native splash

fvm dart run flutter_native_splash:create

note: to edit configs go to: flutter_native_splash.yaml

Build

Prepare Release

Deployment

Usage

Theme

lib/core/themes

color: Theme.of(context).shadowColor
color: Theme.of(context).colorScheme.onSurface
style: Theme.of(context).textTheme.bodyMedium
style: Theme.of(context).textTheme.bodyLarge

Fonts

  • Font Family: notoSansArabic Using Google font
class AppConfigs {
    ...
  // font family
  static const String fontFamily = 'Cairo';
}

class AppFont {
  static const String fontFamily = AppConfigs.fontFamily;
}

Gradient

class AppGradient {
  static const linearGradient = LinearGradient(
    begin: Alignment.topLeft,
    end: Alignment.bottomRight,
    colors: [
      Color(0xFF069ACC),
      Color(0xFF0594C6),
      Color(0xFF0483B6),
      Color(0xFF02679B),
      Color(0xFF004177),
      Color(0xFF003B71),
    ],
    stops: [
      0.1079,
      0.2481,
      0.446,
      0.6603,
      0.8995,
      0.9325,
    ],
  );
}

Routing

Libraries & Tools Used

lib/core/router

Redirection changes the location to a new one based on auth state.

final routerProvider

To navigate to a route using its name, call goNamed():

 // example
context.goNamed(
  AppRouters.checkEmail.name,
  pathParameters: {
    "email": _emailnameController.text,
  },
);

To configure a named route, use the name parameter:

GoRoute(
  path: AppRoutes.splash.path,
  name: AppRoutes.splash.name,
  builder: (_, _) => const SplashScreen(),
),

lib\configs\routes\app_router.dart

enum AppRoutes {
  splash('/', 'splash'),
  onboarding('/onboarding', 'onboarding'),
  login('/login', 'login'),
  ...
}

Multilingual

flutter_localizations:
  sdk: flutter
intl: ^0.20.2
dev_dependencies:
  intl_utils: ^2.8.11
  # dart run intl_utils:generate

Add ARB files

Add the following vscode Extension flutter-intl

Fix my ARB

To generate boilerplate code for localization, run the generate program inside directory where your pubspec.yaml file is located:

https://www.linkedin.com/posts/shehab-mohamed-_%D9%81%D9%89-%D8%B7%D8%B1%D9%82-%D9%83%D8%AA%D9%8A%D8%B1-%D9%81-%D9%81%D9%84%D8%A7%D8%AA%D8%B1-%D8%A7%D9%86%D9%83-%D8%AA%D8%B9%D9%85%D9%84-localizations-activity-7253578654897606656--CFQ/?utm_source=share&utm_medium=member_desktop

Create Local Provider

const defaultLocale = Locale('ar', '');
const languageCodeKey = 'languageCode';

final storageService = locator<StorageService>();

final localProvider = StateNotifierProvider<LocalProvider, Locale>(
  (_) {
    final languageCode = storageService.read(languageCodeKey);
    return LocalProvider(
      languageCode.isEmpty ? defaultLocale.languageCode : languageCode,
    );
  },
);

class LocalProvider extends StateNotifier<Locale> {
  LocalProvider(String languageCode) : super(Locale(languageCode));

  void changeLocale(Locale? locale) {
    state = locale ?? defaultLocale;
    storageService.write(languageCodeKey, state.languageCode);
  }

  final supportedLocales = const [
    Locale('en'), 
    Locale('ar'), 
  ];
}

To change loacal use LocaleChanger widget

class LocaleChanger extends ConsumerWidget {
  const LocaleChanger({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final locale = ref.watch(localProvider);
    final localeNotifier = ref.read(localProvider.notifier);

    return Column(
      children: [
        Align(
          alignment: AlignmentDirectional.centerStart,
          child: Text(
            S.of(context).applanguage,
            style: body14,
          ),
        ),
        const SizedBox(height: 6),
        DropdownButtonFormField<Locale>(
          value: locale,
          onChanged: localeNotifier.changeLocale,
          items: localeNotifier.supportedLocales.map((val) {
            final lang =
                languages.firstWhere((e) => e['code'] == val.languageCode);
            return DropdownMenuItem(
              value: val,
              child: Row(
                children: <Widget>[
                  Text(lang['flag']),
                  const SizedBox(width: 8.0),
                  Text(lang['name']),
                ],
              ),
            );
          }).toList(),
        ),
      ],
    );
  }
}

To use translation

Text(
    S.of(context).welcomeBack,
)

Log

The application uses a custom AppLogs utility for structured, colorful, and configurable logging across different environments.

Features

  • Multiple log levels: Success, Info, Warning, Debug, Error
  • Color-coded console output
  • Configurable log levels
  • Metadata support
  • Release mode logging control

Log Levels

  • Success (🟢): Indicates successful operations
  • Info (🔵): General informational messages
  • Warning (🟠): Potential issues or important notifications
  • Debug (⚪️): Detailed debugging information
  • Error (🚫): Error and critical issue logs

Usage Examples

// Basic logging
AppLogs.success('Operation completed successfully');
AppLogs.info('Application started', 'Startup');
AppLogs.warning('Low disk space', 'Storage');
AppLogs.debug('Fetching user data');
AppLogs.error('Failed to load user profile', 'Authentication', 
    metadata: {'userId': 123});

// Configuring log level
AppLogs.setLogLevel(LogLevel.warning); // Only show warnings and errors

Best Practices

  • Use appropriate log levels
  • Include meaningful tags
  • Add metadata for complex error tracking
  • Avoid logging sensitive information

Configuration Options

Setting Log Level

Control which log levels are displayed:

// Only show warnings and errors
AppLogs.setLogLevel(LogLevel.warning);

Release Mode Logging

By default, logs are disabled in release mode. To enable:

AppLogs.enableReleaseLogging = true;

Customizing Log Styles

You can customize the appearance of log levels:

AppLogs.setLogStyle(LogLevel.success, 
  LogStyle(colorCode: '35', emoji: '🌟')
);

Utilities

SnackBar

The SnackBar utility provides a convenient and consistent way to display temporary notifications across the application. It supports different types of notifications with color-coded backgrounds and integrates with the app's logging system.

Features

  • Multiple SnackBar types: Success, Error, Warning, Info
  • Consistent styling
  • Automatic logging of messages
  • Customizable actions
  • Floating behavior

Usage Examples

// Success Notification
AppSnackBar.success('Operation completed successfully');

// Error Notification
AppSnackBar.error('Something went wrong');

// Warning Notification
AppSnackBar.warning('Proceed with caution');

// Info Notification
AppSnackBar.info('Additional information');

SnackBar with Custom Action

AppSnackBar.success(
  'Saved successfully', 
  action: SnackBarAction(
    label: 'Undo',
    onPressed: () {
      // Undo action logic
    }
  )
);

SnackBar Types

  • Success SnackBar (Green): Positive outcomes and successful operations
  • Error SnackBar (Red): Error messages and critical notifications
  • Warning SnackBar (Amber): Cautionary or important messages
  • Info SnackBar (Blue): Informational messages and updates

Important Notes

  • Requires a global scaffoldKey to be set up in your app
  • Automatically logs messages using AppLogs
  • Default action is to dismiss the SnackBar
  • Only one SnackBar is shown at a time

Best Practices

  • Use appropriate SnackBar type based on the message context
  • Keep messages concise and clear
  • Use SnackBars for temporary, non-blocking notifications
  • Avoid overusing SnackBars to prevent user fatigue

Custom Alert Dialog

The CustomAlertDialog is a flexible and visually appealing alert dialog component that supports multiple dialog types with dynamic styling and behavior.

Features

  • Four distinct dialog types: Error, Warning, Info, Success
  • Dynamic icon and color based on dialog type
  • Customizable title, description, and button texts
  • Centered layout with rounded design
  • Themed typography
  • Callback support for accept action

Dialog Types

  1. Error Dialog (Red)

    • Used for critical or destructive actions
    • Error-related confirmations
  2. Warning Dialog (Orange)

    • Cautions about potential consequences
    • Actions requiring careful consideration
  3. Info Dialog (Primary Color)

    • Informational messages
    • Neutral notifications
  4. Success Dialog (Green)

    • Positive confirmations
    • Celebration or completion messages

Usage Examples

Basic Alert Dialog

await showDialog<bool>(
  context: context,
  builder: (ctx) => CustomAlertDialog(
    title: 'Delete Account',
    description: 'Are you sure you want to delete your account?',
    acceptText: 'Delete',
    cancelText: 'Cancel',
    type: AlertDialogType.error,
    onAccept: () {
      // Perform deletion logic
    },
  ),
);

Different Dialog Types

// Warning Dialog
CustomAlertDialog(
  title: 'Unsaved Changes',
  description: 'You have unsaved changes. Proceed?',
  type: AlertDialogType.warning,
  // ...
)

// Info Dialog
CustomAlertDialog(
  title: 'Update Available',
  description: 'A new version of the app is ready.',
  type: AlertDialogType.info,
  // ...
)

// Success Dialog
CustomAlertDialog(
  title: 'Profile Updated',
  description: 'Your profile has been successfully updated.',
  type: AlertDialogType.success,
  // ...
)

Customization

  • Fully customizable text for title, description, and buttons
  • Supports custom accept action callback
  • Inherits theme styling for consistent design

Best Practices

  • Use appropriate dialog type based on context
  • Keep titles and descriptions concise
  • Provide clear and specific actions
  • Use dialogs for important confirmations or information
  • Avoid overusing dialogs to prevent user frustration

Accessibility Considerations

  • Centered layout for better visibility
  • Large, clear icons
  • High-contrast color scheme
  • Readable typography

Date Helper Utility

The DateHelper class provides a human-readable, localized representation of time elapsed since a given date. It converts raw datetime differences into friendly, easy-to-read time phrases.

Features

  • Supports multiple time granularities:
    • Just now
    • Seconds
    • Minutes
    • Hours
    • Days
    • Weeks
    • Months
    • Years
  • Fully localized time representations
  • Simple, static method for easy usage

Supported Time Ranges

Range Output Format
< 5 seconds "Just now"
< 1 minute "X seconds ago"
< 1 hour "X minutes ago"
< 24 hours "X hours ago"
< 7 days "X days ago"
< 30 days "X weeks ago"
< 365 days "X months ago"
1 year+ "X years ago"

Usage Examples

// Basic usage
DateTime pastDate = DateTime.now().subtract(Duration(hours: 3));
String readableTime = DateHelper.timeAgo(pastDate);
print(readableTime); // "3 hours ago"

// Different time ranges
DateTime justNow = DateTime.now().subtract(Duration(seconds: 3));
DateTime fewMinutesAgo = DateTime.now().subtract(Duration(minutes: 5));
DateTime yesterdayDate = DateTime.now().subtract(Duration(days: 1));
DateTime oldDate = DateTime.now().subtract(Duration(days: 365));

print(DateHelper.timeAgo(justNow));     // "Just now"
print(DateHelper.timeAgo(fewMinutesAgo)); // "5 minutes ago"
print(DateHelper.timeAgo(yesterdayDate)); // "1 day ago"
print(DateHelper.timeAgo(oldDate));       // "1 year ago"

Localization

  • Uses S.current for language-specific time representations
  • Supports multiple languages through generated localization files
  • Automatic translation of time phrases

Best Practices

  • Use for displaying relative timestamps
  • Ideal for social media feeds, chat logs, and activity streams
  • Provides more user-friendly experience than raw datetime
  • Lightweight and performant

Considerations

  • Calculations based on current system time
  • Accuracy depends on device's current datetime setting
  • Recommended for recent time differences

Potential Improvements

  • Add custom date formatting options
  • Support for more granular time representations
  • Configurable time thresholds

Paginated List Widget

A Flutter widget that provides a paginated list with built-in loading states, error handling, and infinite scrolling capabilities using Riverpod for state management.

Features

  • Infinite scrolling with customizable scroll threshold
  • Pull-to-refresh functionality
  • Built-in loading, error, and empty states
  • Customizable widgets for all states
  • Support for list separators
  • Automatic pagination handling
  • Built-in error handling and retry mechanisms

Basic Usage

PaginatedListWidget<User>(
  provider: userListProvider,
  itemBuilder: (context, user) => UserListItem(user: user),
)

Advanced Usage

PaginatedListWidget<User>(
  // Required parameters
  provider: userListProvider,
  itemBuilder: (context, user) => UserListItem(user: user),
  
  // Optional customization
  loadTriggerThreshold: 0.8,
  enablePullToRefresh: true,
  padding: EdgeInsets.all(16.0),
  separatorBuilder: (context, index) => Divider(),
  
  // Custom state widgets
  loadingWidget: CustomLoadingSpinner(),
  errorWidget: CustomErrorWidget(),
  emptyWidget: CustomEmptyState(),
  bottomLoadingWidget: CustomBottomLoader(),
  bottomErrorWidget: CustomBottomError(),
  noMoreDataWidget: CustomNoMoreDataWidget(),
  
  // Optional scroll controller
  scrollController: myScrollController,
)

Setting Up the Provider

Create a state notifier that extends PaginatedListNotifier:

final usersProvider = AutoDisposeStateNotifierProvider<UsersNotifier, PaginationState<User>>(
  (ref) => UsersNotifier(),
);

class UsersNotifier extends PaginatedListNotifier<User> {
  @override
  Future<List<User>> fetchPage(int page) async {
    // Implement your API call here
    final response = await api.getUsers(page: page, limit: pageSize);
    return response.users;
  }
}

Customization Options

Load Trigger Threshold

The loadTriggerThreshold parameter (default: 0.8) determines when to load the next page. It represents the scroll position as a percentage of the total scrollable area:

PaginatedListWidget<User>(
  provider: userListProvider,
  itemBuilder: (context, user) => UserListItem(user: user),
  loadTriggerThreshold: 0.7, // Load next page at 70% scroll
)

Pull-to-Refresh

Enable or disable pull-to-refresh functionality:

PaginatedListWidget<User>(
  provider: userListProvider,
  itemBuilder: (context, user) => UserListItem(user: user),
  enablePullToRefresh: true, // Enables pull-to-refresh
)

Custom State Widgets

Customize the appearance of various states:

PaginatedListWidget<User>(
  provider: userListProvider,
  itemBuilder: (context, user) => UserListItem(user: user),
  loadingWidget: Center(child: CircularProgressIndicator()),
  errorWidget: CustomErrorWidget(),
  emptyWidget: Center(child: Text('No users found')),
  bottomLoadingWidget: CustomBottomLoader(),
  bottomErrorWidget: CustomBottomError(),
  noMoreDataWidget: Center(child: Text('No more users')),
)

Error Handling

The widget automatically handles errors and provides retry functionality. You can customize the error display using the errorWidget and bottomErrorWidget parameters.


Coding Standards

  • Follow Flutter/Dart best practices
  • Use meaningful variable and function names
  • Write comprehensive documentation

Troubleshooting

  • Common issues and their solutions
  • Debugging tips specific to this template

Performance Optimization

  • List any performance-related configurations
  • Mention used optimization techniques

Security

  • Brief overview of security measures
  • Recommendations for secure usage

About

Dentix Flow is a comprehensive dental clinic management system designed to streamline patient records, appointments, treatments, and payments with high efficiency. It features native support for Arabic with a full Right-to-Left (RTL) interface for a seamless user experience.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors