158 lines
4.6 KiB
Dart
158 lines
4.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import '../theme/app_theme.dart';
|
|
import '../models/app_settings.dart';
|
|
|
|
class BreathePage extends StatefulWidget {
|
|
const BreathePage({super.key});
|
|
|
|
@override
|
|
State<BreathePage> createState() => _BreathePageState();
|
|
}
|
|
|
|
class _BreathePageState extends State<BreathePage>
|
|
with SingleTickerProviderStateMixin {
|
|
late AnimationController _controller;
|
|
late Animation<double> _scaleAnimation;
|
|
String _instruction = "Inhale";
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = AnimationController(
|
|
vsync: this,
|
|
duration: const Duration(seconds: 19),
|
|
);
|
|
|
|
_scaleAnimation = TweenSequence<double>([
|
|
TweenSequenceItem(
|
|
tween: Tween(begin: 1.0, end: 1.5)
|
|
.chain(CurveTween(curve: Curves.easeOut)),
|
|
weight: 4,
|
|
),
|
|
TweenSequenceItem(tween: ConstantTween(1.5), weight: 7),
|
|
TweenSequenceItem(
|
|
tween: Tween(begin: 1.5, end: 1.0)
|
|
.chain(CurveTween(curve: Curves.easeIn)),
|
|
weight: 8,
|
|
),
|
|
]).animate(_controller);
|
|
|
|
_controller.addListener(() {
|
|
final val = _controller.value;
|
|
String newText;
|
|
if (val < 0.21) {
|
|
newText = "Inhale (4s)";
|
|
} else if (val < 0.58) {
|
|
newText = "Hold (7s)";
|
|
} else {
|
|
newText = "Exhale (8s)";
|
|
}
|
|
|
|
if (newText != _instruction) {
|
|
setState(() => _instruction = newText);
|
|
if (AppSettings.enableHaptics) HapticFeedback.mediumImpact();
|
|
}
|
|
});
|
|
|
|
_controller.repeat();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFE3F2FD),
|
|
appBar: AppBar(
|
|
leading: const CloseButton(color: AppTheme.primary),
|
|
backgroundColor: Colors.transparent,
|
|
),
|
|
body: Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Text(
|
|
"B R E A T H E",
|
|
style: TextStyle(
|
|
letterSpacing: 4,
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w900,
|
|
color: AppTheme.primary,
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
const Text(
|
|
"4-7-8 Technique",
|
|
style: TextStyle(
|
|
color: AppTheme.textSub,
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
const SizedBox(height: 60),
|
|
AnimatedBuilder(
|
|
animation: _scaleAnimation,
|
|
builder: (context, child) {
|
|
return Container(
|
|
width: 200 * _scaleAnimation.value,
|
|
height: 200 * _scaleAnimation.value,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
gradient: RadialGradient(
|
|
colors: [
|
|
AppTheme.accent.withOpacity(0.4),
|
|
AppTheme.accent.withOpacity(0.1),
|
|
],
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: AppTheme.accent.withOpacity(0.2),
|
|
blurRadius: 30 * _scaleAnimation.value,
|
|
spreadRadius: 10,
|
|
)
|
|
],
|
|
),
|
|
child: Center(
|
|
child: Container(
|
|
width: 150,
|
|
height: 150,
|
|
decoration: const BoxDecoration(
|
|
color: Colors.white,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
_instruction.split(" ")[0],
|
|
style: const TextStyle(
|
|
fontSize: 32,
|
|
fontWeight: FontWeight.w800,
|
|
color: AppTheme.primary,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
const SizedBox(height: 80),
|
|
Text(
|
|
_instruction,
|
|
style: const TextStyle(
|
|
fontSize: 24,
|
|
color: AppTheme.primary,
|
|
fontWeight: FontWeight.w800,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|