Skip to content

Overview

  • File: lib/views/settings_page.dart

The SettingsPage is a Flutter widget that provides a comprehensive settings interface for the HVC Mobile application. It allows users to manage their contact information, default page settings, notification preferences, security settings, and PIN changes. The page is organized into expandable sections for better organization and user experience.

Class Structure

Class Definition

dart
class SettingsPage extends StatelessWidget {
  static const String routeName = '/SettingsPage';
  SettingsPage({Key? key}) : super(key: key);

  final TextEditingController phoneNumberCtrl = TextEditingController();
  final TextEditingController emailCtrl = TextEditingController();
  final TextEditingController departmentCtrl = TextEditingController();
  final TextEditingController specialtyCtrl = TextEditingController();
  final TextEditingController webDefaultPageCtrl = TextEditingController();
  final TextEditingController mobileDefaultPageCtrl = TextEditingController();
  final TextEditingController headsetDefaultPageCtrl = TextEditingController();

  TextEditingController pin1Ctrl = TextEditingController();
  TextEditingController pin2Ctrl = TextEditingController();

  final SettingsController settingsController = Get.find();
  final AuthController authController = Get.find();
  final ContactsController contactsController = Get.find();

  final RxString pin1 = ''.obs;
  final RxString pin2 = ''.obs;
  final FocusNode focusNode = FocusNode();
  final List<String> webOptions = [
    "Dashboard",
    "Contacts",
    "Calendar",
    "Ledger"
  ];
  final List<String> mobileOptions = ["Home", "Events", "Contacts", "Messages"];
  final List<String> headSetOptions = ["Home", "Contacts", "Events", "Camera"];

  RxString localImagePath = ''.obs;
  RxString imgFileName = ''.obs;
  Uint8List? byteArray;

  @override
  Widget build(BuildContext context) {
    // Implementation...
  }
}

Dependencies

Required Packages

  • flutter/material.dart - Core Flutter UI components
  • get/get.dart - State management and routing
  • get_storage/get_storage.dart - Local storage
  • pin_code_fields/pin_code_fields.dart - PIN input field

Internal Dependencies

  • controllers/auth_controller.dart - Authentication management
  • controllers/contacts_controller.dart - Contact management
  • controllers/settings_controller.dart - Settings management
  • hlk_helpers/hlk_extension_methods.dart - Extension methods
  • hlk_helpers/hlk_models.dart - Helper models
  • hlk_helpers/hlk_utils.dart - Utility functions
  • hlk_helpers/hlk_widget.dart - Custom widgets
  • models/core_models.dart - Core data models
  • shared/common.dart - Common utilities
  • shared/constants.dart - Constants
  • widgets/hlk_widgets.dart - Custom widgets

State Management

Controller Initialization

dart
final SettingsController settingsController = Get.find();
final AuthController authController = Get.find();
final ContactsController contactsController = Get.find();

Reactive State Variables

dart
final RxString pin1 = ''.obs;
final RxString pin2 = ''.obs;
RxString localImagePath = ''.obs;
RxString imgFileName = ''.obs;

Text Controllers

dart
final TextEditingController phoneNumberCtrl = TextEditingController();
final TextEditingController emailCtrl = TextEditingController();
final TextEditingController departmentCtrl = TextEditingController();
final TextEditingController specialtyCtrl = TextEditingController();
final TextEditingController webDefaultPageCtrl = TextEditingController();
final TextEditingController mobileDefaultPageCtrl = TextEditingController();
final TextEditingController headsetDefaultPageCtrl = TextEditingController();
TextEditingController pin1Ctrl = TextEditingController();
TextEditingController pin2Ctrl = TextEditingController();

UI Components

Main Layout

dart
Scaffold(
  appBar: AppBar(title: const Text('Settings')),
  body: SingleChildScrollView(
    child: Column(
      children: [
        // Expansion tiles for different settings sections
      ],
    ),
  ),
)

