2025-02-22 19:00:07 +03:00
|
|
|
import 'package:flutter/material.dart';
|
2025-02-22 19:50:05 +03:00
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
|
import 'sensor_provider.dart';
|
|
|
|
|
import 'package:timeago/timeago.dart' as timeago;
|
2025-02-22 19:00:07 +03:00
|
|
|
|
|
|
|
|
void main() {
|
2025-02-22 19:50:05 +03:00
|
|
|
runApp(
|
|
|
|
|
ChangeNotifierProvider(
|
|
|
|
|
create: (context) => SensorProvider(),
|
|
|
|
|
child: const MyApp(),
|
|
|
|
|
),
|
|
|
|
|
);
|
2025-02-22 19:00:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MyApp extends StatelessWidget {
|
|
|
|
|
const MyApp({super.key});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return MaterialApp(
|
2025-02-22 19:50:05 +03:00
|
|
|
title: 'Sensor App',
|
2025-02-22 19:58:36 +03:00
|
|
|
theme: ThemeData(primarySwatch: Colors.blue),
|
2025-02-22 19:50:05 +03:00
|
|
|
home: const SensorScreen(),
|
2025-02-22 19:00:07 +03:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-22 19:50:05 +03:00
|
|
|
class SensorScreen extends StatefulWidget {
|
|
|
|
|
const SensorScreen({super.key});
|
2025-02-22 19:00:07 +03:00
|
|
|
|
|
|
|
|
@override
|
2025-02-22 19:50:05 +03:00
|
|
|
_SensorScreenState createState() => _SensorScreenState();
|
2025-02-22 19:00:07 +03:00
|
|
|
}
|
|
|
|
|
|
2025-02-22 19:50:05 +03:00
|
|
|
class _SensorScreenState extends State<SensorScreen> {
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
|
|
Provider.of<SensorProvider>(context, listen: false).fetchData();
|
2025-02-22 19:00:07 +03:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return Scaffold(
|
2025-02-22 19:58:36 +03:00
|
|
|
appBar: AppBar(title: const Text('Mercury')),
|
2025-02-22 19:00:07 +03:00
|
|
|
body: Center(
|
2025-02-22 19:50:05 +03:00
|
|
|
child: Consumer<SensorProvider>(
|
|
|
|
|
builder: (context, sensorProvider, child) {
|
|
|
|
|
if (sensorProvider.sensorData == null) {
|
|
|
|
|
if (sensorProvider.error != null) {
|
|
|
|
|
return Text('Error: ${sensorProvider.error}');
|
|
|
|
|
}
|
|
|
|
|
return const CircularProgressIndicator();
|
|
|
|
|
} else {
|
|
|
|
|
final data = sensorProvider.sensorData!;
|
2025-02-22 19:58:36 +03:00
|
|
|
final timeAgo = timeago.format(
|
|
|
|
|
DateTime.fromMillisecondsSinceEpoch(data.timestamp),
|
|
|
|
|
);
|
|
|
|
|
return Scrollbar(
|
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
|
primary: true,
|
|
|
|
|
padding: const EdgeInsets.all(16.0),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
// Temperature Card
|
|
|
|
|
Card(
|
|
|
|
|
elevation: 4,
|
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
|
),
|
|
|
|
|
child: Container(
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
padding: const EdgeInsets.all(20),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
gradient: LinearGradient(
|
|
|
|
|
colors: [
|
|
|
|
|
Colors.blue.shade200,
|
|
|
|
|
Colors.blue.shade400,
|
|
|
|
|
],
|
|
|
|
|
begin: Alignment.topLeft,
|
|
|
|
|
end: Alignment.bottomRight,
|
|
|
|
|
),
|
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
const Icon(
|
|
|
|
|
Icons.thermostat,
|
|
|
|
|
size: 40,
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
Text(
|
|
|
|
|
'${data.temperature}°C',
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
fontSize: 32,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
|
const Text(
|
|
|
|
|
'Temperature',
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
color: Colors.white70,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
|
|
|
|
|
|
// Humidity Card
|
|
|
|
|
Card(
|
|
|
|
|
elevation: 4,
|
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
|
),
|
|
|
|
|
child: Container(
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
padding: const EdgeInsets.all(20),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
gradient: LinearGradient(
|
|
|
|
|
colors: [
|
|
|
|
|
Colors.green.shade200,
|
|
|
|
|
Colors.green.shade400,
|
|
|
|
|
],
|
|
|
|
|
begin: Alignment.topLeft,
|
|
|
|
|
end: Alignment.bottomRight,
|
|
|
|
|
),
|
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
const Icon(
|
|
|
|
|
Icons.water_drop,
|
|
|
|
|
size: 40,
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
Text(
|
|
|
|
|
'${data.humidity}%',
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
fontSize: 32,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
|
const Text(
|
|
|
|
|
'Humidity',
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
color: Colors.white70,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
|
|
|
|
|
|
// Dew Point Card
|
|
|
|
|
Card(
|
|
|
|
|
elevation: 4,
|
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
|
),
|
|
|
|
|
child: Container(
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
padding: const EdgeInsets.all(20),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
gradient: LinearGradient(
|
|
|
|
|
colors: [
|
|
|
|
|
Colors.purple.shade200,
|
|
|
|
|
Colors.purple.shade400,
|
|
|
|
|
],
|
|
|
|
|
begin: Alignment.topLeft,
|
|
|
|
|
end: Alignment.bottomRight,
|
|
|
|
|
),
|
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
const Icon(
|
|
|
|
|
Icons.ac_unit,
|
|
|
|
|
size: 40,
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
|
Text(
|
|
|
|
|
'${data.dewPoint}°C',
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
fontSize: 32,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
|
const Text(
|
|
|
|
|
'Dew Point',
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
color: Colors.white70,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
|
|
|
|
|
|
// Timestamp
|
|
|
|
|
Text(
|
|
|
|
|
'🕒 Last updated: $timeAgo',
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
color: Colors.grey,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-02-22 19:50:05 +03:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
2025-02-22 19:00:07 +03:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
floatingActionButton: FloatingActionButton(
|
2025-02-22 19:50:05 +03:00
|
|
|
onPressed: () {
|
|
|
|
|
Provider.of<SensorProvider>(context, listen: false).fetchData();
|
|
|
|
|
},
|
|
|
|
|
child: const Icon(Icons.refresh),
|
2025-02-22 19:21:49 +03:00
|
|
|
),
|
2025-02-22 19:00:07 +03:00
|
|
|
);
|
|
|
|
|
}
|
2025-02-22 19:58:36 +03:00
|
|
|
}
|