279 lines
8.0 KiB
Dart
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,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|