From 8a9f2dc2d104dd8d8a5184d51552090fbe8a5fc5 Mon Sep 17 00:00:00 2001 From: Peter Pavlenko Date: Tue, 19 Oct 2021 22:42:02 +0200 Subject: [PATCH 1/6] Code optimizations: - colors in one place - get rid of duplicity in calendar creation --- lib/helpers.dart | 8 ++ lib/screens/edit_habit_screen.dart | 8 +- lib/widgets/habit.dart | 43 ++--------- lib/widgets/habit_header.dart | 11 ++- lib/widgets/one_day_button.dart | 119 ++++++++++++++++------------- 5 files changed, 92 insertions(+), 97 deletions(-) diff --git a/lib/helpers.dart b/lib/helpers.dart index 2a05442..a27abbd 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -23,3 +23,11 @@ TimeOfDay parseTimeOfDay(String value) { } enum DayType { Clear, Check, Fail, Skip } + +class HaboColors { + static final Color primary = Color(0xFF09BF30); + static final Color red = Colors.red; + static final Color skip = Color(0xFF505050); + static final Color comment = Colors.yellow[700]; + static final Color orange = Colors.orange; +} diff --git a/lib/screens/edit_habit_screen.dart b/lib/screens/edit_habit_screen.dart index 54e781a..4297a47 100644 --- a/lib/screens/edit_habit_screen.dart +++ b/lib/screens/edit_habit_screen.dart @@ -1,4 +1,5 @@ import 'package:Habo/habit_data.dart'; +import 'package:Habo/helpers.dart'; import 'package:Habo/provider.dart'; import 'package:Habo/widgets/text_container.dart'; import 'package:flutter/material.dart'; @@ -69,7 +70,7 @@ class _EditHabitScreenState extends State { Icons.delete, semanticLabel: 'Delete', ), - color: Colors.red, + color: HaboColors.red, tooltip: 'Delete', onPressed: () { Navigator.of(context).pop(); @@ -129,7 +130,8 @@ class _EditHabitScreenState extends State { label: 'Habit', ), Container( - margin: EdgeInsets.symmetric(vertical: 5, horizontal: 20), + margin: + const EdgeInsets.symmetric(vertical: 5, horizontal: 20), child: Row( children: [ Checkbox( @@ -142,7 +144,7 @@ class _EditHabitScreenState extends State { ), Text("Use Two day rule"), Tooltip( - child: Icon( + child: const Icon( Icons.info, color: Colors.grey, size: 18, diff --git a/lib/widgets/habit.dart b/lib/widgets/habit.dart index 3c5183d..2ec2933 100644 --- a/lib/widgets/habit.dart +++ b/lib/widgets/habit.dart @@ -123,58 +123,27 @@ class _HabitState extends State { startingDayOfWeek: Provider.of(context).getWeekStartEnum, builders: CalendarBuilders( dayBuilder: (context, date, _) { - int ind = 0; - String comment = ""; - - if (_habitData.events[date] != null && - _habitData.events[date][0] != 0) { - ind = (_habitData.events[date][0].index); - } - - if (_habitData.events[date] != null && - _habitData.events[date].length > 1 && - _habitData.events[date][1] != null && - _habitData.events[date][1] != "") { - comment = (_habitData.events[date][1]); - } - return OneDayButton( callback: refresh, parent: this, id: widget.habitData.id, date: date, - index: ind, color: Theme.of(context).colorScheme.primaryVariant, - comment: comment, + event: _habitData.events[date], ); }, weekendDayBuilder: (context, date, _) { - int ind = 0; - String comment = ""; - if (_habitData.events[date] != null && - _habitData.events[date][0] != 0) { - ind = (_habitData.events[date][0].index); - } - - if (_habitData.events[date] != null && - _habitData.events[date].length > 1 && - _habitData.events[date][1] != null && - _habitData.events[date][1] != "") { - comment = (_habitData.events[date][1]); - } - return OneDayButton( callback: refresh, parent: this, id: widget.habitData.id, date: date, - index: ind, color: Theme.of(context).colorScheme.primaryVariant, child: Text( date.day.toString(), style: TextStyle(color: Colors.red[300]), ), - comment: comment, + event: _habitData.events[date], ); }, outsideDayBuilder: (context, date, _) { @@ -234,10 +203,10 @@ class _HabitState extends State { alignment: Alignment.center, decoration: BoxDecoration( color: events[0] == DayType.Check - ? Color(0xFF09BF30) + ? HaboColors.primary : events[0] == DayType.Fail - ? Colors.red - : Color(0xFF505050), + ? HaboColors.red + : HaboColors.skip, borderRadius: BorderRadius.circular(15.0), ), child: events[0] == DayType.Check @@ -267,7 +236,7 @@ class _HabitState extends State { width: 8, height: 8, decoration: new BoxDecoration( - color: Colors.yellow[700], + color: HaboColors.comment, shape: BoxShape.circle, ), ), diff --git a/lib/widgets/habit_header.dart b/lib/widgets/habit_header.dart index e93d412..0a6f842 100644 --- a/lib/widgets/habit_header.dart +++ b/lib/widgets/habit_header.dart @@ -1,6 +1,8 @@ import 'package:Habo/widgets/habit.dart'; import 'package:flutter/material.dart'; +import 'package:Habo/helpers.dart'; + class HabitHeader extends StatelessWidget { const HabitHeader({ Key key, @@ -59,11 +61,12 @@ class HabitHeader extends StatelessWidget { decoration: BoxDecoration( border: Border.all( color: (this._orangeStreak) - ? Colors.orange - : Color(0xFF09BF30), + ? HaboColors.orange + : HaboColors.primary, ), - color: - (this._orangeStreak) ? Colors.orange : Color(0xFF09BF30), + color: (this._orangeStreak) + ? HaboColors.orange + : HaboColors.primary, borderRadius: BorderRadius.all(Radius.circular(20)), boxShadow: [ BoxShadow( diff --git a/lib/widgets/one_day_button.dart b/lib/widgets/one_day_button.dart index d50b902..af0493c 100644 --- a/lib/widgets/one_day_button.dart +++ b/lib/widgets/one_day_button.dart @@ -14,8 +14,7 @@ class OneDayButton extends StatelessWidget { this.id, this.parent, this.callback, - this.index, - this.comment}) + this.event}) : super(key: key); final int id; @@ -24,8 +23,7 @@ class OneDayButton extends StatelessWidget { final Widget child; final parent; final void Function() callback; - final int index; - final String comment; + final event; @override Widget build(BuildContext context) { @@ -41,14 +39,14 @@ class OneDayButton extends StatelessWidget { InButton( icon: Icon( Icons.check, - color: Color(0xFF09BF30), + color: HaboColors.primary, semanticLabel: 'Check', ), ), InButton( icon: Icon( Icons.close, - color: Colors.red, + color: HaboColors.red, semanticLabel: 'Fail', ), ), @@ -62,10 +60,24 @@ class OneDayButton extends StatelessWidget { icon: Icon( Icons.chat_bubble_outline, semanticLabel: 'Comment', - color: Colors.yellow[700], + color: HaboColors.comment, ), ) ]; + + int index = 0; + String comment = ""; + + if (event != null) { + if (event[0] != 0) { + index = (event[0].index); + } + + if (event.length > 1 && event[1] != null && event[1] != "") { + comment = (event[1]); + } + } + return Container( margin: const EdgeInsets.all(4.0), child: Material( @@ -106,52 +118,7 @@ class OneDayButton extends StatelessWidget { Provider.of(context, listen: false).playClickSound(); } } else if (icons.indexOf(value) == icons.length - 1) { - TextEditingController commentController = - TextEditingController(text: comment); - AwesomeDialog( - context: context, - dialogType: DialogType.NO_HEADER, - animType: AnimType.BOTTOMSLIDE, - body: Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 10.0, - ), - child: Column( - children: [ - Text("Comment"), - TextField( - controller: commentController, - autofocus: true, - maxLines: 5, - showCursor: true, - textAlignVertical: TextAlignVertical.bottom, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(11), - border: InputBorder.none, - hintText: "Your comment here", - ), - ), - ], - ), - ), - ), - btnOkText: "Save", - btnCancelText: "Close", - btnCancelColor: Colors.grey, - btnOkColor: Color(0xFF09BF30), - btnCancelOnPress: () {}, - btnOkOnPress: () { - Provider.of(context, listen: false).addEvent(id, date, - [DayType.values[index], commentController.text]); - parent.events[date] = [ - DayType.values[index], - commentController.text - ]; - callback(); - }, - )..show(); + showCommentDialog(context, index, comment); } else { if (comment != null && comment != "") { Provider.of(context, listen: false) @@ -170,4 +137,50 @@ class OneDayButton extends StatelessWidget { ), ); } + + showCommentDialog(BuildContext context, int index, String comment) { + TextEditingController commentController = + TextEditingController(text: comment); + AwesomeDialog( + context: context, + dialogType: DialogType.NO_HEADER, + animType: AnimType.BOTTOMSLIDE, + body: Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 10.0, + ), + child: Column( + children: [ + Text("Comment"), + TextField( + controller: commentController, + autofocus: true, + maxLines: 5, + showCursor: true, + textAlignVertical: TextAlignVertical.bottom, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(11), + border: InputBorder.none, + hintText: "Your comment here", + ), + ), + ], + ), + ), + ), + btnOkText: "Save", + btnCancelText: "Close", + btnCancelColor: Colors.grey, + btnOkColor: HaboColors.primary, + btnCancelOnPress: () {}, + btnOkOnPress: () { + Provider.of(context, listen: false).addEvent( + id, date, [DayType.values[index], commentController.text]); + parent.events[date] = [DayType.values[index], commentController.text]; + callback(); + }, + )..show(); + } } From b0bad6f17868531b31e0619e0546d398208546dc Mon Sep 17 00:00:00 2001 From: Peter Pavlenko Date: Thu, 2 Dec 2021 22:25:06 +0100 Subject: [PATCH 2/6] Small code optimizations: - Dispose text controllers - Bounce physics for habits --- lib/screens/create_habit_screen.dart | 9 +++++++++ lib/screens/edit_habit_screen.dart | 23 +++++++++++++++++------ lib/widgets/calendar_column.dart | 1 + 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/screens/create_habit_screen.dart b/lib/screens/create_habit_screen.dart index 09f7a4e..33a24f2 100644 --- a/lib/screens/create_habit_screen.dart +++ b/lib/screens/create_habit_screen.dart @@ -36,6 +36,15 @@ class _CreateHabitScreenState extends State { } } + @override + void dispose() { + title.dispose(); + cue.dispose(); + routine.dispose(); + reward.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/screens/edit_habit_screen.dart b/lib/screens/edit_habit_screen.dart index 4297a47..24f0b10 100644 --- a/lib/screens/edit_habit_screen.dart +++ b/lib/screens/edit_habit_screen.dart @@ -54,6 +54,15 @@ class _EditHabitScreenState extends State { notTime = widget.habitData.notTime; } + @override + void dispose() { + title.dispose(); + cue.dispose(); + routine.dispose(); + reward.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -173,9 +182,9 @@ class _EditHabitScreenState extends State { }, children: [ Container( - padding: EdgeInsets.symmetric(horizontal: 20), - child: Center( - child: Text( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: const Center( + child: const Text( "This section helps you better define your habits. You should define cue, routine, and reward for every habit.", textAlign: TextAlign.center, ), @@ -187,7 +196,8 @@ class _EditHabitScreenState extends State { label: 'Cue', ), ListTile( - contentPadding: EdgeInsets.symmetric(horizontal: 25), + contentPadding: + const EdgeInsets.symmetric(horizontal: 25), title: Text("Notifications"), trailing: Switch( value: notification, @@ -197,7 +207,8 @@ class _EditHabitScreenState extends State { }), ), ListTile( - contentPadding: EdgeInsets.symmetric(horizontal: 25), + contentPadding: + const EdgeInsets.symmetric(horizontal: 25), enabled: notification, title: Text("Notification time"), trailing: InkWell( @@ -219,7 +230,7 @@ class _EditHabitScreenState extends State { ), TextContainer( title: routine, - hint: 'Do 50 pushups', + hint: 'Do 50 push ups', label: 'Routine', ), TextContainer( diff --git a/lib/widgets/calendar_column.dart b/lib/widgets/calendar_column.dart index 3d9ec66..72cfe7f 100644 --- a/lib/widgets/calendar_column.dart +++ b/lib/widgets/calendar_column.dart @@ -22,6 +22,7 @@ class CalendarColumn extends StatelessWidget { child: (calendars.length != 0) ? Container( child: ReorderableListView( + physics: const BouncingScrollPhysics(), children: calendars .map( (index) => Container( From 7db52d173a859348d21ebc773c73451438055820 Mon Sep 17 00:00:00 2001 From: Peter Pavlenko Date: Thu, 2 Dec 2021 22:42:52 +0100 Subject: [PATCH 3/6] Fix calculating of streak. --- lib/widgets/habit.dart | 68 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/lib/widgets/habit.dart b/lib/widgets/habit.dart index 2ec2933..4c5ca61 100644 --- a/lib/widgets/habit.dart +++ b/lib/widgets/habit.dart @@ -257,20 +257,20 @@ class _HabitState extends State { _updateLastStreakNormal() { int inStreak = 0; - var _checkDay = _habitData.events.lastKey(); - - while (_habitData.events[_checkDay] != null && - (_habitData.events[_checkDay][0] == DayType.Check || - _habitData.events[_checkDay][0] == DayType.Skip)) { - if (_habitData.events[_checkDay][0] == DayType.Check) inStreak++; - - if (_habitData.events.lastKeyBefore(_checkDay) != null && - _checkDay - .difference(_habitData.events.lastKeyBefore(_checkDay)) - .inDays != - 1) break; + var checkDayKey = _habitData.events.lastKey(); + var lastDayKey = _habitData.events.lastKey(); + + while (_habitData.events[checkDayKey] != null && + _habitData.events[checkDayKey][0] != DayType.Fail) { + if (_habitData.events[checkDayKey][0] != DayType.Clear) { + if (_habitData.events[lastDayKey][0] != null && + _habitData.events[lastDayKey][0] != DayType.Clear && + lastDayKey.difference(checkDayKey).inDays > 1) break; + lastDayKey = checkDayKey; + } - _checkDay = _habitData.events.lastKeyBefore(_checkDay); + if (_habitData.events[checkDayKey][0] == DayType.Check) inStreak++; + checkDayKey = _habitData.events.lastKeyBefore(checkDayKey); } if (inStreak >= 2) @@ -283,26 +283,34 @@ class _HabitState extends State { _updateLastStreakTwoDay() { int inStreak = 0; - var checkDay = _habitData.events.lastKey(); + var trueLastKey = _habitData.events.lastKey(); - DayType lastDay = DayType.Check; + while (_habitData.events[trueLastKey] != null && + _habitData.events[trueLastKey][0] != null && + _habitData.events[trueLastKey][0] == DayType.Clear) { + trueLastKey = _habitData.events.lastKeyBefore(trueLastKey); + } - while (_habitData.events[checkDay] != null) { - if (_habitData.events[checkDay][0] == DayType.Check) inStreak++; + var checkDayKey = trueLastKey; + var lastDayKey = trueLastKey; + DayType lastDay = DayType.Check; - if (_habitData.events[checkDay][0] == DayType.Fail && - lastDay != DayType.Check) { - break; + while (_habitData.events[checkDayKey] != null) { + if (_habitData.events[checkDayKey][0] != DayType.Clear) { + if (_habitData.events[checkDayKey][0] == DayType.Fail && + (lastDay != DayType.Check && lastDay != DayType.Clear)) { + break; + } + + if (_habitData.events[lastDayKey][0] != null && + _habitData.events[lastDayKey][0] != DayType.Clear && + lastDayKey.difference(checkDayKey).inDays > 1) break; + lastDayKey = checkDayKey; } - if (_habitData.events.lastKeyBefore(checkDay) != null && - checkDay - .difference(_habitData.events.lastKeyBefore(checkDay)) - .inDays != - 1) break; - - lastDay = _habitData.events[checkDay][0]; - checkDay = _habitData.events.lastKeyBefore(checkDay); + lastDay = _habitData.events[checkDayKey][0]; + if (_habitData.events[checkDayKey][0] == DayType.Check) inStreak++; + checkDayKey = _habitData.events.lastKeyBefore(checkDayKey); } if (inStreak >= 2) @@ -311,8 +319,8 @@ class _HabitState extends State { _streakVisible = false; this._habitData.streak = inStreak; - if (_habitData.events[_habitData.events.lastKey()] != null && - _habitData.events[_habitData.events.lastKey()][0] == DayType.Fail) { + if (_habitData.events[trueLastKey] != null && + _habitData.events[trueLastKey][0] == DayType.Fail) { this._orangeStreak = true; } else { this._orangeStreak = false; From 34414dada0cfedf8abd963f35ded2d560501dae6 Mon Sep 17 00:00:00 2001 From: Peter Pavlenko Date: Thu, 2 Dec 2021 22:43:58 +0100 Subject: [PATCH 4/6] Implement statistics. --- lib/helpers.dart | 8 +- lib/provider.dart | 5 + lib/screens/home_screen.dart | 12 + lib/screens/statistics_screen.dart | 66 ++++++ lib/statistics.dart | 108 +++++++++ lib/widgets/monthly_graph.dart | 273 +++++++++++++++++++++++ lib/widgets/overall_statistics_card.dart | 161 +++++++++++++ lib/widgets/statistics_card.dart | 141 ++++++++++++ pubspec.lock | 14 ++ pubspec.yaml | 3 +- 10 files changed, 789 insertions(+), 2 deletions(-) create mode 100644 lib/screens/statistics_screen.dart create mode 100644 lib/statistics.dart create mode 100644 lib/widgets/monthly_graph.dart create mode 100644 lib/widgets/overall_statistics_card.dart create mode 100644 lib/widgets/statistics_card.dart diff --git a/lib/helpers.dart b/lib/helpers.dart index a27abbd..477625f 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -1,5 +1,6 @@ import 'package:Habo/screens/create_habit_screen.dart'; import 'package:Habo/screens/settings_screen.dart'; +import 'package:Habo/screens/statistics_screen.dart'; import 'package:flutter/material.dart'; Future navigateToSettingsPage(context) async { @@ -7,6 +8,11 @@ Future navigateToSettingsPage(context) async { context, MaterialPageRoute(builder: (context) => SettingsScreen())); } +Future navigateToStatisticsPage(context) async { + Navigator.push( + context, MaterialPageRoute(builder: (context) => StatisticsScreen())); +} + Future navigateToCreatePage(context) async { Navigator.push( context, MaterialPageRoute(builder: (context) => CreateHabitScreen())); @@ -27,7 +33,7 @@ enum DayType { Clear, Check, Fail, Skip } class HaboColors { static final Color primary = Color(0xFF09BF30); static final Color red = Colors.red; - static final Color skip = Color(0xFF505050); + static final Color skip = Color(0xFF818181); //0xFF505050 static final Color comment = Colors.yellow[700]; static final Color orange = Colors.orange; } diff --git a/lib/provider.dart b/lib/provider.dart index 75fd48e..8922651 100644 --- a/lib/provider.dart +++ b/lib/provider.dart @@ -5,6 +5,7 @@ import 'package:Habo/habit_data.dart'; import 'package:Habo/model.dart'; import 'package:Habo/notification_center.dart'; import 'package:Habo/settings_data.dart'; +import 'package:Habo/statistics.dart'; import 'package:Habo/widgets/habit.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -38,6 +39,10 @@ class Bloc with ChangeNotifier { _clickPlayer.setAsset('assets/sounds/click.wav'); } + Future getFutureStatsData() async { + return await Statistics.calculateStatistics(allHabits); + } + playCheckSound() { if (settingsData.getSoundEffects) { _checkPlayer.setClip( diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 6711240..c40e7db 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -16,6 +16,18 @@ class HomeScreen extends StatelessWidget { ), backgroundColor: Colors.transparent, actions: [ + IconButton( + icon: const Icon( + Icons.bar_chart, + semanticLabel: 'Statistics', + ), + color: Colors.grey[400], + tooltip: 'Statistics', + onPressed: () { + Provider.of(context, listen: false).hideSnackBar(); + navigateToStatisticsPage(context); + }, + ), IconButton( icon: const Icon( Icons.settings, diff --git a/lib/screens/statistics_screen.dart b/lib/screens/statistics_screen.dart new file mode 100644 index 0000000..254d707 --- /dev/null +++ b/lib/screens/statistics_screen.dart @@ -0,0 +1,66 @@ +import 'package:Habo/helpers.dart'; +import 'package:Habo/provider.dart'; +import 'package:Habo/statistics.dart'; +import 'package:Habo/widgets/overall_statistics_card.dart'; +import 'package:Habo/widgets/statistics_card.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/painting.dart'; +import 'package:provider/provider.dart'; + +class StatisticsScreen extends StatefulWidget { + @override + State createState() => _StatisticsScreenState(); +} + +class _StatisticsScreenState extends State { + @override + Widget build(BuildContext context) { + // var tmp = compute(calculateStatistics, Provider.of(context).allHabits); + return Scaffold( + appBar: AppBar( + title: Text( + 'Statistics', + style: TextStyle(color: Theme.of(context).colorScheme.primary), + ), + backgroundColor: Colors.transparent, + iconTheme: Theme.of(context).iconTheme, + ), + body: FutureBuilder( + future: Provider.of(context).getFutureStatsData(), + builder: + (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return ListView( + scrollDirection: Axis.vertical, + physics: const BouncingScrollPhysics(), + children: [ + OverallStatisticsCard( + total: snapshot.data.total, + habits: snapshot.data.habitsData.length, + ), + ListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: snapshot.data.habitsData + .map( + (index) => Padding( + padding: const EdgeInsets.all(12.0), + child: StatisticsCard(data: index), + ), + ) + .toList(), + ), + ], + ); + } else { + return Center( + child: CircularProgressIndicator( + color: HaboColors.primary, + ), + ); + } + }), + ); + } +} diff --git a/lib/statistics.dart b/lib/statistics.dart new file mode 100644 index 0000000..da11b51 --- /dev/null +++ b/lib/statistics.dart @@ -0,0 +1,108 @@ +import 'dart:collection'; + +import 'package:Habo/helpers.dart'; +import 'package:Habo/widgets/habit.dart'; + +class StatisticsData { + String title = ""; + int topStreak = 0; + int actualStreak = 0; + int checks = 0; + int skips = 0; + int fails = 0; + SplayTreeMap>> monthlyCheck = new SplayTreeMap(); +} + +class OverallStatisticsData { + int checks = 0; + int skips = 0; + int fails = 0; +} + +class AllStatistics { + OverallStatisticsData total = new OverallStatisticsData(); + List habitsData = []; +} + +// TODO: Make it truly async by running in isolate +class Statistics { + static Future calculateStatistics(List _habits) async { + AllStatistics stats = new AllStatistics(); + + if (_habits == null) return stats; + + _habits.forEach( + (habit) { + var stat = StatisticsData(); + stat.title = habit.habitData.title; + + bool usingTwoDayRule = false; + + var lastDay = habit.habitData.events.firstKey(); + + habit.habitData.events.forEach( + (key, value) { + if (value[0] != null && value[0] != DayType.Clear) { + if (key.difference(lastDay).inDays > 1) { + stat.actualStreak = 0; + } + + switch (value[0]) { + case DayType.Check: + stat.checks++; + stat.actualStreak++; + if (stat.actualStreak > stat.topStreak) { + stat.topStreak = stat.actualStreak; + } + usingTwoDayRule = false; + break; + case DayType.Skip: + stat.skips++; + if (usingTwoDayRule) { + stat.actualStreak = 0; + } + break; + case DayType.Fail: + stat.fails++; + if (habit.habitData.twoDayRule) { + if (usingTwoDayRule) { + stat.actualStreak = 0; + } else { + usingTwoDayRule = true; + } + } else { + stat.actualStreak = 0; + } + break; + } + + generateYearIfNull(stat, key.year); + + try { + if (value[0] != DayType.Clear) + stat.monthlyCheck[key.year][value[0]][key.month - 1]++; + } catch (e) {} + + lastDay = key; + } + }, + ); + stats.habitsData.add(stat); + stats.total.checks += stat.checks; + stats.total.fails += stat.fails; + stats.total.skips += stat.skips; + }, + ); + return stats; + } + + static generateYearIfNull(StatisticsData stat, int year) { + if (stat.monthlyCheck[year] == null) { + stat.monthlyCheck[year] = { + DayType.Check: new List.filled(12, 0), + DayType.Skip: new List.filled(12, 0), + DayType.Fail: new List.filled(12, 0), + }; + } + } +} diff --git a/lib/widgets/monthly_graph.dart b/lib/widgets/monthly_graph.dart new file mode 100644 index 0000000..838010f --- /dev/null +++ b/lib/widgets/monthly_graph.dart @@ -0,0 +1,273 @@ +import 'package:Habo/helpers.dart'; +import 'package:Habo/statistics.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; + +class MonthlyGraph extends StatefulWidget { + final StatisticsData data; + + const MonthlyGraph({Key key, this.data}) : super(key: key); + + @override + State createState() => _MonthlyGraphState(data); +} + +class _MonthlyGraphState extends State { + final StatisticsData data; + + bool showCheck = true; + bool showSkip = false; + bool showFail = false; + int year; + + _MonthlyGraphState(this.data) { + year = data.monthlyCheck.lastKey(); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.all(4.0), + child: Material( + color: showCheck + ? HaboColors.primary + : Theme.of(context).colorScheme.primaryVariant, + borderRadius: BorderRadius.circular(10.0), + elevation: 2, + child: SizedBox( + width: 32, + height: 32, + child: IconButton( + splashColor: Colors.transparent, + icon: Icon( + Icons.check, + size: 16, + ), + color: showCheck ? Colors.white : HaboColors.primary, + onPressed: () { + showCheck = !showCheck; + setState(() {}); + }, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: Material( + color: showSkip + ? HaboColors.skip + : Theme.of(context).colorScheme.primaryVariant, + borderRadius: BorderRadius.circular(10.0), + elevation: 2, + child: SizedBox( + width: 32, + height: 32, + child: IconButton( + splashColor: Colors.transparent, + icon: Icon( + Icons.last_page, + size: 16, + ), + color: showSkip ? Colors.white : HaboColors.skip, + onPressed: () { + showSkip = !showSkip; + setState(() {}); + }, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: Material( + color: showFail + ? HaboColors.red + : Theme.of(context).colorScheme.primaryVariant, + borderRadius: BorderRadius.circular(10.0), + elevation: 2, + child: SizedBox( + width: 32, + height: 32, + child: IconButton( + splashColor: Colors.transparent, + icon: Icon( + Icons.close, + size: 16, + ), + color: showFail ? Colors.white : HaboColors.red, + onPressed: () { + showFail = !showFail; + setState(() {}); + }, + ), + ), + ), + ), + ], + ), + DropdownButtonHideUnderline( + child: DropdownButton( + items: data.monthlyCheck.keys.map((int value) { + return new DropdownMenuItem( + value: value.toString(), + child: new Text( + value.toString(), + textAlign: TextAlign.center, + ), + ); + }).toList(), + value: year.toString(), + onChanged: (value) { + year = int.parse(value); + setState(() {}); + }, + ), + ), + ], + ), + Container( + height: 200, + child: BarChart( + BarChartData( + barTouchData: barTouchData, + titlesData: titlesData, + borderData: borderData, + barGroups: barGroups(), + alignment: BarChartAlignment.spaceAround, + gridData: FlGridData( + show: false, + ), + maxY: 31, + ), + swapAnimationDuration: Duration(milliseconds: 150), // Optional + swapAnimationCurve: Curves.linear, // Optional + ), + ), + ], + ); + } + + BarTouchData get barTouchData => BarTouchData( + enabled: false, + touchTooltipData: BarTouchTooltipData( + tooltipBgColor: Colors.transparent, + getTooltipItem: ( + BarChartGroupData group, + int groupIndex, + BarChartRodData rod, + int rodIndex, + ) { + return BarTooltipItem( + "", + const TextStyle( + color: Colors.transparent, + ), + ); + }, + ), + ); + + FlTitlesData get titlesData => FlTitlesData( + show: true, + bottomTitles: SideTitles( + showTitles: true, + getTextStyles: (context, value) => const TextStyle( + color: Color(0xff7589a2), + fontWeight: FontWeight.bold, + fontSize: 12, + ), + margin: 10, + getTitles: (double value) { + switch (value.toInt()) { + case 0: + return 'J'; + case 1: + return 'F'; + case 2: + return 'M'; + case 3: + return 'A'; + case 4: + return 'M'; + case 5: + return 'J'; + case 6: + return 'J'; + case 7: + return 'A'; + case 8: + return 'S'; + case 9: + return 'O'; + case 10: + return 'N'; + case 11: + return 'D'; + default: + return ''; + } + }, + ), + leftTitles: SideTitles(showTitles: false), + topTitles: SideTitles(showTitles: false), + rightTitles: SideTitles(showTitles: false), + ); + + FlBorderData get borderData => FlBorderData( + show: false, + ); + + List barGroups() { + List result = []; + + double width = 10; + if (showCheck) width -= 2; + if (showSkip) width -= 2; + if (showFail) width -= 2; + + for (int i = 0; i < data.monthlyCheck[year][DayType.Check].length; ++i) { + result.add( + BarChartGroupData( + x: i, + barRods: [ + if (showCheck) + BarChartRodData( + y: data.monthlyCheck[year][DayType.Check][i].toDouble(), + colors: [HaboColors.primary], + width: width, + ), + if (showSkip) + BarChartRodData( + y: data.monthlyCheck[year][DayType.Skip][i].toDouble(), + colors: [HaboColors.skip], + width: width, + ), + if (showFail) + BarChartRodData( + y: data.monthlyCheck[year][DayType.Fail][i].toDouble(), + colors: [HaboColors.red], + width: width, + ), + if (!showCheck && !showSkip && !showFail) + BarChartRodData( + y: 0, + colors: [Colors.transparent], + width: 4, + ), + ], + showingTooltipIndicators: [0], + ), + ); + } + return result; + } +} diff --git a/lib/widgets/overall_statistics_card.dart b/lib/widgets/overall_statistics_card.dart new file mode 100644 index 0000000..1522eb7 --- /dev/null +++ b/lib/widgets/overall_statistics_card.dart @@ -0,0 +1,161 @@ +import 'package:Habo/helpers.dart'; +import 'package:Habo/statistics.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; + +class OverallStatisticsCard extends StatelessWidget { + const OverallStatisticsCard({Key key, this.total, this.habits}) + : super(key: key); + + final OverallStatisticsData total; + final int habits; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Center( + child: Column( + children: [ + SizedBox( + height: 12, + ), + Container( + height: 200, + child: Stack( + children: [ + PieChart( + PieChartData( + sections: showingSections(), + ), + swapAnimationDuration: + Duration(milliseconds: 150), // Optional + swapAnimationCurve: Curves.linear, // Optional + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Habits:", + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + Text( + habits.toString(), + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ], + ), + ), + SizedBox( + height: 12, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.check, + color: HaboColors.primary, + ), + Text( + total.checks.toString(), + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.last_page, + color: HaboColors.skip, + ), + Text( + total.skips.toString(), + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.close, + color: HaboColors.red, + ), + Text( + total.fails.toString(), + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + ), + ], + ), + ), + ); + } + + List showingSections() { + return [ + PieChartSectionData( + color: HaboColors.primary, + value: total.checks.toDouble(), + badgeWidget: Icon( + Icons.check, + color: Colors.white, + ), + title: "", + radius: 25.0, + titleStyle: TextStyle( + fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), + ), + PieChartSectionData( + color: HaboColors.skip, + value: total.skips.toDouble(), + badgeWidget: Icon( + Icons.last_page, + color: Colors.white, + ), + title: "", + radius: 25.0, + titleStyle: TextStyle( + fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), + ), + PieChartSectionData( + color: HaboColors.red, + value: total.fails.toDouble(), + badgeWidget: Icon( + Icons.close, + color: Colors.white, + ), + title: "", + radius: 25.0, + titleStyle: TextStyle( + fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), + ), + ]; + } +} diff --git a/lib/widgets/statistics_card.dart b/lib/widgets/statistics_card.dart new file mode 100644 index 0000000..91803a9 --- /dev/null +++ b/lib/widgets/statistics_card.dart @@ -0,0 +1,141 @@ +import 'package:Habo/helpers.dart'; +import 'package:Habo/statistics.dart'; +import 'package:Habo/widgets/monthly_graph.dart'; +import 'package:flutter/material.dart'; + +class StatisticsCard extends StatelessWidget { + const StatisticsCard({ + Key key, + @required this.data, + }) : super(key: key); + + final StatisticsData data; + + @override + Widget build(BuildContext context) { + return Material( + borderRadius: BorderRadius.circular(15.0), + color: Theme.of(context).colorScheme.primaryVariant, + elevation: 3, + child: Container( + padding: const EdgeInsets.all(15.0), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + data.title, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + SizedBox( + height: 16, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text( + 'Top streak', + ), + Text( + data.topStreak.toString(), + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + Column( + children: [ + Text( + 'Actual streak', + ), + Text( + data.actualStreak.toString(), + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + ), + SizedBox( + height: 16, + ), + Text('Total'), + SizedBox( + height: 16, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.check, + color: HaboColors.primary, + ), + Text( + data.checks.toString(), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.last_page, + color: HaboColors.skip, + ), + Text( + data.skips.toString(), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.close, + color: HaboColors.red, + ), + Text( + data.fails.toString(), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + ), + SizedBox( + height: 16, + ), + MonthlyGraph(data: data), + ], + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index e868b9f..9ea58fc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -64,6 +64,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + equatable: + dependency: transitive + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" fake_async: dependency: transitive description: @@ -85,6 +92,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.2" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + url: "https://pub.dartlang.org" + source: hosted + version: "0.40.2" flare_flutter: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8e86158..1da29bd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ description: Minimalistic habit tracker. version: 0.6.1+5 environment: - sdk: ">=2.2.0 <3.0.0" + sdk: ">=2.3.0 <3.0.0" dependencies: flutter: @@ -29,6 +29,7 @@ dependencies: url_launcher: ^6.0.10 awesome_dialog: ^2.0.0 just_audio: ^0.9.11 + fl_chart: ^0.40.2 dev_dependencies: flutter_test: From 7d67beeeaf7652ee8119f00e7945735b6076dce5 Mon Sep 17 00:00:00 2001 From: Peter Pavlenko Date: Sat, 4 Dec 2021 21:32:15 +0100 Subject: [PATCH 5/6] Fix statistics with no habits. --- assets/images/noDataStatistics.svg | 1 + lib/screens/statistics_screen.dart | 49 ++++++++------- lib/statistics.dart | 2 + lib/widgets/empty_statistics_image.dart | 38 ++++++++++++ lib/widgets/monthly_graph.dart | 71 +++++++++++++--------- lib/widgets/overall_statistics_card.dart | 77 ++++++++++++------------ lib/widgets/statistics_card.dart | 37 ++++++++---- 7 files changed, 177 insertions(+), 98 deletions(-) create mode 100644 assets/images/noDataStatistics.svg create mode 100644 lib/widgets/empty_statistics_image.dart diff --git a/assets/images/noDataStatistics.svg b/assets/images/noDataStatistics.svg new file mode 100644 index 0000000..3ec035e --- /dev/null +++ b/assets/images/noDataStatistics.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/screens/statistics_screen.dart b/lib/screens/statistics_screen.dart index 254d707..e034185 100644 --- a/lib/screens/statistics_screen.dart +++ b/lib/screens/statistics_screen.dart @@ -1,6 +1,7 @@ import 'package:Habo/helpers.dart'; import 'package:Habo/provider.dart'; import 'package:Habo/statistics.dart'; +import 'package:Habo/widgets/empty_statistics_image.dart'; import 'package:Habo/widgets/overall_statistics_card.dart'; import 'package:Habo/widgets/statistics_card.dart'; import 'package:flutter/cupertino.dart'; @@ -31,28 +32,32 @@ class _StatisticsScreenState extends State { builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { - return ListView( - scrollDirection: Axis.vertical, - physics: const BouncingScrollPhysics(), - children: [ - OverallStatisticsCard( - total: snapshot.data.total, - habits: snapshot.data.habitsData.length, - ), - ListView( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - children: snapshot.data.habitsData - .map( - (index) => Padding( - padding: const EdgeInsets.all(12.0), - child: StatisticsCard(data: index), - ), - ) - .toList(), - ), - ], - ); + if (snapshot.data.habitsData.length == 0) { + return EmptyStatisticsImage(); + } else { + return ListView( + scrollDirection: Axis.vertical, + physics: const BouncingScrollPhysics(), + children: [ + OverallStatisticsCard( + total: snapshot.data.total, + habits: snapshot.data.habitsData.length, + ), + ListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: snapshot.data.habitsData + .map( + (index) => Padding( + padding: const EdgeInsets.all(12.0), + child: StatisticsCard(data: index), + ), + ) + .toList(), + ), + ], + ); + } } else { return Center( child: CircularProgressIndicator( diff --git a/lib/statistics.dart b/lib/statistics.dart index da11b51..294f463 100644 --- a/lib/statistics.dart +++ b/lib/statistics.dart @@ -87,6 +87,8 @@ class Statistics { } }, ); + + generateYearIfNull(stat, DateTime.now().year); stats.habitsData.add(stat); stats.total.checks += stat.checks; stats.total.fails += stat.fails; diff --git a/lib/widgets/empty_statistics_image.dart b/lib/widgets/empty_statistics_image.dart new file mode 100644 index 0000000..b4b6910 --- /dev/null +++ b/lib/widgets/empty_statistics_image.dart @@ -0,0 +1,38 @@ +import 'package:Habo/helpers.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class EmptyStatisticsImage extends StatelessWidget { + const EmptyStatisticsImage({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + navigateToCreatePage(context); + }, + child: Center( + child: Container( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 300, + height: 300, + child: SvgPicture.asset('assets/images/noDataStatistics.svg', + semanticsLabel: 'Empty list'), + ), + Text( + "There are no data about habits.", + style: TextStyle(color: Colors.grey), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/widgets/monthly_graph.dart b/lib/widgets/monthly_graph.dart index 838010f..124b247 100644 --- a/lib/widgets/monthly_graph.dart +++ b/lib/widgets/monthly_graph.dart @@ -135,7 +135,7 @@ class _MonthlyGraphState extends State { ], ), Container( - height: 200, + height: 150, child: BarChart( BarChartData( barTouchData: barTouchData, @@ -234,37 +234,52 @@ class _MonthlyGraphState extends State { if (showSkip) width -= 2; if (showFail) width -= 2; - for (int i = 0; i < data.monthlyCheck[year][DayType.Check].length; ++i) { + if (data.monthlyCheck.length != 0) { + for (int i = 0; i < data.monthlyCheck[year][DayType.Check].length; ++i) { + result.add( + BarChartGroupData( + x: i, + barRods: [ + if (showCheck) + BarChartRodData( + y: data.monthlyCheck[year][DayType.Check][i].toDouble(), + colors: [HaboColors.primary], + width: width, + ), + if (showSkip) + BarChartRodData( + y: data.monthlyCheck[year][DayType.Skip][i].toDouble(), + colors: [HaboColors.skip], + width: width, + ), + if (showFail) + BarChartRodData( + y: data.monthlyCheck[year][DayType.Fail][i].toDouble(), + colors: [HaboColors.red], + width: width, + ), + if (!showCheck && !showSkip && !showFail) + BarChartRodData( + y: 0, + colors: [Colors.transparent], + width: 4, + ), + ], + showingTooltipIndicators: [0], + ), + ); + } + } else { result.add( BarChartGroupData( - x: i, + x: 0, barRods: [ - if (showCheck) - BarChartRodData( - y: data.monthlyCheck[year][DayType.Check][i].toDouble(), - colors: [HaboColors.primary], - width: width, - ), - if (showSkip) - BarChartRodData( - y: data.monthlyCheck[year][DayType.Skip][i].toDouble(), - colors: [HaboColors.skip], - width: width, - ), - if (showFail) - BarChartRodData( - y: data.monthlyCheck[year][DayType.Fail][i].toDouble(), - colors: [HaboColors.red], - width: width, - ), - if (!showCheck && !showSkip && !showFail) - BarChartRodData( - y: 0, - colors: [Colors.transparent], - width: 4, - ), + BarChartRodData( + y: 0, + colors: [Colors.transparent], + width: width, + ), ], - showingTooltipIndicators: [0], ), ); } diff --git a/lib/widgets/overall_statistics_card.dart b/lib/widgets/overall_statistics_card.dart index 1522eb7..6129d99 100644 --- a/lib/widgets/overall_statistics_card.dart +++ b/lib/widgets/overall_statistics_card.dart @@ -57,7 +57,7 @@ class OverallStatisticsCard extends StatelessWidget { ), ), SizedBox( - height: 12, + height: 22, ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -72,7 +72,7 @@ class OverallStatisticsCard extends StatelessWidget { Text( total.checks.toString(), style: const TextStyle( - fontSize: 22, + fontSize: 24, fontWeight: FontWeight.bold, ), ), @@ -88,7 +88,7 @@ class OverallStatisticsCard extends StatelessWidget { Text( total.skips.toString(), style: const TextStyle( - fontSize: 22, + fontSize: 24, fontWeight: FontWeight.bold, ), ), @@ -104,7 +104,7 @@ class OverallStatisticsCard extends StatelessWidget { Text( total.fails.toString(), style: const TextStyle( - fontSize: 22, + fontSize: 24, fontWeight: FontWeight.bold, ), ), @@ -120,42 +120,45 @@ class OverallStatisticsCard extends StatelessWidget { List showingSections() { return [ - PieChartSectionData( - color: HaboColors.primary, - value: total.checks.toDouble(), - badgeWidget: Icon( - Icons.check, - color: Colors.white, + if (total.checks != 0) + PieChartSectionData( + color: HaboColors.primary, + value: total.checks.toDouble(), + badgeWidget: Icon( + Icons.check, + color: Colors.white, + ), + title: "", + radius: 25.0, + titleStyle: TextStyle( + fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), ), - title: "", - radius: 25.0, - titleStyle: TextStyle( - fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), - ), - PieChartSectionData( - color: HaboColors.skip, - value: total.skips.toDouble(), - badgeWidget: Icon( - Icons.last_page, - color: Colors.white, + if (total.skips != 0) + PieChartSectionData( + color: HaboColors.skip, + value: total.skips.toDouble(), + badgeWidget: Icon( + Icons.last_page, + color: Colors.white, + ), + title: "", + radius: 25.0, + titleStyle: TextStyle( + fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), ), - title: "", - radius: 25.0, - titleStyle: TextStyle( - fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), - ), - PieChartSectionData( - color: HaboColors.red, - value: total.fails.toDouble(), - badgeWidget: Icon( - Icons.close, - color: Colors.white, + if (total.fails != 0) + PieChartSectionData( + color: HaboColors.red, + value: total.fails.toDouble(), + badgeWidget: Icon( + Icons.close, + color: Colors.white, + ), + title: "", + radius: 25.0, + titleStyle: TextStyle( + fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), ), - title: "", - radius: 25.0, - titleStyle: TextStyle( - fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white), - ), ]; } } diff --git a/lib/widgets/statistics_card.dart b/lib/widgets/statistics_card.dart index 91803a9..2ca799a 100644 --- a/lib/widgets/statistics_card.dart +++ b/lib/widgets/statistics_card.dart @@ -23,12 +23,16 @@ class StatisticsCard extends StatelessWidget { children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, children: [ - Text( - data.title, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, + Expanded( + child: Text( + data.title, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, ), ), ], @@ -43,11 +47,14 @@ class StatisticsCard extends StatelessWidget { children: [ Text( 'Top streak', + style: const TextStyle( + fontSize: 18, + ), ), Text( data.topStreak.toString(), style: const TextStyle( - fontSize: 22, + fontSize: 24, fontWeight: FontWeight.bold, ), ), @@ -57,11 +64,14 @@ class StatisticsCard extends StatelessWidget { children: [ Text( 'Actual streak', + style: const TextStyle( + fontSize: 18, + ), ), Text( data.actualStreak.toString(), style: const TextStyle( - fontSize: 22, + fontSize: 24, fontWeight: FontWeight.bold, ), ), @@ -72,7 +82,12 @@ class StatisticsCard extends StatelessWidget { SizedBox( height: 16, ), - Text('Total'), + Text( + 'Total', + style: const TextStyle( + fontSize: 18, + ), + ), SizedBox( height: 16, ), @@ -89,7 +104,7 @@ class StatisticsCard extends StatelessWidget { Text( data.checks.toString(), style: const TextStyle( - fontSize: 18, + fontSize: 22, fontWeight: FontWeight.bold, ), ), @@ -105,7 +120,7 @@ class StatisticsCard extends StatelessWidget { Text( data.skips.toString(), style: const TextStyle( - fontSize: 18, + fontSize: 22, fontWeight: FontWeight.bold, ), ), @@ -121,7 +136,7 @@ class StatisticsCard extends StatelessWidget { Text( data.fails.toString(), style: const TextStyle( - fontSize: 18, + fontSize: 22, fontWeight: FontWeight.bold, ), ), From e51a867e04d373f3fb783a32958bf49405525597 Mon Sep 17 00:00:00 2001 From: Peter Pavlenko Date: Sat, 4 Dec 2021 23:04:04 +0100 Subject: [PATCH 6/6] Fix opening links in new android. - Added intent to open https in API 30+. --- android/app/src/main/AndroidManifest.xml | 9 +++++++++ pubspec.yaml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f644dfb..7b4f418 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,6 +8,15 @@ FlutterApplication and put your custom class here. --> + + + + + + + + + diff --git a/pubspec.yaml b/pubspec.yaml index 1da29bd..8f66165 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Minimalistic habit tracker. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.6.1+5 +version: 0.6.2 environment: sdk: ">=2.3.0 <3.0.0"