import 'dart:convert'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; import '../constants.dart'; import '../devices/devices.dart'; class ChartData { ChartData(this.time, this.watts); int time; double watts; } class CheckBoxDevice { CheckBoxDevice(this.device); final DeviceIdOnly device; bool enabled = false; List data = List.empty(); } class Graphs extends StatefulWidget { const Graphs({super.key}); @override State createState() => _GraphsState(); } class _GraphsState extends State { String? start_date; String? end_date; List devices = List.empty(); final TextEditingController categoryController = TextEditingController(); final TextEditingController startDateController = TextEditingController(); final TextEditingController endDateController = TextEditingController(); @override void dispose() { // Clean up the controller when the widget is removed from the // widget tree. categoryController.dispose(); startDateController.dispose(); endDateController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return FutureBuilder>( future: Category.fetch_ids_only(), builder: (context, AsyncSnapshot> categories) { if (!categories.hasData) { return SpinKitWanderingCubes( color: Colors.deepPurple[100], size: 80.0, ); } return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('Graphs'), actions: [ IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => Navigator.of(context).pushReplacementNamed( '/', )) ], ), body: Center( child: Column(children: [ const SizedBox( height: 40, ), DropdownMenu( label: const Text('Category'), controller: categoryController, width: 200, onSelected: (Category? category) { if (category != null) { setState(() { devices = category.devices .map((dev) => CheckBoxDevice(dev as DeviceIdOnly)) .toList(); }); } }, dropdownMenuEntries: categories.data! .map((category) => DropdownMenuEntry( label: category.Name(), value: category)) .toList(), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: devices.map((device) { return SizedBox( width: 200, height: 40, child: CheckboxListTile( title: Text(device.device.device_descriptor ?? device.device.device_id), controlAffinity: ListTileControlAffinity.leading, splashRadius: 10.0, value: device.enabled, onChanged: (bool? value) { setState(() { device.enabled = value!; }); })); }).toList()), Row(mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Start'), SizedBox( width: 200, height: 40, child: TextField( controller: startDateController, decoration: const InputDecoration( icon: Icon(Icons.calendar_today), ), readOnly: true, onTap: () async { final DateTime? pickedDate = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2023), lastDate: DateTime(2101)); if (pickedDate != null) { final String formattedDate = DateFormat('dd.MM.yyyy').format(pickedDate); startDateController.text = formattedDate; } }, )) ]), Row(mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('End'), SizedBox( width: 200, height: 40, child: TextField( controller: endDateController, decoration: const InputDecoration( icon: Icon(Icons.calendar_today), ), readOnly: true, onTap: () async { final DateTime? pickedDate = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2023), lastDate: DateTime(2101)); if (pickedDate != null) { final String formattedDate = DateFormat('dd.MM.yyyy').format(pickedDate); endDateController.text = formattedDate; } }, )) ]), ElevatedButton( onPressed: () { setState(() { for (var device in devices) { if (!device.enabled) { device.data = List.empty(); continue; } int start_date; int end_date; try { start_date = (DateFormat('dd.MM.yyyy') .parse(startDateController.text) .millisecondsSinceEpoch / 1000) as int; } on Exception catch (_) { start_date = 0; print('no start date set'); } try { end_date = (DateFormat('dd.MM.yyyy') .parse(endDateController.text) .millisecondsSinceEpoch / 1000) as int; } on Exception catch (_) { end_date = 0; print('no end date set'); } if (start_date == 0 || end_date == 0) { device.data = List.empty(); continue; } const String filter_type = 'hourly'; http .get(Uri.parse( "${Constants.BASE_URL}/plug_data/${device.device.device_id}/$start_date/$end_date/$filter_type")) .then((response) { if (response.statusCode != 200) { throw Exception("Failed to fetch data"); } final data = jsonDecode(jsonDecode(response.body)) as List; final List chart_data = []; for (final entry in data) { final pair = entry as List; chart_data.add(ChartData(pair[0], pair[1])); } device.data = chart_data; }); } }); }, child: const Text('Update')), if (devices.any((device) => device.data.isNotEmpty)) LineChart( LineChartData( lineBarsData: devices .map((device) { if (device.data.isNotEmpty) { final data = device.data.map((data) { return FlSpot(data.time as double, data.watts); }).toList(); print('chart data: $data'); return LineChartBarData( spots: data, ); } else { return null; } }) .where((element) => element != null) .map((opt) => opt!) .toList(), // titlesData: FlTitlesData(bottomTitles: AxisTitles(sideTitles: )) ), duration: const Duration(milliseconds: 200), curve: Curves.linear, ) ]))); }); } }