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.