rtc_prd/airhub_app/lib/pages/home_page.dart
2026-02-06 16:03:32 +08:00

279 lines
8.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../theme/app_colors.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
late AnimationController _mascotController;
late Animation<double> _mascotAnimation;
@override
void initState() {
super.initState();
// Mascot floating animation
_mascotController = AnimationController(
duration: const Duration(seconds: 4),
vsync: this,
)..repeat(reverse: true);
_mascotAnimation = Tween<double>(begin: -10, end: 10).animate(
CurvedAnimation(parent: _mascotController, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_mascotController.dispose();
super.dispose();
}
void _handleConnect() {
Navigator.of(context).pushNamed('/bluetooth');
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: [
// Gradient Background
_buildGradientBackground(),
SafeArea(
child: Column(
children: [
// Header (Logo)
_buildHeader(),
// Main Content (Mascot)
Expanded(child: _buildBody()),
// Footer (Button)
_buildFooter(),
],
),
),
],
),
);
}
Widget _buildGradientBackground() {
final size = MediaQuery.of(context).size;
return Positioned.fill(
child: Stack(
children: [
// Layer 1
Positioned(
top: -size.width * 0.2,
left: -size.width * 0.2,
width: size.width * 1.5,
height: size.width * 1.5,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
const Color(0xFFC4B5FD).withOpacity(0.4), // Violet tinge
Colors.transparent,
],
radius: 0.6,
),
),
),
),
// Layer 2
Positioned(
bottom: size.height * 0.1,
right: -size.width * 0.3,
width: size.width * 1.2,
height: size.width * 1.2,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
const Color(0xFF67E8F9).withOpacity(0.3), // Cyan tinge
Colors.transparent,
],
radius: 0.6,
),
),
),
),
// Layer 3
Positioned(
bottom: -size.width * 0.5,
left: size.width * 0.1,
width: size.width * 1.5,
height: size.width * 1.5,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
const Color(0xFFF9A8D4).withOpacity(0.3), // Pink tinge
Colors.transparent,
],
radius: 0.6,
),
),
),
),
],
),
);
}
Widget _buildHeader() {
return Container(
height: 80,
alignment: Alignment.center,
child: Text(
'Airhub',
// Use Press Start 2P pixel font per HTML CSS
style: GoogleFonts.pressStart2p(
fontSize: 28,
color: const Color(0xFF4B5563), // gray-600 per HTML
letterSpacing: 2,
// Crisp pixel-stepped shadows (0 blur) per HTML
shadows: const [
Shadow(
color: Color(0x40A78BFA), // rgba(139, 92, 246, 0.25)
offset: Offset(1, 1),
blurRadius: 0,
),
Shadow(
color: Color(0x26A78BFA), // rgba(139, 92, 246, 0.15)
offset: Offset(2, 2),
blurRadius: 0,
),
],
),
),
);
}
Widget _buildBody() {
return Center(
child: AnimatedBuilder(
animation: _mascotAnimation,
builder: (context, child) {
return Transform.translate(
offset: Offset(0, _mascotAnimation.value),
child: child,
);
},
child: Container(
// Glow effect behind mascot
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0xFF8B5CF6).withOpacity(0.3),
blurRadius: 60,
spreadRadius: 20,
),
],
),
child: Image.asset(
'assets/www/home_mascot.png',
width: 280,
fit: BoxFit.contain,
errorBuilder: (_, __, ___) =>
const Icon(Icons.adb, size: 200, color: Colors.grey),
),
),
),
);
}
Widget _buildFooter() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 56),
child: Container(
height: 58, // HTML: height: 58px
constraints: const BoxConstraints(maxWidth: 300), // HTML: width: min(300px, 82vw)
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(29), // HTML: border-radius: 29px
gradient: AppColors.btnPrimaryGradient,
// 5-layer box-shadow per HTML CSS --btn-primary-glow
boxShadow: [
// 0 0 15px rgba(34, 211, 238, 0.35) - cyan outer glow
BoxShadow(
color: const Color(0xFF22D3EE).withOpacity(0.35),
offset: Offset.zero,
blurRadius: 15,
),
// 0 0 30px rgba(99, 102, 241, 0.25) - indigo wider glow
BoxShadow(
color: const Color(0xFF6366F1).withOpacity(0.25),
offset: Offset.zero,
blurRadius: 30,
),
// 0 6px 20px rgba(99, 102, 241, 0.4) - bottom shadow
BoxShadow(
color: const Color(0xFF6366F1).withOpacity(0.4),
offset: const Offset(0, 6),
blurRadius: 20,
),
],
),
child: Stack(
children: [
// Shine overlay (top half gradient)
Positioned.fill(
child: ClipRRect(
borderRadius: BorderRadius.circular(29),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.white.withOpacity(0.15),
Colors.transparent,
],
stops: const [0.0, 0.5],
),
),
),
),
),
// Button content
Material(
color: Colors.transparent,
child: InkWell(
onTap: _handleConnect,
borderRadius: BorderRadius.circular(29),
child: Center(
// HTML button has NO icon, only text "立即连接"
child: Text(
'立即连接',
style: const TextStyle(
fontFamily: 'Inter',
fontSize: 17, // HTML: font-size: 17px
fontWeight: FontWeight.w600,
color: Colors.white,
letterSpacing: 0.5,
),
),
),
),
),
],
),
),
);
}
}