login_view.dart 13.8 KB
import 'dart:math' as math;

import 'package:doublefeel_flutter/core/config/app_environment_config.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';

import '../controllers/login_controller.dart';

class LoginView extends GetView<LoginController> {
  const LoginView({super.key});

  static const _designHeight = 812.0;
  static const _brandColor = Color(0xFF845EEE);
  static const _titleColor = Color(0xFF0F0F11);

  @override
  Widget build(BuildContext context) {
    return AnnotatedRegion<SystemUiOverlayStyle>(
      value: const SystemUiOverlayStyle(
        statusBarColor: Colors.transparent,
        statusBarIconBrightness: Brightness.dark,
        statusBarBrightness: Brightness.light,
        systemNavigationBarColor: Colors.transparent,
        systemNavigationBarIconBrightness: Brightness.dark,
      ),
      child: Scaffold(
        backgroundColor: Colors.white,
        body: LayoutBuilder(
          builder: (context, constraints) {
            final screenSize = MediaQuery.sizeOf(context);
            final width = constraints.maxWidth.isFinite
                ? constraints.maxWidth
                : screenSize.width;
            final height = constraints.maxHeight.isFinite
                ? constraints.maxHeight
                : screenSize.height;
            final buttonWidth = math.min(280.0, math.max(0.0, width - 64.0));

            double top(double value) => height * value / _designHeight;

            return Stack(
              fit: StackFit.expand,
              children: [
                // Background Gradient
                const DecoratedBox(
                  decoration: BoxDecoration(
                    gradient: LinearGradient(
                      begin: Alignment.topCenter,
                      end: Alignment.bottomCenter,
                      colors: [
                        Color(0xFFECE3FF),
                        Color(0xFFEDE4FF),
                        Color(0xFFEEE6FF),
                        Color(0xFFF9F6FF),
                      ],
                      stops: [0, 0.33173, 0.74038, 1],
                    ),
                  ),
                ),

                // Beautiful custom illustration (Instead of red text placeholder)
                Positioned(
                  top: top(140),
                  left: 0,
                  right: 0,
                  child: Center(
                    child: SizedBox(
                      width: 220,
                      height: 220,
                      child: Stack(
                        alignment: Alignment.center,
                        children: [
                          // Base glow circle
                          Container(
                            width: 180,
                            height: 180,
                            decoration: BoxDecoration(
                              shape: BoxShape.circle,
                              gradient: RadialGradient(
                                colors: [
                                  _brandColor.withValues(alpha: 0.25),
                                  Colors.transparent,
                                ],
                              ),
                            ),
                          ),
                          // Premium geometric shapes with glassmorphism glow
                          Positioned(
                            top: 20,
                            left: 30,
                            child: Container(
                              width: 60,
                              height: 60,
                              decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                color: Colors.white.withValues(alpha: 0.5),
                                border: Border.all(
                                  color: Colors.white.withValues(alpha: 0.6),
                                  width: 1.5,
                                ),
                              ),
                            ),
                          ),
                          Positioned(
                            bottom: 30,
                            right: 40,
                            child: Container(
                              width: 80,
                              height: 80,
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(24),
                                gradient: LinearGradient(
                                  begin: Alignment.topLeft,
                                  end: Alignment.bottomRight,
                                  colors: [
                                    Colors.white.withValues(alpha: 0.6),
                                    Colors.white.withValues(alpha: 0.2),
                                  ],
                                ),
                                border: Border.all(
                                  color: Colors.white.withValues(alpha: 0.8),
                                  width: 1.5,
                                ),
                              ),
                            ),
                          ),
                          // Heart inside glass sphere
                          Container(
                            width: 120,
                            height: 120,
                            decoration: BoxDecoration(
                              shape: BoxShape.circle,
                              gradient: LinearGradient(
                                begin: Alignment.topLeft,
                                end: Alignment.bottomRight,
                                colors: [
                                  Colors.white.withValues(alpha: 0.8),
                                  Colors.white.withValues(alpha: 0.1),
                                ],
                              ),
                              border: Border.all(
                                color: Colors.white.withValues(alpha: 0.9),
                                width: 2,
                              ),
                              boxShadow: [
                                BoxShadow(
                                  color: _brandColor.withValues(alpha: 0.15),
                                  blurRadius: 20,
                                  offset: const Offset(0, 8),
                                ),
                              ],
                            ),
                            child: const Center(
                              child: Icon(
                                Icons.favorite_rounded,
                                color: _brandColor,
                                size: 54,
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ),

                // Welcome texts
                Positioned(
                  top: top(420),
                  left: 0,
                  right: 0,
                  child: const Column(
                    children: [
                      Text(
                        '欢迎使用DoubleFeel',
                        textAlign: TextAlign.center,
                        style: TextStyle(
                          color: _titleColor,
                          fontSize: 28,
                          fontWeight: FontWeight.w800,
                          letterSpacing: 0.5,
                        ),
                      ),
                      SizedBox(height: 16),
                      Text(
                        '开启压力预警与健康陪伴之旅\n让爱与关心从不缺席',
                        textAlign: TextAlign.center,
                        style: TextStyle(
                          color: Color(0xFF666666),
                          fontSize: 14,
                          fontWeight: FontWeight.w400,
                          height: 1.4,
                        ),
                      ),
                    ],
                  ),
                ),

                // Phone login button
                Positioned(
                  top: top(560),
                  left: 0,
                  right: 0,
                  child: Center(
                    child: _LoginButton(
                      width: buttonWidth,
                      label: '手机号登录/注册',
                      onPressed: controller.onPhoneLoginPressed,
                    ),
                  ),
                ),

                // Apple login button
                Positioned(
                  top: top(628),
                  left: 0,
                  right: 0,
                  child: Center(
                    child: _LoginButton(
                      width: buttonWidth,
                      label: '通过Apple登录',
                      icon: Icons.apple,
                      onPressed: controller.onAppleLoginPressed,
                    ),
                  ),
                ),

                // Terms agreement text + checkbox
                Positioned(
                  top: top(712),
                  left: 0,
                  right: 0,
                  child: const _AgreementText(),
                ),

                if (kDebugMode)
                  Positioned(
                    left: 0,
                    right: 0,
                    bottom: MediaQuery.paddingOf(context).bottom + 12,
                    child: Center(
                      child: TextButton(
                        onPressed: controller.onDebugPressed,
                        style: TextButton.styleFrom(
                          foregroundColor: _titleColor.withValues(alpha: 0.55),
                          textStyle: const TextStyle(
                            fontSize: 12,
                            fontWeight: FontWeight.w600,
                          ),
                        ),
                        child: Builder(builder: (context) {
                          final environmentConfig =
                              Get.find<AppEnvironmentConfig>();
                          return Text(environmentConfig.environment.value.name);
                        }),
                      ),
                    ),
                  ),
              ],
            );
          },
        ),
      ),
    );
  }
}

class _LoginButton extends StatelessWidget {
  const _LoginButton({
    required this.width,
    required this.label,
    required this.onPressed,
    this.icon,
  });

  final double width;
  final String label;
  final IconData? icon;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: width,
      height: 52,
      child: ElevatedButton(
        onPressed: onPressed,
        style: ElevatedButton.styleFrom(
          backgroundColor: LoginView._brandColor,
          foregroundColor: Colors.white,
          disabledBackgroundColor: LoginView._brandColor.withValues(alpha: 0.5),
          disabledForegroundColor: Colors.white,
          elevation: 0,
          shadowColor: Colors.transparent,
          padding: EdgeInsets.symmetric(horizontal: icon == null ? 24 : 12),
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(26),
          ),
          textStyle: const TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.w700,
          ),
        ),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (icon != null) ...[
              Icon(icon, size: 24),
              const SizedBox(width: 8),
            ],
            Flexible(
              child: Text(
                label,
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _AgreementText extends GetView<LoginController> {
  const _AgreementText();

  static const _mutedColor = Color(0xFF78787D);
  static const _linkColor = Color(0xFF14121E);

  @override
  Widget build(BuildContext context) {
    const baseStyle = TextStyle(
      color: _mutedColor,
      fontSize: 12,
      fontWeight: FontWeight.w400,
      height: 1.2,
    );
    const linkStyle = TextStyle(
      color: _linkColor,
      fontSize: 12,
      fontWeight: FontWeight.w600,
      height: 1.2,
      decoration: TextDecoration.underline,
      decorationColor: _linkColor,
    );

    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Obx(() {
          final isChecked = controller.termsChecked.value;
          return GestureDetector(
            onTap: controller.toggleTermsChecked,
            child: Padding(
              padding: const EdgeInsets.only(right: 8.0, top: 4.0, bottom: 4.0),
              child: Icon(
                isChecked
                    ? Icons.check_circle_rounded
                    : Icons.radio_button_unchecked_rounded,
                color: isChecked ? LoginView._brandColor : _mutedColor,
                size: 18,
              ),
            ),
          );
        }),
        Text.rich(
          TextSpan(
            style: baseStyle,
            children: [
              const TextSpan(text: '我已阅读并同意'),
              TextSpan(
                text: '《用户协议》',
                style: linkStyle,
                recognizer: TapGestureRecognizer()
                  ..onTap = controller.openUserTerms,
              ),
              const TextSpan(text: '与'),
              TextSpan(
                text: '《隐私协议》',
                style: linkStyle,
                recognizer: TapGestureRecognizer()
                  ..onTap = controller.openPrivacyPolicy,
              ),
            ],
          ),
          textAlign: TextAlign.center,
        ),
      ],
    );
  }
}