@@ -202,6 +202,28 @@ class HealthAppState extends State<HealthApp> {
202202 });
203203 }
204204
205+ /// Fetch single data point by UUID and type.
206+ Future <void > fetchDataByUUID (
207+ BuildContext context, {
208+ required String uuid,
209+ required HealthDataType type,
210+ }) async {
211+ try {
212+ // fetch health data
213+ HealthDataPoint ? healthPoint = await health.getHealthDataByUUID (
214+ uuid: uuid,
215+ type: type,
216+ );
217+
218+ if (healthPoint != null ) {
219+ // save all the new data points (only the first 100)
220+ if (context.mounted) openDetailBottomSheet (context, healthPoint);
221+ }
222+ } catch (error) {
223+ debugPrint ("Exception in getHealthDataByUUID: $error " );
224+ }
225+ }
226+
205227 /// Add some random health data.
206228 /// Note that you should ensure that you have permissions to add the
207229 /// following data types.
@@ -579,6 +601,23 @@ class HealthAppState extends State<HealthApp> {
579601 });
580602 }
581603
604+ /// Display bottom sheet dialog of selected HealthDataPoint
605+ void openDetailBottomSheet (
606+ BuildContext context,
607+ HealthDataPoint ? healthPoint,
608+ ) {
609+ showModalBottomSheet (
610+ context: context,
611+ isScrollControlled: true ,
612+ shape: const RoundedRectangleBorder (
613+ borderRadius: BorderRadius .vertical (top: Radius .circular (16 )),
614+ ),
615+ builder: (BuildContext context) => _detailedBottomSheet (
616+ healthPoint: healthPoint,
617+ ),
618+ );
619+ }
620+
582621 // UI building below
583622
584623 @override
@@ -783,46 +822,79 @@ class HealthAppState extends State<HealthApp> {
783822 ],
784823 );
785824
786- Widget get _contentDataReady => ListView .builder (
787- itemCount: _healthDataList.length,
788- itemBuilder: (_, index) {
789- // filter out manual entires if not wanted
790- if (recordingMethodsToFilter
791- .contains (_healthDataList[index].recordingMethod)) {
792- return Container ();
793- }
794-
795- HealthDataPoint p = _healthDataList[index];
796- if (p.value is AudiogramHealthValue ) {
797- return ListTile (
798- title: Text ("${p .typeString }: ${p .value }" ),
799- trailing: Text (p.unitString),
800- subtitle: Text ('${p .dateFrom } - ${p .dateTo }\n ${p .recordingMethod }' ),
801- );
802- }
803- if (p.value is WorkoutHealthValue ) {
804- return ListTile (
805- title: Text (
806- "${p .typeString }: ${(p .value as WorkoutHealthValue ).totalEnergyBurned } ${(p .value as WorkoutHealthValue ).totalEnergyBurnedUnit ?.name }" ),
807- trailing:
808- Text ((p.value as WorkoutHealthValue ).workoutActivityType.name),
809- subtitle: Text ('${p .dateFrom } - ${p .dateTo }\n ${p .recordingMethod }' ),
810- );
811- }
812- if (p.value is NutritionHealthValue ) {
813- return ListTile (
814- title: Text (
815- "${p .typeString } ${(p .value as NutritionHealthValue ).mealType }: ${(p .value as NutritionHealthValue ).name }" ),
816- trailing:
817- Text ('${(p .value as NutritionHealthValue ).calories } kcal' ),
818- subtitle: Text ('${p .dateFrom } - ${p .dateTo }\n ${p .recordingMethod }' ),
819- );
820- }
821- return ListTile (
822- title: Text ("${p .typeString }: ${p .value }" ),
823- trailing: Text (p.unitString),
824- subtitle: Text ('${p .dateFrom } - ${p .dateTo }\n ${p .recordingMethod }' ),
825- );
825+ Widget get _contentDataReady => Builder (builder: (context) {
826+ return ListView .builder (
827+ itemCount: _healthDataList.length,
828+ itemBuilder: (_, index) {
829+ // filter out manual entires if not wanted
830+ if (recordingMethodsToFilter
831+ .contains (_healthDataList[index].recordingMethod)) {
832+ return Container ();
833+ }
834+
835+ HealthDataPoint p = _healthDataList[index];
836+ if (p.value is AudiogramHealthValue ) {
837+ return ListTile (
838+ title: Text ("${p .typeString }: ${p .value }" ),
839+ trailing: Text (p.unitString),
840+ subtitle: Text ('${p .dateFrom } - ${p .dateTo }\n ${p .recordingMethod }' ),
841+ onTap: () {
842+ fetchDataByUUID (
843+ context,
844+ uuid: p.uuid,
845+ type: p.type,
846+ );
847+ },
848+ );
849+ }
850+ if (p.value is WorkoutHealthValue ) {
851+ return ListTile (
852+ title: Text (
853+ "${p .typeString }: ${(p .value as WorkoutHealthValue ).totalEnergyBurned } ${(p .value as WorkoutHealthValue ).totalEnergyBurnedUnit ?.name }" ),
854+ trailing: Text (
855+ (p.value as WorkoutHealthValue ).workoutActivityType.name),
856+ subtitle:
857+ Text ('${p .dateFrom } - ${p .dateTo }\n ${p .recordingMethod }' ),
858+ onTap: () {
859+ fetchDataByUUID (
860+ context,
861+ uuid: p.uuid,
862+ type: p.type,
863+ );
864+ },
865+ );
866+ }
867+ if (p.value is NutritionHealthValue ) {
868+ return ListTile (
869+ title: Text (
870+ "${p .typeString } ${(p .value as NutritionHealthValue ).mealType }: ${(p .value as NutritionHealthValue ).name }" ),
871+ trailing: Text (
872+ '${(p .value as NutritionHealthValue ).calories } kcal' ),
873+ subtitle:
874+ Text ('${p .dateFrom } - ${p .dateTo }\n ${p .recordingMethod }' ),
875+ onTap: () {
876+ fetchDataByUUID (
877+ context,
878+ uuid: p.uuid,
879+ type: p.type,
880+ );
881+ },
882+ );
883+ }
884+ return ListTile (
885+ title: Text ("${p .typeString }: ${p .value }" ),
886+ trailing: Text (p.unitString),
887+ subtitle:
888+ Text ('${p .dateFrom } - ${p .dateTo }\n ${p .recordingMethod }' ),
889+ onTap: () {
890+ fetchDataByUUID (
891+ context,
892+ uuid: p.uuid,
893+ type: p.type,
894+ );
895+ },
896+ );
897+ });
826898 });
827899
828900 final Widget _contentNoData = const Text ('No Data to show' );
@@ -878,4 +950,49 @@ class HealthAppState extends State<HealthApp> {
878950 AppState .PERMISSIONS_REVOKED => _permissionsRevoked,
879951 AppState .PERMISSIONS_NOT_REVOKED => _permissionsNotRevoked,
880952 };
953+
954+ Widget _detailedBottomSheet ({HealthDataPoint ? healthPoint}) {
955+ return DraggableScrollableSheet (
956+ expand: false ,
957+ initialChildSize: 0.5 ,
958+ minChildSize: 0.3 ,
959+ maxChildSize: 0.9 ,
960+ builder: (BuildContext listContext, scrollController) {
961+ return Container (
962+ padding: const EdgeInsets .all (16 ),
963+ child: Column (
964+ children: [
965+ const Text (
966+ "Health Data Details" ,
967+ style: TextStyle (fontSize: 18 , fontWeight: FontWeight .bold),
968+ ),
969+ const SizedBox (height: 10 ),
970+ healthPoint == null
971+ ? const Text ('UUID Not Found!' )
972+ : Expanded (
973+ child: ListView .builder (
974+ controller: scrollController,
975+ itemCount: healthPoint.toJson ().entries.length,
976+ itemBuilder: (context, index) {
977+ String key =
978+ healthPoint.toJson ().keys.elementAt (index);
979+ var value = healthPoint.toJson ()[key];
980+
981+ return ListTile (
982+ title: Text (
983+ key.replaceAll ('_' , ' ' ).toUpperCase (),
984+ style:
985+ const TextStyle (fontWeight: FontWeight .bold),
986+ ),
987+ subtitle: Text (value.toString ()),
988+ );
989+ },
990+ ),
991+ ),
992+ ],
993+ ),
994+ );
995+ },
996+ );
997+ }
881998}
0 commit comments