Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Share Wellbeing Score button #42

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions lib/pages/checkup.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:nudge_me/main.dart';
import 'package:nudge_me/notification.dart';
import 'package:pedometer/pedometer.dart';
import 'dart:async';
import 'package:nudge_me/model/user_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timezone/timezone.dart' as tz;

class Checkup extends StatelessWidget {
@override
Expand Down Expand Up @@ -76,6 +79,31 @@ class _CheckupWidgetsState extends State<CheckupWidgets> {
return userSupportCode;
}

/// returns `true` if given [List] is monotonically decreasing
bool _isDecreasing(List<dynamic> items) {
for (int i = 0; i < items.length - 1; ++i) {
if (items[i] <= items[i + 1]) {
return false;
}
}
return true;
}

/// nudges user if score drops n times in the last n+1 weeks.
/// For example if n == 2 and we have these 3 weeks/scores 8 7 6, the user
/// will be nudged.
void _checkWellbeing(final int n) async {
assert(n >= 1);
final List<WellbeingItem> items =
await UserWellbeingDB().getLastNWeeks(n + 1);
if (items.length == n + 1 && _isDecreasing(items)) {
// if there were enough scores, and they were decreasing
// TODO: perform proper nudge here
scheduleNotification(
tz.TZDateTime.now(tz.local).add(const Duration(seconds: 2)));
}
}

@override
Widget build(BuildContext context) {
return Column(children: [
Expand Down Expand Up @@ -124,6 +152,8 @@ class _CheckupWidgetsState extends State<CheckupWidgets> {
SharedPreferences.getInstance().then((value) =>
value.setInt(PREV_STEP_COUNT_KEY, _currentTotalSteps));
Navigator.pop(context);

_checkWellbeing(2); // nudges if scores dropped twice
},
child: const Text('Done'))
]);
Expand Down
50 changes: 50 additions & 0 deletions lib/shared/share_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'dart:ui';

import 'package:flutter/rendering.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;

import 'package:flutter/material.dart';
import 'package:printing/printing.dart';

/// [StatelessWidget] that can share a widget as a PDF
class ShareButton extends StatelessWidget {
final GlobalKey _printKey;
final String _filename;

/// Uses a [GlobalKey] to find the widget to be shared
const ShareButton(this._printKey, this._filename);

@override
Widget build(BuildContext context) {
return FlatButton(
onPressed: _share,
child: Icon(
Icons.share,
color: Colors.blueAccent,
));
}

/// will get an [ImageProvider] for the widget associated with _printKey
Future<ImageProvider> _fromWidgetKey() async {
final RenderRepaintBoundary wrappedWidget =
_printKey.currentContext.findRenderObject();
final img = await wrappedWidget.toImage();
// needs to be a PNG format, otherwise the conversion won't work
final byteData = await img.toByteData(format: ImageByteFormat.png);
return MemoryImage(byteData.buffer.asUint8List());
}

void _share() async {
final doc = pw.Document();
final ImageProvider flutterImg = await _fromWidgetKey();
final pw.ImageProvider img = await flutterImageProvider(flutterImg);

doc.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context ctx) => pw.Center(child: pw.Image.provider(img))));

// opens the share panel
await Printing.sharePdf(bytes: doc.save(), filename: _filename);
}
}
68 changes: 40 additions & 28 deletions lib/shared/wellbeing_graph.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart';
import 'package:nudge_me/model/user_model.dart';
import 'package:nudge_me/shared/share_button.dart';

const RECOMMENDED_STEPS_IN_WEEK = 70000;