Expansion Tiles

dart
ExpansionTile(
  iconColor: kAppColorBlue,
  initiallyExpanded: true,
  maintainState: true,
  childrenPadding: const EdgeInsets.all(20),
  expandedCrossAxisAlignment: CrossAxisAlignment.start,
  tilePadding: const EdgeInsets.all(20),
  title: const Text('Contact Information', style: blueBoldTextStyle),
  children: [
    // Contact information fields
  ],
)

Profile Image

dart
GestureDetector(
  onTap: () {
    // Image selection dialog
  },
  child: Row(
    children: [
      Obx(
        () => localImagePath.value.isEmpty &&
                GetUtils.isNullOrBlank(user.photoUrl)! &&
                (contactsController.byteArrayList.isEmpty ||
                    byteArray == null)
            ? RoundedText(
                text: user.name.toInitials().substring(0, 1),
                radius: radius,
                width: radius,
                height: radius,
                backgroundColor: kAppColorLightBlue,
                textStyle: const TextStyle(
                  fontWeight: FontWeight.w400,
                  fontSize: 20,
                  color: kAppColorGrey,
                ),
              )
            : CircleAvatar(
                radius: radius / 2,
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(radius * 2),
                  child: contactsController.byteArrayList.isNotEmpty
                      ? Image.memory(Uint8List.fromList(
                          contactsController.byteArrayList))
                      : Image.network(user.photoUrl!),
                ),
              ),
      ),
      horizontalSpace(0.02),
      Text(
        user.name,
        style: normalStyle,
      ),
    ],
  ),
)

Text Fields

dart
TextField(
  controller: phoneNumberCtrl,
  decoration: getInputDecorationNoBorder('Enter Phone Number', true),
  maxLines: 1,
)
dart
DropdownButtonFormField<String>(
  decoration: InputDecoration(
    labelText: 'Web Default Page',
    labelStyle: const TextStyle(
      color: kAppColorBlue,
      fontSize: 16,
    ),
    border: const OutlineInputBorder(
      borderSide: BorderSide(
        color: kAppColorBlue,
      ),
    ),
  ),
  value: webDefaultPageCtrl.text.isEmpty
      ? webOptions.first
      : webDefaultPageCtrl.text,
  onChanged: (newValue) {
    webDefaultPageCtrl.text = newValue!;
  },
  items: webOptions.map<DropdownMenuItem<String>>((String value) {
    return DropdownMenuItem<String>(
      value: value,
      child: Text(value),
    );
  }).toList(),
)

PIN Input Fields

dart
PinCodeTextField(
  controller: pin1Ctrl,
  appContext: context,
  autoDismissKeyboard: true,
  keyboardType: TextInputType.number,
  length: 4,
  obscureText: false,
  animationType: AnimationType.fade,
  pinTheme: pinTheme,
  cursorColor: kAppColorBlack,
  animationDuration: const Duration(milliseconds: 300),
  enableActiveFill: true,
  beforeTextPaste: (x) {
    return true;
  },
  onCompleted: (value) {
    pin1.value = value;
    logInfo(pin1.value);
    focusNode.requestFocus();
  },
  onChanged: (value) {},
)

Action Buttons

dart
projectActionButton(
  action: () async {
    // Update action
  },
  load: contactsController.loading,
  btnText: 'UPDATE',
)

Settings Management

Contact Information

