change_phone_view.dart 10 KB
import 'package:doublefeel_flutter/app/modules/change_phone/controllers/change_phone_controller.dart';
import 'package:doublefeel_flutter/core/theme/app_colors.dart';
import 'package:doublefeel_flutter/core/util/size_extensions.dart';
import 'package:doublefeel_flutter/r.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';

class ChangePhoneView extends GetView<ChangePhoneController> {
  const ChangePhoneView({super.key});

  @override
  Widget build(BuildContext context) {
    return AnnotatedRegion<SystemUiOverlayStyle>(
      value: const SystemUiOverlayStyle(
        statusBarColor: AppColors.backgroundPage,
        statusBarIconBrightness: Brightness.dark,
        statusBarBrightness: Brightness.light,
        systemNavigationBarColor: AppColors.backgroundPage,
        systemNavigationBarIconBrightness: Brightness.dark,
      ),
      child: Scaffold(
        resizeToAvoidBottomInset: false,
        backgroundColor: AppColors.backgroundPage,
        body: SafeArea(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              SizedBox(
                height: 44.dp,
                child: Align(
                  alignment: Alignment.centerLeft,
                  child: IconButton(
                    onPressed: Get.back,
                    padding: EdgeInsets.only(left: 16.dp),
                    splashColor: Colors.transparent,
                    highlightColor: Colors.transparent,
                    icon: Image.asset(
                      R.assetsImagesNavBackIcon,
                      width: 28.dp,
                      height: 28.dp,
                    ),
                  ),
                ),
              ),
              Expanded(
                child: Obx(() {
                  final oldPhoneStep = controller.isOldPhoneStep;
                  return SingleChildScrollView(
                    padding: EdgeInsets.symmetric(horizontal: 28.dp),
                    child: Column(
                      children: [
                        SizedBox(height: 16.dp),
                        Text(
                          oldPhoneStep ? '原手机号验证' : '新手机号绑定',
                          style: TextStyle(
                            color: AppColors.textPrimary,
                            fontSize: 24.dp,
                            fontWeight: FontWeight.w600,
                            height: 1.35,
                          ),
                        ),
                        if (oldPhoneStep) ...[
                          SizedBox(height: 4.dp),
                          SizedBox(
                            height: 40.dp,
                            child: Text(
                              '请输入该账号绑定的原手机号${controller.maskedOldPhone},完成手机验证',
                              textAlign: TextAlign.center,
                              style: TextStyle(
                                color: AppColors.textSecondary,
                                fontSize: 14.dp,
                                fontWeight: FontWeight.w400,
                                height: 1.45,
                              ),
                            ),
                          ),
                          SizedBox(height: 55.dp),
                        ] else
                          SizedBox(height: 103.dp),
                        _PhoneField(controller: controller),
                        SizedBox(height: 8.dp),
                        _CodeField(controller: controller),
                        SizedBox(height: 68.dp),
                        _SubmitButton(controller: controller),
                        if (oldPhoneStep) ...[
                          SizedBox(height: 12.dp),
                          TextButton(
                            onPressed: controller.openHelp,
                            style: TextButton.styleFrom(
                              foregroundColor: AppColors.chartBlue,
                              minimumSize: Size.zero,
                              padding: EdgeInsets.symmetric(
                                horizontal: 8.dp,
                                vertical: 4.dp,
                              ),
                              tapTargetSize: MaterialTapTargetSize.shrinkWrap,
                            ),
                            child: Text(
                              '现手机已不可用?',
                              style: TextStyle(
                                fontSize: 12.dp,
                                fontWeight: FontWeight.w500,
                              ),
                            ),
                          ),
                        ],
                      ],
                    ),
                  );
                }),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _PhoneField extends StatelessWidget {
  const _PhoneField({required this.controller});

  final ChangePhoneController controller;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 52.dp,
      child: Obx(() => TextField(
            controller: controller.phoneController,
            keyboardType: TextInputType.phone,
            maxLength: 11,
            inputFormatters: [FilteringTextInputFormatter.digitsOnly],
            style: TextStyle(
              color: AppColors.textPrimary,
              fontSize: 16.dp,
              fontWeight: FontWeight.w400,
            ),
            decoration: _inputDecoration(
              hintText: '',
              prefix: Padding(
                padding: EdgeInsets.only(right: 12.dp),
                child: Text(
                  '+86',
                  style: TextStyle(
                    color: AppColors.textTertiary,
                    fontSize: 16.dp,
                    fontWeight: FontWeight.w400,
                  ),
                ),
              ),
              suffix: controller.phoneInput.value.isEmpty
                  ? null
                  : IconButton(
                      onPressed: controller.clearPhone,
                      icon: Icon(
                        Icons.cancel,
                        color: const Color(0xFFB0B0B6),
                        size: 24.dp,
                      ),
                    ),
            ),
          )),
    );
  }
}

class _CodeField extends StatelessWidget {
  const _CodeField({required this.controller});

  final ChangePhoneController controller;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 52.dp,
      child: Obx(() {
        final seconds = controller.countdownSeconds.value;
        final requesting = controller.isRequestingCode.value;
        final buttonText = requesting
            ? '发送中'
            : seconds > 0
                ? '重新发送(${seconds}s)'
                : '发送验证码';
        return TextField(
          controller: controller.codeController,
          focusNode: controller.codeFocusNode,
          keyboardType: TextInputType.number,
          maxLength: 6,
          inputFormatters: [FilteringTextInputFormatter.digitsOnly],
          style: TextStyle(
            color: AppColors.textPrimary,
            fontSize: 16.dp,
            fontWeight: FontWeight.w400,
          ),
          decoration: _inputDecoration(
            hintText: '输入验证码',
            suffix: controller.phoneInput.value.isEmpty
                ? null
                : TextButton(
                    onPressed: controller.canRequestCode
                        ? controller.requestVerificationCode
                        : null,
                    style: TextButton.styleFrom(
                      minimumSize: Size.zero,
                      padding: EdgeInsets.symmetric(horizontal: 12.dp),
                      tapTargetSize: MaterialTapTargetSize.shrinkWrap,
                      foregroundColor: AppColors.primary,
                      disabledForegroundColor: AppColors.textTertiary,
                    ),
                    child: Text(
                      buttonText,
                      style: TextStyle(
                        fontSize: 14.dp,
                        fontWeight: FontWeight.w500,
                      ),
                    ),
                  ),
          ),
        );
      }),
    );
  }
}

class _SubmitButton extends StatelessWidget {
  const _SubmitButton({required this.controller});

  final ChangePhoneController controller;

  @override
  Widget build(BuildContext context) {
    return Obx(() => SizedBox(
          width: double.infinity,
          height: 48.dp,
          child: ElevatedButton(
            onPressed: controller.canSubmit ? controller.submit : null,
            style: ElevatedButton.styleFrom(
              elevation: 0,
              backgroundColor: AppColors.primary,
              disabledBackgroundColor: AppColors.primary.withValues(alpha: 0.4),
              foregroundColor: Colors.white,
              disabledForegroundColor: Colors.white,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(24.dp),
              ),
              textStyle:
                  TextStyle(fontSize: 16.dp, fontWeight: FontWeight.w600),
            ),
            child: const Text('确定'),
          ),
        ));
  }
}

InputDecoration _inputDecoration({
  required String hintText,
  Widget? prefix,
  Widget? suffix,
}) {
  final border = OutlineInputBorder(
    borderRadius: BorderRadius.circular(27),
    borderSide: BorderSide.none,
  );
  return InputDecoration(
    hintText: hintText,
    hintStyle: const TextStyle(color: AppColors.textTertiary, fontSize: 16),
    counterText: '',
    filled: true,
    fillColor: Colors.white,
    contentPadding: const EdgeInsets.symmetric(horizontal: 28, vertical: 0),
    prefixIcon: prefix == null ? null : Center(child: prefix),
    prefixIconConstraints: const BoxConstraints(minWidth: 0),
    suffixIcon: suffix,
    suffixIconConstraints: const BoxConstraints(minWidth: 0),
    border: border,
    enabledBorder: border,
    focusedBorder: border,
  );
}