Skip to content

Commit

Permalink
refactor(app, medications page): Extract widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianWagner2 committed Jul 25, 2022
1 parent bbb9595 commit feb8395
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 283 deletions.
301 changes: 18 additions & 283 deletions app/lib/common/pages/medications/medication.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// ignore_for_file: avoid_returning_null_for_void

import 'package:google_fonts/google_fonts.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import '../../../reports/models/warning_level.dart';
import '../../module.dart';
import '../../l10n.dart';
import '../../models/medication/medication.dart';
import '../../theme.dart';
import '../../utilities/module.dart';
import '../../utilities/pdf_utils.dart';
import '../../widgets/module.dart';
import 'cubit.dart';
import 'widgets/module.dart';

class MedicationPage extends StatelessWidget {
const MedicationPage(this.id);
Expand All @@ -27,7 +32,9 @@ class MedicationPage extends StatelessWidget {
loading: () => Center(child: CircularProgressIndicator()),
loaded: (medication) => _buildMedicationsPage(
filterUserGuidelines(medication),
context,
isOkGuideline: medication.guidelines[0].warningLevel ==
WarningLevel.ok.toString(),
context: context,
),
),
);
Expand All @@ -37,23 +44,24 @@ class MedicationPage extends StatelessWidget {
}

Widget _buildMedicationsPage(
MedicationWithGuidelines medication,
BuildContext context,
) {
MedicationWithGuidelines medication, {
required bool isOkGuideline,
required BuildContext context,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(medication),
SizedBox(height: 20),
_buildDisclaimer(context),
Disclaimer(),
SizedBox(height: 20),
_SubHeader(
SubHeader(
context.l10n.medications_page_header_guideline,
tooltip: context.l10n.medications_page_tooltip_guideline,
),
SizedBox(height: 12),
if (medication.guidelines.isNotEmpty)
ClinicalAnnotationCard(medication)
ClinicalAnnotationCard(medication, isOkGuideline: isOkGuideline)
else
Text(context.l10n.medications_page_no_guidelines_for_phenotype),
],
Expand Down Expand Up @@ -97,277 +105,4 @@ class MedicationPage extends StatelessWidget {
],
);
}

Widget _buildDisclaimer(BuildContext context) {
return Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8)),
color: PharmeTheme.surfaceColor,
border: Border.all(color: PharmeTheme.errorColor, width: 1.2),
),
child: Row(children: [
Icon(
Icons.warning_rounded,
size: 52,
color: PharmeTheme.errorColor,
),
SizedBox(width: 8),
Flexible(
child: Text(
context.l10n.medications_page_disclaimer,
style: PharmeTheme.textTheme.labelMedium!.copyWith(
fontWeight: FontWeight.w100,
),
),
),
]),
);
}
}

class ClinicalAnnotationCard extends StatelessWidget {
const ClinicalAnnotationCard(this.medication);

final MedicationWithGuidelines medication;

@override
Widget build(BuildContext context) {
return Expanded(
child: RoundedCard(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
child: SingleChildScrollView(
child: Column(children: [
_buildHeader(context),
SizedBox(height: 16),
if (medication.guidelines[0].implication.isNotNullOrBlank ||
medication.guidelines[0].cpicImplication.isNotNullOrBlank) ...[
_buildImplicationInfo(context),
SizedBox(height: 16),
],
if (medication.guidelines[0].recommendation.isNotNullOrBlank ||
medication
.guidelines[0].cpicRecommendation.isNotNullOrBlank) ...[
_buildRecommendationCard(context),
SizedBox(height: 16),
],
_buildSourcesSection(context),
SizedBox(height: 16),
]),
),
),
);
}

Widget _buildImplicationInfo(BuildContext context) {
return Row(children: [
Text(
context.l10n.medications_page_info_big_i,
style: GoogleFonts.robotoSlab(
fontSize: 48,
fontWeight: FontWeight.w900,
),
),
SizedBox(width: 24),
Flexible(
child: Text(
medication.guidelines[0].implication ??
medication.guidelines[0].cpicImplication!,
style: PharmeTheme.textTheme.bodySmall,
),
),
]);
}

Widget _buildHeader(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
context.l10n.medications_page_gene_name(
medication.guidelines[0].phenotype.geneSymbol.name,
),
style: PharmeTheme.textTheme.bodyLarge!.copyWith(
color: Colors.black.withOpacity(0.5),
),
),
Row(children: [
Text(
medication.guidelines[0].cpicClassification!.toUpperCase(),
style: PharmeTheme.textTheme.bodyLarge!.copyWith(
fontWeight: FontWeight.bold,
color: Colors.black.withOpacity(0.7),
),
),
SizedBox(width: 6),
_TooltipIcon(context.l10n.medications_page_tooltip_classification),
]),
],
);
}