dart
projectActionButton(
  action: () async {
    if (phoneNumberCtrl.text.isNotEmpty ||
        emailCtrl.text.isNotEmpty ||
        departmentCtrl.text.isNotEmpty ||
        specialtyCtrl.text.isNotEmpty) {
      final phoneNumber = phoneNumberCtrl.text.isNotEmpty
          ? phoneNumberCtrl.text.trim()
          : getString(user.phoneNumber);
      final email = emailCtrl.text.isNotEmpty
          ? emailCtrl.text.trim()
          : getString(user.email);
      final department = departmentCtrl.text.isNotEmpty
          ? departmentCtrl.text.trim()
          : getString(user.department);
      final specialty = specialtyCtrl.text.isNotEmpty
          ? specialtyCtrl.text.trim()
          : getString(user.specialty);

      await contactsController.updateUser(
        phoneNumber: phoneNumber,
        email: email,
        department: department,
        specialty: specialty,
        headsetDefaultPage: headsetDefaultPageCtrl.text,
        webDefaultPage: webDefaultPageCtrl.text,
        mobileDefaultPage: mobileDefaultPageCtrl.text,
        timeOffset: 0,
        filename: imgFileName.value,
        fileByteArray: byteArray,
      );
    }
  },
  load: contactsController.loading,
  btnText: 'UPDATE',
)

Notification Settings

dart
Obx(
  () => SwitchListTile(
    title: const Text('Enable Notifications'),
    value: settingsController.enableNotification.value,
    onChanged: (bool value) {
      settingsController.enableNotification.value = value;
    },
  ),
)

Security Settings

dart
Obx(
  () => SwitchListTile(
    title: const Text('Keep Me Logged In'),
    value: settingsController.keepMeLoggedIn.value,
    onChanged: (bool value) {
      HlkDialog.showMessageInAlertDialog(
        title: 'Security Warning',
        message:
            'This will disable auto logout on this device and should only be used on a personal device for security purposes. If this is a personal device, click OK. If not, click Cancel.',
        dialogContent: [
          AlertDialogContent(
            'OK',
            (otherContext) {
              settingsController.keepMeLoggedIn.value = value;
              if (value) {
                settingsController.enableBiometric.value =
                    !settingsController.keepMeLoggedIn.value;
              }
              Get.back();
            },
          ),
          AlertDialogContent(
              'CANCEL', (otherContext) => Get.back()),
        ],
      );
    },
  ),
)

PIN Change

dart
projectActionButton(
  action: () async {
    if (pin1.value == pin2.value && pin1.isNotEmpty) {
      await authController.resetPin(pin1.value);
    }
    pin1.value = '';
    pin2.value = '';
    pin1Ctrl.clear();
    pin2Ctrl.clear();
  },
  load: authController.loading,
  btnText: 'UPDATE',
)

Security Features

Biometric Authentication

dart
if (settingsController.canAuthenticate)
  Obx(
    () => SwitchListTile(
      title: const Text('Enable Biometric Log In'),
      value: settingsController.enableBiometric.value,
      onChanged: (bool value) async {
        HlkDialog.showMessageInAlertDialog(
          title: 'Biometrics Authentication',
          message:
              'Use biometric log in for faster and easier access to your account. You can turn this feature on or off at any time under settings.',
          dialogContent: [
            AlertDialogContent(
              settingsController.enableBiometric.value
                  ? 'DISABLE'
                  : 'ENABLE',
              (otherContext) async {
                if (value) {
                  settingsController.enableBiometric.value =
                      await authController.getBioToken();

                  if (settingsController.enableBiometric.value) {
                    settingsController.keepMeLoggedIn.value = false;
                  }
                  settingsController.keepMeLoggedIn.value =
                      !settingsController.enableBiometric.value;
                } else {
                  settingsController.enableBiometric.value = value;
                }
                Get.back();
                if (authController.biometricErrorMessage.isNotEmpty) {
                  HlkDialog.showErrorSnackBar(
                      authController.biometricErrorMessage.value);
                  throw Exception(
                      authController.biometricErrorMessage.value);
                }
              },
            ),
            AlertDialogContent(
              'NOT NOW',
              (otherContext) => Get.back(),
            ),
          ],
        );
      },
    ),
  )

PIN Validation

dart
validator: (v) {
  return pin1.value == pin2.value ? null : 'Pin mismatched';
},

Released under the MIT License.