/// a [StatefulWidget] that displays the last wellbeing items in a graph,
/// along with a share button
class WellbeingGraph extends StatefulWidget {
final bool animate;

Expand All @@ -14,6 +17,7 @@ class WellbeingGraph extends StatefulWidget {
}

class _WellbeingGraphState extends State<WellbeingGraph> {
final GlobalKey _printKey = GlobalKey();
Future<List<WellbeingItem>> _wellbeingItems;

@override
Expand Down Expand Up @@ -42,33 +46,38 @@ class _WellbeingGraphState extends State<WellbeingGraph> {
);
final seriesList = [scoreSeries, stepSeries];

return new charts.NumericComboChart(
seriesList,
animate: animate,
defaultRenderer: new charts.LineRendererConfig(),
customSeriesRenderers: [
new charts.BarRendererConfig(
cornerStrategy: const charts.ConstCornerStrategy(25),
customRendererId: 'customBar')
],
behaviors: [
new charts.SeriesLegend(), // adds labels to colors
new charts.RangeAnnotation([
new charts.RangeAnnotationSegment(
8, // start score for healthy
10, // end score for healthy
charts.RangeAnnotationAxisType.measure,
endLabel: 'Healthy',
color: charts.MaterialPalette.gray.shade200,
),
]),
// using title as axes label:
new charts.ChartTitle('Week Number',
behaviorPosition: charts.BehaviorPosition.bottom,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.PanAndZoomBehavior(),
],
return Flexible(
child: RepaintBoundary( // uses [RepaintBoundary] so we have .toImage()
key: _printKey, // this container will be 'printed'/shared
child: charts.NumericComboChart(
seriesList,
animate: animate,
defaultRenderer: new charts.LineRendererConfig(),
customSeriesRenderers: [
new charts.BarRendererConfig(
cornerStrategy: const charts.ConstCornerStrategy(25),
customRendererId: 'customBar')
],
behaviors: [
new charts.SeriesLegend(), // adds labels to colors
new charts.RangeAnnotation([
new charts.RangeAnnotationSegment(
8, // start score for healthy
10, // end score for healthy
charts.RangeAnnotationAxisType.measure,
endLabel: 'Healthy',
color: charts.MaterialPalette.gray.shade200,
),
]),
// using title as axes label:
new charts.ChartTitle('Week Number',
behaviorPosition: charts.BehaviorPosition.bottom,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.PanAndZoomBehavior(),
],
),
),
);
}

Expand All @@ -79,7 +88,10 @@ class _WellbeingGraphState extends State<WellbeingGraph> {
builder: (context, snapshot) {
if (snapshot.hasData) {
final items = snapshot.data;
return _getGraph(items, widget.animate);
final graph = _getGraph(items, widget.animate);
return Column(
children: [graph, ShareButton(_printKey, 'wellbeing-score.pdf')],
);
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
}
Expand Down
63 changes: 63 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.41.1"
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.13"
args:
dependency: transitive
description:
Expand All @@ -29,6 +36,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0-nullsafety.1"
barcode:
dependency: transitive
description:
name: barcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.17.1"
boolean_selector:
dependency: transitive
description:
Expand Down Expand Up @@ -296,6 +310,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.19"
intl:
dependency: transitive
description:
Expand Down Expand Up @@ -380,6 +401,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.1"
path_parsing:
dependency: transitive
description:
name: path_parsing
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
path_provider_linux:
dependency: transitive
description:
Expand All @@ -401,6 +429,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+3"
pdf:
dependency: "direct main"
description:
name: pdf
url: "https://pub.dartlang.org"
source: hosted
version: "1.13.0"
pedantic:
dependency: transitive
description:
Expand All @@ -415,6 +450,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
platform:
dependency: transitive
description:
Expand All @@ -436,6 +478,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
printing:
dependency: "direct main"
description:
name: printing
url: "https://pub.dartlang.org"
source: hosted
version: "3.7.2"
process:
dependency: transitive
description:
Expand Down Expand Up @@ -464,6 +513,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
qr:
dependency: transitive
description:
name: qr
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
quiver:
dependency: transitive
description:
Expand Down Expand Up @@ -672,6 +728,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "4.5.1"
yaml:
dependency: transitive
description:
Expand Down
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ dependencies:
flutter_local_notifications: ^3.0.2
pedometer: ^2.0.2
timezone: ^0.5.9 # needed for scheduling notifications
pdf: ^1.13.0
printing: ^3.7.2

# data-related
provider: ^3.0.0
Expand Down