Please also see the contribution guide in the root folder.
- Install
Flutter
- Open a terminal in VSCode in the
app
directory- Run
dart pub get
to fetch all dart dependencies - Run
flutter pub get
to fetch all flutter dependencies and setup all generated code - Run
dart run build_runner build --delete-conflicting-outputs
ordart run build_runner watch --delete-conflicting-outputs
to re-generate code upon file changes while developing
- Run
You should now be able to run the app by opening the debug panel on the left and pressing the green triangle at the top (or using the shortcut F5).
For (cleaning) generated code, you might want to add the following aliases to your shell configuration:
alias flutter-generate='dart run build_runner build --delete-conflicting-outputs'
alias flutter-clean='find . -maxdepth 20 -type f \( -name "*.inject.summary" -o -name "*.inject.dart" -o -name "*.g.dart" \) -delete'
... can be super painful, because Java, Gradle, and Kotlin versions may be wrong.
Often problems in packages arise that rely on older Gradle versions.
The places to check are:
- Your Flutter version
- Your
JAVA_HOME
version - The Gradle version in
gradle-wrapper.properties
; the recommended versions are "between 7.3 and 7.6.1." (see Android Java Gradle migration guide) - The Java version in
android/app/build/gradle
- The Java version used for Gradle (check in Android Studio)
- The Kotlin version in
settings.gradle
The app consists of multiple so-called modules. Our main modules (usually app
screens) correspond to the direct subfolders of lib/
.
Common functions used by modules such as models
, widgets
, and utilities
are living in common
. All such functions are exported from
common/module.dart
.
The structure of an example module lib/my_module
should look as follows:
my_module
module.dart
(see example below):- exports everything that is required by other modules, i.e., page(s) and possibly the cubit
- declares all routes as functions returning
AutoRoute
- may contain initialization code (
initMyModule()
)
widgets
:my_widget.dart
: containsMyWidget
and helpers
pages
:my_module.dart
: containsMyModulePage
and helpersmy_child_page.dart
: containsmy_complex_page
: create a folder for complex pages (e.g., tabbed ones); might want to create an own module if getting too complex
utils.dart
: contains utilities used throughout this modulecubit.dart
: containsMyModuleCubit
andMyModuleState
s (if needed)
Example for my_module/module.dart
; the page is used as a root page in the tab
router, which is why the empty router MyModuleRootPage
and adding
AutoRoute(path: '', page: MyModuleRoute.page)
to children is needed.
import '../common/module.dart';
// For generated routes
export 'cubit.dart';
export 'pages/my_module.dart';
export 'pages/my_child_page.dart';
export 'pages/my_complex_page/page.dart';
@RoutePage()
class MyModuleRootPage extends AutoRouter {}
AutoRoute myChildRoute() => AutoRoute(
path: 'my_child',
page: MyChildRoute.page,
);
AutoRoute myComplexRoute() => AutoRoute(
path: 'my_complex',
page: MyComplexRoute.page,
);
AutoRoute myModuleRoute({ required List<AutoRoute> children }) => AutoRoute(
path: 'my_module',
page: MyModuleRootRoute.page,
children: [
AutoRoute(path: '', page: MyModuleRoute.page),
...children, // includes myChildRoute()
],
);
Add the icon as assets/icon/icon.png
(configured in pubspec.yaml
) and run
flutter pub run flutter_launcher_icons:main
This will generate icons for both iOS as well as Android.
Another option is to use, e.g., https://easyappicon.com/.
🙅 Not working yet due to login redirect, but keeping script for Sinai version (login without redirect) – can adopt once different login types are supported.
Check e60efb4f2fc3ba2efa7735ffb06ec5fdb64d7af6
for a rudimentary script
version, removed afterwards due to too many merge conflicts.
Currently, the user instructions are a static HTML file that need to be updated manually if changes in the app content occur (at least the screenshots are linked and do not need to be updated after re-generation).
We decided to host a PDF of the user instructions (for the study). To create this PDF, follow these steps:
- Open the HTML file in Chrome
- Print with "Save to PDF" using default settings, format US Letter
- Add page numbers online
- Position: bottom center
- Text:
{n} of {p}
- Font size: 10
If you would like to test with specific test data but you don't have a user with suitable data available, adapt the code that gets the lab results as shown below.
// TODO(after-testing): remove test data adaption
var labResults = json.map<LabResult>(LabResult.fromJson).toList();
final cyp2c19Result = labResults.firstWhere((labResult) => labResult.gene == "CYP2C19");
labResults = labResults.filter((labResult) => labResult.gene != "CYP2C19").toList();
labResults = [...labResults, LabResult(gene: "CYP2C19", variant: "*2/*2", phenotype: "Poor Metabolizer", allelesTested: cyp2c19Result.allelesTested)];
return labResults;
You can use the CPIC API to get reasonable genotype-phenotype pairings, e.g.,
with
https://api.cpicpgx.org/v1/diplotype?genesymbol=eq.CYP2C9&select=genesymbol,diplotype,generesult
.
Some more examples are collected below (this is for testing what is shown for Warfarin):
UserData.instance.diplotypes!['CYP2C9'] = Diplotype(
gene: 'CYP2C9',
resultType: 'Diplotype',
genotype: '*11/*13',
phenotype: 'Poor Metabolizer',
allelesTested: '',
);
UserData.instance.diplotypes!['VKORC1'] = Diplotype(
gene: 'VKORC1',
resultType: 'Diplotype',
genotype: '-1639G>A variant carriers',
phenotype: '-1639G>A variant carriers',
allelesTested: '',
);
UserData.instance.diplotypes!['CYP4F2'] = Diplotype(
gene: 'CYP4F2',
resultType: 'Diplotype',
genotype: 'rs2108622 T carriers',
phenotype: 'rs2108622 T carriers',
allelesTested: '',
);
UserData.instance.diplotypes!['CYP2C'] = Diplotype(
gene: 'CYP2C',
resultType: 'Diplotype',
genotype: 'rs12777823 A carriers',
phenotype: 'rs12777823 A carriers',
allelesTested: '',
);