157 lines
4.4 KiB
Dart
157 lines
4.4 KiB
Dart
import 'dart:async';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||
|
||
final notifications = FlutterLocalNotificationsPlugin();
|
||
|
||
void main() async {
|
||
WidgetsFlutterBinding.ensureInitialized();
|
||
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
|
||
const settings = InitializationSettings(android: androidSettings);
|
||
await notifications.initialize(settings);
|
||
await notifications.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.requestNotificationsPermission();
|
||
runApp(const TimerApp());
|
||
}
|
||
|
||
class TimerApp extends StatelessWidget {
|
||
const TimerApp({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) => MaterialApp(
|
||
theme: ThemeData(useMaterial3: true, colorScheme: ColorScheme.fromSeed(
|
||
seedColor: Colors.deepPurple,
|
||
brightness: Brightness.light,
|
||
)),
|
||
home: const TimerScreen(),
|
||
);
|
||
}
|
||
|
||
class TimerScreen extends StatefulWidget {
|
||
const TimerScreen({super.key});
|
||
|
||
@override
|
||
State<TimerScreen> createState() => _TimerScreenState();
|
||
}
|
||
|
||
class _TimerScreenState extends State<TimerScreen> {
|
||
int duration = 60;
|
||
int remaining = 60;
|
||
Timer? timer;
|
||
bool isRunning = false;
|
||
|
||
void _showNotification() async {
|
||
const androidDetails = AndroidNotificationDetails(
|
||
'timer_channel',
|
||
'Таймер',
|
||
channelDescription: 'Уведомления таймера',
|
||
importance: Importance.high,
|
||
priority: Priority.high,
|
||
);
|
||
const details = NotificationDetails(android: androidDetails);
|
||
await notifications.show(0, 'Таймер завершён!', 'Время вышло.', details);
|
||
}
|
||
|
||
void _startTimer() {
|
||
setState(() => isRunning = true);
|
||
timer = Timer.periodic(const Duration(seconds: 1), (_) {
|
||
if (remaining > 0) {
|
||
setState(() => remaining--);
|
||
} else {
|
||
timer?.cancel();
|
||
_showNotification();
|
||
setState(() => isRunning = false);
|
||
}
|
||
});
|
||
}
|
||
|
||
void _pauseTimer() {
|
||
timer?.cancel();
|
||
setState(() => isRunning = false);
|
||
}
|
||
|
||
void _resetTimer() {
|
||
timer?.cancel();
|
||
setState(() {
|
||
isRunning = false;
|
||
remaining = duration;
|
||
});
|
||
}
|
||
|
||
void _selectDuration() {
|
||
showModalBottomSheet(
|
||
context: context,
|
||
builder: (_) => Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [1, 5, 10, 15, 30, 60].map((m) {
|
||
return ListTile(
|
||
title: Text('$m мин'),
|
||
onTap: () {
|
||
setState(() {
|
||
duration = m * 60;
|
||
remaining = duration;
|
||
});
|
||
Navigator.pop(context);
|
||
},
|
||
);
|
||
}).toList(),
|
||
),
|
||
);
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
timer?.cancel();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final minutes = (remaining / 60).floor();
|
||
final seconds = remaining % 60;
|
||
final progress = duration > 0 ? (remaining / duration) as double : 0.0;
|
||
|
||
return Scaffold(
|
||
appBar: AppBar(title: const Text('Таймер')),
|
||
body: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
GestureDetector(
|
||
onTap: _selectDuration,
|
||
child: Stack(
|
||
alignment: Alignment.center,
|
||
children: [
|
||
SizedBox(
|
||
width: 200,
|
||
height: 200,
|
||
child: CircularProgressIndicator(
|
||
value: progress,
|
||
strokeWidth: 8,
|
||
),
|
||
),
|
||
Text(
|
||
'${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}',
|
||
style: Theme.of(context).textTheme.displayMedium,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
const SizedBox(height: 40),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
FloatingActionButton(
|
||
onPressed: isRunning ? _pauseTimer : _startTimer,
|
||
child: Icon(isRunning ? Icons.pause : Icons.play_arrow),
|
||
),
|
||
if (remaining != duration) ...[
|
||
const SizedBox(width: 16),
|
||
OutlinedButton(onPressed: _resetTimer, child: const Text('Сброс')),
|
||
],
|
||
],
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|