Widget _buildRecommendationCard(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: medication.guidelines[0].warningLevel == WarningLevel.danger.name
? Color(0xFFFFAFAF)
: medication.guidelines[0].warningLevel == WarningLevel.ok.name
? Color(0xFF00FF00)
: Color(0xFFFFEBCC),
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_SubHeader(
context.l10n.medications_page_header_recommendation,
),
Icon(
medication.guidelines[0].warningLevel ==
WarningLevel.danger.name
? Icons.dangerous_rounded
: medication.guidelines[0].warningLevel ==
WarningLevel.ok.name
? Icons.check_circle_rounded
: Icons.warning_rounded,
size: 32),
],
),
SizedBox(height: 4),
Text(
medication.guidelines[0].recommendation ??
medication.guidelines[0].cpicRecommendation!,
style: PharmeTheme.textTheme.bodyLarge,
),
]),
),
);
}

Widget _buildSourcesSection(BuildContext context) {
return Column(children: [
_SubHeader(
context.l10n.medications_page_header_further_info,
tooltip: context.l10n.medications_page_tooltip_further_info,
),
if (medication.pharmgkbId.isNotNullOrBlank) ...[
SizedBox(height: 8),
_buildSourceCard(
context.l10n.medications_page_sources_pharmGkb_name,
context.l10n.medications_page_sources_pharmGkb_description,
() => _launchPharmGkbUrl(medication.pharmgkbId),
),
],
SizedBox(height: 8),
_buildSourceCard(
context.l10n.medications_page_sources_cpic_name,
context.l10n.medications_page_sources_cpic_description,
() => _launchUrl(Uri.parse(medication.guidelines[0].cpicGuidelineUrl)),
),
]);
}

Widget _buildSourceCard(
String name,
String description,
GestureTapCallback onTap,
) {
return GestureDetector(
onTap: onTap,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8)),
gradient: LinearGradient(colors: [
PharmeTheme.primaryColor.withOpacity(0.8),
PharmeTheme.secondaryColor.withOpacity(0.8),
]),
),
child: Padding(
padding: const EdgeInsets.all(8),
child: Row(children: [
Expanded(
flex: 3,
child: Text(
name,
style: PharmeTheme.textTheme.bodyMedium!.copyWith(
color: Colors.white,
),
),
),
SizedBox(width: 4),
Expanded(
flex: 10,
child: Text(
description,
style: PharmeTheme.textTheme.bodySmall!.copyWith(
color: Colors.white,
),
),
),
]),
),
),
);
}
}

class _SubHeader extends StatelessWidget {
const _SubHeader(
this.title, {
this.tooltip,
});

final String title;
final String? tooltip;

@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
title.toUpperCase(),
style: PharmeTheme.textTheme.bodySmall!.copyWith(letterSpacing: 2),
),
if (tooltip.isNotNullOrBlank) ...[
SizedBox(width: 8),
_TooltipIcon(tooltip!),
]
],
);
}
}

class _TooltipIcon extends StatelessWidget {
const _TooltipIcon(this.message);

final String message;

@override
Widget build(BuildContext context) {
return Tooltip(
message: message,
triggerMode: TooltipTriggerMode.tap,
showDuration: const Duration(seconds: 2),
child: Icon(Icons.help_outline_rounded, size: 16),
);
}
}

Future<void> _launchPharmGkbUrl(String? id) async {
var url = 'https://pharmgkb.org';

// redirect to specific pharmgkb page if id is present
if (id.isNotNullOrBlank) {
url += '/chemical/$id/clinicalAnnotation';
}

await _launchUrl(Uri.parse(url));
}

Future<void> _launchUrl(Uri url) async {
if (!await launchUrl(url)) throw Error();
}
Loading

0 comments on commit feb8395

Please sign in to comment.