// screens/auth/auth_shared.dart — 登录/注册两屏共用的原型 1:1 骨架。 // 对应 login.html / register.html 的公共部分: // body:--page 底、居中、pad 24; // 主题切换:主界面同款小衣服 ThemePickerPill(onSurface 浅色变体), // 放表单面板右上角(2026-07-03 用户拍板,替代原型的 A/B/C 圆钮); // .auth:两栏卡片(surface / 1px border / r-xl / sh-3,overflow hidden); // .brand:左侧品牌渐变面板 linear-gradient(150deg, side-bg, side-active-bg)。 // 窄屏(≤ context.isMobile)折叠为单列,隐藏 pts/steps(原型 @media 760px 同构, // 断点遵项目规则统一走 isMobile)。 import 'package:flutter/material.dart'; import '../../core/responsive/responsive.dart'; import '../../core/theme/app_dims.g.dart'; import '../../core/theme/context_tokens.dart'; import '../../widgets/theme_picker_pill.dart'; /// 整页骨架:page 底 + 居中滚动 + 两栏卡片 + 右上角主题切换器。 /// [brand]/[form] 为两栏内容;宽屏按 [brandWidth]/[formWidth] 定宽 /// (其一为 null 表示该栏弹性 1fr),窄屏纵向堆叠。 class AuthPageScaffold extends StatelessWidget { final Widget brand; final Widget form; final double maxWidth; final double narrowMaxWidth; final double? brandWidth; // null = 弹性栏 final double? formWidth; // null = 弹性栏 const AuthPageScaffold({ super.key, required this.brand, required this.form, required this.maxWidth, required this.narrowMaxWidth, this.brandWidth, this.formWidth, }); @override Widget build(BuildContext context) { final t = context.tokens; final narrow = context.isMobile; // 表单面板右上角叠主题小衣服(宽/窄屏都落在白底表单区上) final formWithPill = Stack( children: [ form, const Positioned( top: 14, right: 16, child: ThemePickerPill(onSurface: true)), ], ); final card = Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration( color: t.surface, border: Border.all(color: t.border), borderRadius: BorderRadius.circular(AppDims.rXl), boxShadow: [ BoxShadow( color: t.shadow, offset: const Offset(0, 12), blurRadius: 34), ], ), child: narrow ? Column( mainAxisSize: MainAxisSize.min, children: [brand, formWithPill]) : IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (brandWidth != null) SizedBox(width: brandWidth, child: brand) else Expanded(child: brand), if (formWidth != null) SizedBox(width: formWidth, child: formWithPill) else Expanded(child: formWithPill), ], ), ), ); return Scaffold( backgroundColor: t.page, body: SingleChildScrollView( padding: const EdgeInsets.all(24), child: ConstrainedBox( constraints: BoxConstraints( minHeight: MediaQuery.of(context).size.height - 48), child: Center( child: ConstrainedBox( constraints: BoxConstraints(maxWidth: narrow ? narrowMaxWidth : maxWidth), child: card, ), ), ), ), ); } } /// 品牌渐变面板容器(.brand):linear-gradient(150deg, side-bg, side-active-bg)。 class AuthBrandPanel extends StatelessWidget { final EdgeInsets padding; final double? minHeight; final Widget child; const AuthBrandPanel( {super.key, required this.padding, this.minHeight, required this.child}); @override Widget build(BuildContext context) { final t = context.tokens; return Container( constraints: minHeight != null ? BoxConstraints(minHeight: minHeight!) : null, padding: padding, decoration: BoxDecoration( // 150deg ≈ 从左上偏上往右下(CSS 角度顺时针自 12 点起) gradient: LinearGradient( begin: const Alignment(-0.5, -1), end: const Alignment(0.5, 1), colors: [t.sideBg, t.sideActiveBg], ), ), child: child, ); } }