import 'dart:async'; import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart' as K; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tasktracker/Analytics.dart'; import 'package:tasktracker/Categories.dart'; import 'package:tasktracker/Journal.dart'; import 'package:tasktracker/Projects.dart'; import 'package:tasktracker/Todos.dart'; import 'package:tasktracker/Welcome.dart'; import 'package:tasktracker/splash.dart'; import 'package:tasktracker/theme_provider.dart'; import 'Settings/Settings.dart'; import 'package:wakelock/wakelock.dart'; import 'Data.dart'; import 'NewTask.dart'; import 'newActivity.dart'; import 'Tasks.dart'; import 'Activities.dart'; import 'User.dart' as User; import 'package:syncfusion_flutter_charts/charts.dart'; import 'Dialogs.dart'; import 'CustomWidgets.dart'; import 'package:firebase_core/firebase_core.dart'; final GlobalKey navigatorKey = new GlobalKey(); showAlertDialog(BuildContext context, String title, String message) { // set up the button Widget okButton = TextButton( child: Text("OK"), onPressed: () { Navigator.of(context).pop(); }, ); // set up the AlertDialog AlertDialog alert = AlertDialog( title: Text(title), content: Text(message), actions: [ okButton, ], ); // show the dialog showDialog( context: context, builder: (BuildContext context) { return alert; }, ); } extension HexColor on Color { /// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#". static Color fromHex(String hexString) { final buffer = StringBuffer(); if (hexString.length == 6 || hexString.length == 7) buffer.write('ff'); buffer.write(hexString.replaceFirst('#', '')); return Color(int.parse(buffer.toString(), radix: 16)); } /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`)./home/warlock/Desktop/Task Tracker/tasktracker String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}' '${alpha.toRadixString(16).padLeft(2, '0')}' '${red.toRadixString(16).padLeft(2, '0')}' '${green.toRadixString(16).padLeft(2, '0')}' '${blue.toRadixString(16).padLeft(2, '0')}'; } void main() async { //Wakelock.enable(); // or Wakelock.toggle(on: true); if (!K.kIsWeb && (Platform.isAndroid || Platform.isIOS)) { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); } // // ByteData data = await PlatformAssetBundle().load('assets/ca/certificate.crt'); // SecurityContext.defaultContext.setTrustedCertificatesBytes(data.buffer.asUint8List()); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); // This widget is the root of your application. @override Widget build(BuildContext context) => ChangeNotifierProvider( create: (context) => ThemeProvider(), builder: (context, _) { final themeProvider = Provider.of(context); return MaterialApp( title: 'Task Tracker', themeMode: themeProvider.themeMode, theme: ThemeData( accentColor: Colors.redAccent, brightness: Brightness.light, primaryColor: Colors.amber, fontFamily: 'Noto-Sans'), darkTheme: ThemeData( backgroundColor: Colors.black, accentColor: Colors.redAccent, brightness: Brightness.dark, primaryColor: Colors.amber, fontFamily: 'Noto-Sans'), navigatorKey: navigatorKey, //home: const SplashScreen(), initialRoute: '/', routes: { '/': (context) => const SplashScreen(), '/welcome': (context) => const WelcomePage(), '/home': (context) => const MyHomePage(), '/Tasks': (context) => const Tasks(), '/Categories': (context) => const Categories(), '/Activities': (context) => const Activities(), '/Settings': (context) => const SettingsPage(), '/Projects': (context) => const Projects(), '/Journal': (context) => const JournalPage(), '/Todos': (context) => const TodosPage(), '/Analytics': (context) => const AnalyticsPage() }); }); } List days = []; String curDay = ""; DateTime? firstDay = null; DateTime? lastDay = null; DateTimeRange? taskTypeRange = null; DateTimeRange? catsRange = null; DateTimeRange? prodRange = null; List pastProductivityData = [ ProductivityMapData('02/24', 32), ProductivityMapData('02/25', 5), ProductivityMapData('02/26', 56), ProductivityMapData('02/27', 33), ProductivityMapData('02/28', 50) ]; List productivityData = [ ProductivityMapData('02/24', 35), ProductivityMapData('02/25', 28), ProductivityMapData('02/26', 34), ProductivityMapData('02/27', 32), ProductivityMapData('02/28', 40) ]; List taskTypesData = [ TaskTypeMapData('Eat', 3600, Colors.green), TaskTypeMapData('Play', 300, Colors.blue) ]; List catsData = [ CatMapData('Jan', 35, Colors.green), CatMapData('Feb', 28, Colors.blueAccent), CatMapData('Mar', 34, Colors.yellow), CatMapData('Apr', 32, Colors.grey), ]; List dailyData = [ CatMapData('Jan', 35, Colors.green), CatMapData('Feb', 28, Colors.blueAccent), CatMapData('Mar', 34, Colors.yellow), CatMapData('Apr', 32, Colors.grey), ]; List hourglassCatData = []; List relativeTodos = []; class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { var connectivitySub; var refreshSub; @override void initState() { // TODO: implement initState print("Im home!"); init(context); super.initState(); print("Initializing refresh stream on main dart"); // connectivitySub=Connectivity().onConnectivityChanged.listen((result) { // if (this.mounted) { // setState(() {}); // } // }); refreshSub = User.refreshStream.stream.listen((event) { if (event == false) { //Update done, Lets go update data LoadStats(); } }); LoadStats(); startLoadStatRefresher(); // User.progressDialog=progressDialog; } double hourglassTime = 0; List hourglassColors = [Colors.black]; List hourglassStops = [1]; int hourglassTotalTime = 0; void startLoadStatRefresher() async { int lastMinute = 0; while (true) { if (DateTime.now().minute != lastMinute) { lastMinute = DateTime.now().minute; LoadStats(); } await Refresh(); // hourglassTime+=0.05; // if(hourglassTime > 1){ // hourglassTime=0; // } hourglassTime = ((DateTime.now().hour * 60) + DateTime.now().minute) / 1440; // hourglassTime = 1; // print('hourglass time : $hourglassTime'); hourglassColors = []; hourglassStops = []; hourglassTotalTime = 0; hourglassCatData.forEach((element) { // if(element.time > hourglassTotalTime){ hourglassTotalTime += element.time; // } }); // print('hourglass cat data'); double stopsTotal = 0; for (int i = 0; i < hourglassCatData.length; i++) { CatMapData element = hourglassCatData[i]; // print('${element.name} : ${element.time} / $hourglassTotalTime = ${element.time / hourglassTotalTime}'); double thisStop = (element.time / hourglassTotalTime); hourglassColors.add(element.color); hourglassStops.add(stopsTotal + thisStop); stopsTotal += thisStop; if (i < hourglassCatData.length - 1) { hourglassColors.add(hourglassCatData[i + 1].color); hourglassStops.add(stopsTotal + thisStop + 0.001); } } // print('total Stops ${stopsTotal}'); // print('maxT: $hourglassTotalTime'); if (hourglassColors.isEmpty) { hourglassColors.add(Colors.black); hourglassStops.add(1); } // print('hourglass \n$hourglassColors \n$hourglassStops'); setState(() {}); await Future.delayed(Duration(seconds: 1)); } } void init(BuildContext context) async { await Future.delayed(Duration(seconds: 1)); refreshSub = User.refreshStream.stream.listen((value) { print("Streaming refresh : $value"); if (value) { // Dialogs.waiting("Syncing..."); print("Opening progress dialog"); } else { // Dialogs.hide(); // Dialogs.hide(); print("Closing progress dialog"); } }); //await User.refreshUserData(); } DateTime? lastProductive = null; @override void dispose() { // TODO: implement dispose super.dispose(); connectivitySub?.cancel(); refreshSub?.closeSteam(); } String ongoingActName = ""; DateTime ongoingActStime = DateTime.now(); Future Refresh() async { final prefs = await SharedPreferences.getInstance(); if (prefs.containsKey('current_activity')) { List data = []; try { data = prefs.getString('current_activity')!.split(''); ongoingActName = data[0]; ongoingActStime = DateTime.parse(data[2]); } catch (e) { ongoingActName = ""; } } else { ongoingActName = ""; } if (this.mounted) { setState(() {}); } } bool loadingStats = false; DateFormat dFormat = DateFormat("yyyy-MM-dd"); void LoadStats() async { // return; // await User.refreshUserData(); if (loadingStats) { print('loading stats already'); return; } else { loadingStats = true; } await Refresh(); Map catTimeMap = {}; Map catBriefMap = {}; Map productivtyActs = {}; Map unproductivtyActs = {}; Map taskTypesDuration = {}; hourglassCatData = []; hourglassColors = []; firstDay = null; lastDay = null; String lastDate = ""; lastProductive = null; days = []; for (var element in User.activities) { if (lastDay == null) { lastDay = element.endTime; } if (taskTypeRange == null) { print("$lastDay - $firstDay"); taskTypeRange = DateTimeRange( start: lastDay!.subtract(const Duration(days: 0)), end: lastDay!); } if (catsRange == null) { print("$lastDay - $firstDay"); catsRange = DateTimeRange( start: lastDay!.subtract(const Duration(days: 0)), end: lastDay!); } if (prodRange == null) { print("$lastDay - $firstDay"); prodRange = DateTimeRange( start: lastDay!.subtract(const Duration(days: 7)), end: lastDay!); } firstDay = element.startTime; String thisDate = dFormat.format(element.startTime); int thisMinutes = element.endTime.difference(element.startTime).inMinutes; if (!days.contains(thisDate)) { days.add(dFormat.format(element.startTime)); } if (curDay == "") { curDay = dFormat.format(DateTime.now()); } if ((element.startTime.isAfter(taskTypeRange!.start) && element.startTime.isBefore(taskTypeRange!.end)) || (dFormat.format(element.startTime) == dFormat.format(taskTypeRange!.start) || dFormat.format(element.startTime) == dFormat.format(taskTypeRange!.end))) { if (taskTypesDuration.containsKey(element.taskType)) { taskTypesDuration[element.taskType] = taskTypesDuration[element.taskType]! + thisMinutes; } else { taskTypesDuration.putIfAbsent(element.taskType, () => thisMinutes); } } if (element.taskType.cat?.productive ?? false) { if (lastProductive == null) { lastProductive = element.trueEndTime; } } if ((element.startTime.isAfter(prodRange!.start) && element.startTime.isBefore(prodRange!.end)) || (dFormat.format(element.startTime) == dFormat.format(prodRange!.start) || dFormat.format(element.startTime) == dFormat.format(prodRange!.end))) { if (element.taskType.cat?.productive ?? false) { // if (lastProductive == null) { // lastProductive = element.trueEndTime; // } if (productivtyActs.containsKey(thisDate)) { productivtyActs[thisDate] = (productivtyActs[thisDate]! + thisMinutes); } else { productivtyActs.putIfAbsent(thisDate, () => thisMinutes); } } else { if (unproductivtyActs.containsKey(thisDate)) { unproductivtyActs[thisDate] = (unproductivtyActs[thisDate]! + thisMinutes); } else { unproductivtyActs.putIfAbsent(thisDate, () => thisMinutes); } } } if (thisDate == curDay) { if (element.taskType.cat == null) { continue; } print("Null : ${thisMinutes}"); if (catTimeMap.containsKey(element.taskType.cat)) { catTimeMap[element.taskType.cat!] = (catTimeMap[element.taskType.cat]! + thisMinutes); } else { catTimeMap.putIfAbsent(element.taskType.cat!, () => thisMinutes); } } if ((element.startTime.isAfter(catsRange!.start) && element.startTime.isBefore(catsRange!.end)) || (dFormat.format(element.startTime) == dFormat.format(catsRange!.start) || dFormat.format(element.startTime) == dFormat.format(catsRange!.end))) { if (element.taskType.cat == null) { continue; } print("Null : ${thisMinutes}"); if (catBriefMap.containsKey(element.taskType.cat)) { catBriefMap[element.taskType.cat!] = (catBriefMap[element.taskType.cat]! + thisMinutes); } else { catBriefMap.putIfAbsent(element.taskType.cat!, () => thisMinutes); } } if (dFormat.format(element.startTime) == dFormat.format(DateTime.now())) { if (element.taskType.cat == null) { continue; } print("Null : ${thisMinutes}"); int? existingEntryIndex; for (int i = 0; i < hourglassCatData.length; i++) { if (hourglassCatData[i].name == element.taskType.category) { existingEntryIndex = i; } } if (existingEntryIndex == null) { hourglassCatData.add(CatMapData(element.taskType.category, thisMinutes, HexColor.fromHex(element.taskType.cat!.color))); } else { hourglassCatData[existingEntryIndex!].time += thisMinutes; } } hourglassCatData.sort((a, b) => a.time.compareTo(b.time)); // hourglassCatData = hourglassCatData.reversed.toList(); } dailyData = []; productivityData = []; taskTypesData = []; catsData = []; int trackedTime = 0; catTimeMap.forEach((key, value) { //print(key.name + " : $value"); Color barCol = HexColor.fromHex(key.color); dailyData.add(CatMapData(key.name, value, barCol)); trackedTime += value; }); int untrackedTime = 1440 - trackedTime; if (untrackedTime < 0) { User.refreshUserData().then((val) => LoadStats()); print("Shit went wrong!"); } print("Tracked time : $trackedTime"); dailyData.sort((a, b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()); }); if (untrackedTime > 0) { dailyData.add(CatMapData("Untracked", 1440 - trackedTime, Colors.black)); } else {} bool untrackedUnprod = await Settings.getUntrackedUnproductive(); for (var element in days) { // if(productivtyActs.containsKey(element) && unproductivtyActs.containsKey(element)){ int prodActs = (productivtyActs[element] ?? 0); int unprodActs = (unproductivtyActs[element] ?? 0); double prod = (untrackedUnprod) ? ((prodActs / 1440) * 100) : ((prodActs / unprodActs) * 100); var newProdData = ProductivityMapData(element, prod); if (prod > 0 && !productivityData.contains(newProdData)) { productivityData.add(newProdData); } // } } //Past Prod Map pastProdData = AnalyticTools.getProductivities( DateTimeRange( start: prodRange!.start .subtract(Duration(days: prodRange!.duration.inDays + 1)), end: prodRange!.start.subtract(Duration(days: 1)))); pastProductivityData = []; pastProdData.forEach((key, value) { pastProductivityData.add(ProductivityMapData( dFormat .format(key.add(Duration(days: prodRange!.duration.inDays + 1))), value)); }); taskTypesDuration.forEach((key, value) { print("$key : $value"); taskTypesData.add( TaskTypeMapData(key.name, value, HexColor.fromHex(key.cat!.color))); }); taskTypesData.sort((a, b) { return a.time.compareTo(b.time); }); catBriefMap.forEach((key, value) { print(key.name + " : $value"); Color barCol = HexColor.fromHex(key.color); catsData.add(CatMapData(key.name, value, barCol)); }); catsData.sort((a, b) => a.time.compareTo(b.time)); //relative TOdos List relativeTodoDays = []; relativeTodos = []; for (int i = 0; i < 2; i++) { relativeTodoDays .add(dFormat.format(DateTime.now().add(Duration(days: i)))); } User.todos.forEach((element) { if (relativeTodoDays.contains(dFormat.format(element.dueDate))) { //Suitaable relativeTodos.add(element); } }); //curDay = days[0]; if (this.mounted) { setState(() {}); } // print('productivity data'); // productivityData.forEach((element) { // print(element.day); // }); loadingStats = false; // loadingStats=false; } void showOfflineSnack() async { await Future.delayed(const Duration(seconds: 1)); if (User.offline) { const SnackBar offlineSnack = SnackBar( content: Text('Offline'), duration: Duration(seconds: 100), ); // ScaffoldMessenger.of(context).showSnackBar(offlineSnack); } } @override Widget build(BuildContext context) { double avgProd = 0; double pastAvgProd = 0; int _avgProdCount = 0; int _pastAvgProdCount = 0; productivityData.forEach((element) { avgProd += element.productivity; _avgProdCount++; }); pastProductivityData.forEach((element) { pastAvgProd += element.productivity; _pastAvgProdCount++; }); avgProd = avgProd / _avgProdCount; pastAvgProd = pastAvgProd / _pastAvgProdCount; bool landscape = ((MediaQuery.of(context).size.width / MediaQuery.of(context).size.height) > 1); return Scaffold( floatingActionButton: FloatingActionButton.extended( onPressed: () { Navigator.of(context) .push(MaterialPageRoute(builder: (context) => NewActivity())); }, label: Text("New Activity"), icon: Icon(Icons.add)), appBar: AppBar( title: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, children: [ Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row(children: [ FaIcon(FontAwesomeIcons.chartBar), SizedBox(width: 10), Text('Summary') ]), Row( children: [ (User.offline) ? Icon(Icons .signal_cellular_connected_no_internet_4_bar_outlined) : InkWell( onTap: () { setState(() async { await User.refreshUserData(); LoadStats(); }); }, child: Icon(Icons.refresh, size: 30), ) ], ) ], ), //Container(color: Colors.red,child: Text("Offline",style:TextStyle(fontSize: 5))), ], )), drawer: landscape ? null : navDrawer(context, 0), body: SafeArea( child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, children: [ landscape ? navDrawer(context, 0) : Container(), (User.activities.isEmpty) ? Container( width: 1000, child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 1, child: Container(), ), Expanded( flex: 2, child: Image(image: AssetImage('images/empty.png'))), Expanded( flex: 2, child: Text( "Add your first activity to access Summary", style: TextStyle( color: Colors.grey, fontStyle: FontStyle.italic), )) ]), ) : Expanded( child: Column( mainAxisSize: MainAxisSize.max, children: [ if (User.offline) Container( child: Container( width: 1000, color: Colors.red, child: Align( alignment: Alignment.center, child: Text("Offline")))), Expanded( child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( children: [ (false) ? Container( padding: EdgeInsets.all(20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.max, children: [ Text("Good\nMorning!", style: TextStyle( fontSize: 23, fontStyle: FontStyle.italic)), Text( "12%", style: TextStyle(fontSize: 30), ), Column( children: [ Text(DateFormat("yy - MM-dd") .format(DateTime.now())), Text( DateFormat("HH:mm") .format(DateTime.now()), style: TextStyle( fontSize: 40)), ], ) ], ), ) : Container(), //Ongoing activity card (ongoingActName == "") ? Container() : Container( padding: EdgeInsets.all(10), child: Card( color: Colors.white12, elevation: 20, shadowColor: Colors.green, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 25, vertical: 20), child: Column( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Row( children: [ SpinKitPouringHourGlass( color: Colors.amber), SizedBox( width: 20, ), Flexible( child: Text( "You are engaged in '$ongoingActName' since ${DateFormat("hh:mm").format(ongoingActStime)}.", style: TextStyle( fontSize: 19), textAlign: TextAlign.center, )), ], ), SizedBox( height: 10, ), Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Container( child: Text(MinutesToTimeString( DateTime.now() .difference( ongoingActStime) .inMinutes))), Row( mainAxisAlignment: MainAxisAlignment .end, children: [ InkWell( onTap: () { Dialogs .ongoing(); }, child: Container( decoration: BoxDecoration( color: Colors .green, borderRadius: BorderRadius.circular( 10)), child: Padding( padding: EdgeInsets.symmetric( vertical: 8, horizontal: 15), child: Text( 'Take Action')))), ], ), ], ) ], ), )), ), if (relativeTodos.isNotEmpty) Container( padding: EdgeInsets.all(10), child: Card( color: Colors.white10, elevation: 20, shadowColor: Colors.black, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 10), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Text('Close To-Do', style: TextStyle( fontSize: 16)), MaterialButton( height: 30, color: Colors.green, onPressed: () { Navigator.of(context) .pushNamed('/Todos'); }, child: Row( children: [ Text('More'), Icon(Icons .keyboard_arrow_right) ], ), ) ], ), LimitedBox( maxHeight: 250, child: ListView.builder( shrinkWrap: true, itemCount: relativeTodos.length, itemBuilder: (context, index) { return Container( padding: EdgeInsets.all(10), margin: EdgeInsets.symmetric( vertical: 2), decoration: BoxDecoration( color: Colors.black26, borderRadius: BorderRadius .circular( 10)), child: Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ SizedBox( width: MediaQuery.of( context) .size .width * 0.7, child: Text( '${relativeTodos[index].task!.name} -> ${relativeTodos[index].metadata}')), Container( padding: EdgeInsets .symmetric( horizontal: 5), decoration: BoxDecoration( borderRadius: BorderRadius .circular( 20), color: Colors .blue), child: Text(DateFormat( 'MM/dd') .format(relativeTodos[ index] .dueDate))) ], ), ); }, ), ) ], ), )), ), Container( padding: EdgeInsets.all(10), child: Card( color: Colors.white10, elevation: 20, shadowColor: Colors.black, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 25, vertical: 20), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Container( width: 100, height: 150, child: CustomPaint( painter: HourglassPainter( hourglassTime, hourglassColors, hourglassStops), )), Column( children: [ Text( DateFormat("MMMM-dd") .format( DateTime.now()), style: TextStyle( fontSize: 18)), Text( DateFormat("hh:mm a") .format( DateTime.now()), style: TextStyle( fontSize: 40, fontWeight: FontWeight .bold)), Container( height: 20, ) ], ) ], ) ], ), )), ), Container( height: 400, padding: EdgeInsets.all(10), child: Card( color: Colors.white10, elevation: 20, shadowColor: Colors.black, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 20), child: Column( children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Text("Productivity", style: TextStyle( color: Colors.green, fontWeight: FontWeight.bold)), InkWell( onTap: () async { DateTimeRange? value = await showDateRangePicker( context: context, firstDate: firstDay ?? DateTime .now(), lastDate: lastDay ?? DateTime .now()); if (value != null) { prodRange = DateTimeRange( start: DateTime( value.start .year, value.start .month, value.start .day), end: DateTime( value.end .year, value.end .month, value.end .day, 23, 59, 59)); } LoadStats(); }, child: Text((prodRange != null) ? (DateFormat("MM/dd") .format( prodRange! .start) + " - " + DateFormat("MM/dd") .format( prodRange! .end)) : 'n/a'), ) ], ), ), Divider(), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Row( children: [ Text("Today : "), Text( "${(productivityData.length > 0) ? productivityData[0].productivity.toStringAsFixed(1) : 'n/a'}%", style: TextStyle( fontSize: 20, color: (productivityData .length > 1) ? ((productivityData[ 0] .productivity > productivityData[ 1] .productivity) ? Colors .lightGreenAccent : Colors .red) : Colors.white)) ], ), Row( children: [ Text("Yesterday : "), Text( "${(productivityData.length > 1) ? productivityData[1].productivity.toStringAsFixed(1) : 'n/a'}%", style: TextStyle( fontSize: 18)) ], ), ], ), Expanded( child: SfCartesianChart( // Initialize category axis primaryXAxis: CategoryAxis(), series: < LineSeries< ProductivityMapData, String>>[ LineSeries< ProductivityMapData, String>( // Bind data source markerSettings: MarkerSettings( isVisible: true, shape: DataMarkerType .circle), dataSource: productivityData.reversed .toList(), xValueMapper: (ProductivityMapData sales, _) => DateFormat('MM-dd') .format(dFormat.parse( sales.day)), yValueMapper: (ProductivityMapData sales, _) => sales.productivity, dataLabelMapper: (ProductivityMapData sales, _) => sales.productivity.toStringAsFixed(1) + "%", dataLabelSettings: DataLabelSettings( overflowMode: OverflowMode.hide, showZeroValue: false, isVisible: true), onPointTap: (ChartPointDetails point) { Dialogs.showJournalLink( dFormat.parse(productivityData[ productivityData .length - point .pointIndex! - 1] .day)); //showAlertDialog(context, productivityData[point.pointIndex!].day, "I'll show you detailed info about this day in future, When my master creates the feature"); }, pointColorMapper: (ProductivityMapData sales, _) => (User.journalExists(dFormat.parse(sales.day)) ? Colors.lightGreenAccent : Colors.green)), LineSeries< ProductivityMapData, String>( // Bind data source // dashArray: [0,0.5,1], dashArray: [2, 5], markerSettings: MarkerSettings( isVisible: true, shape: DataMarkerType .circle), dataSource: pastProductivityData .reversed .toList(), xValueMapper: (ProductivityMapData sales, _) => DateFormat('MM-dd') .format(dFormat.parse( sales.day)), yValueMapper: (ProductivityMapData sales, _) => sales.productivity, // dataLabelMapper: (ProductivityMapData sales, _) => sales.productivity.toStringAsFixed(1) + "%", // dataLabelSettings: DataLabelSettings(overflowMode: OverflowMode.hide, showZeroValue: false, isVisible: true), color: Colors.grey // pointColorMapper: (ProductivityMapData sales, _)=> (User.journalExists(dFormat.parse(sales.day)) ? Colors.lightGreenAccent : Colors.green) ) //color: User.journalExists(dFormat.parse(productivityData[(productivityData.length-point.pointIndex!-1) as int].day)) ?Colors.green : Colors.red, ]), ), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Row( children: [ Text("Average : "), Text( "${(productivityData.length > 0) ? avgProd.toStringAsFixed(1) : 'n/a'}%", style: TextStyle( fontSize: 18, color: (avgProd > pastAvgProd) ? Colors .lightGreenAccent : Colors.red)) ], ), Row( children: [ Text("Past Average : "), Text( "${(productivityData.length > 1) ? pastAvgProd.toStringAsFixed(1) : 'n/a'}%", style: TextStyle( fontSize: 18)) ], ), ], ), SizedBox( height: 20, ), if (lastProductive != null && DateTime.now() .difference( lastProductive!) .inMinutes > 60) RichText( text: TextSpan(children: < TextSpan>[ TextSpan( text: "You haven't been productive in last", style: TextStyle( color: Colors.orange)), TextSpan( text: " ${MinutesToTimeString(DateTime.now().difference(lastProductive!).inMinutes)}", style: TextStyle( color: Colors.redAccent, fontWeight: FontWeight.bold)) ])) ], ), )), ), Container( height: 400, padding: EdgeInsets.all(10), child: Card( color: Colors.white10, elevation: 20, shadowColor: Colors.black, child: Padding( padding: EdgeInsets.all(8), child: (!days.isEmpty) ? Column( children: [ Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Padding( padding: EdgeInsets .all( 20), child: Text( 'Daily Briefing', style: TextStyle( fontWeight: FontWeight.bold))), dayPickerWidget( days, value: curDay, onChange: (_value) { print( 'new val : $_value'); curDay = _value; setState(() { LoadStats(); }); }), ]), Expanded( child: SfCircularChart( legend: Legend( isVisible: true, position: LegendPosition .bottom, overflowMode: LegendItemOverflowMode .wrap), series: < CircularSeries>[ // Render pie chart PieSeries( dataSource: dailyData, pointColorMapper: (CatMapData data, _) => data.color, xValueMapper: (CatMapData data, _) => data.name, yValueMapper: (CatMapData data, _) => data.time, dataLabelMapper: (CatMapData sales, _) => MinutesToTimeString(sales .time), dataLabelSettings: DataLabelSettings( isVisible: true, useSeriesColor: true, overflowMode: OverflowMode .shift, showZeroValue: false)) ])) ], ) : Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment .center, children: [ CircularProgressIndicator() ])))), Container( height: (taskTypesData.length * 45) .clamp(350, 1000) .toDouble(), padding: EdgeInsets.all(10), child: Card( color: Colors.white10, elevation: 20, shadowColor: Colors.black, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 25, vertical: 25), child: Column(children: [ Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Text("Task Types", style: TextStyle( fontWeight: FontWeight .bold)), InkWell( onTap: () async { DateTimeRange? value = await showDateRangePicker( context: context, firstDate: firstDay ?? DateTime .now(), lastDate: lastDay ?? DateTime .now()); if (value != null) { taskTypeRange = value; } LoadStats(); }, child: Text((taskTypeRange != null) ? (DateFormat("MM/dd") .format( taskTypeRange! .start) + " - " + DateFormat( "MM/dd") .format( taskTypeRange! .end)) : 'n/a'), ) ]), Expanded( // maxHeight: 300, // maxWidth: 100, child: SfCartesianChart( primaryXAxis: CategoryAxis(), //primaryYAxis: NumericAxis(minimum: 0, maximum: 40, interval: 10), series: < ChartSeries< TaskTypeMapData, String>>[ BarSeries( dataSource: taskTypesData, xValueMapper: (TaskTypeMapData data, _) => data.task, yValueMapper: (TaskTypeMapData data, _) => data.time / 60, pointColorMapper: (TaskTypeMapData data, _) => data.color, dataLabelMapper: (TaskTypeMapData data, _) => MinutesToTimeString( data.time), dataLabelSettings: DataLabelSettings( isVisible: true), color: Color.fromRGBO( 8, 142, 255, 1)) ]), ) ])))), Container( height: (catsData.length * 45) .clamp(350, 1000) .toDouble(), padding: EdgeInsets.all(10), child: Card( color: Colors.white10, elevation: 20, shadowColor: Colors.black, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 25, vertical: 25), child: Column(children: [ Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Text("Categories", style: TextStyle( fontWeight: FontWeight .bold)), InkWell( onTap: () async { DateTimeRange? value = await showDateRangePicker( context: context, firstDate: firstDay ?? DateTime .now(), lastDate: lastDay ?? DateTime .now()); if (value != null) { catsRange = value; } LoadStats(); }, child: Text((catsRange != null) ? (DateFormat("MM/dd") .format( catsRange! .start) + " - " + DateFormat( "MM/dd") .format( catsRange! .end)) : 'n/a'), ) ]), Expanded( // maxHeight: 300, // maxWidth: 100, child: SfCartesianChart( primaryXAxis: CategoryAxis(), //primaryYAxis: NumericAxis(minimum: 0, maximum: 40, interval: 10), series: < ChartSeries>[ BarSeries( dataSource: catsData, xValueMapper: (CatMapData data, _) => data.name, yValueMapper: (CatMapData data, _) => data.time / 60, pointColorMapper: (CatMapData data, _) => data.color, dataLabelMapper: (CatMapData data, _) => MinutesToTimeString( data.time), dataLabelSettings: DataLabelSettings( isVisible: true), color: Color.fromRGBO( 8, 142, 255, 1)) ]), ) ])))), ], ), ), ), ], ), ), ], ), ), ); } Widget dayPickerWidget(List list, {required String value, required Function(String value) onChange}) { if (!list.contains(value)) { print("resetting"); onChange(list[0]); } bool nextAvailable = (list.indexOf(value) < (list.length - 1)); bool prevAvailable = (list.indexOf(value) > 0); return Row( children: [ InkWell( onTap: () { if (nextAvailable) { onChange(list[list.indexOf(value) + 1]); } }, child: Container( height: 40, width: 40, child: Icon(Icons.arrow_back_ios, size: 18, color: (nextAvailable) ? Colors.white : Colors.grey), ), ), Text( value, ), InkWell( onTap: () { if (prevAvailable) { onChange(list[list.indexOf(value) - 1]); } }, child: Container( height: 40, width: 40, child: Icon(Icons.arrow_forward_ios, size: 18, color: (prevAvailable) ? Colors.white : Colors.grey), )) ], ); } } Widget moreButton() { return MaterialButton( onPressed: () {}, color: Colors.green, child: Row( children: [Text('More'), Icon(Icons.keyboard_arrow_right)], )); } Drawer navDrawer(BuildContext context, int pageIndex) { return Drawer( child: ListView( children: [ Padding( padding: EdgeInsets.all(16), child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Time Tracker", style: TextStyle( fontSize: 25, color: Theme.of(context).accentColor, fontWeight: FontWeight.bold)), Icon( Icons.more_time, size: 30, ), ])), Divider(), ListTile( selected: (pageIndex == 0), title: Text('Summary'), leading: Icon(Icons.article_outlined, color: Theme.of(context).primaryColor), onTap: () { if (pageIndex == 0) { return; } Navigator.of(context).pushReplacementNamed('/home'); }, ), ListTile( selected: (pageIndex == 1), title: Text('Analytics'), leading: Icon(Icons.analytics_outlined, color: Theme.of(context).primaryColor), onTap: () { if (pageIndex == 1) { return; } Navigator.of(context).pushReplacementNamed('/Analytics'); }, ), Divider(), ListTile( selected: (pageIndex == 2), title: Text('Activities'), leading: Icon(Icons.task, color: Theme.of(context).primaryColor), onTap: () { if (pageIndex == 2) { return; } Navigator.of(context).pushReplacementNamed('/Activities'); }, ), ListTile( selected: (pageIndex == 3), title: Text('Task Types'), leading: Icon(Icons.task, color: Theme.of(context).primaryColor), onTap: () { if (pageIndex == 3) { return; } Navigator.of(context).pushReplacementNamed('/Tasks'); }, ), ListTile( selected: (pageIndex == 4), title: Text('Categories'), leading: Icon(Icons.account_tree_outlined, color: Theme.of(context).primaryColor), onTap: () { if (pageIndex == 4) { return; } Navigator.of(context).pushReplacementNamed('/Categories'); }, ), Divider(), ListTile( selected: (pageIndex == 7), title: Text('Projects'), leading: FaIcon(FontAwesomeIcons.rocket, color: Theme.of(context).primaryColor), onTap: () { if (pageIndex == 7) { return; } Navigator.of(context).pushReplacementNamed('/Projects'); }, ), ListTile( selected: (pageIndex == 9), title: Text('To-Do'), leading: FaIcon(FontAwesomeIcons.calendarCheck, color: Theme.of(context).primaryColor), onTap: () { if (pageIndex == 9) { return; } Navigator.of(context).pushReplacementNamed('/Todos'); }, ), ListTile( selected: (pageIndex == 8), title: Text('Journal'), leading: FaIcon(FontAwesomeIcons.bookJournalWhills, color: Theme.of(context).primaryColor), onTap: () { if (pageIndex == 8) { return; } Navigator.of(context).pushReplacementNamed('/Journal'); }, ), // ListTile( // selected: (pageIndex == 7), // title: Text('TODO'), // leading: Icon(Icons.check, color: Theme.of(context).primaryColor), // onTap: () { // if (pageIndex == 7) { // return; // } // // Navigator.of(context).pushReplacementNamed('/Todo'); // }, // ), Divider(), ListTile( selected: (pageIndex == 5), title: Text('Settings'), leading: Icon(Icons.settings, color: Colors.blueGrey), onTap: () { if (pageIndex == 5) { return; } Navigator.of(context).pushNamed('/Settings'); }, ), ListTile( selected: (pageIndex == 6), title: Text('About'), leading: Icon(Icons.help_outline_outlined), onTap: () { showAboutDialog(context: context); }, ), ], )); } class CatMapData { CatMapData(this.name, this.time, this.color); final String name; int time; final Color color; } class ProductivityMapData { ProductivityMapData(this.day, this.productivity); final String day; final double productivity; } class TaskTypeMapData { TaskTypeMapData(this.task, this.time, this.color); final Color color; final String task; final int time; } String MinutesToTimeString(minutes) { int hours = (minutes / 60).floor(); int mins = minutes % 60; String str = ""; if (hours > 0) { str += hours.toString() + "h"; } if (mins > 0) { str += ((hours > 0) ? " " : "") + mins.toString() + "m"; } return str; }