ui improvements to most components
This commit is contained in:
parent
6c987009dc
commit
0dfcedff0b
27
TODO
27
TODO
@ -1,35 +1,31 @@
|
|||||||
BUGFIXES:
|
BUGFIXES:
|
||||||
General/Framework:
|
General/Framework:
|
||||||
☐ fix preloading of dropdown values (appear blank at first as of now)
|
|
||||||
☐ make sure 'null' isn't shown in text fields
|
☐ make sure 'null' isn't shown in text fields
|
||||||
Log Entry:
|
|
||||||
☐ glucose target isn't displaed correctly anymore
|
|
||||||
Basal/Bolus:
|
Basal/Bolus:
|
||||||
☐ "no element" error on creating basal/bolus rates when working from apk
|
☐ "no element" error on creating basal/bolus rates when working from apk
|
||||||
|
|
||||||
MAIN TASKS:
|
MAIN TASKS:
|
||||||
Layout:
|
Layout:
|
||||||
☐ make a styleguide (actively decide what components should look like)
|
☐ make a styleguide (actively decide what components should look like)
|
||||||
☐ make components rounder/nicer/closer to new material style
|
☐ make components rounder/nicer/closer to new material style @started(21-12-08 02:17)
|
||||||
General/Framework:
|
General/Framework:
|
||||||
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
|
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
|
||||||
☐ add functionality to delete dead records (meaning: set deleted flag and no relations)
|
|
||||||
☐ clean up controllers (dispose method of each stateful widget)
|
☐ clean up controllers (dispose method of each stateful widget)
|
||||||
☐ improve dropdown component
|
|
||||||
☐ account for deleted/disabled elements in dropdowns
|
☐ account for deleted/disabled elements in dropdowns
|
||||||
☐ hide dropdown overlay on tapping anywhere else (especially menu)
|
|
||||||
☐ add clear button to dropdown (or all text fields?)
|
|
||||||
☐ check through all detail forms and set required fields/according messages
|
☐ check through all detail forms and set required fields/according messages
|
||||||
☐ implement component for durations
|
☐ implement component for durations
|
||||||
☐ change placement of delete and floating button because its very easy to accidentally hit delete
|
☐ change placement of delete and floating button because its very easy to accidentally hit delete
|
||||||
|
☐ hide details like accuracies etc when picking meals
|
||||||
Basal/Bolus:
|
Basal/Bolus:
|
||||||
☐ add save and close and next buttons on rate creations
|
☐ add save and close and next buttons on rate creations
|
||||||
|
☐ always calculate other glucose measurement from active one and make other one readonly
|
||||||
Log Entry:
|
Log Entry:
|
||||||
☐ add save and close button
|
☐ add save and close button
|
||||||
☐ move on to newly created entry after saving
|
☐ move on to newly created entry after saving
|
||||||
☐ add option to specify trend for blood sugar
|
|
||||||
☐ recalculate bolus upon deactivating 'set manually' option
|
☐ recalculate bolus upon deactivating 'set manually' option
|
||||||
☐ account for delayed percentage setting on meals
|
☐ account for delayed percentage setting on choosing meals
|
||||||
|
☐ give option to supply quantity
|
||||||
|
☐ give option to pick meal from a different log entry (that doesn't have an associated bolus yet)
|
||||||
Event Types:
|
Event Types:
|
||||||
☐ add colors as indicators for log entries (and later graphs in reports)
|
☐ add colors as indicators for log entries (and later graphs in reports)
|
||||||
Settings:
|
Settings:
|
||||||
@ -39,18 +35,22 @@ MAIN TASKS:
|
|||||||
☐ add field for active insulin duration
|
☐ add field for active insulin duration
|
||||||
☐ add setting for carb units/bread units
|
☐ add setting for carb units/bread units
|
||||||
☐ add option to switch 'save' and 'save & close' buttons
|
☐ add option to switch 'save' and 'save & close' buttons
|
||||||
|
☐ add functionality to delete dead records (meaning: set deleted flag and no relations to undeleted records)
|
||||||
|
|
||||||
FUTURE TASKS:
|
FUTURE TASKS:
|
||||||
General/Framework:
|
General/Framework:
|
||||||
|
☐ setup objectbox sync server
|
||||||
☐ add explanations to each section
|
☐ add explanations to each section
|
||||||
☐ find a better way to work with multiple glucose measurements (or disable it?)
|
☐ find a better way to work with multiple glucose measurements (or disable it?)
|
||||||
☐ evaluate if some fields should be readonly instead of completely hidden
|
☐ evaluate if some fields should be readonly instead of completely hidden
|
||||||
☐ alternate languages
|
☐ alternate languages
|
||||||
☐ log hba1c
|
☐ log hba1c
|
||||||
|
☐ add recipe calculator
|
||||||
Reports:
|
Reports:
|
||||||
☐ evaluate what type of reports there should be
|
☐ evaluate what type of reports there should be
|
||||||
Log Overview:
|
Log Overview:
|
||||||
☐ add pagination
|
☐ add pagination
|
||||||
|
☐ add filters
|
||||||
Log Entry:
|
Log Entry:
|
||||||
☐ check if there is still an active bolus when suggesting glucose bolus
|
☐ check if there is still an active bolus when suggesting glucose bolus
|
||||||
Event Types:
|
Event Types:
|
||||||
@ -58,8 +58,15 @@ FUTURE TASKS:
|
|||||||
☐ implement reminders as push notifications
|
☐ implement reminders as push notifications
|
||||||
Settings:
|
Settings:
|
||||||
☐ add option to hide extra customization options (ie. changing pre calculated values)?
|
☐ add option to hide extra customization options (ie. changing pre calculated values)?
|
||||||
|
☐ option to switch theme
|
||||||
|
|
||||||
Archive:
|
Archive:
|
||||||
|
✔ fix preloading of dropdown values (appear blank at first as of now) @done(21-12-09 05:31) @project(BUGFIXES.General/Framework)
|
||||||
|
✔ glucose target isn't displayed correctly anymore @done(21-12-09 05:31) @project(BUGFIXES.Log Entry)
|
||||||
|
✔ hide dropdown overlay on tapping anywhere else (especially menu) @done(21-12-07 21:04) @project(MAIN TASKS.General/Framework)
|
||||||
|
✔ add clear button to dropdown @done(21-12-07 21:21) @project(MAIN TASKS.General/Framework)
|
||||||
|
✔ add option to specify trend for blood sugar @done(21-12-07 14:20) @project(MAIN TASKS.Log Entry)
|
||||||
|
✔ always calculate other glucose measurement from active one and make other one readonly @done(21-12-07 14:33) @project(MAIN TASKS.Log Entry)
|
||||||
✔ scrollbars in rate overview not showing @done(21-12-06 20:01) @project(BUGFIXES.Basal/Bolus)
|
✔ scrollbars in rate overview not showing @done(21-12-06 20:01) @project(BUGFIXES.Basal/Bolus)
|
||||||
✔ order category lists (meals, meal sources,...) alphabetically @done(21-12-06 20:34) @project(MAIN TASKS.General/Framework)
|
✔ order category lists (meals, meal sources,...) alphabetically @done(21-12-06 20:34) @project(MAIN TASKS.General/Framework)
|
||||||
✔ add delay to auto conversions @done(21-12-06 20:25) @project(MAIN TASKS.General/Framework)
|
✔ add delay to auto conversions @done(21-12-06 20:25) @project(MAIN TASKS.General/Framework)
|
||||||
|
@ -44,7 +44,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "com.example.diameter"
|
applicationId "com.example.diameter"
|
||||||
minSdkVersion 16
|
minSdkVersion 21
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
@ -6,18 +6,46 @@ class AppTheme {
|
|||||||
|
|
||||||
static ThemeData lightTheme = FlexColorScheme.light(
|
static ThemeData lightTheme = FlexColorScheme.light(
|
||||||
surfaceStyle: FlexSurface.medium,
|
surfaceStyle: FlexSurface.medium,
|
||||||
scheme: FlexScheme.mandyRed,
|
scheme: FlexScheme.aquaBlue,
|
||||||
fontFamily: 'RobotoCondensed',
|
fontFamily: 'Roboto',
|
||||||
).toTheme;
|
).toTheme;
|
||||||
|
|
||||||
static ThemeData darkTheme = FlexColorScheme.light(
|
static ThemeData darkTheme = FlexColorScheme.dark(
|
||||||
scheme: FlexScheme.mandyRed,
|
scheme: FlexScheme.aquaBlue,
|
||||||
fontFamily: 'RobotoCondensed',
|
fontFamily: 'Roboto',
|
||||||
).toTheme;
|
).toTheme;
|
||||||
|
|
||||||
static ThemeData makeTheme(ThemeData baseThemeData) {
|
static ThemeData makeTheme(ThemeData baseThemeData) {
|
||||||
return baseThemeData.copyWith(
|
return baseThemeData.copyWith(
|
||||||
|
cardTheme: baseThemeData.cardTheme.copyWith(
|
||||||
|
color: baseThemeData.bottomAppBarColor,
|
||||||
|
elevation: 1,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
scrollbarTheme: baseThemeData.scrollbarTheme.copyWith(
|
||||||
|
isAlwaysShown: true,
|
||||||
|
),
|
||||||
|
textTheme: baseThemeData.textTheme.copyWith(
|
||||||
|
subtitle2: TextStyle(
|
||||||
|
color: baseThemeData.primaryColor,
|
||||||
|
letterSpacing: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
inputDecorationTheme: baseThemeData.inputDecorationTheme.copyWith(
|
||||||
|
fillColor: baseThemeData.textSelectionTheme.selectionColor,
|
||||||
|
border: const UnderlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(8.0),
|
||||||
|
topRight: Radius.circular(8.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||||
backgroundColor: baseThemeData.primaryColor));
|
backgroundColor: baseThemeData.primaryColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ class AutoCompleteDropdownButton<T> extends StatefulWidget {
|
|||||||
final List<T> items;
|
final List<T> items;
|
||||||
final void Function(T? value) onChanged;
|
final void Function(T? value) onChanged;
|
||||||
final List<T> Function(String? value)? applyQuery;
|
final List<T> Function(String? value)? applyQuery;
|
||||||
|
final TextEditingController controller;
|
||||||
|
|
||||||
const AutoCompleteDropdownButton(
|
const AutoCompleteDropdownButton(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
@ -14,7 +15,8 @@ class AutoCompleteDropdownButton<T> extends StatefulWidget {
|
|||||||
required this.label,
|
required this.label,
|
||||||
required this.items,
|
required this.items,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
this.applyQuery})
|
this.applyQuery,
|
||||||
|
required this.controller})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -24,10 +26,10 @@ class AutoCompleteDropdownButton<T> extends StatefulWidget {
|
|||||||
|
|
||||||
class _AutoCompleteDropdownButtonState<T>
|
class _AutoCompleteDropdownButtonState<T>
|
||||||
extends State<AutoCompleteDropdownButton<T>> {
|
extends State<AutoCompleteDropdownButton<T>> {
|
||||||
TextEditingController controller = TextEditingController(text: '');
|
|
||||||
late List<T> options;
|
late List<T> options;
|
||||||
late List<T> suggestions;
|
late List<T> suggestions;
|
||||||
|
|
||||||
|
final FocusNode focusNode = FocusNode();
|
||||||
final LayerLink layerLink = LayerLink();
|
final LayerLink layerLink = LayerLink();
|
||||||
OverlayEntry? entry;
|
OverlayEntry? entry;
|
||||||
bool isOpen = false;
|
bool isOpen = false;
|
||||||
@ -35,25 +37,32 @@ class _AutoCompleteDropdownButtonState<T>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
controller.text = widget.selectedItem == null
|
|
||||||
? ''
|
|
||||||
: widget.selectedItem!.toString();
|
|
||||||
options = widget.items;
|
options = widget.items;
|
||||||
suggestions = [];
|
suggestions = [];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void toggleOverlay() {
|
void toggleOverlay() {
|
||||||
isOpen ? hideOverlay() : showOverlay();
|
isOpen ? hideOverlay() : showOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void showOverlay() {
|
void showOverlay() {
|
||||||
hideOverlay();
|
hideOverlay();
|
||||||
|
focusNode.requestFocus();
|
||||||
|
|
||||||
List<Widget> items = [];
|
List<Widget> items = [];
|
||||||
Divider? divider;
|
Divider? divider;
|
||||||
|
|
||||||
|
ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final overlay = Overlay.of(context)!;
|
final overlay = Overlay.of(context)!;
|
||||||
final renderBox = context.findRenderObject() as RenderBox;
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
|
|
||||||
@ -79,16 +88,14 @@ class _AutoCompleteDropdownButtonState<T>
|
|||||||
items.addAll(options
|
items.addAll(options
|
||||||
.where((item) =>
|
.where((item) =>
|
||||||
!(widget.selectedItem != null &&
|
!(widget.selectedItem != null &&
|
||||||
item.toString() ==
|
item.toString() == widget.selectedItem!.toString()) &&
|
||||||
widget.selectedItem!.toString()) &&
|
|
||||||
!suggestions.contains(item))
|
!suggestions.contains(item))
|
||||||
.map((item) => buildListTile(item))
|
.map((item) => buildListTile(item))
|
||||||
.toList());
|
.toList());
|
||||||
|
|
||||||
final screenHeight = MediaQuery.of(context).size.height;
|
final screenHeight = MediaQuery.of(context).size.height;
|
||||||
final neededHeight =
|
final neededHeight = renderBox.size.height * (items.length - 1) +
|
||||||
renderBox.size.height * (items.length - 1) + (divider?.height ??
|
(divider?.height ?? renderBox.size.height);
|
||||||
renderBox.size.height);
|
|
||||||
final availableHeight = screenHeight -
|
final availableHeight = screenHeight -
|
||||||
(renderBox.localToGlobal(Offset.zero).dy + renderBox.size.height);
|
(renderBox.localToGlobal(Offset.zero).dy + renderBox.size.height);
|
||||||
bool displayAbove = neededHeight > availableHeight &&
|
bool displayAbove = neededHeight > availableHeight &&
|
||||||
@ -107,9 +114,13 @@ class _AutoCompleteDropdownButtonState<T>
|
|||||||
displayAbove ? Alignment.bottomLeft : Alignment.topLeft,
|
displayAbove ? Alignment.bottomLeft : Alignment.topLeft,
|
||||||
offset: Offset(0, renderBox.size.height * (displayAbove ? -1 : 1)),
|
offset: Offset(0, renderBox.size.height * (displayAbove ? -1 : 1)),
|
||||||
showWhenUnlinked: false,
|
showWhenUnlinked: false,
|
||||||
|
child: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
isAlwaysShown: true,
|
||||||
child: Material(
|
child: Material(
|
||||||
elevation: 8,
|
elevation: 8,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: items,
|
children: items,
|
||||||
),
|
),
|
||||||
@ -117,6 +128,7 @@ class _AutoCompleteDropdownButtonState<T>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
overlay.insert(entry!);
|
overlay.insert(entry!);
|
||||||
@ -139,13 +151,14 @@ class _AutoCompleteDropdownButtonState<T>
|
|||||||
|
|
||||||
void hideOverlay() {
|
void hideOverlay() {
|
||||||
entry?.remove();
|
entry?.remove();
|
||||||
|
setState(() {
|
||||||
entry = null;
|
entry = null;
|
||||||
isOpen = false;
|
isOpen = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleChanged(item) {
|
void handleChanged(T? item) {
|
||||||
widget.onChanged(item);
|
widget.onChanged(item);
|
||||||
controller.text = item.toString();
|
|
||||||
hideOverlay();
|
hideOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,18 +191,38 @@ class _AutoCompleteDropdownButtonState<T>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CompositedTransformTarget(
|
return Focus(
|
||||||
|
focusNode: focusNode,
|
||||||
|
onFocusChange: (isFocused) {
|
||||||
|
if (!isFocused) {
|
||||||
|
hideOverlay();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: CompositedTransformTarget(
|
||||||
link: layerLink,
|
link: layerLink,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
onChanged: onChangeQuery,
|
onChanged: onChangeQuery,
|
||||||
onTap: toggleOverlay,
|
onTap: toggleOverlay,
|
||||||
controller: controller,
|
controller: widget.controller,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: widget.label,
|
labelText: widget.label,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
widget.selectedItem != null
|
||||||
|
? IconButton(
|
||||||
|
onPressed: () => handleChanged(null),
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
iconSize: 20.0,
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
IconButton(
|
||||||
onPressed: toggleOverlay,
|
onPressed: toggleOverlay,
|
||||||
icon: const Icon(Icons.arrow_drop_down),
|
icon: const Icon(Icons.arrow_drop_down),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
1
lib/config.dart
Normal file
1
lib/config.dart
Normal file
@ -0,0 +1 @@
|
|||||||
|
String secret = 'm4Gwehzgv18jZ5gCVUBZl5li3Z0FX2Yb';
|
@ -1,4 +1,6 @@
|
|||||||
import 'package:diameter/components/app_theme.dart';
|
import 'package:diameter/components/app_theme.dart';
|
||||||
|
import 'package:diameter/config.dart';
|
||||||
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/object_box.dart';
|
import 'package:diameter/object_box.dart';
|
||||||
import 'package:diameter/screens/accuracy_detail.dart';
|
import 'package:diameter/screens/accuracy_detail.dart';
|
||||||
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
||||||
@ -23,17 +25,29 @@ import 'package:diameter/screens/accuracy_list.dart';
|
|||||||
import 'package:diameter/screens/basal/basal_profile_list.dart';
|
import 'package:diameter/screens/basal/basal_profile_list.dart';
|
||||||
import 'package:diameter/screens/bolus/bolus_profile_list.dart';
|
import 'package:diameter/screens/bolus/bolus_profile_list.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
|
import 'package:objectbox/objectbox.dart';
|
||||||
|
|
||||||
late ObjectBox objectBox;
|
late ObjectBox objectBox;
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
objectBox = await ObjectBox.create();
|
objectBox = await ObjectBox.create();
|
||||||
|
|
||||||
|
Sync.isAvailable();
|
||||||
|
SyncClient syncClient = Sync.client(
|
||||||
|
objectBox.store,
|
||||||
|
'wss://127.0.0.1:9999',
|
||||||
|
SyncCredentials.sharedSecretString(secret)
|
||||||
|
);
|
||||||
|
syncClient.start();
|
||||||
|
syncClient.requestUpdates(subscribeForFuturePushes: false);
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MaterialApp(
|
GestureDetector(
|
||||||
|
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
child: MaterialApp(
|
||||||
theme: AppTheme.makeTheme(AppTheme.lightTheme),
|
theme: AppTheme.makeTheme(AppTheme.lightTheme),
|
||||||
darkTheme: AppTheme.makeTheme(AppTheme.darkTheme),
|
darkTheme: AppTheme.makeTheme(AppTheme.darkTheme),
|
||||||
themeMode: ThemeMode.system,
|
themeMode: Settings.themeMode,
|
||||||
initialRoute: '/',
|
initialRoute: '/',
|
||||||
routes: {
|
routes: {
|
||||||
'/': (context) => const LogScreen(),
|
'/': (context) => const LogScreen(),
|
||||||
@ -49,7 +63,8 @@ Future<void> main() async {
|
|||||||
Routes.meal: (context) => const MealDetailScreen(),
|
Routes.meal: (context) => const MealDetailScreen(),
|
||||||
Routes.mealCategories: (context) => const MealCategoryListScreen(),
|
Routes.mealCategories: (context) => const MealCategoryListScreen(),
|
||||||
Routes.mealCategory: (context) => const MealCategoryDetailScreen(),
|
Routes.mealCategory: (context) => const MealCategoryDetailScreen(),
|
||||||
Routes.mealPortionTypes: (context) => const MealPortionTypeListScreen(),
|
Routes.mealPortionTypes: (context) =>
|
||||||
|
const MealPortionTypeListScreen(),
|
||||||
Routes.mealPortionType: (context) =>
|
Routes.mealPortionType: (context) =>
|
||||||
const MealPortionTypeDetailScreen(),
|
const MealPortionTypeDetailScreen(),
|
||||||
Routes.mealSources: (context) => const MealSourceListScreen(),
|
Routes.mealSources: (context) => const MealSourceListScreen(),
|
||||||
@ -61,5 +76,6 @@ Future<void> main() async {
|
|||||||
Routes.settings: (context) => const SettingsScreen(),
|
Routes.settings: (context) => const SettingsScreen(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show Accuracy_;
|
import 'package:diameter/objectbox.g.dart' show Accuracy_;
|
||||||
|
|
||||||
@Entity(uid: 291512798403320400)
|
@Entity(uid: 291512798403320400)
|
||||||
|
@Sync()
|
||||||
class Accuracy {
|
class Accuracy {
|
||||||
static final Box<Accuracy> box = objectBox.store.box<Accuracy>();
|
static final Box<Accuracy> box = objectBox.store.box<Accuracy>();
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show Basal_, BasalProfile_;
|
import 'package:diameter/objectbox.g.dart' show Basal_, BasalProfile_;
|
||||||
|
|
||||||
@Entity(uid: 1467758525778521891)
|
@Entity(uid: 1467758525778521891)
|
||||||
|
@Sync()
|
||||||
class Basal {
|
class Basal {
|
||||||
static final Box<Basal> box = objectBox.store.box<Basal>();
|
static final Box<Basal> box = objectBox.store.box<Basal>();
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show BasalProfile_;
|
import 'package:diameter/objectbox.g.dart' show BasalProfile_;
|
||||||
|
|
||||||
@Entity(uid: 3613736032926903785)
|
@Entity(uid: 3613736032926903785)
|
||||||
|
@Sync()
|
||||||
class BasalProfile {
|
class BasalProfile {
|
||||||
static final Box<BasalProfile> box = objectBox.store.box<BasalProfile>();
|
static final Box<BasalProfile> box = objectBox.store.box<BasalProfile>();
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show Bolus_, BolusProfile_;
|
import 'package:diameter/objectbox.g.dart' show Bolus_, BolusProfile_;
|
||||||
|
|
||||||
@Entity(uid: 3417770529060202389)
|
@Entity(uid: 3417770529060202389)
|
||||||
|
@Sync()
|
||||||
class Bolus {
|
class Bolus {
|
||||||
static final Box<Bolus> box = objectBox.store.box<Bolus>();
|
static final Box<Bolus> box = objectBox.store.box<Bolus>();
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show BolusProfile_;
|
import 'package:diameter/objectbox.g.dart' show BolusProfile_;
|
||||||
|
|
||||||
@Entity(uid: 8812452529027052317)
|
@Entity(uid: 8812452529027052317)
|
||||||
|
@Sync()
|
||||||
class BolusProfile {
|
class BolusProfile {
|
||||||
static final Box<BolusProfile> box = objectBox.store.box<BolusProfile>();
|
static final Box<BolusProfile> box = objectBox.store.box<BolusProfile>();
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show GlucoseTarget_;
|
import 'package:diameter/objectbox.g.dart' show GlucoseTarget_;
|
||||||
|
|
||||||
@Entity(uid: 5041265995704044399)
|
@Entity(uid: 5041265995704044399)
|
||||||
|
@Sync()
|
||||||
class GlucoseTarget {
|
class GlucoseTarget {
|
||||||
static final Box<GlucoseTarget> box = objectBox.store.box<GlucoseTarget>();
|
static final Box<GlucoseTarget> box = objectBox.store.box<GlucoseTarget>();
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show LogBolus_, LogEntry_;
|
import 'package:diameter/objectbox.g.dart' show LogBolus_, LogEntry_;
|
||||||
|
|
||||||
@Entity(uid: 8033487006694871160)
|
@Entity(uid: 8033487006694871160)
|
||||||
|
@Sync()
|
||||||
class LogBolus {
|
class LogBolus {
|
||||||
static final Box<LogBolus> box = objectBox.store.box<LogBolus>();
|
static final Box<LogBolus> box = objectBox.store.box<LogBolus>();
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show LogEntry_;
|
import 'package:diameter/objectbox.g.dart' show LogEntry_;
|
||||||
|
|
||||||
@Entity(uid: 752131069307970560)
|
@Entity(uid: 752131069307970560)
|
||||||
|
@Sync()
|
||||||
class LogEntry {
|
class LogEntry {
|
||||||
static final Box<LogEntry> box = objectBox.store.box<LogEntry>();
|
static final Box<LogEntry> box = objectBox.store.box<LogEntry>();
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ class LogEntry {
|
|||||||
DateTime time;
|
DateTime time;
|
||||||
int? mgPerDl;
|
int? mgPerDl;
|
||||||
double? mmolPerL;
|
double? mmolPerL;
|
||||||
|
double? glucoseTrend;
|
||||||
String? notes;
|
String? notes;
|
||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
@ -25,6 +27,7 @@ class LogEntry {
|
|||||||
required this.time,
|
required this.time,
|
||||||
this.mgPerDl,
|
this.mgPerDl,
|
||||||
this.mmolPerL,
|
this.mmolPerL,
|
||||||
|
this.glucoseTrend,
|
||||||
this.notes,
|
this.notes,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -43,8 +46,8 @@ class LogEntry {
|
|||||||
|
|
||||||
static bool hasUncorrectedGlucose(int id) {
|
static bool hasUncorrectedGlucose(int id) {
|
||||||
final entry = box.get(id);
|
final entry = box.get(id);
|
||||||
if (((entry?.mgPerDl ?? 0) > Settings.targetMgPerDl() ||
|
if (((entry?.mgPerDl ?? 0) > Settings.targetMgPerDl ||
|
||||||
(entry?.mmolPerL ?? 0) > Settings.targetMmolPerL())) {
|
(entry?.mmolPerL ?? 0) > Settings.targetMmolPerL)) {
|
||||||
return !LogBolus.glucoseBolusForEntryExists(id);
|
return !LogBolus.glucoseBolusForEntryExists(id);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -6,6 +6,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show LogEvent_, LogEventType_;
|
import 'package:diameter/objectbox.g.dart' show LogEvent_, LogEventType_;
|
||||||
|
|
||||||
@Entity(uid: 4303325892753185970)
|
@Entity(uid: 4303325892753185970)
|
||||||
|
@Sync()
|
||||||
class LogEvent {
|
class LogEvent {
|
||||||
static final Box<LogEvent> box = objectBox.store.box<LogEvent>();
|
static final Box<LogEvent> box = objectBox.store.box<LogEvent>();
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show LogEventType_;
|
import 'package:diameter/objectbox.g.dart' show LogEventType_;
|
||||||
|
|
||||||
@Entity(uid: 8362795406595606110)
|
@Entity(uid: 8362795406595606110)
|
||||||
|
@Sync()
|
||||||
class LogEventType {
|
class LogEventType {
|
||||||
static final Box<LogEventType> box = objectBox.store.box<LogEventType>();
|
static final Box<LogEventType> box = objectBox.store.box<LogEventType>();
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show LogMeal_, LogEntry_;
|
import 'package:diameter/objectbox.g.dart' show LogMeal_, LogEntry_;
|
||||||
|
|
||||||
@Entity(uid: 411177866700467286)
|
@Entity(uid: 411177866700467286)
|
||||||
|
@Sync()
|
||||||
class LogMeal {
|
class LogMeal {
|
||||||
static final Box<LogMeal> box = objectBox.store.box<LogMeal>();
|
static final Box<LogMeal> box = objectBox.store.box<LogMeal>();
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
enum PortionCarbsParameter { carbsRatio, portionSize, carbsPerPortion }
|
enum PortionCarbsParameter { carbsRatio, portionSize, carbsPerPortion }
|
||||||
|
|
||||||
@Entity(uid: 382130101578692012)
|
@Entity(uid: 382130101578692012)
|
||||||
|
@Sync()
|
||||||
class Meal {
|
class Meal {
|
||||||
static final Box<Meal> box = objectBox.store.box<Meal>();
|
static final Box<Meal> box = objectBox.store.box<Meal>();
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show MealCategory_;
|
import 'package:diameter/objectbox.g.dart' show MealCategory_;
|
||||||
|
|
||||||
@Entity(uid: 3158200688796904913)
|
@Entity(uid: 3158200688796904913)
|
||||||
|
@Sync()
|
||||||
class MealCategory {
|
class MealCategory {
|
||||||
static final Box<MealCategory> box = objectBox.store.box<MealCategory>();
|
static final Box<MealCategory> box = objectBox.store.box<MealCategory>();
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show MealPortionType_;
|
import 'package:diameter/objectbox.g.dart' show MealPortionType_;
|
||||||
|
|
||||||
@Entity(uid: 2111511899235985637)
|
@Entity(uid: 2111511899235985637)
|
||||||
|
@Sync()
|
||||||
class MealPortionType {
|
class MealPortionType {
|
||||||
static final Box<MealPortionType> box = objectBox.store.box<MealPortionType>();
|
static final Box<MealPortionType> box = objectBox.store.box<MealPortionType>();
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import 'package:objectbox/objectbox.dart';
|
|||||||
import 'package:diameter/objectbox.g.dart' show MealSource_;
|
import 'package:diameter/objectbox.g.dart' show MealSource_;
|
||||||
|
|
||||||
@Entity(uid: 1283034494527412242)
|
@Entity(uid: 1283034494527412242)
|
||||||
|
@Sync()
|
||||||
class MealSource {
|
class MealSource {
|
||||||
static final Box<MealSource> box = objectBox.store.box<MealSource>();
|
static final Box<MealSource> box = objectBox.store.box<MealSource>();
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:diameter/main.dart';
|
import 'package:diameter/main.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:objectbox/objectbox.dart';
|
import 'package:objectbox/objectbox.dart';
|
||||||
|
|
||||||
enum GlucoseDisplayMode { activeOnly, bothForList, bothForDetail, both }
|
enum GlucoseDisplayMode { activeOnly, bothForList, bothForDetail, both }
|
||||||
@ -39,14 +40,18 @@ List<String> nutritionMeasurementLabels = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
@Entity(uid: 3989341091218179227)
|
@Entity(uid: 3989341091218179227)
|
||||||
|
@Sync()
|
||||||
class Settings {
|
class Settings {
|
||||||
static final Box<Settings> box = objectBox.store.box<Settings>();
|
static final Box<Settings> box = objectBox.store.box<Settings>();
|
||||||
|
|
||||||
// properties
|
// properties
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
int nutritionMeasurementIndex;
|
int nutritionMeasurementIndex;
|
||||||
int glucoseDisplayModeIndex;
|
int glucoseDisplayModeIndex;
|
||||||
int glucoseMeasurementIndex;
|
int glucoseMeasurementIndex;
|
||||||
|
int targetGlucoseMgPerDl;
|
||||||
|
double targetGlucoseMmolPerL;
|
||||||
|
|
||||||
String dateFormat;
|
String dateFormat;
|
||||||
String? longDateFormat;
|
String? longDateFormat;
|
||||||
@ -57,8 +62,7 @@ class Settings {
|
|||||||
bool showConfirmationDialogOnDelete;
|
bool showConfirmationDialogOnDelete;
|
||||||
bool showConfirmationDialogOnStopEvent;
|
bool showConfirmationDialogOnStopEvent;
|
||||||
|
|
||||||
int targetGlucoseMgPerDl;
|
bool useDarkTheme;
|
||||||
double targetGlucoseMmolPerL;
|
|
||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
Settings({
|
Settings({
|
||||||
@ -75,6 +79,7 @@ class Settings {
|
|||||||
this.showConfirmationDialogOnStopEvent = true,
|
this.showConfirmationDialogOnStopEvent = true,
|
||||||
this.targetGlucoseMgPerDl = 100,
|
this.targetGlucoseMgPerDl = 100,
|
||||||
this.targetGlucoseMmolPerL = 5.49,
|
this.targetGlucoseMmolPerL = 5.49,
|
||||||
|
this.useDarkTheme = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
@ -97,13 +102,16 @@ class Settings {
|
|||||||
static String get glucoseMeasurementSuffix =>
|
static String get glucoseMeasurementSuffix =>
|
||||||
glucoseMeasurementSuffixes[get().glucoseMeasurementIndex];
|
glucoseMeasurementSuffixes[get().glucoseMeasurementIndex];
|
||||||
|
|
||||||
static int targetMgPerDl() => get().targetGlucoseMgPerDl;
|
static int get targetMgPerDl => get().targetGlucoseMgPerDl;
|
||||||
static double targetMmolPerL() => get().targetGlucoseMmolPerL;
|
static double get targetMmolPerL => get().targetGlucoseMmolPerL;
|
||||||
|
|
||||||
|
static ThemeMode get themeMode =>
|
||||||
|
get().useDarkTheme ? ThemeMode.dark : ThemeMode.light;
|
||||||
|
|
||||||
static void put(Settings settings) => box.put(settings);
|
static void put(Settings settings) => box.put(settings);
|
||||||
|
|
||||||
static void reset() {
|
static void reset() {
|
||||||
box.removeAll();
|
box.removeAll();
|
||||||
box.put(Settings());
|
box.put(Settings(useDarkTheme: ThemeMode.system == ThemeMode.dark));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"id": "2:1467758525778521891",
|
"id": "2:1467758525778521891",
|
||||||
"lastPropertyId": "6:3409466778841164684",
|
"lastPropertyId": "6:3409466778841164684",
|
||||||
"name": "Basal",
|
"name": "Basal",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:4281816825522738642",
|
"id": "1:4281816825522738642",
|
||||||
@ -49,6 +50,7 @@
|
|||||||
"id": "3:3613736032926903785",
|
"id": "3:3613736032926903785",
|
||||||
"lastPropertyId": "5:8140071977687660397",
|
"lastPropertyId": "5:8140071977687660397",
|
||||||
"name": "BasalProfile",
|
"name": "BasalProfile",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:353771983641472117",
|
"id": "1:353771983641472117",
|
||||||
@ -83,6 +85,7 @@
|
|||||||
"id": "4:3417770529060202389",
|
"id": "4:3417770529060202389",
|
||||||
"lastPropertyId": "9:7440090146687096977",
|
"lastPropertyId": "9:7440090146687096977",
|
||||||
"name": "Bolus",
|
"name": "Bolus",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:8141647919190345775",
|
"id": "1:8141647919190345775",
|
||||||
@ -140,6 +143,7 @@
|
|||||||
"id": "5:8812452529027052317",
|
"id": "5:8812452529027052317",
|
||||||
"lastPropertyId": "5:8082994824481464395",
|
"lastPropertyId": "5:8082994824481464395",
|
||||||
"name": "BolusProfile",
|
"name": "BolusProfile",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:4233863196673391978",
|
"id": "1:4233863196673391978",
|
||||||
@ -172,8 +176,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "6:752131069307970560",
|
"id": "6:752131069307970560",
|
||||||
"lastPropertyId": "9:1692732373071965573",
|
"lastPropertyId": "10:2505303363495348118",
|
||||||
"name": "LogEntry",
|
"name": "LogEntry",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:5528657304180237933",
|
"id": "1:5528657304180237933",
|
||||||
@ -205,6 +210,11 @@
|
|||||||
"id": "9:1692732373071965573",
|
"id": "9:1692732373071965573",
|
||||||
"name": "deleted",
|
"name": "deleted",
|
||||||
"type": 1
|
"type": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "10:2505303363495348118",
|
||||||
|
"name": "glucoseTrend",
|
||||||
|
"type": 8
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"relations": []
|
"relations": []
|
||||||
@ -213,6 +223,7 @@
|
|||||||
"id": "7:4303325892753185970",
|
"id": "7:4303325892753185970",
|
||||||
"lastPropertyId": "12:3041952167628926163",
|
"lastPropertyId": "12:3041952167628926163",
|
||||||
"name": "LogEvent",
|
"name": "LogEvent",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:6648501734758557663",
|
"id": "1:6648501734758557663",
|
||||||
@ -281,6 +292,7 @@
|
|||||||
"id": "8:8362795406595606110",
|
"id": "8:8362795406595606110",
|
||||||
"lastPropertyId": "8:1869014400856897151",
|
"lastPropertyId": "8:1869014400856897151",
|
||||||
"name": "LogEventType",
|
"name": "LogEventType",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:1430413826199774000",
|
"id": "1:1430413826199774000",
|
||||||
@ -336,6 +348,7 @@
|
|||||||
"id": "9:411177866700467286",
|
"id": "9:411177866700467286",
|
||||||
"lastPropertyId": "17:7341439841011629937",
|
"lastPropertyId": "17:7341439841011629937",
|
||||||
"name": "LogMeal",
|
"name": "LogMeal",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:962999525294133158",
|
"id": "1:962999525294133158",
|
||||||
@ -441,6 +454,7 @@
|
|||||||
"id": "10:382130101578692012",
|
"id": "10:382130101578692012",
|
||||||
"lastPropertyId": "15:8283810711091063880",
|
"lastPropertyId": "15:8283810711091063880",
|
||||||
"name": "Meal",
|
"name": "Meal",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:612386612600420389",
|
"id": "1:612386612600420389",
|
||||||
@ -535,6 +549,7 @@
|
|||||||
"id": "11:3158200688796904913",
|
"id": "11:3158200688796904913",
|
||||||
"lastPropertyId": "4:824435977543069541",
|
"lastPropertyId": "4:824435977543069541",
|
||||||
"name": "MealCategory",
|
"name": "MealCategory",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:3678943122076184840",
|
"id": "1:3678943122076184840",
|
||||||
@ -564,6 +579,7 @@
|
|||||||
"id": "12:2111511899235985637",
|
"id": "12:2111511899235985637",
|
||||||
"lastPropertyId": "4:5680236937391945907",
|
"lastPropertyId": "4:5680236937391945907",
|
||||||
"name": "MealPortionType",
|
"name": "MealPortionType",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:65428405312238271",
|
"id": "1:65428405312238271",
|
||||||
@ -593,6 +609,7 @@
|
|||||||
"id": "13:1283034494527412242",
|
"id": "13:1283034494527412242",
|
||||||
"lastPropertyId": "8:4547899751779962180",
|
"lastPropertyId": "8:4547899751779962180",
|
||||||
"name": "MealSource",
|
"name": "MealSource",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:7205380295259922130",
|
"id": "1:7205380295259922130",
|
||||||
@ -654,6 +671,7 @@
|
|||||||
"id": "14:8033487006694871160",
|
"id": "14:8033487006694871160",
|
||||||
"lastPropertyId": "18:7503231998671134983",
|
"lastPropertyId": "18:7503231998671134983",
|
||||||
"name": "LogBolus",
|
"name": "LogBolus",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:8254237730262024662",
|
"id": "1:8254237730262024662",
|
||||||
@ -752,6 +770,7 @@
|
|||||||
"id": "15:291512798403320400",
|
"id": "15:291512798403320400",
|
||||||
"lastPropertyId": "7:6675647182186603076",
|
"lastPropertyId": "7:6675647182186603076",
|
||||||
"name": "Accuracy",
|
"name": "Accuracy",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:8405388350474524599",
|
"id": "1:8405388350474524599",
|
||||||
@ -794,8 +813,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "16:3989341091218179227",
|
"id": "16:3989341091218179227",
|
||||||
"lastPropertyId": "22:3595473653451456068",
|
"lastPropertyId": "23:3611447442844013652",
|
||||||
"name": "Settings",
|
"name": "Settings",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:7803753645747063723",
|
"id": "1:7803753645747063723",
|
||||||
@ -862,6 +882,11 @@
|
|||||||
"id": "22:3595473653451456068",
|
"id": "22:3595473653451456068",
|
||||||
"name": "targetGlucoseMmolPerL",
|
"name": "targetGlucoseMmolPerL",
|
||||||
"type": 8
|
"type": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "23:3611447442844013652",
|
||||||
|
"name": "useDarkTheme",
|
||||||
|
"type": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"relations": []
|
"relations": []
|
||||||
@ -870,6 +895,7 @@
|
|||||||
"id": "17:5041265995704044399",
|
"id": "17:5041265995704044399",
|
||||||
"lastPropertyId": "7:1333487551279074696",
|
"lastPropertyId": "7:1333487551279074696",
|
||||||
"name": "GlucoseTarget",
|
"name": "GlucoseTarget",
|
||||||
|
"flags": 2,
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"id": "1:4322960567133959537",
|
"id": "1:4322960567133959537",
|
||||||
|
@ -7,7 +7,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:objectbox/flatbuffers/flat_buffers.dart' as fb;
|
import 'package:objectbox/flatbuffers/flat_buffers.dart' as fb;
|
||||||
import 'package:objectbox/internal.dart'; // generated code can access "internal" functionality
|
import 'package:objectbox/internal.dart'; // generated code can access "internal" functionality
|
||||||
import 'package:objectbox/objectbox.dart';
|
import 'package:objectbox/objectbox.dart';
|
||||||
import 'package:objectbox_flutter_libs/objectbox_flutter_libs.dart';
|
import 'package:objectbox_sync_flutter_libs/objectbox_sync_flutter_libs.dart';
|
||||||
|
|
||||||
import 'models/accuracy.dart';
|
import 'models/accuracy.dart';
|
||||||
import 'models/basal.dart';
|
import 'models/basal.dart';
|
||||||
@ -33,7 +33,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(2, 1467758525778521891),
|
id: const IdUid(2, 1467758525778521891),
|
||||||
name: 'Basal',
|
name: 'Basal',
|
||||||
lastPropertyId: const IdUid(6, 3409466778841164684),
|
lastPropertyId: const IdUid(6, 3409466778841164684),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 4281816825522738642),
|
id: const IdUid(1, 4281816825522738642),
|
||||||
@ -74,7 +74,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(3, 3613736032926903785),
|
id: const IdUid(3, 3613736032926903785),
|
||||||
name: 'BasalProfile',
|
name: 'BasalProfile',
|
||||||
lastPropertyId: const IdUid(5, 8140071977687660397),
|
lastPropertyId: const IdUid(5, 8140071977687660397),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 353771983641472117),
|
id: const IdUid(1, 353771983641472117),
|
||||||
@ -108,7 +108,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(4, 3417770529060202389),
|
id: const IdUid(4, 3417770529060202389),
|
||||||
name: 'Bolus',
|
name: 'Bolus',
|
||||||
lastPropertyId: const IdUid(9, 7440090146687096977),
|
lastPropertyId: const IdUid(9, 7440090146687096977),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 8141647919190345775),
|
id: const IdUid(1, 8141647919190345775),
|
||||||
@ -164,7 +164,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(5, 8812452529027052317),
|
id: const IdUid(5, 8812452529027052317),
|
||||||
name: 'BolusProfile',
|
name: 'BolusProfile',
|
||||||
lastPropertyId: const IdUid(5, 8082994824481464395),
|
lastPropertyId: const IdUid(5, 8082994824481464395),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 4233863196673391978),
|
id: const IdUid(1, 4233863196673391978),
|
||||||
@ -197,8 +197,8 @@ final _entities = <ModelEntity>[
|
|||||||
ModelEntity(
|
ModelEntity(
|
||||||
id: const IdUid(6, 752131069307970560),
|
id: const IdUid(6, 752131069307970560),
|
||||||
name: 'LogEntry',
|
name: 'LogEntry',
|
||||||
lastPropertyId: const IdUid(9, 1692732373071965573),
|
lastPropertyId: const IdUid(10, 2505303363495348118),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 5528657304180237933),
|
id: const IdUid(1, 5528657304180237933),
|
||||||
@ -229,6 +229,11 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(9, 1692732373071965573),
|
id: const IdUid(9, 1692732373071965573),
|
||||||
name: 'deleted',
|
name: 'deleted',
|
||||||
type: 1,
|
type: 1,
|
||||||
|
flags: 0),
|
||||||
|
ModelProperty(
|
||||||
|
id: const IdUid(10, 2505303363495348118),
|
||||||
|
name: 'glucoseTrend',
|
||||||
|
type: 8,
|
||||||
flags: 0)
|
flags: 0)
|
||||||
],
|
],
|
||||||
relations: <ModelRelation>[],
|
relations: <ModelRelation>[],
|
||||||
@ -237,7 +242,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(7, 4303325892753185970),
|
id: const IdUid(7, 4303325892753185970),
|
||||||
name: 'LogEvent',
|
name: 'LogEvent',
|
||||||
lastPropertyId: const IdUid(12, 3041952167628926163),
|
lastPropertyId: const IdUid(12, 3041952167628926163),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 6648501734758557663),
|
id: const IdUid(1, 6648501734758557663),
|
||||||
@ -302,7 +307,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(8, 8362795406595606110),
|
id: const IdUid(8, 8362795406595606110),
|
||||||
name: 'LogEventType',
|
name: 'LogEventType',
|
||||||
lastPropertyId: const IdUid(8, 1869014400856897151),
|
lastPropertyId: const IdUid(8, 1869014400856897151),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 1430413826199774000),
|
id: const IdUid(1, 1430413826199774000),
|
||||||
@ -355,7 +360,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(9, 411177866700467286),
|
id: const IdUid(9, 411177866700467286),
|
||||||
name: 'LogMeal',
|
name: 'LogMeal',
|
||||||
lastPropertyId: const IdUid(17, 7341439841011629937),
|
lastPropertyId: const IdUid(17, 7341439841011629937),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 962999525294133158),
|
id: const IdUid(1, 962999525294133158),
|
||||||
@ -453,7 +458,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(10, 382130101578692012),
|
id: const IdUid(10, 382130101578692012),
|
||||||
name: 'Meal',
|
name: 'Meal',
|
||||||
lastPropertyId: const IdUid(15, 8283810711091063880),
|
lastPropertyId: const IdUid(15, 8283810711091063880),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 612386612600420389),
|
id: const IdUid(1, 612386612600420389),
|
||||||
@ -542,7 +547,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(11, 3158200688796904913),
|
id: const IdUid(11, 3158200688796904913),
|
||||||
name: 'MealCategory',
|
name: 'MealCategory',
|
||||||
lastPropertyId: const IdUid(4, 824435977543069541),
|
lastPropertyId: const IdUid(4, 824435977543069541),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 3678943122076184840),
|
id: const IdUid(1, 3678943122076184840),
|
||||||
@ -571,7 +576,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(12, 2111511899235985637),
|
id: const IdUid(12, 2111511899235985637),
|
||||||
name: 'MealPortionType',
|
name: 'MealPortionType',
|
||||||
lastPropertyId: const IdUid(4, 5680236937391945907),
|
lastPropertyId: const IdUid(4, 5680236937391945907),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 65428405312238271),
|
id: const IdUid(1, 65428405312238271),
|
||||||
@ -600,7 +605,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(13, 1283034494527412242),
|
id: const IdUid(13, 1283034494527412242),
|
||||||
name: 'MealSource',
|
name: 'MealSource',
|
||||||
lastPropertyId: const IdUid(8, 4547899751779962180),
|
lastPropertyId: const IdUid(8, 4547899751779962180),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 7205380295259922130),
|
id: const IdUid(1, 7205380295259922130),
|
||||||
@ -657,7 +662,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(14, 8033487006694871160),
|
id: const IdUid(14, 8033487006694871160),
|
||||||
name: 'LogBolus',
|
name: 'LogBolus',
|
||||||
lastPropertyId: const IdUid(18, 7503231998671134983),
|
lastPropertyId: const IdUid(18, 7503231998671134983),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 8254237730262024662),
|
id: const IdUid(1, 8254237730262024662),
|
||||||
@ -752,7 +757,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(15, 291512798403320400),
|
id: const IdUid(15, 291512798403320400),
|
||||||
name: 'Accuracy',
|
name: 'Accuracy',
|
||||||
lastPropertyId: const IdUid(7, 6675647182186603076),
|
lastPropertyId: const IdUid(7, 6675647182186603076),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 8405388350474524599),
|
id: const IdUid(1, 8405388350474524599),
|
||||||
@ -795,8 +800,8 @@ final _entities = <ModelEntity>[
|
|||||||
ModelEntity(
|
ModelEntity(
|
||||||
id: const IdUid(16, 3989341091218179227),
|
id: const IdUid(16, 3989341091218179227),
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
lastPropertyId: const IdUid(22, 3595473653451456068),
|
lastPropertyId: const IdUid(23, 3611447442844013652),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 7803753645747063723),
|
id: const IdUid(1, 7803753645747063723),
|
||||||
@ -862,6 +867,11 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(22, 3595473653451456068),
|
id: const IdUid(22, 3595473653451456068),
|
||||||
name: 'targetGlucoseMmolPerL',
|
name: 'targetGlucoseMmolPerL',
|
||||||
type: 8,
|
type: 8,
|
||||||
|
flags: 0),
|
||||||
|
ModelProperty(
|
||||||
|
id: const IdUid(23, 3611447442844013652),
|
||||||
|
name: 'useDarkTheme',
|
||||||
|
type: 1,
|
||||||
flags: 0)
|
flags: 0)
|
||||||
],
|
],
|
||||||
relations: <ModelRelation>[],
|
relations: <ModelRelation>[],
|
||||||
@ -870,7 +880,7 @@ final _entities = <ModelEntity>[
|
|||||||
id: const IdUid(17, 5041265995704044399),
|
id: const IdUid(17, 5041265995704044399),
|
||||||
name: 'GlucoseTarget',
|
name: 'GlucoseTarget',
|
||||||
lastPropertyId: const IdUid(7, 1333487551279074696),
|
lastPropertyId: const IdUid(7, 1333487551279074696),
|
||||||
flags: 0,
|
flags: 2,
|
||||||
properties: <ModelProperty>[
|
properties: <ModelProperty>[
|
||||||
ModelProperty(
|
ModelProperty(
|
||||||
id: const IdUid(1, 4322960567133959537),
|
id: const IdUid(1, 4322960567133959537),
|
||||||
@ -1143,13 +1153,14 @@ ModelDefinition getObjectBoxModel() {
|
|||||||
objectToFB: (LogEntry object, fb.Builder fbb) {
|
objectToFB: (LogEntry object, fb.Builder fbb) {
|
||||||
final notesOffset =
|
final notesOffset =
|
||||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||||
fbb.startTable(10);
|
fbb.startTable(11);
|
||||||
fbb.addInt64(0, object.id);
|
fbb.addInt64(0, object.id);
|
||||||
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
|
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
|
||||||
fbb.addInt64(2, object.mgPerDl);
|
fbb.addInt64(2, object.mgPerDl);
|
||||||
fbb.addFloat64(3, object.mmolPerL);
|
fbb.addFloat64(3, object.mmolPerL);
|
||||||
fbb.addOffset(7, notesOffset);
|
fbb.addOffset(7, notesOffset);
|
||||||
fbb.addBool(8, object.deleted);
|
fbb.addBool(8, object.deleted);
|
||||||
|
fbb.addFloat64(9, object.glucoseTrend);
|
||||||
fbb.finish(fbb.endTable());
|
fbb.finish(fbb.endTable());
|
||||||
return object.id;
|
return object.id;
|
||||||
},
|
},
|
||||||
@ -1167,6 +1178,8 @@ ModelDefinition getObjectBoxModel() {
|
|||||||
.vTableGetNullable(buffer, rootOffset, 8),
|
.vTableGetNullable(buffer, rootOffset, 8),
|
||||||
mmolPerL: const fb.Float64Reader()
|
mmolPerL: const fb.Float64Reader()
|
||||||
.vTableGetNullable(buffer, rootOffset, 10),
|
.vTableGetNullable(buffer, rootOffset, 10),
|
||||||
|
glucoseTrend: const fb.Float64Reader()
|
||||||
|
.vTableGetNullable(buffer, rootOffset, 22),
|
||||||
notes: const fb.StringReader()
|
notes: const fb.StringReader()
|
||||||
.vTableGetNullable(buffer, rootOffset, 18));
|
.vTableGetNullable(buffer, rootOffset, 18));
|
||||||
|
|
||||||
@ -1694,7 +1707,7 @@ ModelDefinition getObjectBoxModel() {
|
|||||||
final longTimeFormatOffset = object.longTimeFormat == null
|
final longTimeFormatOffset = object.longTimeFormat == null
|
||||||
? null
|
? null
|
||||||
: fbb.writeString(object.longTimeFormat!);
|
: fbb.writeString(object.longTimeFormat!);
|
||||||
fbb.startTable(23);
|
fbb.startTable(24);
|
||||||
fbb.addInt64(0, object.id);
|
fbb.addInt64(0, object.id);
|
||||||
fbb.addOffset(1, dateFormatOffset);
|
fbb.addOffset(1, dateFormatOffset);
|
||||||
fbb.addOffset(2, longDateFormatOffset);
|
fbb.addOffset(2, longDateFormatOffset);
|
||||||
@ -1708,6 +1721,7 @@ ModelDefinition getObjectBoxModel() {
|
|||||||
fbb.addInt64(19, object.glucoseMeasurementIndex);
|
fbb.addInt64(19, object.glucoseMeasurementIndex);
|
||||||
fbb.addInt64(20, object.targetGlucoseMgPerDl);
|
fbb.addInt64(20, object.targetGlucoseMgPerDl);
|
||||||
fbb.addFloat64(21, object.targetGlucoseMmolPerL);
|
fbb.addFloat64(21, object.targetGlucoseMmolPerL);
|
||||||
|
fbb.addBool(22, object.useDarkTheme);
|
||||||
fbb.finish(fbb.endTable());
|
fbb.finish(fbb.endTable());
|
||||||
return object.id;
|
return object.id;
|
||||||
},
|
},
|
||||||
@ -1740,7 +1754,8 @@ ModelDefinition getObjectBoxModel() {
|
|||||||
targetGlucoseMgPerDl:
|
targetGlucoseMgPerDl:
|
||||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 44, 0),
|
const fb.Int64Reader().vTableGet(buffer, rootOffset, 44, 0),
|
||||||
targetGlucoseMmolPerL:
|
targetGlucoseMmolPerL:
|
||||||
const fb.Float64Reader().vTableGet(buffer, rootOffset, 46, 0));
|
const fb.Float64Reader().vTableGet(buffer, rootOffset, 46, 0),
|
||||||
|
useDarkTheme: const fb.BoolReader().vTableGet(buffer, rootOffset, 48, false));
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
}),
|
}),
|
||||||
@ -1921,6 +1936,10 @@ class LogEntry_ {
|
|||||||
/// see [LogEntry.deleted]
|
/// see [LogEntry.deleted]
|
||||||
static final deleted =
|
static final deleted =
|
||||||
QueryBooleanProperty<LogEntry>(_entities[4].properties[5]);
|
QueryBooleanProperty<LogEntry>(_entities[4].properties[5]);
|
||||||
|
|
||||||
|
/// see [LogEntry.glucoseTrend]
|
||||||
|
static final glucoseTrend =
|
||||||
|
QueryDoubleProperty<LogEntry>(_entities[4].properties[6]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [LogEvent] entity fields to define ObjectBox queries.
|
/// [LogEvent] entity fields to define ObjectBox queries.
|
||||||
@ -2337,6 +2356,10 @@ class Settings_ {
|
|||||||
/// see [Settings.targetGlucoseMmolPerL]
|
/// see [Settings.targetGlucoseMmolPerL]
|
||||||
static final targetGlucoseMmolPerL =
|
static final targetGlucoseMmolPerL =
|
||||||
QueryDoubleProperty<Settings>(_entities[14].properties[12]);
|
QueryDoubleProperty<Settings>(_entities[14].properties[12]);
|
||||||
|
|
||||||
|
/// see [Settings.useDarkTheme]
|
||||||
|
static final useDarkTheme =
|
||||||
|
QueryBooleanProperty<Settings>(_entities[14].properties[13]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [GlucoseTarget] entity fields to define ObjectBox queries.
|
/// [GlucoseTarget] entity fields to define ObjectBox queries.
|
||||||
|
@ -22,12 +22,13 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _accuracyForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _accuracyForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _confidenceRatingController = TextEditingController(text: '');
|
final _confidenceRatingController = TextEditingController(text: '');
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
bool _forCarbsRatio = false;
|
bool _forCarbsRatio = true;
|
||||||
bool _forPortionSize = false;
|
bool _forPortionSize = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -43,13 +44,25 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_accuracy = Accuracy.get(widget.id);
|
_accuracy = Accuracy.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _accuracy == null;
|
_isNew = _accuracy == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
@ -67,7 +80,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
Accuracy.box.put(accuracy);
|
Accuracy.box.put(accuracy);
|
||||||
Accuracy.reorder(
|
Accuracy.reorder(
|
||||||
accuracy, int.tryParse(_confidenceRatingController.text));
|
accuracy, int.tryParse(_confidenceRatingController.text));
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Accuracy saved');
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Accuracy saved', accuracy]);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = false;
|
_isSaving = false;
|
||||||
@ -77,8 +90,8 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
void handleCancelAction() {
|
void handleCancelAction() {
|
||||||
if (Settings.get().showConfirmationDialogOnCancel &&
|
if (Settings.get().showConfirmationDialogOnCancel &&
|
||||||
(_isNew &&
|
(_isNew &&
|
||||||
(_forCarbsRatio ||
|
(!_forCarbsRatio ||
|
||||||
_forPortionSize ||
|
!_forPortionSize ||
|
||||||
_valueController.text != '' ||
|
_valueController.text != '' ||
|
||||||
int.tryParse(_confidenceRatingController.text) != null ||
|
int.tryParse(_confidenceRatingController.text) != null ||
|
||||||
_notesController.text != '')) ||
|
_notesController.text != '')) ||
|
||||||
@ -106,7 +119,10 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
title: Text(_isNew ? 'New Accuracy' : _accuracy!.value),
|
title: Text(_isNew ? 'New Accuracy' : _accuracy!.value),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: AccuracyDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: AccuracyDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -155,14 +171,16 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: _isSaving ? null : handleSaveAction,
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
|
@ -16,6 +16,8 @@ class AccuracyListScreen extends StatefulWidget {
|
|||||||
class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
||||||
List<Accuracy> _accuracies = Accuracy.getAll();
|
List<Accuracy> _accuracies = Accuracy.getAll();
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -84,8 +86,11 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _accuracies.isNotEmpty
|
child: _accuracies.isNotEmpty
|
||||||
? ReorderableListView.builder(
|
? Scrollbar(
|
||||||
padding: const EdgeInsets.only(top: 10.0),
|
controller: _scrollController,
|
||||||
|
child: ReorderableListView.builder(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
scrollController: _scrollController,
|
||||||
itemCount: _accuracies.length,
|
itemCount: _accuracies.length,
|
||||||
onReorder: (oldIndex, newIndex) {
|
onReorder: (oldIndex, newIndex) {
|
||||||
Accuracy.reorder(_accuracies[oldIndex], newIndex);
|
Accuracy.reorder(_accuracies[oldIndex], newIndex);
|
||||||
@ -93,8 +98,9 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
|||||||
},
|
},
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final accuracy = _accuracies[index];
|
final accuracy = _accuracies[index];
|
||||||
return ListTile(
|
return Card(
|
||||||
key: Key(index.toString()),
|
key: Key(accuracy.id.toString()),
|
||||||
|
child: ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
@ -102,9 +108,12 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
|||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
AccuracyDetailScreen(id: accuracy.id),
|
AccuracyDetailScreen(id: accuracy.id),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
title: Text(accuracy.value),
|
title: Text(
|
||||||
|
accuracy.value.toUpperCase(),
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
leading: Row(
|
leading: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: const [
|
children: const [
|
||||||
@ -138,7 +147,6 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
|||||||
handleToggleForCarbsRatioAction(
|
handleToggleForCarbsRatioAction(
|
||||||
accuracy),
|
accuracy),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 24),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
@ -146,8 +154,10 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
: const Center(
|
: const Center(
|
||||||
child: Text('You have not created any Accuracies yet!'),
|
child: Text('You have not created any Accuracies yet!'),
|
||||||
),
|
),
|
||||||
@ -161,7 +171,7 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const AccuracyDetailScreen(),
|
builder: (context) => const AccuracyDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -34,6 +34,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
|||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _basalForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _basalForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0);
|
TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0);
|
||||||
TimeOfDay _endTime = const TimeOfDay(hour: 0, minute: 0);
|
TimeOfDay _endTime = const TimeOfDay(hour: 0, minute: 0);
|
||||||
@ -64,13 +65,25 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
|||||||
updateEndTime();
|
updateEndTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_basal = Basal.get(widget.id);
|
_basal = Basal.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _basal == null;
|
_isNew = _basal == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateStartTime() {
|
void updateStartTime() {
|
||||||
@ -141,7 +154,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
|||||||
);
|
);
|
||||||
basal.basalProfile.targetId = widget.basalProfileId;
|
basal.basalProfile.targetId = widget.basalProfileId;
|
||||||
Basal.put(basal);
|
Basal.put(basal);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Basal Rate saved');
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Basal Rate saved', basal]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -183,7 +196,11 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
|||||||
'${_isNew ? 'New' : 'Edit'} Basal Rate for ${BasalProfile.get(widget.basalProfileId)?.name}'),
|
'${_isNew ? 'New' : 'Edit'} Basal Rate for ${BasalProfile.get(widget.basalProfileId)?.name}'),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: BasalDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: BasalDetailScreen.routeName),
|
||||||
body: Column(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
FormWrapper(
|
FormWrapper(
|
||||||
formState: _basalForm,
|
formState: _basalForm,
|
||||||
@ -247,6 +264,8 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: _isSaving ? null : handleSaveAction,
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
|
@ -23,6 +23,8 @@ class BasalListScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _BasalListScreenState extends State<BasalListScreen> {
|
class _BasalListScreenState extends State<BasalListScreen> {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
widget.reload();
|
widget.reload();
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
|
|||||||
id: basal.id,
|
id: basal.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDelete(Basal basal) {
|
void onDelete(Basal basal) {
|
||||||
@ -108,14 +110,33 @@ class _BasalListScreenState extends State<BasalListScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return widget.basalRates.isNotEmpty ? ListView.builder(
|
return widget.basalRates.isNotEmpty ? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
controller: _scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: widget.basalRates.length,
|
itemCount: widget.basalRates.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final basal = widget.basalRates[index];
|
final basal = widget.basalRates[index];
|
||||||
final error = validateTimePeriod(index);
|
final error = validateTimePeriod(index);
|
||||||
return ListTile(
|
return Card(
|
||||||
tileColor: error != null ? Colors.red.shade100 : null,
|
child: Column(
|
||||||
|
children: [
|
||||||
|
error != null
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.all(5.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.warning, color: Theme.of(context).errorColor),
|
||||||
|
Text(
|
||||||
|
error, style: TextStyle(color: Theme.of(context).errorColor)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
) : Container(),
|
||||||
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
handleEditAction(basal);
|
handleEditAction(basal);
|
||||||
},
|
},
|
||||||
@ -129,9 +150,6 @@ class _BasalListScreenState extends State<BasalListScreen> {
|
|||||||
Expanded(child: Text('${basal.units} U')),
|
Expanded(child: Text('${basal.units} U')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
subtitle: error != null
|
|
||||||
? Text(error, style: const TextStyle(color: Colors.red))
|
|
||||||
: Container(),
|
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -144,8 +162,12 @@ class _BasalListScreenState extends State<BasalListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
) : const Center(
|
) : const Center(
|
||||||
child: Text('You have not created any Basal Rates yet!'),
|
child: Text('You have not created any Basal Rates yet!'),
|
||||||
);
|
);
|
||||||
|
@ -29,6 +29,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
|||||||
bool _isNew = true;
|
bool _isNew = true;
|
||||||
|
|
||||||
final GlobalKey<FormState> _basalProfileForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _basalProfileForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
late FloatingActionButton addBasalButton;
|
late FloatingActionButton addBasalButton;
|
||||||
late IconButton refreshButton;
|
late IconButton refreshButton;
|
||||||
@ -214,7 +215,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
@ -223,13 +224,14 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
|||||||
});
|
});
|
||||||
if (_basalProfileForm.currentState!.validate()) {
|
if (_basalProfileForm.currentState!.validate()) {
|
||||||
await checkActiveProfiles();
|
await checkActiveProfiles();
|
||||||
BasalProfile.put(BasalProfile(
|
BasalProfile basalProfile = BasalProfile(
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
name: _nameController.text,
|
name: _nameController.text,
|
||||||
active: _active,
|
active: _active,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
));
|
);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Basal Profile saved');
|
BasalProfile.put(basalProfile);
|
||||||
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Basal Profile saved', basalProfile]);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
bottomNav = detailBottomRow;
|
bottomNav = detailBottomRow;
|
||||||
@ -284,7 +286,10 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
|||||||
renderTabButtons(tabController.index);
|
renderTabButtons(tabController.index);
|
||||||
});
|
});
|
||||||
List<Widget> tabs = [
|
List<Widget> tabs = [
|
||||||
SingleChildScrollView(
|
Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -308,9 +313,9 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
|||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
suffixText: '',
|
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
BooleanFormField(
|
BooleanFormField(
|
||||||
value: _active,
|
value: _active,
|
||||||
@ -326,10 +331,14 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!_isNew) {
|
if (!_isNew) {
|
||||||
tabs.add(BasalListScreen(basalProfile: _basalProfile!, basalRates: _basalRates, reload: reload));
|
tabs.add(BasalListScreen(
|
||||||
|
basalProfile: _basalProfile!,
|
||||||
|
basalRates: _basalRates,
|
||||||
|
reload: reload));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -16,15 +16,15 @@ class BasalProfileListScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
late List<BasalProfile> _basalProfiles;
|
late List<BasalProfile> _basalProfiles;
|
||||||
Widget banner = Container();
|
Widget banner = Container();
|
||||||
bool pickActiveProfileMode = false;
|
|
||||||
|
|
||||||
final BasalProfile? _activeProfile = BasalProfile.getActive(DateTime.now());
|
final BasalProfile? _activeProfile = BasalProfile.getActive(DateTime.now());
|
||||||
|
|
||||||
void refresh({String? message}) {
|
void refresh({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
pickActiveProfileMode = false;
|
|
||||||
_basalProfiles = BasalProfile.getAll();
|
_basalProfiles = BasalProfile.getAll();
|
||||||
});
|
});
|
||||||
updateBanner();
|
updateBanner();
|
||||||
@ -106,6 +106,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
banner = MaterialBanner(
|
banner = MaterialBanner(
|
||||||
content: AutoCompleteDropdownButton(
|
content: AutoCompleteDropdownButton(
|
||||||
|
controller: TextEditingController(text: ''),
|
||||||
items: _basalProfiles,
|
items: _basalProfiles,
|
||||||
label: 'Default Basal Profile',
|
label: 'Default Basal Profile',
|
||||||
onChanged: onPickActive,
|
onChanged: onPickActive,
|
||||||
@ -119,7 +120,6 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
pickActiveProfileMode = true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
BasalProfileDetailScreen(id: basalProfile?.id ?? 0, active: active),
|
BasalProfileDetailScreen(id: basalProfile?.id ?? 0, active: active),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((result) => refresh(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNew(bool active) {
|
void onNew(bool active) {
|
||||||
@ -164,30 +164,38 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
banner,
|
banner,
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _basalProfiles.isNotEmpty
|
child: _basalProfiles.isNotEmpty
|
||||||
? ListView.builder(
|
? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
controller: _scrollController,
|
||||||
itemCount: _basalProfiles.length,
|
itemCount: _basalProfiles.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final basalProfile = _basalProfiles[index];
|
final basalProfile = _basalProfiles[index];
|
||||||
double dailyTotal =
|
double dailyTotal =
|
||||||
Basal.getDailyTotalForProfile(basalProfile.id);
|
Basal.getDailyTotalForProfile(basalProfile.id);
|
||||||
String activeProfileText = basalProfile.active
|
String activeProfileText = basalProfile.active
|
||||||
? 'Default Profile'
|
? ' (Default Profile)'
|
||||||
: basalProfile.id == _activeProfile?.id
|
: basalProfile.id == _activeProfile?.id
|
||||||
? 'Current Active Profile'
|
? ' (Current Active Profile)'
|
||||||
: '';
|
: '';
|
||||||
if (activeProfileText != '' &&
|
return Card(
|
||||||
(basalProfile.notes ?? '') != '') {
|
child: ListTile(
|
||||||
activeProfileText += '\n';
|
isThreeLine: true,
|
||||||
}
|
|
||||||
return ListTile(
|
|
||||||
selected: basalProfile.active ||
|
selected: basalProfile.active ||
|
||||||
basalProfile.id == _activeProfile?.id,
|
basalProfile.id == _activeProfile?.id,
|
||||||
onTap: () => onEdit(basalProfile),
|
onTap: () => onEdit(basalProfile),
|
||||||
title: Text(basalProfile.name),
|
title: Text(
|
||||||
subtitle: Row(
|
basalProfile.name.toUpperCase() + activeProfileText,
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
subtitle: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 10.0),
|
||||||
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'$activeProfileText${basalProfile.notes ?? ''}'),
|
basalProfile.notes ?? ''
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: dailyTotal > 0
|
children: dailyTotal > 0
|
||||||
@ -200,6 +208,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -212,8 +221,10 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: const Center(
|
: const Center(
|
||||||
child: Text('You have not created any Basal Profiles yet!'),
|
child: Text('You have not created any Basal Profiles yet!'),
|
||||||
|
@ -35,6 +35,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _bolusForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _bolusForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0);
|
TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0);
|
||||||
TimeOfDay _endTime = const TimeOfDay(hour: 0, minute: 0);
|
TimeOfDay _endTime = const TimeOfDay(hour: 0, minute: 0);
|
||||||
@ -72,13 +73,25 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
updateEndTime();
|
updateEndTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_bolus = Bolus.get(widget.id);
|
_bolus = Bolus.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _bolus == null;
|
_isNew = _bolus == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateStartTime() {
|
void updateStartTime() {
|
||||||
@ -154,7 +167,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
);
|
);
|
||||||
bolus.bolusProfile.targetId = widget.bolusProfileId;
|
bolus.bolusProfile.targetId = widget.bolusProfileId;
|
||||||
Bolus.put(bolus);
|
Bolus.put(bolus);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Bolus Rate saved');
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Bolus Rate saved', bolus]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -232,7 +245,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
'${_isNew ? 'New' : 'Edit'} Bolus Rate for ${BolusProfile.get(widget.bolusProfileId)?.name}'),
|
'${_isNew ? 'New' : 'Edit'} Bolus Rate for ${BolusProfile.get(widget.bolusProfileId)?.name}'),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: BolusDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: BolusDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
FormWrapper(
|
FormWrapper(
|
||||||
@ -390,6 +406,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: _isSaving ? null : handleSaveAction,
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
|
@ -20,6 +20,8 @@ class BolusListScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _BolusListScreenState extends State<BolusListScreen> {
|
class _BolusListScreenState extends State<BolusListScreen> {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
widget.reload();
|
widget.reload();
|
||||||
|
|
||||||
@ -45,7 +47,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
id: bolus.id,
|
id: bolus.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDelete(Bolus bolus) {
|
void onDelete(Bolus bolus) {
|
||||||
@ -104,28 +106,39 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return widget.bolusRates.isNotEmpty ? ListView.builder(
|
return widget.bolusRates.isNotEmpty ? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
controller: _scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: widget.bolusRates.length,
|
itemCount: widget.bolusRates.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final bolus = widget.bolusRates[index];
|
final bolus = widget.bolusRates[index];
|
||||||
final error = validateTimePeriod(index);
|
final error = validateTimePeriod(index);
|
||||||
return ListTile(
|
return Card(
|
||||||
isThreeLine: true,
|
child: Column(
|
||||||
tileColor: error != null ? Colors.red.shade100 : null,
|
children: [
|
||||||
|
error != null
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.all(5.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.warning, color: Theme.of(context).errorColor),
|
||||||
|
Text(
|
||||||
|
error, style: TextStyle(color: Theme.of(context).errorColor)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
) : Container(),
|
||||||
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
handleEditAction(bolus);
|
handleEditAction(bolus);
|
||||||
},
|
},
|
||||||
title: Text(
|
isThreeLine: true,
|
||||||
'${DateTimeUtils.displayTime(bolus.startTime)} - ${DateTimeUtils.displayTime(bolus.endTime)}'),
|
title: Text('${DateTimeUtils.displayTime(bolus.startTime)} - ${DateTimeUtils.displayTime(bolus.endTime)}'),
|
||||||
subtitle: Column(
|
subtitle: Row(
|
||||||
children: [
|
|
||||||
error != null
|
|
||||||
? Text(error,
|
|
||||||
style: const TextStyle(color: Colors.red))
|
|
||||||
: const Text(''),
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -133,8 +146,8 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
children: (bolus.units > 0 && bolus.carbs > 0)
|
children: (bolus.units > 0 && bolus.carbs > 0)
|
||||||
? [
|
? [
|
||||||
Text((bolus.carbs / bolus.units).toStringAsPrecision(2)),
|
Text((bolus.carbs / bolus.units).toStringAsPrecision(2)),
|
||||||
const Text('carbs per U',
|
Text('${Settings.nutritionMeasurementSuffix} carbs per U',
|
||||||
textScaleFactor: 0.75),
|
textAlign: TextAlign.center, textScaleFactor: 0.75),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
@ -145,7 +158,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
? [
|
? [
|
||||||
Text((bolus.units / bolus.carbs * 12).toStringAsPrecision(2)),
|
Text((bolus.units / bolus.carbs * 12).toStringAsPrecision(2)),
|
||||||
const Text('U per bread unit',
|
const Text('U per bread unit',
|
||||||
textScaleFactor: 0.75),
|
textAlign: TextAlign.center, textScaleFactor: 0.75),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
@ -156,12 +169,11 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
? [
|
? [
|
||||||
Text((((Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL)! / bolus.units)).toString()),
|
Text((((Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL)! / bolus.units)).toString()),
|
||||||
Text('${Settings.glucoseMeasurementSuffix} per unit',
|
Text('${Settings.glucoseMeasurementSuffix} per unit',
|
||||||
textScaleFactor: 0.75),
|
textAlign: TextAlign.center, textScaleFactor: 0.75),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
@ -176,8 +188,12 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
) : const Center(
|
) : const Center(
|
||||||
child: Text('You have not created any Bolus Rates yet!'),
|
child: Text('You have not created any Bolus Rates yet!'),
|
||||||
);
|
);
|
||||||
|
@ -28,6 +28,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
|||||||
bool _isNew = true;
|
bool _isNew = true;
|
||||||
|
|
||||||
final GlobalKey<FormState> _bolusProfileForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _bolusProfileForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
late FloatingActionButton addBolusButton;
|
late FloatingActionButton addBolusButton;
|
||||||
late IconButton refreshButton;
|
late IconButton refreshButton;
|
||||||
@ -211,7 +212,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
@ -221,15 +222,15 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
|||||||
|
|
||||||
if (_bolusProfileForm.currentState!.validate()) {
|
if (_bolusProfileForm.currentState!.validate()) {
|
||||||
await checkActiveProfiles();
|
await checkActiveProfiles();
|
||||||
BolusProfile.put(
|
BolusProfile bolusProfile = BolusProfile(
|
||||||
BolusProfile(
|
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
name: _nameController.text,
|
name: _nameController.text,
|
||||||
active: _active,
|
active: _active,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Bolus Profile saved');
|
BolusProfile.put(bolusProfile);
|
||||||
|
Navigator.pop(context,
|
||||||
|
['${_isNew ? 'New' : ''} Bolus Profile saved', bolusProfile]);
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -286,7 +287,10 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
List<Widget> tabs = [
|
List<Widget> tabs = [
|
||||||
SingleChildScrollView(
|
Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -308,10 +312,11 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
|||||||
TextFormField(
|
TextFormField(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
BooleanFormField(
|
BooleanFormField(
|
||||||
value: _active,
|
value: _active,
|
||||||
@ -327,6 +332,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!_isNew) {
|
if (!_isNew) {
|
||||||
|
@ -15,15 +15,15 @@ class BolusProfileListScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
List<BolusProfile> _bolusProfiles = [];
|
List<BolusProfile> _bolusProfiles = [];
|
||||||
Widget banner = Container();
|
Widget banner = Container();
|
||||||
bool pickActiveProfileMode = false;
|
|
||||||
|
|
||||||
final BolusProfile? _activeProfile = BolusProfile.getActive(DateTime.now());
|
final BolusProfile? _activeProfile = BolusProfile.getActive(DateTime.now());
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
pickActiveProfileMode = false;
|
|
||||||
_bolusProfiles = BolusProfile.getAll();
|
_bolusProfiles = BolusProfile.getAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -99,7 +99,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
BolusProfile.setAllInactive;
|
BolusProfile.setAllInactive;
|
||||||
bolusProfile.active = true;
|
bolusProfile.active = true;
|
||||||
BolusProfile.put(bolusProfile);
|
BolusProfile.put(bolusProfile);
|
||||||
reload(message: '${bolusProfile.name} has been set as your active Profile');
|
reload(
|
||||||
|
message: '${bolusProfile.name} has been set as your active Profile');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +108,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
banner = MaterialBanner(
|
banner = MaterialBanner(
|
||||||
content: AutoCompleteDropdownButton(
|
content: AutoCompleteDropdownButton(
|
||||||
|
controller: TextEditingController(text: ''),
|
||||||
items: _bolusProfiles,
|
items: _bolusProfiles,
|
||||||
label: 'Default Basal Profile',
|
label: 'Default Basal Profile',
|
||||||
onChanged: onPickActive,
|
onChanged: onPickActive,
|
||||||
@ -120,7 +122,6 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
pickActiveProfileMode = true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +132,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
BolusProfileDetailScreen(id: bolusProfile?.id ?? 0, active: active),
|
BolusProfileDetailScreen(id: bolusProfile?.id ?? 0, active: active),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNew(bool active) {
|
void onNew(bool active) {
|
||||||
@ -165,26 +166,33 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
banner,
|
banner,
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _bolusProfiles.isNotEmpty
|
child: _bolusProfiles.isNotEmpty
|
||||||
? ListView.builder(
|
? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
controller: _scrollController,
|
||||||
itemCount: _bolusProfiles.length,
|
itemCount: _bolusProfiles.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final bolusProfile = _bolusProfiles[index];
|
final bolusProfile = _bolusProfiles[index];
|
||||||
String activeProfileText = bolusProfile.active
|
String activeProfileText = bolusProfile.active
|
||||||
? 'Default Profile'
|
? ' (Default Profile)'
|
||||||
: bolusProfile.id == _activeProfile?.id
|
: bolusProfile.id == _activeProfile?.id
|
||||||
? 'Current Active Profile'
|
? ' (Current Active Profile)'
|
||||||
: '';
|
: '';
|
||||||
if (activeProfileText != '' && (bolusProfile.notes ?? '') != '') {
|
return Card(
|
||||||
activeProfileText += '\n';
|
child: ListTile(
|
||||||
}
|
selected: bolusProfile.active ||
|
||||||
return ListTile(
|
bolusProfile.id == _activeProfile?.id,
|
||||||
selected: bolusProfile.active || bolusProfile.id == _activeProfile?.id,
|
|
||||||
onTap: () => onEdit(bolusProfile),
|
onTap: () => onEdit(bolusProfile),
|
||||||
title: Text(
|
title: Text(
|
||||||
bolusProfile.name,
|
bolusProfile.name.toUpperCase() +
|
||||||
|
activeProfileText,
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
subtitle: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 10.0),
|
||||||
|
child: Text(bolusProfile.notes ?? ''),
|
||||||
),
|
),
|
||||||
isThreeLine: activeProfileText != '' && (bolusProfile.notes ?? '') != '',
|
|
||||||
subtitle: Text('$activeProfileText${bolusProfile.notes ?? ''}'),
|
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -193,12 +201,15 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
Icons.delete,
|
Icons.delete,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
),
|
),
|
||||||
onPressed: () => handleDeleteAction(bolusProfile),
|
onPressed: () =>
|
||||||
|
handleDeleteAction(bolusProfile),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: const Center(
|
: const Center(
|
||||||
child: Text('You have not created any Bolus Profiles yet!'),
|
child: Text('You have not created any Bolus Profiles yet!'),
|
||||||
|
@ -8,6 +8,7 @@ import 'package:diameter/navigation.dart';
|
|||||||
import 'package:diameter/screens/log/log_entry/log_entry.dart';
|
import 'package:diameter/screens/log/log_entry/log_entry.dart';
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
class LogScreen extends StatefulWidget {
|
class LogScreen extends StatefulWidget {
|
||||||
static const String routeName = '/log';
|
static const String routeName = '/log';
|
||||||
@ -20,6 +21,8 @@ class LogScreen extends StatefulWidget {
|
|||||||
class _LogScreenState extends State<LogScreen> {
|
class _LogScreenState extends State<LogScreen> {
|
||||||
late Map<DateTime, List<LogEntry>> _logEntryDailyMap;
|
late Map<DateTime, List<LogEntry>> _logEntryDailyMap;
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -74,12 +77,16 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _logEntryDailyMap.isNotEmpty
|
child: _logEntryDailyMap.isNotEmpty
|
||||||
? ListView.builder(
|
? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: _logEntryDailyMap.length,
|
itemCount: _logEntryDailyMap.length,
|
||||||
itemBuilder: (context, dateIndex) {
|
itemBuilder: (context, dateIndex) {
|
||||||
List<DateTime> dateList = _logEntryDailyMap.keys.toList();
|
List<DateTime> dateList =
|
||||||
|
_logEntryDailyMap.keys.toList();
|
||||||
final date = dateList[dateIndex];
|
final date = dateList[dateIndex];
|
||||||
final entryList = _logEntryDailyMap[date];
|
final entryList = _logEntryDailyMap[date];
|
||||||
final tiles = <Widget>[];
|
final tiles = <Widget>[];
|
||||||
@ -92,7 +99,9 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
color: GlucoseTarget.getColorForGlucose(
|
color: GlucoseTarget.getColorForGlucose(
|
||||||
mgPerDl: logEntry.mgPerDl ?? 0,
|
mgPerDl: logEntry.mgPerDl ?? 0,
|
||||||
mmolPerL: logEntry.mmolPerL ?? 0));
|
mmolPerL: logEntry.mmolPerL ?? 0));
|
||||||
tiles.add(ListTile(
|
tiles.add(
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
@ -100,7 +109,7 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
LogEntryScreen(id: logEntry.id),
|
LogEntryScreen(id: logEntry.id),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
title: Row(
|
title: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
@ -115,18 +124,39 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: logEntry.mgPerDl != null &&
|
children: logEntry.mgPerDl != null &&
|
||||||
(Settings.glucoseMeasurement ==
|
(Settings.glucoseMeasurement ==
|
||||||
GlucoseMeasurement.mgPerDl ||
|
GlucoseMeasurement
|
||||||
|
.mgPerDl ||
|
||||||
Settings.glucoseDisplayMode ==
|
Settings.glucoseDisplayMode ==
|
||||||
GlucoseDisplayMode.both ||
|
GlucoseDisplayMode.both ||
|
||||||
Settings.glucoseDisplayMode ==
|
Settings.glucoseDisplayMode ==
|
||||||
GlucoseDisplayMode
|
GlucoseDisplayMode
|
||||||
.bothForList)
|
.bothForList)
|
||||||
? [
|
? [
|
||||||
Text(logEntry.mgPerDl.toString(),
|
Row(
|
||||||
style: glucoseStyle),
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
Text(
|
Text(
|
||||||
|
logEntry.mgPerDl.toString(),
|
||||||
|
style: glucoseStyle),
|
||||||
|
logEntry.glucoseTrend != null
|
||||||
|
? Transform.rotate(
|
||||||
|
angle: logEntry
|
||||||
|
.glucoseTrend! *
|
||||||
|
math.pi /
|
||||||
|
180,
|
||||||
|
child: Icon(
|
||||||
|
Icons.arrow_upward,
|
||||||
|
color: glucoseStyle
|
||||||
|
.color,
|
||||||
|
size: 16.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
'mg/dl',
|
'mg/dl',
|
||||||
style: glucoseStyle,
|
|
||||||
textScaleFactor: 0.75,
|
textScaleFactor: 0.75,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -137,17 +167,40 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: logEntry.mmolPerL != null &&
|
children: logEntry.mmolPerL != null &&
|
||||||
(Settings.glucoseMeasurement ==
|
(Settings.glucoseMeasurement ==
|
||||||
GlucoseMeasurement.mmolPerL ||
|
GlucoseMeasurement
|
||||||
|
.mmolPerL ||
|
||||||
Settings.glucoseDisplayMode ==
|
Settings.glucoseDisplayMode ==
|
||||||
GlucoseDisplayMode.both ||
|
GlucoseDisplayMode.both ||
|
||||||
Settings.glucoseDisplayMode ==
|
Settings.glucoseDisplayMode ==
|
||||||
GlucoseDisplayMode
|
GlucoseDisplayMode
|
||||||
.bothForList)
|
.bothForList)
|
||||||
? [
|
? [
|
||||||
Text(logEntry.mmolPerL.toString(), style: glucoseStyle),
|
Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
Text(
|
Text(
|
||||||
|
logEntry.mmolPerL
|
||||||
|
.toString(),
|
||||||
|
style: glucoseStyle),
|
||||||
|
logEntry.glucoseTrend != null
|
||||||
|
? Transform.rotate(
|
||||||
|
angle: logEntry
|
||||||
|
.glucoseTrend! *
|
||||||
|
math.pi /
|
||||||
|
180,
|
||||||
|
child: Icon(
|
||||||
|
Icons.arrow_upward,
|
||||||
|
color: glucoseStyle
|
||||||
|
.color,
|
||||||
|
size: 16.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
'mmol/l',
|
'mmol/l',
|
||||||
style: glucoseStyle,
|
|
||||||
textScaleFactor: 0.75,
|
textScaleFactor: 0.75,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -189,15 +242,24 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return ListBody(
|
return ListBody(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(DateTimeUtils.displayDate(date))
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
child: Text(
|
||||||
|
DateTimeUtils.displayDate(date).toUpperCase(),
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
)
|
||||||
] +
|
] +
|
||||||
tiles,
|
tiles +
|
||||||
|
[const Divider()]
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: const Center(
|
: const Center(
|
||||||
child: Text('You have not created any Log Entries yet!'),
|
child: Text('You have not created any Log Entries yet!'),
|
||||||
@ -212,7 +274,7 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const LogEntryScreen(),
|
builder: (context) => const LogEntryScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -10,6 +10,7 @@ import 'package:diameter/models/log_entry.dart';
|
|||||||
import 'package:diameter/models/log_meal.dart';
|
import 'package:diameter/models/log_meal.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
|
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
|
||||||
import 'package:diameter/utils/utils.dart';
|
import 'package:diameter/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _logBolusForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _logBolusForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _unitsController = TextEditingController(text: '');
|
final _unitsController = TextEditingController(text: '');
|
||||||
final _carbsController = TextEditingController(text: '');
|
final _carbsController = TextEditingController(text: '');
|
||||||
@ -63,6 +65,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
final _delayedUnitsController = TextEditingController(text: '');
|
final _delayedUnitsController = TextEditingController(text: '');
|
||||||
final _immediateUnitsController = TextEditingController(text: '');
|
final _immediateUnitsController = TextEditingController(text: '');
|
||||||
|
|
||||||
|
final _mealController = TextEditingController(text: '');
|
||||||
|
|
||||||
bool _setManually = false;
|
bool _setManually = false;
|
||||||
BolusType _bolusType = BolusType.meal;
|
BolusType _bolusType = BolusType.meal;
|
||||||
LogMeal? _meal;
|
LogMeal? _meal;
|
||||||
@ -85,6 +89,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
_notesController.text = _logBolus!.notes ?? '';
|
_notesController.text = _logBolus!.notes ?? '';
|
||||||
_setManually = _logBolus!.setManually;
|
_setManually = _logBolus!.setManually;
|
||||||
_meal = _logBolus!.meal.target;
|
_meal = _logBolus!.meal.target;
|
||||||
|
_mealController.text = (_meal ?? '').toString();
|
||||||
_rate = _logBolus!.rate.target;
|
_rate = _logBolus!.rate.target;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +101,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
: 0))
|
: 0))
|
||||||
.toString();
|
.toString();
|
||||||
_mgPerDlTargetController.text =
|
_mgPerDlTargetController.text =
|
||||||
(_logBolus?.mgPerDlTarget ?? Settings.targetMgPerDl()).toString();
|
(_logBolus?.mgPerDlTarget ?? Settings.targetMgPerDl).toString();
|
||||||
_mgPerDlCorrectionController.text = (_logBolus?.mgPerDlCorrection ??
|
_mgPerDlCorrectionController.text = (_logBolus?.mgPerDlCorrection ??
|
||||||
max(
|
max(
|
||||||
(int.tryParse(_mgPerDlCurrentController.text) ?? 0) -
|
(int.tryParse(_mgPerDlCurrentController.text) ?? 0) -
|
||||||
@ -109,7 +114,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
: 0))
|
: 0))
|
||||||
.toString();
|
.toString();
|
||||||
_mmolPerLTargetController.text =
|
_mmolPerLTargetController.text =
|
||||||
(_logBolus?.mmolPerLTarget ?? Settings.targetMmolPerL()).toString();
|
(_logBolus?.mmolPerLTarget ?? Settings.targetMmolPerL).toString();
|
||||||
_mmolPerLCorrectionController.text = (_logBolus?.mmolPerLCorrection ??
|
_mmolPerLCorrectionController.text = (_logBolus?.mmolPerLCorrection ??
|
||||||
max(
|
max(
|
||||||
(double.tryParse(_mmolPerLCurrentController.text) ?? 0) -
|
(double.tryParse(_mmolPerLCurrentController.text) ?? 0) -
|
||||||
@ -131,13 +136,32 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
updateDelayedRatio();
|
updateDelayedRatio();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_logBolus = LogBolus.get(widget.id);
|
_logBolus = LogBolus.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _logBolus == null;
|
_isNew = _logBolus == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateLogMeal(LogMeal? value) {
|
||||||
|
setState(() {
|
||||||
|
_meal = value;
|
||||||
|
_mealController.text = (_meal ?? '').toString();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateDelayedRatio() {
|
void updateDelayedRatio() {
|
||||||
@ -157,15 +181,15 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSelectMeal(LogMeal meal) {
|
void onSelectMeal(LogMeal? meal) {
|
||||||
|
updateLogMeal(meal);
|
||||||
|
if (meal != null && meal.carbsPerPortion != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_meal = meal;
|
|
||||||
if (meal.carbsPerPortion != null) {
|
|
||||||
_carbsController.text = meal.carbsPerPortion.toString();
|
_carbsController.text = meal.carbsPerPortion.toString();
|
||||||
onChangeCarbs();
|
onChangeCarbs();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onChangeCarbs() {
|
void onChangeCarbs() {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -332,7 +356,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
LogBolus.put(delayedBolus);
|
LogBolus.put(delayedBolus);
|
||||||
}
|
}
|
||||||
|
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Bolus Saved');
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Bolus Saved', logBolus, delayedBolus]);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = false;
|
_isSaving = false;
|
||||||
@ -349,9 +373,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
_mmolPerLCurrentController.text !=
|
_mmolPerLCurrentController.text !=
|
||||||
(_logEntry?.mmolPerL.toString() ?? ''))) ||
|
(_logEntry?.mmolPerL.toString() ?? ''))) ||
|
||||||
_mgPerDlTargetController.text !=
|
_mgPerDlTargetController.text !=
|
||||||
Settings.targetMgPerDl().toString() ||
|
Settings.targetMgPerDl.toString() ||
|
||||||
_mmolPerLTargetController.text !=
|
_mmolPerLTargetController.text !=
|
||||||
Settings.targetMmolPerL().toString() ||
|
Settings.targetMmolPerL.toString() ||
|
||||||
_delayController.text != '' ||
|
_delayController.text != '' ||
|
||||||
_setManually ||
|
_setManually ||
|
||||||
_notesController.text != '')) ||
|
_notesController.text != '')) ||
|
||||||
@ -391,7 +415,10 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
title: Text(_isNew ? 'New Bolus' : 'Edit Bolus'),
|
title: Text(_isNew ? 'New Bolus' : 'Edit Bolus'),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: LogBolusDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: LogBolusDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -462,8 +489,12 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
children: _bolusType == BolusType.glucose
|
children: _bolusType == BolusType.glucose
|
||||||
? [
|
? [
|
||||||
Row(
|
Row(
|
||||||
children: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
|
children: Settings.glucoseMeasurement ==
|
||||||
[GlucoseDisplayMode.both, GlucoseDisplayMode.bothForDetail].contains(Settings.glucoseDisplayMode)
|
GlucoseMeasurement.mgPerDl ||
|
||||||
|
[
|
||||||
|
GlucoseDisplayMode.both,
|
||||||
|
GlucoseDisplayMode.bothForDetail
|
||||||
|
].contains(Settings.glucoseDisplayMode)
|
||||||
? [
|
? [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -474,12 +505,15 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
labelText: 'Current',
|
labelText: 'Current',
|
||||||
suffixText: 'mg/dl',
|
suffixText: 'mg/dl',
|
||||||
),
|
),
|
||||||
controller: _mgPerDlCurrentController,
|
controller:
|
||||||
|
_mgPerDlCurrentController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) async {
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(
|
||||||
|
const Duration(seconds: 1));
|
||||||
onChangeGlucose(
|
onChangeGlucose(
|
||||||
calculateFrom:
|
calculateFrom:
|
||||||
GlucoseMeasurement.mgPerDl);
|
GlucoseMeasurement
|
||||||
|
.mgPerDl);
|
||||||
},
|
},
|
||||||
keyboardType: const TextInputType
|
keyboardType: const TextInputType
|
||||||
.numberWithOptions(),
|
.numberWithOptions(),
|
||||||
@ -495,12 +529,15 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
labelText: 'Target',
|
labelText: 'Target',
|
||||||
suffixText: 'mg/dl',
|
suffixText: 'mg/dl',
|
||||||
),
|
),
|
||||||
controller: _mgPerDlTargetController,
|
controller:
|
||||||
|
_mgPerDlTargetController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) async {
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(
|
||||||
|
const Duration(seconds: 1));
|
||||||
onChangeGlucose(
|
onChangeGlucose(
|
||||||
calculateFrom:
|
calculateFrom:
|
||||||
GlucoseMeasurement.mgPerDl);
|
GlucoseMeasurement
|
||||||
|
.mgPerDl);
|
||||||
},
|
},
|
||||||
keyboardType: const TextInputType
|
keyboardType: const TextInputType
|
||||||
.numberWithOptions(),
|
.numberWithOptions(),
|
||||||
@ -522,7 +559,10 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[GlucoseDisplayMode.both, GlucoseDisplayMode.bothForDetail].contains(Settings.glucoseDisplayMode)
|
[
|
||||||
|
GlucoseDisplayMode.both,
|
||||||
|
GlucoseDisplayMode.bothForDetail
|
||||||
|
].contains(Settings.glucoseDisplayMode)
|
||||||
? IconButton(
|
? IconButton(
|
||||||
onPressed: () => onChangeGlucose(
|
onPressed: () => onChangeGlucose(
|
||||||
calculateFrom:
|
calculateFrom:
|
||||||
@ -535,13 +575,17 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
|
children: Settings.glucoseMeasurement ==
|
||||||
[GlucoseDisplayMode.both, GlucoseDisplayMode.bothForDetail].contains(Settings.glucoseDisplayMode)
|
GlucoseMeasurement.mmolPerL ||
|
||||||
|
[
|
||||||
|
GlucoseDisplayMode.both,
|
||||||
|
GlucoseDisplayMode.bothForDetail
|
||||||
|
].contains(Settings.glucoseDisplayMode)
|
||||||
? [
|
? [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(right: 5),
|
const EdgeInsets.only(right: 5.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Current',
|
labelText: 'Current',
|
||||||
@ -550,10 +594,12 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
controller:
|
controller:
|
||||||
_mmolPerLCurrentController,
|
_mmolPerLCurrentController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) async {
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(
|
||||||
|
const Duration(seconds: 1));
|
||||||
onChangeGlucose(
|
onChangeGlucose(
|
||||||
calculateFrom:
|
calculateFrom:
|
||||||
GlucoseMeasurement.mmolPerL);
|
GlucoseMeasurement
|
||||||
|
.mmolPerL);
|
||||||
},
|
},
|
||||||
keyboardType: const TextInputType
|
keyboardType: const TextInputType
|
||||||
.numberWithOptions(),
|
.numberWithOptions(),
|
||||||
@ -569,12 +615,15 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
labelText: 'Target',
|
labelText: 'Target',
|
||||||
suffixText: 'mmol/l',
|
suffixText: 'mmol/l',
|
||||||
),
|
),
|
||||||
controller: _mmolPerLTargetController,
|
controller:
|
||||||
|
_mmolPerLTargetController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) async {
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(
|
||||||
|
const Duration(seconds: 1));
|
||||||
onChangeGlucose(
|
onChangeGlucose(
|
||||||
calculateFrom:
|
calculateFrom:
|
||||||
GlucoseMeasurement.mmolPerL);
|
GlucoseMeasurement
|
||||||
|
.mmolPerL);
|
||||||
},
|
},
|
||||||
keyboardType: const TextInputType
|
keyboardType: const TextInputType
|
||||||
.numberWithOptions(),
|
.numberWithOptions(),
|
||||||
@ -596,11 +645,15 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[GlucoseDisplayMode.both, GlucoseDisplayMode.bothForDetail].contains(Settings.glucoseDisplayMode)
|
[
|
||||||
|
GlucoseDisplayMode.both,
|
||||||
|
GlucoseDisplayMode.bothForDetail
|
||||||
|
].contains(Settings.glucoseDisplayMode)
|
||||||
? IconButton(
|
? IconButton(
|
||||||
onPressed: () => onChangeGlucose(
|
onPressed: () => onChangeGlucose(
|
||||||
calculateFrom:
|
calculateFrom:
|
||||||
GlucoseMeasurement.mgPerDl),
|
GlucoseMeasurement
|
||||||
|
.mgPerDl),
|
||||||
icon: const Icon(Icons.calculate),
|
icon: const Icon(Icons.calculate),
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
@ -609,26 +662,51 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
AutoCompleteDropdownButton<LogMeal>(
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<LogMeal>(
|
||||||
|
controller: _mealController,
|
||||||
selectedItem: _meal,
|
selectedItem: _meal,
|
||||||
label: 'Meal',
|
label: 'Meal',
|
||||||
items: _logMeals,
|
items: _logMeals,
|
||||||
onChanged: (value) {
|
onChanged: onSelectMeal,
|
||||||
if (value != null) {
|
|
||||||
onSelectMeal(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
TextFormField(
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _meal == null
|
||||||
|
? const LogMealDetailScreen()
|
||||||
|
: LogMealDetailScreen(
|
||||||
|
id: _meal!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateLogMeal(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
_meal == null ? Icons.add : Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 10.0),
|
||||||
|
child: TextFormField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Carbs',
|
labelText: 'Carbs',
|
||||||
suffixText: Settings.nutritionMeasurementSuffix,
|
suffixText: Settings.nutritionMeasurementSuffix,
|
||||||
),
|
),
|
||||||
controller: _carbsController,
|
controller: _carbsController,
|
||||||
onChanged: (_) => onChangeCarbs(),
|
onChanged: (_) => onChangeCarbs(),
|
||||||
keyboardType: const TextInputType.numberWithOptions(
|
keyboardType:
|
||||||
|
const TextInputType.numberWithOptions(
|
||||||
decimal: true),
|
decimal: true),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
@ -670,7 +748,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
),
|
),
|
||||||
controller: _immediateUnitsController,
|
controller: _immediateUnitsController,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
enabled: (int.tryParse(_delayController.text) ??
|
enabled:
|
||||||
|
(int.tryParse(_delayController.text) ??
|
||||||
0) !=
|
0) !=
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
@ -686,7 +765,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
),
|
),
|
||||||
controller: _delayedUnitsController,
|
controller: _delayedUnitsController,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
enabled: (int.tryParse(_delayController.text) ??
|
enabled:
|
||||||
|
(int.tryParse(_delayController.text) ??
|
||||||
0) !=
|
0) !=
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
@ -699,15 +779,17 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: _isSaving ? null : handleSaveAction,
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
|
@ -23,6 +23,8 @@ class LogBolusListScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
widget.reload();
|
widget.reload();
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
|||||||
id: logBolus.id,
|
id: logBolus.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDelete(LogBolus logBolus) {
|
void onDelete(LogBolus logBolus) {
|
||||||
@ -77,13 +79,16 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
|||||||
id: mealId,
|
id: mealId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return widget.logBoli.isNotEmpty
|
return widget.logBoli.isNotEmpty
|
||||||
? ListView.builder(
|
? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: widget.logBoli.length,
|
itemCount: widget.logBoli.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
@ -91,7 +96,8 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
|||||||
String titleText = '${bolus.units} U ${(bolus.delay ?? 0) != 0
|
String titleText = '${bolus.units} U ${(bolus.delay ?? 0) != 0
|
||||||
? ' (delayed by ${bolus.delay} min)'
|
? ' (delayed by ${bolus.delay} min)'
|
||||||
: ''}';
|
: ''}';
|
||||||
return ListTile(
|
return Card(
|
||||||
|
child: ListTile(
|
||||||
onTap: () => handleEditAction(bolus),
|
onTap: () => handleEditAction(bolus),
|
||||||
title: Text(titleText),
|
title: Text(titleText),
|
||||||
subtitle: Text(bolus.carbs != null ?
|
subtitle: Text(bolus.carbs != null ?
|
||||||
@ -117,8 +123,10 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: const Center(
|
: const Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -13,6 +13,7 @@ import 'package:diameter/screens/log/log_entry/log_meal_list.dart';
|
|||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:diameter/utils/utils.dart';
|
import 'package:diameter/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
class LogEntryScreen extends StatefulWidget {
|
class LogEntryScreen extends StatefulWidget {
|
||||||
static const String routeName = '/log-entry';
|
static const String routeName = '/log-entry';
|
||||||
@ -28,12 +29,15 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
LogEntry? _logEntry;
|
LogEntry? _logEntry;
|
||||||
List<LogMeal> _logMeals = [];
|
List<LogMeal> _logMeals = [];
|
||||||
List<LogBolus> _logBoli = [];
|
List<LogBolus> _logBoli = [];
|
||||||
|
|
||||||
bool _isNew = true;
|
bool _isNew = true;
|
||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> logEntryForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> logEntryForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
late DateTime _time;
|
late DateTime _time;
|
||||||
|
double? _glucoseTrend;
|
||||||
|
|
||||||
final _timeController = TextEditingController(text: '');
|
final _timeController = TextEditingController(text: '');
|
||||||
final _dateController = TextEditingController(text: '');
|
final _dateController = TextEditingController(text: '');
|
||||||
@ -90,6 +94,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
_time = _logEntry!.time;
|
_time = _logEntry!.time;
|
||||||
_mgPerDlController.text = (_logEntry!.mgPerDl ?? '').toString();
|
_mgPerDlController.text = (_logEntry!.mgPerDl ?? '').toString();
|
||||||
_mmolPerLController.text = (_logEntry!.mmolPerL ?? '').toString();
|
_mmolPerLController.text = (_logEntry!.mmolPerL ?? '').toString();
|
||||||
|
_glucoseTrend = _logEntry!.glucoseTrend;
|
||||||
_notesController.text = _logEntry!.notes ?? '';
|
_notesController.text = _logEntry!.notes ?? '';
|
||||||
} else {
|
} else {
|
||||||
_time = DateTime.now();
|
_time = DateTime.now();
|
||||||
@ -126,26 +131,21 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
_dateController.text = DateTimeUtils.displayDate(_time);
|
_dateController.text = DateTimeUtils.displayDate(_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void convertBetweenMgPerDlAndMmolPerL({GlucoseMeasurement? calculateFrom}) {
|
void convertBetweenMgPerDlAndMmolPerL() {
|
||||||
int? mgPerDl;
|
int? mgPerDl;
|
||||||
double? mmolPerL;
|
double? mmolPerL;
|
||||||
|
|
||||||
if (calculateFrom != GlucoseMeasurement.mmolPerL &&
|
if (Settings.glucoseMeasurement != GlucoseMeasurement.mmolPerL &&
|
||||||
_mgPerDlController.text != '') {
|
_mgPerDlController.text != '') {
|
||||||
mgPerDl = int.tryParse(_mgPerDlController.text);
|
mgPerDl = int.tryParse(_mgPerDlController.text);
|
||||||
}
|
|
||||||
if (calculateFrom != GlucoseMeasurement.mgPerDl &&
|
|
||||||
_mmolPerLController.text != '') {
|
|
||||||
mmolPerL = double.tryParse(_mmolPerLController.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mgPerDl != null && mmolPerL == null) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_mmolPerLController.text =
|
_mmolPerLController.text =
|
||||||
Utils.convertMgPerDlToMmolPerL(mgPerDl!).toString();
|
Utils.convertMgPerDlToMmolPerL(mgPerDl!).toString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (mmolPerL != null && mgPerDl == null) {
|
if (Settings.glucoseMeasurement != GlucoseMeasurement.mgPerDl &&
|
||||||
|
_mmolPerLController.text != '') {
|
||||||
|
mmolPerL = double.tryParse(_mmolPerLController.text);
|
||||||
setState(() {
|
setState(() {
|
||||||
_mgPerDlController.text =
|
_mgPerDlController.text =
|
||||||
Utils.convertMmolPerLToMgPerDl(mmolPerL!).toString();
|
Utils.convertMmolPerLToMgPerDl(mmolPerL!).toString();
|
||||||
@ -158,15 +158,17 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
_isSaving = true;
|
_isSaving = true;
|
||||||
});
|
});
|
||||||
if (logEntryForm.currentState!.validate()) {
|
if (logEntryForm.currentState!.validate()) {
|
||||||
LogEntry.put(LogEntry(
|
LogEntry logEntry = LogEntry(
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
time: _time,
|
time: _time,
|
||||||
mgPerDl: int.tryParse(_mgPerDlController.text),
|
mgPerDl: int.tryParse(_mgPerDlController.text),
|
||||||
mmolPerL: double.tryParse(_mmolPerLController.text),
|
mmolPerL: double.tryParse(_mmolPerLController.text),
|
||||||
|
glucoseTrend: _glucoseTrend,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
));
|
);
|
||||||
|
LogEntry.put(logEntry);
|
||||||
Navigator.pushReplacementNamed(context, '/log',
|
Navigator.pushReplacementNamed(context, '/log',
|
||||||
arguments: '${_isNew ? 'New' : ''} Log Entry Saved');
|
arguments: ['${_isNew ? 'New' : ''} Log Entry Saved', logEntry]);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = false;
|
_isSaving = false;
|
||||||
@ -204,7 +206,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
return LogMealDetailScreen(logEntryId: _logEntry!.id);
|
return LogMealDetailScreen(logEntryId: _logEntry!.id);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAddNewBolus() async {
|
void handleAddNewBolus() async {
|
||||||
@ -215,7 +217,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
return LogBolusDetailScreen(logEntryId: _logEntry!.id);
|
return LogBolusDetailScreen(logEntryId: _logEntry!.id);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderTabButtons(index) {
|
void renderTabButtons(index) {
|
||||||
@ -251,7 +253,10 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
renderTabButtons(tabController.index);
|
renderTabButtons(tabController.index);
|
||||||
});
|
});
|
||||||
List<Widget> tabs = [
|
List<Widget> tabs = [
|
||||||
SingleChildScrollView(
|
Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -322,16 +327,16 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
labelText: 'mg/dl',
|
labelText: 'mg/dl',
|
||||||
suffixText: 'mg/dl',
|
suffixText: 'mg/dl',
|
||||||
),
|
),
|
||||||
|
readOnly: Settings.glucoseMeasurement ==
|
||||||
|
GlucoseMeasurement.mmolPerL,
|
||||||
controller: _mgPerDlController,
|
controller: _mgPerDlController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) async {
|
||||||
await Future.delayed(
|
await Future.delayed(
|
||||||
const Duration(seconds: 1));
|
const Duration(seconds: 1));
|
||||||
convertBetweenMgPerDlAndMmolPerL(
|
convertBetweenMgPerDlAndMmolPerL();
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement.mgPerDl);
|
|
||||||
},
|
},
|
||||||
keyboardType:
|
keyboardType: const TextInputType
|
||||||
const TextInputType.numberWithOptions(),
|
.numberWithOptions(),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value!.trim().isEmpty &&
|
if (value!.trim().isEmpty &&
|
||||||
_mmolPerLController.text
|
_mmolPerLController.text
|
||||||
@ -344,17 +349,11 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
Settings.glucoseDisplayMode ==
|
[
|
||||||
GlucoseDisplayMode.both ||
|
GlucoseDisplayMode.both,
|
||||||
Settings.glucoseDisplayMode ==
|
|
||||||
GlucoseDisplayMode.bothForDetail
|
GlucoseDisplayMode.bothForDetail
|
||||||
? IconButton(
|
].contains(Settings.glucoseDisplayMode)
|
||||||
onPressed: () =>
|
? const SizedBox(width: 10.0)
|
||||||
convertBetweenMgPerDlAndMmolPerL(
|
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement.mmolPerL),
|
|
||||||
icon: const Icon(Icons.calculate),
|
|
||||||
)
|
|
||||||
: Container(),
|
: Container(),
|
||||||
Settings.glucoseMeasurement ==
|
Settings.glucoseMeasurement ==
|
||||||
GlucoseMeasurement.mmolPerL ||
|
GlucoseMeasurement.mmolPerL ||
|
||||||
@ -368,13 +367,13 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
labelText: 'mmol/l',
|
labelText: 'mmol/l',
|
||||||
suffixText: 'mmol/l',
|
suffixText: 'mmol/l',
|
||||||
),
|
),
|
||||||
|
readOnly: Settings.glucoseMeasurement ==
|
||||||
|
GlucoseMeasurement.mgPerDl,
|
||||||
controller: _mmolPerLController,
|
controller: _mmolPerLController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) async {
|
||||||
await Future.delayed(
|
await Future.delayed(
|
||||||
const Duration(seconds: 1));
|
const Duration(seconds: 1));
|
||||||
convertBetweenMgPerDlAndMmolPerL(
|
convertBetweenMgPerDlAndMmolPerL();
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement.mmolPerL);
|
|
||||||
},
|
},
|
||||||
keyboardType:
|
keyboardType:
|
||||||
const TextInputType.numberWithOptions(
|
const TextInputType.numberWithOptions(
|
||||||
@ -391,32 +390,37 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
Settings.glucoseDisplayMode ==
|
Transform.rotate(
|
||||||
GlucoseDisplayMode.both ||
|
angle: (_glucoseTrend ?? 90) * math.pi / 180,
|
||||||
Settings.glucoseDisplayMode ==
|
child: IconButton(
|
||||||
GlucoseDisplayMode.bothForDetail
|
onPressed: () => setState(() {
|
||||||
? IconButton(
|
_glucoseTrend = (_glucoseTrend ?? -45) + 45;
|
||||||
onPressed: () =>
|
if (_glucoseTrend! > 180) {
|
||||||
convertBetweenMgPerDlAndMmolPerL(
|
_glucoseTrend = null;
|
||||||
calculateFrom:
|
}
|
||||||
GlucoseMeasurement.mgPerDl),
|
}),
|
||||||
icon: const Icon(Icons.calculate),
|
icon: Icon(Icons.arrow_upward,
|
||||||
)
|
color: _glucoseTrend != null
|
||||||
: Container(),
|
? Theme.of(context).iconTheme.color
|
||||||
|
: Theme.of(context).disabledColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!_isNew) {
|
if (!_isNew) {
|
||||||
|
@ -10,6 +10,11 @@ import 'package:diameter/models/meal_portion_type.dart';
|
|||||||
import 'package:diameter/models/meal_source.dart';
|
import 'package:diameter/models/meal_source.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
|
import 'package:diameter/screens/accuracy_detail.dart';
|
||||||
|
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
||||||
|
import 'package:diameter/screens/meal/meal_detail.dart';
|
||||||
|
import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
|
||||||
|
import 'package:diameter/screens/meal/meal_source_detail.dart';
|
||||||
import 'package:diameter/utils/utils.dart';
|
import 'package:diameter/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -30,8 +35,10 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
LogMeal? _logMeal;
|
LogMeal? _logMeal;
|
||||||
bool _isNew = true;
|
bool _isNew = true;
|
||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
|
bool _isExpanded = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _carbsRatioController = TextEditingController(text: '');
|
final _carbsRatioController = TextEditingController(text: '');
|
||||||
@ -46,6 +53,13 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
Accuracy? _portionSizeAccuracy;
|
Accuracy? _portionSizeAccuracy;
|
||||||
Accuracy? _carbsRatioAccuracy;
|
Accuracy? _carbsRatioAccuracy;
|
||||||
|
|
||||||
|
final _mealController = TextEditingController(text: '');
|
||||||
|
final _mealSourceController = TextEditingController(text: '');
|
||||||
|
final _mealCategoryController = TextEditingController(text: '');
|
||||||
|
final _mealPortionTypeController = TextEditingController(text: '');
|
||||||
|
final _portionSizeAccuracyController = TextEditingController(text: '');
|
||||||
|
final _carbsRatioAccuracyController = TextEditingController(text: '');
|
||||||
|
|
||||||
List<Meal> _meals = [];
|
List<Meal> _meals = [];
|
||||||
List<MealCategory> _mealCategories = [];
|
List<MealCategory> _mealCategories = [];
|
||||||
List<MealPortionType> _mealPortionTypes = [];
|
List<MealPortionType> _mealPortionTypes = [];
|
||||||
@ -72,22 +86,89 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
_carbsPerPortionController.text =
|
_carbsPerPortionController.text =
|
||||||
(_logMeal!.carbsPerPortion ?? '').toString();
|
(_logMeal!.carbsPerPortion ?? '').toString();
|
||||||
_notesController.text = _logMeal!.notes ?? '';
|
_notesController.text = _logMeal!.notes ?? '';
|
||||||
|
|
||||||
|
_meal = _logMeal!.meal.target;
|
||||||
|
_mealController.text = (_meal ?? '').toString();
|
||||||
|
_mealSource = _logMeal!.mealSource.target;
|
||||||
|
_mealSourceController.text = (_mealSource ?? '').toString();
|
||||||
|
_mealCategory = _logMeal!.mealCategory.target;
|
||||||
|
_mealCategoryController.text = (_mealCategory ?? '').toString();
|
||||||
|
_mealPortionType = _logMeal!.mealPortionType.target;
|
||||||
|
_mealPortionTypeController.text = (_mealPortionType ?? '').toString();
|
||||||
|
_portionSizeAccuracy = _logMeal!.portionSizeAccuracy.target;
|
||||||
|
_portionSizeAccuracyController.text =
|
||||||
|
(_portionSizeAccuracy ?? '').toString();
|
||||||
|
_carbsRatioAccuracy = _logMeal!.carbsRatioAccuracy.target;
|
||||||
|
_carbsRatioAccuracyController.text =
|
||||||
|
(_carbsRatioAccuracy ?? '').toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_logMeal = LogMeal.get(widget.id);
|
_logMeal = LogMeal.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _logMeal == null;
|
_isNew = _logMeal == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onSelectMeal(Meal meal) async {
|
void updateCarbsRatioAccuracy(Accuracy? value) {
|
||||||
|
setState(() {
|
||||||
|
_carbsRatioAccuracy = value;
|
||||||
|
_carbsRatioAccuracyController.text =
|
||||||
|
(_carbsRatioAccuracy ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePortionSizeAccuracy(Accuracy? value) {
|
||||||
|
setState(() {
|
||||||
|
_portionSizeAccuracy = value;
|
||||||
|
_portionSizeAccuracyController.text =
|
||||||
|
(_portionSizeAccuracy ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMealCategory(MealCategory? value) {
|
||||||
|
setState(() {
|
||||||
|
_mealCategory = value;
|
||||||
|
_mealCategoryController.text = (_mealCategory ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMealPortionType(MealPortionType? value) {
|
||||||
|
setState(() {
|
||||||
|
_mealPortionType = value;
|
||||||
|
_mealPortionTypeController.text = (_mealPortionType ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMealSource(MealSource? value) {
|
||||||
|
setState(() {
|
||||||
|
_mealSource = value;
|
||||||
|
_mealSourceController.text = (_mealSource ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onSelectMeal(Meal? meal) async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_meal = meal;
|
_meal = meal;
|
||||||
_valueController.text = meal.value;
|
_valueController.text = _mealController.text = (_meal ?? '').toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (meal != null) {
|
||||||
if (meal.carbsRatio != null) {
|
if (meal.carbsRatio != null) {
|
||||||
_carbsRatioController.text = meal.carbsRatio.toString();
|
_carbsRatioController.text = meal.carbsRatio.toString();
|
||||||
}
|
}
|
||||||
@ -98,21 +179,21 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
_carbsPerPortionController.text = meal.carbsPerPortion.toString();
|
_carbsPerPortionController.text = meal.carbsPerPortion.toString();
|
||||||
}
|
}
|
||||||
if (meal.mealSource.hasValue) {
|
if (meal.mealSource.hasValue) {
|
||||||
_mealSource = meal.mealSource.target;
|
updateMealSource(meal.mealSource.target);
|
||||||
}
|
}
|
||||||
if (meal.mealCategory.hasValue) {
|
if (meal.mealCategory.hasValue) {
|
||||||
_mealCategory = meal.mealCategory.target;
|
updateMealCategory(meal.mealCategory.target);
|
||||||
}
|
}
|
||||||
if (meal.mealPortionType.hasValue) {
|
if (meal.mealPortionType.hasValue) {
|
||||||
_mealPortionType = meal.mealPortionType.target;
|
updateMealPortionType(meal.mealPortionType.target);
|
||||||
}
|
}
|
||||||
if (meal.portionSizeAccuracy.hasValue) {
|
if (meal.portionSizeAccuracy.hasValue) {
|
||||||
_portionSizeAccuracy = meal.portionSizeAccuracy.target;
|
updatePortionSizeAccuracy(meal.portionSizeAccuracy.target);
|
||||||
}
|
}
|
||||||
if (meal.carbsRatioAccuracy.hasValue) {
|
if (meal.carbsRatioAccuracy.hasValue) {
|
||||||
_carbsRatioAccuracy = meal.carbsRatioAccuracy.target;
|
updateCarbsRatioAccuracy(meal.carbsRatioAccuracy.target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
@ -136,7 +217,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
||||||
|
|
||||||
LogMeal.put(logMeal);
|
LogMeal.put(logMeal);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Saved');
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Meal Saved', logMeal]);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = false;
|
_isSaving = false;
|
||||||
@ -233,7 +314,10 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
title: Text(_isNew ? 'New Meal' : _logMeal!.value),
|
title: Text(_isNew ? 'New Meal' : _logMeal!.value),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -252,7 +336,11 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<Meal>(
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<Meal>(
|
||||||
|
controller: _mealController,
|
||||||
selectedItem: _meal,
|
selectedItem: _meal,
|
||||||
label: 'Meal',
|
label: 'Meal',
|
||||||
items: _meals,
|
items: _meals,
|
||||||
@ -262,35 +350,24 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<MealSource>(
|
|
||||||
selectedItem: _mealSource,
|
|
||||||
label: 'Meal Source',
|
|
||||||
items: _mealSources,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_mealSource = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<MealCategory>(
|
IconButton(
|
||||||
selectedItem: _mealCategory,
|
onPressed: () {
|
||||||
label: 'Meal Category',
|
Navigator.push(
|
||||||
items: _mealCategories,
|
context,
|
||||||
onChanged: (value) {
|
MaterialPageRoute(
|
||||||
setState(() {
|
builder: (context) => _meal == null
|
||||||
_mealCategory = value;
|
? const MealDetailScreen()
|
||||||
});
|
: MealDetailScreen(id: _meal!.id),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<MealPortionType>(
|
).then((result) {
|
||||||
selectedItem: _mealPortionType,
|
onSelectMeal(result?[1]);
|
||||||
label: 'Meal Portion Type',
|
reload(message: result?[0]);
|
||||||
items: _mealPortionTypes,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_mealPortionType = value;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
icon: Icon(_meal == null ? Icons.add : Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -325,9 +402,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Portion size',
|
labelText: 'Portion size',
|
||||||
suffixText:
|
suffixText: Settings.nutritionMeasurementSuffix,
|
||||||
Settings.nutritionMeasurementSuffix,
|
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
controller: _portionSizeController,
|
controller: _portionSizeController,
|
||||||
keyboardType: const TextInputType.numberWithOptions(
|
keyboardType: const TextInputType.numberWithOptions(
|
||||||
@ -347,24 +422,13 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<Accuracy>(
|
|
||||||
selectedItem: _portionSizeAccuracy,
|
|
||||||
label: 'Portion Size Accuracy',
|
|
||||||
items: _portionSizeAccuracies,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_portionSizeAccuracy = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Carbs per portion',
|
labelText: 'Carbs per portion',
|
||||||
suffixText:
|
suffixText: Settings.nutritionMeasurementSuffix,
|
||||||
Settings.nutritionMeasurementSuffix,
|
|
||||||
),
|
),
|
||||||
controller: _carbsPerPortionController,
|
controller: _carbsPerPortionController,
|
||||||
keyboardType: const TextInputType.numberWithOptions(
|
keyboardType: const TextInputType.numberWithOptions(
|
||||||
@ -384,29 +448,226 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<Accuracy>(
|
|
||||||
selectedItem: _carbsRatioAccuracy,
|
|
||||||
label: 'Carbs Ratio Accuracy',
|
|
||||||
items: _carbsRatioAccuracies,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_carbsRatioAccuracy = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => setState(() {
|
||||||
|
_isExpanded = !_isExpanded;
|
||||||
|
}),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'ADDITIONAL FIELDS',
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Icon(_isExpanded
|
||||||
|
? Icons.expand_less
|
||||||
|
: Icons.expand_more),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: _isExpanded
|
||||||
|
? [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<MealSource>(
|
||||||
|
controller: _mealSourceController,
|
||||||
|
selectedItem: _mealSource,
|
||||||
|
label: 'Meal Source',
|
||||||
|
items: _mealSources,
|
||||||
|
onChanged: updateMealSource,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
_mealSource == null
|
||||||
|
? const MealSourceDetailScreen()
|
||||||
|
: MealSourceDetailScreen(
|
||||||
|
id: _mealSource!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateMealSource(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_mealSource == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child:
|
||||||
|
AutoCompleteDropdownButton<MealCategory>(
|
||||||
|
controller: _mealCategoryController,
|
||||||
|
selectedItem: _mealCategory,
|
||||||
|
label: 'Meal Category',
|
||||||
|
items: _mealCategories,
|
||||||
|
onChanged: updateMealCategory,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _mealCategory ==
|
||||||
|
null
|
||||||
|
? const MealCategoryDetailScreen()
|
||||||
|
: MealCategoryDetailScreen(
|
||||||
|
id: _mealCategory!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateMealCategory(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_mealCategory == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<
|
||||||
|
MealPortionType>(
|
||||||
|
controller: _mealPortionTypeController,
|
||||||
|
selectedItem: _mealPortionType,
|
||||||
|
label: 'Meal Portion Type',
|
||||||
|
items: _mealPortionTypes,
|
||||||
|
onChanged: updateMealPortionType,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _mealPortionType ==
|
||||||
|
null
|
||||||
|
? const MealPortionTypeDetailScreen()
|
||||||
|
: MealPortionTypeDetailScreen(
|
||||||
|
id: _mealPortionType!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateMealPortionType(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_mealPortionType == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<Accuracy>(
|
||||||
|
controller: _portionSizeAccuracyController,
|
||||||
|
selectedItem: _portionSizeAccuracy,
|
||||||
|
label: 'Portion Size Accuracy',
|
||||||
|
items: _portionSizeAccuracies,
|
||||||
|
onChanged: updatePortionSizeAccuracy,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _portionSizeAccuracy == null
|
||||||
|
? const AccuracyDetailScreen()
|
||||||
|
: AccuracyDetailScreen(
|
||||||
|
id: _portionSizeAccuracy!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updatePortionSizeAccuracy(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_portionSizeAccuracy == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<Accuracy>(
|
||||||
|
controller: _carbsRatioAccuracyController,
|
||||||
|
selectedItem: _carbsRatioAccuracy,
|
||||||
|
label: 'Carbs Ratio Accuracy',
|
||||||
|
items: _carbsRatioAccuracies,
|
||||||
|
onChanged: updateCarbsRatioAccuracy,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _carbsRatioAccuracy == null
|
||||||
|
? const AccuracyDetailScreen()
|
||||||
|
: AccuracyDetailScreen(
|
||||||
|
id: _carbsRatioAccuracy!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateCarbsRatioAccuracy(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_carbsRatioAccuracy == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: _isSaving ? null : handleSaveAction,
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
|
@ -19,6 +19,8 @@ class LogMealListScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LogMealListScreenState extends State<LogMealListScreen> {
|
class _LogMealListScreenState extends State<LogMealListScreen> {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
widget.reload();
|
widget.reload();
|
||||||
|
|
||||||
@ -44,7 +46,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
|
|||||||
id: meal.id,
|
id: meal.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDelete(LogMeal logMeal) {
|
void onDelete(LogMeal logMeal) {
|
||||||
@ -67,12 +69,16 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return widget.logMeals.isNotEmpty
|
return widget.logMeals.isNotEmpty
|
||||||
? ListView.builder(
|
? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: widget.logMeals.length,
|
itemCount: widget.logMeals.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final meal = widget.logMeals[index];
|
final meal = widget.logMeals[index];
|
||||||
return ListTile(
|
return Card(
|
||||||
|
child: ListTile(
|
||||||
onTap: () => handleEditAction(meal),
|
onTap: () => handleEditAction(meal),
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
@ -98,8 +104,10 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
|
|||||||
),
|
),
|
||||||
onPressed: () => handleDeleteAction(meal),
|
onPressed: () => handleDeleteAction(meal),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: const Center(
|
: const Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -8,6 +8,8 @@ import 'package:diameter/models/log_event.dart';
|
|||||||
import 'package:diameter/models/log_event_type.dart';
|
import 'package:diameter/models/log_event_type.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
|
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
||||||
|
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -35,6 +37,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
List<BasalProfile> _basalProfiles = [];
|
List<BasalProfile> _basalProfiles = [];
|
||||||
|
|
||||||
final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
late DateTime _time;
|
late DateTime _time;
|
||||||
DateTime? _endTime;
|
DateTime? _endTime;
|
||||||
@ -49,9 +52,14 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
LogEventType? _eventType;
|
LogEventType? _eventType;
|
||||||
|
final _eventTypeController = TextEditingController(text: '');
|
||||||
|
|
||||||
bool _hasEndTime = false;
|
bool _hasEndTime = false;
|
||||||
|
|
||||||
BolusProfile? _bolusProfile;
|
BolusProfile? _bolusProfile;
|
||||||
BasalProfile? _basalProfile;
|
BasalProfile? _basalProfile;
|
||||||
|
final _bolusProfileController = TextEditingController(text: '');
|
||||||
|
final _basalProfileController = TextEditingController(text: '');
|
||||||
|
|
||||||
List<LogEventType> _logEventTypes = [];
|
List<LogEventType> _logEventTypes = [];
|
||||||
|
|
||||||
@ -69,9 +77,16 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
(_logEvent!.reminderDuration ?? '').toString();
|
(_logEvent!.reminderDuration ?? '').toString();
|
||||||
_hasEndTime = _logEvent!.hasEndTime;
|
_hasEndTime = _logEvent!.hasEndTime;
|
||||||
_notesController.text = _logEvent!.notes ?? '';
|
_notesController.text = _logEvent!.notes ?? '';
|
||||||
|
|
||||||
_eventType = _logEvent!.eventType.target;
|
_eventType = _logEvent!.eventType.target;
|
||||||
|
_eventTypeController.text = (_eventType ?? '').toString();
|
||||||
|
|
||||||
_basalProfile = _logEvent!.basalProfile.target;
|
_basalProfile = _logEvent!.basalProfile.target;
|
||||||
|
_basalProfileController.text = (_basalProfile ?? '').toString();
|
||||||
|
|
||||||
_bolusProfile = _logEvent!.bolusProfile.target;
|
_bolusProfile = _logEvent!.bolusProfile.target;
|
||||||
|
_bolusProfileController.text = (_bolusProfile ?? '').toString();
|
||||||
|
|
||||||
_time = _logEvent!.time;
|
_time = _logEvent!.time;
|
||||||
_endTime = _logEvent!.endTime;
|
_endTime = _logEvent!.endTime;
|
||||||
} else {
|
} else {
|
||||||
@ -115,25 +130,47 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
_endDateController.text = DateTimeUtils.displayDate(_endTime);
|
_endDateController.text = DateTimeUtils.displayDate(_endTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSelectEventType(LogEventType eventType) {
|
void updateBasalProfile(BasalProfile? value) {
|
||||||
|
setState(() {
|
||||||
|
_basalProfile = value;
|
||||||
|
_basalProfileController.text = (_basalProfile ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateBolusProfile(BolusProfile? value) {
|
||||||
|
setState(() {
|
||||||
|
_bolusProfile = value;
|
||||||
|
_bolusProfileController.text = (_bolusProfile ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSelectEventType(LogEventType? eventType) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_eventType = eventType;
|
_eventType = eventType;
|
||||||
|
_eventTypeController.text = (_eventType ?? '').toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (eventType != null) {
|
||||||
|
setState(() {
|
||||||
_hasEndTime = eventType.hasEndTime;
|
_hasEndTime = eventType.hasEndTime;
|
||||||
if (eventType.basalProfile.target != null) {
|
|
||||||
_basalProfile = eventType.basalProfile.target;
|
|
||||||
}
|
|
||||||
if (eventType.bolusProfile.target != null) {
|
|
||||||
_bolusProfile = eventType.bolusProfile.target;
|
|
||||||
}
|
|
||||||
if (eventType.defaultReminderDuration != null) {
|
if (eventType.defaultReminderDuration != null) {
|
||||||
_reminderDurationController.text =
|
_reminderDurationController.text =
|
||||||
eventType.defaultReminderDuration.toString();
|
eventType.defaultReminderDuration.toString();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (eventType.basalProfile.target != null) {
|
||||||
|
updateBasalProfile(eventType.basalProfile.target);
|
||||||
|
}
|
||||||
|
if (eventType.bolusProfile.target != null) {
|
||||||
|
updateBolusProfile(eventType.bolusProfile.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> checkIfActiveEventOfTypeExistsBeforeSaving() async {
|
Future<void> checkIfActiveEventOfTypeExistsBeforeSaving() async {
|
||||||
if (_eventType != null && LogEvent.eventTypeExistsForTime(_eventType!.id, _time)) {
|
if (_eventType != null &&
|
||||||
|
LogEvent.eventTypeExistsForTime(_eventType!.id, _time)) {
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -180,7 +217,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
event.basalProfile.target = _basalProfile;
|
event.basalProfile.target = _basalProfile;
|
||||||
event.bolusProfile.target = _bolusProfile;
|
event.bolusProfile.target = _bolusProfile;
|
||||||
LogEvent.put(event);
|
LogEvent.put(event);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Event Saved');
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Event Saved', event]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
@ -223,7 +260,10 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
title: Text(_isNew ? 'New Event' : 'Edit Event'),
|
title: Text(_isNew ? 'New Event' : 'Edit Event'),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -231,14 +271,11 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
formState: _logEventForm,
|
formState: _logEventForm,
|
||||||
fields: [
|
fields: [
|
||||||
AutoCompleteDropdownButton<LogEventType>(
|
AutoCompleteDropdownButton<LogEventType>(
|
||||||
|
controller: _eventTypeController,
|
||||||
selectedItem: _eventType,
|
selectedItem: _eventType,
|
||||||
label: 'Event Type',
|
label: 'Event Type',
|
||||||
items: _logEventTypes,
|
items: _logEventTypes,
|
||||||
onChanged: (value) {
|
onChanged: onSelectEventType,
|
||||||
if (value != null) {
|
|
||||||
onSelectEventType(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -355,25 +392,73 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
enabled: _hasEndTime,
|
enabled: _hasEndTime,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<BolusProfile>(
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<
|
||||||
|
BolusProfile>(
|
||||||
|
controller: _bolusProfileController,
|
||||||
selectedItem: _bolusProfile,
|
selectedItem: _bolusProfile,
|
||||||
label: 'Bolus Profile',
|
label: 'Bolus Profile',
|
||||||
items: _bolusProfiles,
|
items: _bolusProfiles,
|
||||||
onChanged: (value) {
|
onChanged: updateBolusProfile,
|
||||||
setState(() {
|
),
|
||||||
_bolusProfile = value;
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _bolusProfile ==
|
||||||
|
null
|
||||||
|
? const BolusProfileDetailScreen()
|
||||||
|
: BolusProfileDetailScreen(
|
||||||
|
id: _basalProfile!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateBolusProfile(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
icon: Icon(_bolusProfile == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<BasalProfile>(
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<
|
||||||
|
BasalProfile>(
|
||||||
|
controller: _basalProfileController,
|
||||||
selectedItem: _basalProfile,
|
selectedItem: _basalProfile,
|
||||||
label: 'Basal Profile',
|
label: 'Basal Profile',
|
||||||
items: _basalProfiles,
|
items: _basalProfiles,
|
||||||
onChanged: (value) {
|
onChanged: updateBasalProfile,
|
||||||
setState(() {
|
),
|
||||||
_basalProfile = value;
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _basalProfile ==
|
||||||
|
null
|
||||||
|
? const BasalProfileDetailScreen()
|
||||||
|
: BasalProfileDetailScreen(
|
||||||
|
id: _basalProfile!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateBasalProfile(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
icon: Icon(_basalProfile == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
@ -381,15 +466,17 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: _isSaving ? null : handleSaveAction,
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
|
@ -17,6 +17,7 @@ class LogEventListScreen extends StatefulWidget {
|
|||||||
class _LogEventListScreenState extends State<LogEventListScreen> {
|
class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||||
List<LogEvent> _activeEvents = [];
|
List<LogEvent> _activeEvents = [];
|
||||||
late Map<DateTime, List<LogEvent>> _logEventDailyMap;
|
late Map<DateTime, List<LogEvent>> _logEventDailyMap;
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -51,7 +52,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
return const LogEventDetailScreen();
|
return const LogEventDetailScreen();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleEditAction(LogEvent event) {
|
void handleEditAction(LogEvent event) {
|
||||||
@ -62,7 +63,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
id: event.id,
|
id: event.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDelete(LogEvent logEvent) {
|
void onDelete(LogEvent logEvent) {
|
||||||
@ -116,7 +117,10 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _logEventDailyMap.isNotEmpty
|
child: _logEventDailyMap.isNotEmpty
|
||||||
? ListView.builder(
|
? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
itemCount: _logEventDailyMap.length,
|
itemCount: _logEventDailyMap.length,
|
||||||
@ -132,29 +136,31 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
final tiles = <Widget>[];
|
final tiles = <Widget>[];
|
||||||
if (eventList != null) {
|
if (eventList != null) {
|
||||||
for (LogEvent event in eventList) {
|
for (LogEvent event in eventList) {
|
||||||
tiles.add(ListTile(
|
tiles.add(Card(
|
||||||
|
child: ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
handleEditAction(event);
|
handleEditAction(event);
|
||||||
},
|
},
|
||||||
title: Row(
|
title: Row(
|
||||||
crossAxisAlignment:
|
crossAxisAlignment:
|
||||||
CrossAxisAlignment.center,
|
CrossAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Text(date == null
|
||||||
child: Text(date == null
|
|
||||||
? DateTimeUtils
|
? DateTimeUtils
|
||||||
.displayDateTime(
|
.displayDateTime(
|
||||||
event.time)
|
event.time)
|
||||||
: DateTimeUtils.displayTime(
|
: DateTimeUtils.displayTime(
|
||||||
event.isEndEvent
|
event.isEndEvent
|
||||||
? event.endTime
|
? event.endTime
|
||||||
: event.time))),
|
: event.time),
|
||||||
|
),
|
||||||
const SizedBox(width: 24),
|
const SizedBox(width: 24),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(event.title ??
|
child: Text(
|
||||||
event.eventType.target?.value ??
|
event.title ?? event.eventType.target?.value ?? '',
|
||||||
''),
|
// style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -190,17 +196,24 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return eventList != null && eventList.isNotEmpty ? ListBody(
|
return eventList != null && eventList.isNotEmpty ? ListBody(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(DateTimeUtils.displayDate(date,
|
Padding(
|
||||||
fallback: 'Active Events'))
|
padding: const EdgeInsets.all(10.0),
|
||||||
] + tiles,
|
child: Text(
|
||||||
|
DateTimeUtils.displayDate(date, fallback: 'Active Events').toUpperCase(),
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
] + tiles +
|
||||||
|
[const Divider()],
|
||||||
) : Container();
|
) : Container();
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: const Center(
|
: const Center(
|
||||||
child: Text('There are no Events!'),
|
child: Text('There are no Events!'),
|
||||||
|
@ -7,6 +7,8 @@ import 'package:diameter/models/bolus_profile.dart';
|
|||||||
import 'package:diameter/models/log_event_type.dart';
|
import 'package:diameter/models/log_event_type.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
|
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
||||||
|
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class EventTypeDetailScreen extends StatefulWidget {
|
class EventTypeDetailScreen extends StatefulWidget {
|
||||||
@ -15,8 +17,7 @@ class EventTypeDetailScreen extends StatefulWidget {
|
|||||||
const EventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
const EventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_EventTypeDetailScreenState createState() =>
|
_EventTypeDetailScreenState createState() => _EventTypeDetailScreenState();
|
||||||
_EventTypeDetailScreenState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
||||||
@ -28,13 +29,17 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
List<BasalProfile> _basalProfiles = [];
|
List<BasalProfile> _basalProfiles = [];
|
||||||
|
|
||||||
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _defaultReminderDurationController = TextEditingController(text: '');
|
final _defaultReminderDurationController = TextEditingController(text: '');
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
bool _hasEndTime = false;
|
bool _hasEndTime = false;
|
||||||
BolusProfile? _bolusProfile;
|
BolusProfile? _bolusProfile;
|
||||||
BasalProfile? _basalProfile;
|
BasalProfile? _basalProfile;
|
||||||
|
final _bolusProfileController = TextEditingController(text: '');
|
||||||
|
final _basalProfileController = TextEditingController(text: '');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -52,17 +57,45 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
_hasEndTime = _logEventType!.hasEndTime;
|
_hasEndTime = _logEventType!.hasEndTime;
|
||||||
_notesController.text = _logEventType!.notes ?? '';
|
_notesController.text = _logEventType!.notes ?? '';
|
||||||
_basalProfile = _logEventType!.basalProfile.target;
|
_basalProfile = _logEventType!.basalProfile.target;
|
||||||
|
_basalProfileController.text = (_basalProfile ?? '').toString();
|
||||||
_bolusProfile = _logEventType!.bolusProfile.target;
|
_bolusProfile = _logEventType!.bolusProfile.target;
|
||||||
|
_bolusProfileController.text = (_bolusProfile ?? '').toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_logEventType = LogEventType.get(widget.id);
|
_logEventType = LogEventType.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _logEventType == null;
|
_isNew = _logEventType == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateBasalProfile(BasalProfile? value) {
|
||||||
|
setState(() {
|
||||||
|
_basalProfile = value;
|
||||||
|
_basalProfileController.text = (_basalProfile ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateBolusProfile(BolusProfile? value) {
|
||||||
|
setState(() {
|
||||||
|
_bolusProfile = value;
|
||||||
|
_bolusProfileController.text = (_bolusProfile ?? '').toString();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
@ -81,7 +114,8 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
eventType.basalProfile.target = _basalProfile;
|
eventType.basalProfile.target = _basalProfile;
|
||||||
eventType.bolusProfile.target = _bolusProfile;
|
eventType.bolusProfile.target = _bolusProfile;
|
||||||
LogEventType.put(eventType);
|
LogEventType.put(eventType);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Log Event Type Saved');
|
Navigator.pop(
|
||||||
|
context, ['${_isNew ? 'New' : ''} Log Event Type Saved', eventType]);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = false;
|
_isSaving = false;
|
||||||
@ -121,18 +155,18 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
),
|
),
|
||||||
drawer:
|
drawer:
|
||||||
const Navigation(currentLocation: EventTypeDetailScreen.routeName),
|
const Navigation(currentLocation: EventTypeDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FormWrapper(
|
FormWrapper(formState: _logEventTypeForm, fields: [
|
||||||
formState: _logEventTypeForm,
|
|
||||||
fields: [
|
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _valueController,
|
controller: _valueController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Name',
|
labelText: 'Name',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value!.trim().isEmpty) {
|
if (value!.trim().isEmpty) {
|
||||||
@ -151,50 +185,112 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
children: _hasEndTime ? [
|
children: _hasEndTime
|
||||||
TextFormField(
|
? [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: TextFormField(
|
||||||
controller: _defaultReminderDurationController,
|
controller: _defaultReminderDurationController,
|
||||||
keyboardType: const TextInputType.numberWithOptions(),
|
keyboardType:
|
||||||
|
const TextInputType.numberWithOptions(),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Default Reminder Duration',
|
labelText: 'Default Reminder Duration',
|
||||||
suffixText: ' min',
|
suffixText: ' min',
|
||||||
enabled: _hasEndTime,
|
enabled: _hasEndTime,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<BolusProfile>(
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<
|
||||||
|
BolusProfile>(
|
||||||
selectedItem: _bolusProfile,
|
selectedItem: _bolusProfile,
|
||||||
|
controller: _bolusProfileController,
|
||||||
label: 'Bolus Profile',
|
label: 'Bolus Profile',
|
||||||
items: _bolusProfiles,
|
items: _bolusProfiles,
|
||||||
onChanged: (value) {
|
onChanged: updateBolusProfile,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _bolusProfile ==
|
||||||
|
null
|
||||||
|
? const BolusProfileDetailScreen()
|
||||||
|
: BolusProfileDetailScreen(
|
||||||
|
id: _bolusProfile!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_bolusProfile = value;
|
updateBolusProfile(result?[1]);
|
||||||
|
_bolusProfileController.text =
|
||||||
|
_bolusProfile.toString();
|
||||||
|
});
|
||||||
|
reload(message: result?[0]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
icon: Icon(_bolusProfile == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child:
|
||||||
AutoCompleteDropdownButton<BasalProfile>(
|
AutoCompleteDropdownButton<BasalProfile>(
|
||||||
|
controller: _basalProfileController,
|
||||||
selectedItem: _basalProfile,
|
selectedItem: _basalProfile,
|
||||||
label: 'Basal Profile',
|
label: 'Basal Profile',
|
||||||
items: _basalProfiles,
|
items: _basalProfiles,
|
||||||
onChanged: (value) {
|
onChanged: updateBasalProfile,
|
||||||
setState(() {
|
),
|
||||||
_basalProfile = value;
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _basalProfile ==
|
||||||
|
null
|
||||||
|
? const BasalProfileDetailScreen()
|
||||||
|
: BasalProfileDetailScreen(
|
||||||
|
id: _basalProfile!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateBasalProfile(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
icon: Icon(_basalProfile == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
),
|
),
|
||||||
] : []),
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
]
|
]),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: _isSaving ? null : handleSaveAction,
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
|
@ -14,6 +14,8 @@ class LogEventTypeListScreen extends StatefulWidget {
|
|||||||
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||||
List<LogEventType> _logEventTypes = [];
|
List<LogEventType> _logEventTypes = [];
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -49,23 +51,30 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _logEventTypes.isNotEmpty ? ListView.builder(
|
child: _logEventTypes.isNotEmpty
|
||||||
|
? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
itemCount: _logEventTypes.length,
|
itemCount: _logEventTypes.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final logEventType = _logEventTypes[index];
|
final logEventType = _logEventTypes[index];
|
||||||
return ListTile(
|
return Card(
|
||||||
|
child: ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
EventTypeDetailScreen(
|
EventTypeDetailScreen(id: logEventType.id),
|
||||||
id: logEventType.id),
|
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
title: Text(logEventType.value),
|
title: Text(
|
||||||
|
logEventType.value.toUpperCase(),
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
subtitle: Text(logEventType.notes ?? ''),
|
subtitle: Text(logEventType.notes ?? ''),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -73,18 +82,21 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
LogEventType.remove(logEventType.id);
|
LogEventType.remove(logEventType.id);
|
||||||
reload(
|
reload(message: 'Log Event Type deleted');
|
||||||
message: 'Log Event Type deleted');
|
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.delete,
|
icon: const Icon(Icons.delete,
|
||||||
color: Colors.blue),
|
color: Colors.blue),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
) : const Center(
|
),
|
||||||
child: Text('You have not created any Log Event Types yet!'),
|
)
|
||||||
|
: const Center(
|
||||||
|
child:
|
||||||
|
Text('You have not created any Log Event Types yet!'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -96,7 +108,7 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const EventTypeDetailScreen(),
|
builder: (context) => const EventTypeDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -22,6 +22,7 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
|||||||
bool _isNew = true;
|
bool _isNew = true;
|
||||||
|
|
||||||
final GlobalKey<FormState> _mealCategoryForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _mealCategoryForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
@ -37,22 +38,38 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealCategory = MealCategory.get(widget.id);
|
_mealCategory = MealCategory.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _mealCategory == null;
|
_isNew = _mealCategory == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
if (_mealCategoryForm.currentState!.validate()) {
|
if (_mealCategoryForm.currentState!.validate()) {
|
||||||
MealCategory.put(MealCategory(
|
MealCategory mealCategory = MealCategory(
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
value: _valueController.text,
|
value: _valueController.text,
|
||||||
notes: _notesController.text));
|
notes: _notesController.text,
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Category saved');
|
);
|
||||||
|
MealCategory.put(mealCategory);
|
||||||
|
Navigator.pop(context, [
|
||||||
|
'${_isNew ? 'New' : ''} Meal Category saved', mealCategory
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +98,10 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
|||||||
),
|
),
|
||||||
drawer:
|
drawer:
|
||||||
const Navigation(currentLocation: MealCategoryDetailScreen.routeName),
|
const Navigation(currentLocation: MealCategoryDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -104,15 +124,17 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
|||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
|
@ -17,6 +17,8 @@ class MealCategoryListScreen extends StatefulWidget {
|
|||||||
class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
||||||
List<MealCategory> _mealCategories = [];
|
List<MealCategory> _mealCategories = [];
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -75,13 +77,16 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _mealCategories.isNotEmpty ? ListView.builder(
|
child: _mealCategories.isNotEmpty ? Scrollbar(
|
||||||
padding: const EdgeInsets.only(top: 10.0),
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
itemCount: _mealCategories.length,
|
itemCount: _mealCategories.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final mealCategory = _mealCategories[index];
|
final mealCategory = _mealCategories[index];
|
||||||
|
return Card(
|
||||||
return ListTile(
|
child: ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
@ -91,10 +96,12 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
id: mealCategory.id,
|
id: mealCategory.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
title: Text(mealCategory.value),
|
title: Text(
|
||||||
subtitle: Text(mealCategory.notes ?? ''),
|
mealCategory.value.toUpperCase(),
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -108,8 +115,10 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
): const Center(
|
): const Center(
|
||||||
child: Text('You have not created any Meal Categories yet!'),
|
child: Text('You have not created any Meal Categories yet!'),
|
||||||
),
|
),
|
||||||
@ -123,7 +132,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const MealCategoryDetailScreen(),
|
builder: (context) => const MealCategoryDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -9,6 +9,10 @@ import 'package:diameter/models/meal_portion_type.dart';
|
|||||||
import 'package:diameter/models/meal_source.dart';
|
import 'package:diameter/models/meal_source.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
|
import 'package:diameter/screens/accuracy_detail.dart';
|
||||||
|
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
||||||
|
import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
|
||||||
|
import 'package:diameter/screens/meal/meal_source_detail.dart';
|
||||||
import 'package:diameter/utils/utils.dart';
|
import 'package:diameter/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -24,10 +28,13 @@ class MealDetailScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _MealDetailScreenState extends State<MealDetailScreen> {
|
class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||||
Meal? _meal;
|
Meal? _meal;
|
||||||
|
|
||||||
bool _isNew = true;
|
bool _isNew = true;
|
||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
|
bool _isExpanded = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _mealForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _mealForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _carbsRatioController = TextEditingController(text: '');
|
final _carbsRatioController = TextEditingController(text: '');
|
||||||
@ -44,6 +51,12 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
Accuracy? _portionSizeAccuracy;
|
Accuracy? _portionSizeAccuracy;
|
||||||
Accuracy? _carbsRatioAccuracy;
|
Accuracy? _carbsRatioAccuracy;
|
||||||
|
|
||||||
|
final _mealSourceController = TextEditingController(text: '');
|
||||||
|
final _mealCategoryController = TextEditingController(text: '');
|
||||||
|
final _mealPortionTypeController = TextEditingController(text: '');
|
||||||
|
final _portionSizeAccuracyController = TextEditingController(text: '');
|
||||||
|
final _carbsRatioAccuracyController = TextEditingController(text: '');
|
||||||
|
|
||||||
List<MealCategory> _mealCategories = [];
|
List<MealCategory> _mealCategories = [];
|
||||||
List<MealPortionType> _mealPortionTypes = [];
|
List<MealPortionType> _mealPortionTypes = [];
|
||||||
List<MealSource> _mealSources = [];
|
List<MealSource> _mealSources = [];
|
||||||
@ -68,27 +81,75 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
_portionSizeController.text = (_meal!.portionSize ?? '').toString();
|
_portionSizeController.text = (_meal!.portionSize ?? '').toString();
|
||||||
_carbsPerPortionController.text =
|
_carbsPerPortionController.text =
|
||||||
(_meal!.carbsPerPortion ?? '').toString();
|
(_meal!.carbsPerPortion ?? '').toString();
|
||||||
_delayedBolusPercentage =
|
_delayedBolusPercentage = _meal!.delayedBolusPercentage ?? 0;
|
||||||
_meal!.delayedBolusPercentage ?? 0;
|
|
||||||
_delayedBolusDurationController.text =
|
_delayedBolusDurationController.text =
|
||||||
(_meal!.delayedBolusDuration ?? '').toString();
|
(_meal!.delayedBolusDuration ?? '').toString();
|
||||||
_notesController.text = _meal!.notes ?? '';
|
_notesController.text = _meal!.notes ?? '';
|
||||||
|
|
||||||
_mealSource = _meal!.mealSource.target;
|
_mealSource = _meal!.mealSource.target;
|
||||||
|
_mealSourceController.text = (_mealSource ?? '').toString();
|
||||||
_mealCategory = _meal!.mealCategory.target;
|
_mealCategory = _meal!.mealCategory.target;
|
||||||
|
_mealCategoryController.text = (_mealCategory ?? '').toString();
|
||||||
_mealPortionType = _meal!.mealPortionType.target;
|
_mealPortionType = _meal!.mealPortionType.target;
|
||||||
|
_mealPortionTypeController.text = (_mealPortionType ?? '').toString();
|
||||||
_portionSizeAccuracy = _meal!.portionSizeAccuracy.target;
|
_portionSizeAccuracy = _meal!.portionSizeAccuracy.target;
|
||||||
|
_portionSizeAccuracyController.text =
|
||||||
|
(_portionSizeAccuracy ?? '').toString();
|
||||||
_carbsRatioAccuracy = _meal!.carbsRatioAccuracy.target;
|
_carbsRatioAccuracy = _meal!.carbsRatioAccuracy.target;
|
||||||
|
_carbsRatioAccuracyController.text =
|
||||||
|
(_carbsRatioAccuracy ?? '').toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_meal = Meal.get(widget.id);
|
_meal = Meal.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _meal == null;
|
_isNew = _meal == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCarbsRatioAccuracy(Accuracy? value) {
|
||||||
|
setState(() {
|
||||||
|
_carbsRatioAccuracy = value;
|
||||||
|
_carbsRatioAccuracyController.text =
|
||||||
|
(_carbsRatioAccuracy ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePortionSizeAccuracy(Accuracy? value) {
|
||||||
|
setState(() {
|
||||||
|
_portionSizeAccuracy = value;
|
||||||
|
_portionSizeAccuracyController.text =
|
||||||
|
(_portionSizeAccuracy ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMealCategory(MealCategory? value) {
|
||||||
|
setState(() {
|
||||||
|
_mealCategory = value;
|
||||||
|
_mealCategoryController.text = (_mealCategory ?? '').toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMealPortionType(MealPortionType? value) {
|
||||||
|
setState(() {
|
||||||
|
_mealPortionType = value;
|
||||||
|
_mealPortionTypeController.text = (_mealPortionType ?? '').toString();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
@ -114,7 +175,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
meal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
meal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
||||||
|
|
||||||
Meal.put(meal);
|
Meal.put(meal);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Saved');
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Meal Saved', meal]);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = false;
|
_isSaving = false;
|
||||||
@ -152,8 +213,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
_portionSizeAccuracy != _meal!.portionSizeAccuracy.target ||
|
_portionSizeAccuracy != _meal!.portionSizeAccuracy.target ||
|
||||||
int.tryParse(_delayedBolusDurationController.text) !=
|
int.tryParse(_delayedBolusDurationController.text) !=
|
||||||
_meal!.delayedBolusDuration ||
|
_meal!.delayedBolusDuration ||
|
||||||
_delayedBolusPercentage !=
|
_delayedBolusPercentage != _meal!.delayedBolusPercentage ||
|
||||||
_meal!.delayedBolusPercentage ||
|
|
||||||
_notesController.text != (_meal!.notes ?? ''))))) {
|
_notesController.text != (_meal!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
Dialogs.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -165,40 +225,40 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onSelectMealSource(MealSource mealSource) async {
|
Future<void> onSelectMealSource(MealSource? mealSource) async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealSource = mealSource;
|
_mealSource = mealSource;
|
||||||
|
_mealSourceController.text = _mealSource.toString();
|
||||||
|
});
|
||||||
|
if (mealSource != null) {
|
||||||
if (mealSource.defaultCarbsRatioAccuracy.hasValue) {
|
if (mealSource.defaultCarbsRatioAccuracy.hasValue) {
|
||||||
_carbsRatioAccuracy = mealSource.defaultCarbsRatioAccuracy.target;
|
updateCarbsRatioAccuracy(mealSource.defaultCarbsRatioAccuracy.target);
|
||||||
}
|
}
|
||||||
if (mealSource.defaultPortionSizeAccuracy.hasValue) {
|
if (mealSource.defaultPortionSizeAccuracy.hasValue) {
|
||||||
_portionSizeAccuracy = mealSource.defaultPortionSizeAccuracy.target;
|
updatePortionSizeAccuracy(mealSource.defaultPortionSizeAccuracy.target);
|
||||||
}
|
}
|
||||||
if (mealSource.defaultMealCategory.hasValue) {
|
if (mealSource.defaultMealCategory.hasValue) {
|
||||||
_mealCategory = mealSource.defaultMealCategory.target;
|
updateMealCategory(mealSource.defaultMealCategory.target);
|
||||||
}
|
}
|
||||||
if (mealSource.defaultMealPortionType.hasValue) {
|
if (mealSource.defaultMealPortionType.hasValue) {
|
||||||
_mealPortionType = mealSource.defaultMealPortionType.target;
|
updateMealPortionType(mealSource.defaultMealPortionType.target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateThirdMeasurementOfPortionCarbsRelation(
|
void calculateThirdMeasurementOfPortionCarbsRelation(
|
||||||
{PortionCarbsParameter? parameterToBeCalculated}) {
|
{PortionCarbsParameter? changedParameter}) {
|
||||||
double? carbsRatio;
|
double? carbsRatio;
|
||||||
double? portionSize;
|
double? portionSize;
|
||||||
double? carbsPerPortion;
|
double? carbsPerPortion;
|
||||||
|
|
||||||
if (parameterToBeCalculated != PortionCarbsParameter.carbsRatio &&
|
if (_carbsRatioController.text != '') {
|
||||||
_carbsRatioController.text != '') {
|
|
||||||
carbsRatio = double.tryParse(_carbsRatioController.text);
|
carbsRatio = double.tryParse(_carbsRatioController.text);
|
||||||
}
|
}
|
||||||
if (parameterToBeCalculated != PortionCarbsParameter.portionSize &&
|
if (_portionSizeController.text != '') {
|
||||||
_portionSizeController.text != '') {
|
|
||||||
portionSize = double.tryParse(_portionSizeController.text);
|
portionSize = double.tryParse(_portionSizeController.text);
|
||||||
}
|
}
|
||||||
if (parameterToBeCalculated != PortionCarbsParameter.carbsPerPortion &&
|
if (_carbsRatioController.text != '') {
|
||||||
_carbsRatioController.text != '') {
|
|
||||||
carbsPerPortion = double.tryParse(_carbsPerPortionController.text);
|
carbsPerPortion = double.tryParse(_carbsPerPortionController.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,9 +292,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
title: Text(_isNew ? 'New Meal' : _meal!.value),
|
title: Text(_isNew ? 'New Meal' : _meal!.value),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: MealDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: MealDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FormWrapper(
|
FormWrapper(
|
||||||
formState: _mealForm,
|
formState: _mealForm,
|
||||||
@ -251,35 +313,66 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<MealSource>(
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<MealSource>(
|
||||||
|
controller: _mealSourceController,
|
||||||
selectedItem: _mealSource,
|
selectedItem: _mealSource,
|
||||||
label: 'Meal Source',
|
label: 'Meal Source',
|
||||||
items: _mealSources,
|
items: _mealSources,
|
||||||
onChanged: (value) {
|
onChanged: onSelectMealSource,
|
||||||
if (value != null) {
|
|
||||||
onSelectMealSource(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<MealCategory>(
|
),
|
||||||
selectedItem: _mealCategory,
|
IconButton(
|
||||||
label: 'Meal Category',
|
onPressed: () {
|
||||||
items: _mealCategories,
|
Navigator.push(
|
||||||
onChanged: (value) {
|
context,
|
||||||
setState(() {
|
MaterialPageRoute(
|
||||||
_mealCategory = value;
|
builder: (context) => _mealSource == null
|
||||||
|
? const MealSourceDetailScreen()
|
||||||
|
: MealSourceDetailScreen(id: _mealSource!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
onSelectMealSource(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
icon:
|
||||||
|
Icon(_mealSource == null ? Icons.add : Icons.edit),
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<MealPortionType>(
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<MealPortionType>(
|
||||||
|
controller: _mealPortionTypeController,
|
||||||
selectedItem: _mealPortionType,
|
selectedItem: _mealPortionType,
|
||||||
label: 'Meal Portion Type',
|
label: 'Meal Portion Type',
|
||||||
items: _mealPortionTypes,
|
items: _mealPortionTypes,
|
||||||
onChanged: (value) {
|
onChanged: updateMealPortionType,
|
||||||
setState(() {
|
),
|
||||||
_mealPortionType = value;
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _mealPortionType == null
|
||||||
|
? const MealPortionTypeDetailScreen()
|
||||||
|
: MealPortionTypeDetailScreen(
|
||||||
|
id: _mealPortionType!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateMealPortionType(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
icon: Icon(
|
||||||
|
_mealPortionType == null ? Icons.add : Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -298,24 +391,12 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
const SizedBox(width: 10),
|
||||||
onPressed: () =>
|
|
||||||
calculateThirdMeasurementOfPortionCarbsRelation(
|
|
||||||
parameterToBeCalculated:
|
|
||||||
PortionCarbsParameter.carbsRatio),
|
|
||||||
icon: const Icon(Icons.calculate),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Portion size',
|
labelText: 'Portion size',
|
||||||
suffixText: Settings.nutritionMeasurementSuffix,
|
suffixText: Settings.nutritionMeasurementSuffix,
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
controller: _portionSizeController,
|
controller: _portionSizeController,
|
||||||
keyboardType: const TextInputType.numberWithOptions(
|
keyboardType: const TextInputType.numberWithOptions(
|
||||||
@ -326,27 +407,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
const SizedBox(width: 10),
|
||||||
onPressed: () =>
|
|
||||||
calculateThirdMeasurementOfPortionCarbsRelation(
|
|
||||||
parameterToBeCalculated:
|
|
||||||
PortionCarbsParameter.portionSize),
|
|
||||||
icon: const Icon(Icons.calculate),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
AutoCompleteDropdownButton<Accuracy>(
|
|
||||||
selectedItem: _portionSizeAccuracy,
|
|
||||||
label: 'Portion Size Accuracy',
|
|
||||||
items: _portionSizeAccuracies,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_portionSizeAccuracy = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@ -362,43 +423,48 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
|
||||||
onPressed: () =>
|
|
||||||
calculateThirdMeasurementOfPortionCarbsRelation(
|
|
||||||
parameterToBeCalculated:
|
|
||||||
PortionCarbsParameter.carbsPerPortion),
|
|
||||||
icon: const Icon(Icons.calculate),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<Accuracy>(
|
TextFormField(
|
||||||
selectedItem: _carbsRatioAccuracy,
|
controller: _notesController,
|
||||||
label: 'Carbs Ratio Accuracy',
|
decoration: const InputDecoration(
|
||||||
items: _carbsRatioAccuracies,
|
labelText: 'Notes',
|
||||||
onChanged: (value) {
|
),
|
||||||
setState(() {
|
keyboardType: TextInputType.multiline,
|
||||||
_carbsRatioAccuracy = value;
|
minLines: 2,
|
||||||
});
|
maxLines: 5,
|
||||||
},
|
),
|
||||||
|
const Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'BOLUS DELAY',
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
// ignore: todo
|
// ignore: todo
|
||||||
// TODO: display according to time format
|
// TODO: display according to time format
|
||||||
TextFormField(
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Delayed Bolus Duration',
|
labelText: 'Duration',
|
||||||
suffixText: ' min',
|
suffixText: ' min',
|
||||||
),
|
),
|
||||||
controller: _delayedBolusDurationController,
|
controller: _delayedBolusDurationController,
|
||||||
keyboardType: const TextInputType.numberWithOptions(),
|
keyboardType: const TextInputType.numberWithOptions(),
|
||||||
),
|
),
|
||||||
Padding(
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 5.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const Text('Delayed Bolus Percentage:'),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Slider(
|
child: Slider(
|
||||||
label: '${_delayedBolusPercentage.floor().toString()}%',
|
label:
|
||||||
|
'${_delayedBolusPercentage.floor().toString()}%',
|
||||||
divisions: 100,
|
divisions: 100,
|
||||||
value: _delayedBolusPercentage,
|
value: _delayedBolusPercentage,
|
||||||
min: 0,
|
min: 0,
|
||||||
@ -407,25 +473,157 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_delayedBolusPercentage = value;
|
_delayedBolusPercentage = value;
|
||||||
});
|
});
|
||||||
}
|
}),
|
||||||
),
|
),
|
||||||
|
const Text('%', textScaleFactor: 1.5),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => setState(() {
|
||||||
|
_isExpanded = !_isExpanded;
|
||||||
|
}),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'ADDITIONAL FIELDS',
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Icon(_isExpanded
|
||||||
|
? Icons.expand_less
|
||||||
|
: Icons.expand_more),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: _isExpanded
|
||||||
|
? [
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<
|
||||||
|
MealCategory>(
|
||||||
|
controller: _mealCategoryController,
|
||||||
|
selectedItem: _mealCategory,
|
||||||
|
label: 'Meal Category',
|
||||||
|
items: _mealCategories,
|
||||||
|
onChanged: updateMealCategory,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _mealCategory ==
|
||||||
|
null
|
||||||
|
? const MealCategoryDetailScreen()
|
||||||
|
: MealCategoryDetailScreen(
|
||||||
|
id: _mealCategory!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateMealCategory(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_mealCategory == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextFormField(
|
Padding(
|
||||||
controller: _notesController,
|
padding:
|
||||||
decoration: const InputDecoration(
|
const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
labelText: 'Notes',
|
child: Row(
|
||||||
alignLabelWithHint: true,
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<Accuracy>(
|
||||||
|
controller:
|
||||||
|
_portionSizeAccuracyController,
|
||||||
|
selectedItem: _portionSizeAccuracy,
|
||||||
|
label: 'Portion Size Accuracy',
|
||||||
|
items: _portionSizeAccuracies,
|
||||||
|
onChanged: updatePortionSizeAccuracy,
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
_portionSizeAccuracy == null
|
||||||
|
? const AccuracyDetailScreen()
|
||||||
|
: AccuracyDetailScreen(
|
||||||
|
id: _portionSizeAccuracy!
|
||||||
|
.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updatePortionSizeAccuracy(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_portionSizeAccuracy == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<Accuracy>(
|
||||||
|
controller: _carbsRatioAccuracyController,
|
||||||
|
selectedItem: _carbsRatioAccuracy,
|
||||||
|
label: 'Carbs Ratio Accuracy',
|
||||||
|
items: _carbsRatioAccuracies,
|
||||||
|
onChanged: updateCarbsRatioAccuracy,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
_carbsRatioAccuracy == null
|
||||||
|
? const AccuracyDetailScreen()
|
||||||
|
: AccuracyDetailScreen(
|
||||||
|
id: _carbsRatioAccuracy!
|
||||||
|
.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
updateCarbsRatioAccuracy(result?[1]);
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_carbsRatioAccuracy == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: _isSaving ? null : handleSaveAction,
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
|
@ -17,6 +17,8 @@ class MealListScreen extends StatefulWidget {
|
|||||||
class _MealListScreenState extends State<MealListScreen> {
|
class _MealListScreenState extends State<MealListScreen> {
|
||||||
List<Meal> _meals = [];
|
List<Meal> _meals = [];
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -68,13 +70,17 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _meals.isNotEmpty ? ListView.builder(
|
child: _meals.isNotEmpty ? Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
itemCount: _meals.length,
|
itemCount: _meals.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final meal = _meals[index];
|
final meal = _meals[index];
|
||||||
String portionType = meal.mealPortionType.hasValue ? ' per ${meal.mealPortionType.target!.value}' : '';
|
String portionType = meal.mealPortionType.hasValue ? ' per ${meal.mealPortionType.target!.value}' : '';
|
||||||
return ListTile(
|
return Card(
|
||||||
|
child: ListTile(
|
||||||
isThreeLine: true,
|
isThreeLine: true,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@ -83,10 +89,15 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
MealDetailScreen(id: meal.id),
|
MealDetailScreen(id: meal.id),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
title: Text(meal.value),
|
title: Text(
|
||||||
subtitle: Row(
|
meal.value.toUpperCase(),
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
subtitle: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
@ -96,6 +107,8 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: ((meal.carbsPerPortion ?? 0) > 0)
|
children: ((meal.carbsPerPortion ?? 0) > 0)
|
||||||
? [
|
? [
|
||||||
Text(meal.carbsPerPortion!.toStringAsPrecision(3)),
|
Text(meal.carbsPerPortion!.toStringAsPrecision(3)),
|
||||||
@ -113,15 +126,19 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
Text(meal.portionSize!.toStringAsPrecision(3)),
|
Text(meal.portionSize!.toStringAsPrecision(3)),
|
||||||
Text(
|
Text(
|
||||||
'${Settings.nutritionMeasurementSuffix}$portionType',
|
'${Settings.nutritionMeasurementSuffix}$portionType',
|
||||||
textScaleFactor: 0.75),
|
textAlign: TextAlign.center,
|
||||||
|
textScaleFactor: 0.75
|
||||||
|
),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => handleDeleteAction(meal),
|
onPressed: () => handleDeleteAction(meal),
|
||||||
@ -130,8 +147,10 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
): const Center(
|
): const Center(
|
||||||
child: Text('You have not created any Meals yet!'),
|
child: Text('You have not created any Meals yet!'),
|
||||||
),
|
),
|
||||||
@ -145,7 +164,7 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const MealDetailScreen(),
|
builder: (context) => const MealDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -24,6 +24,7 @@ class _MealPortionTypeDetailScreenState
|
|||||||
bool _isNew = true;
|
bool _isNew = true;
|
||||||
|
|
||||||
final GlobalKey<FormState> _mealPortionTypeForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _mealPortionTypeForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
@ -39,23 +40,37 @@ class _MealPortionTypeDetailScreenState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealPortionType = MealPortionType.get(widget.id);
|
_mealPortionType = MealPortionType.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _mealPortionType == null;
|
_isNew = _mealPortionType == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
if (_mealPortionTypeForm.currentState!.validate()) {
|
if (_mealPortionTypeForm.currentState!.validate()) {
|
||||||
MealPortionType.put(MealPortionType(
|
MealPortionType mealPortionType = MealPortionType(
|
||||||
id: _mealPortionType?.id ?? 0,
|
id: _mealPortionType?.id ?? 0,
|
||||||
value: _valueController.text,
|
value: _valueController.text,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
));
|
);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Portion Type saved');
|
MealPortionType.put(mealPortionType);
|
||||||
|
Navigator.pop(context,
|
||||||
|
['${_isNew ? 'New' : ''} Meal Portion Type saved', mealPortionType]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,12 +97,14 @@ class _MealPortionTypeDetailScreenState
|
|||||||
bool isNew = _mealPortionType == null;
|
bool isNew = _mealPortionType == null;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(isNew ? 'New Meal Portion Type' : _mealPortionType!.value),
|
||||||
isNew ? 'New Meal Portion Type' : _mealPortionType!.value),
|
|
||||||
),
|
),
|
||||||
drawer: const Navigation(
|
drawer: const Navigation(
|
||||||
currentLocation: MealPortionTypeDetailScreen.routeName),
|
currentLocation: MealPortionTypeDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -110,15 +127,17 @@ class _MealPortionTypeDetailScreenState
|
|||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
|
@ -18,6 +18,8 @@ class MealPortionTypeListScreen extends StatefulWidget {
|
|||||||
class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
||||||
List<MealPortionType> _mealPortionTypes = [];
|
List<MealPortionType> _mealPortionTypes = [];
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -73,13 +75,16 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _mealPortionTypes.isNotEmpty ? ListView.builder(
|
child: _mealPortionTypes.isNotEmpty ? Scrollbar(
|
||||||
padding: const EdgeInsets.only(top: 10.0),
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
itemCount: _mealPortionTypes.length,
|
itemCount: _mealPortionTypes.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final mealPortionType = _mealPortionTypes[index];
|
final mealPortionType = _mealPortionTypes[index];
|
||||||
|
return Card(
|
||||||
return ListTile(
|
child: ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
@ -92,8 +97,10 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
).then(
|
).then(
|
||||||
(message) => reload(message: message));
|
(message) => reload(message: message));
|
||||||
},
|
},
|
||||||
title: Text(mealPortionType.value),
|
title: Text(
|
||||||
subtitle: Text(mealPortionType.notes ?? ''),
|
mealPortionType.value.toUpperCase(),
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -107,8 +114,10 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
) : const Center(
|
) : const Center(
|
||||||
child: Text('You have not created any Meal Portion Types yet!'),
|
child: Text('You have not created any Meal Portion Types yet!'),
|
||||||
),
|
),
|
||||||
@ -122,7 +131,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const MealPortionTypeDetailScreen(),
|
builder: (context) => const MealPortionTypeDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -8,6 +8,9 @@ import 'package:diameter/models/meal_portion_type.dart';
|
|||||||
import 'package:diameter/models/meal_source.dart';
|
import 'package:diameter/models/meal_source.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
|
import 'package:diameter/screens/accuracy_detail.dart';
|
||||||
|
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
||||||
|
import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class MealSourceDetailScreen extends StatefulWidget {
|
class MealSourceDetailScreen extends StatefulWidget {
|
||||||
@ -31,13 +34,19 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
List<MealPortionType> _mealPortionTypes = [];
|
List<MealPortionType> _mealPortionTypes = [];
|
||||||
|
|
||||||
final GlobalKey<FormState> _mealSourceForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _mealSourceForm = GlobalKey<FormState>();
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
Accuracy? _defaultCarbsRatioAccuracy;
|
Accuracy? _defaultCarbsRatioAccuracy;
|
||||||
Accuracy? _defaultPortionSizeAccuracy;
|
Accuracy? _defaultPortionSizeAccuracy;
|
||||||
MealCategory? _defaultMealCategory;
|
MealCategory? _defaultMealCategory;
|
||||||
MealPortionType? _defaultMealPortionType;
|
MealPortionType? _defaultMealPortionType;
|
||||||
|
final _defaultCarbsRatioAccuracyController = TextEditingController(text: '');
|
||||||
|
final _defaultPortionSizeAccuracyController = TextEditingController(text: '');
|
||||||
|
final _defaultMealCategoryController = TextEditingController(text: '');
|
||||||
|
final _defaultMealPortionTypeController = TextEditingController(text: '');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -56,22 +65,37 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
|
|
||||||
_defaultPortionSizeAccuracy =
|
_defaultPortionSizeAccuracy =
|
||||||
_mealSource!.defaultPortionSizeAccuracy.target;
|
_mealSource!.defaultPortionSizeAccuracy.target;
|
||||||
|
_defaultPortionSizeAccuracyController.text = (_defaultPortionSizeAccuracy ?? '').toString();
|
||||||
_defaultCarbsRatioAccuracy =
|
_defaultCarbsRatioAccuracy =
|
||||||
_mealSource!.defaultCarbsRatioAccuracy.target;
|
_mealSource!.defaultCarbsRatioAccuracy.target;
|
||||||
|
_defaultCarbsRatioAccuracyController.text = (_defaultCarbsRatioAccuracy ?? '').toString();
|
||||||
|
|
||||||
_defaultMealCategory = _mealSource!.defaultMealCategory.target;
|
_defaultMealCategory = _mealSource!.defaultMealCategory.target;
|
||||||
_defaultMealPortionType =
|
_defaultMealCategoryController.text = (_defaultMealCategory ?? '').toString();
|
||||||
_mealSource!.defaultMealPortionType.target;
|
_defaultMealPortionType = _mealSource!.defaultMealPortionType.target;
|
||||||
|
_defaultMealPortionTypeController.text = (_defaultMealPortionType ?? '').toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealSource = MealSource.get(widget.id);
|
_mealSource = MealSource.get(widget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_isNew = _mealSource == null;
|
_isNew = _mealSource == null;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
@ -81,12 +105,11 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
);
|
);
|
||||||
mealSource.defaultCarbsRatioAccuracy.target = _defaultCarbsRatioAccuracy;
|
mealSource.defaultCarbsRatioAccuracy.target = _defaultCarbsRatioAccuracy;
|
||||||
mealSource.defaultPortionSizeAccuracy.target =
|
mealSource.defaultPortionSizeAccuracy.target = _defaultPortionSizeAccuracy;
|
||||||
_defaultPortionSizeAccuracy;
|
|
||||||
mealSource.defaultMealCategory.target = _defaultMealCategory;
|
mealSource.defaultMealCategory.target = _defaultMealCategory;
|
||||||
mealSource.defaultMealPortionType.target = _defaultMealPortionType;
|
mealSource.defaultMealPortionType.target = _defaultMealPortionType;
|
||||||
MealSource.put(mealSource);
|
MealSource.put(mealSource);
|
||||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Source saved');
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Meal Source saved', mealSource]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleCancelAction() {
|
void handleCancelAction() {
|
||||||
@ -108,8 +131,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
_mealSource!.defaultMealCategory.target ||
|
_mealSource!.defaultMealCategory.target ||
|
||||||
_defaultMealPortionType !=
|
_defaultMealPortionType !=
|
||||||
_mealSource!.defaultMealPortionType.target ||
|
_mealSource!.defaultMealPortionType.target ||
|
||||||
_notesController.text !=
|
_notesController.text != (_mealSource!.notes ?? ''))))) {
|
||||||
(_mealSource!.notes ?? ''))))) {
|
|
||||||
Dialogs.showCancelConfirmationDialog(
|
Dialogs.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
@ -128,7 +150,10 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
),
|
),
|
||||||
drawer:
|
drawer:
|
||||||
const Navigation(currentLocation: MealSourceDetailScreen.routeName),
|
const Navigation(currentLocation: MealSourceDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: Scrollbar(
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -147,59 +172,194 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<Accuracy>(
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<Accuracy>(
|
||||||
selectedItem: _defaultCarbsRatioAccuracy,
|
selectedItem: _defaultCarbsRatioAccuracy,
|
||||||
|
controller: _defaultCarbsRatioAccuracyController,
|
||||||
label: 'Default Carbs Ratio Accuracy',
|
label: 'Default Carbs Ratio Accuracy',
|
||||||
items: _carbsRatioAccuracies,
|
items: _carbsRatioAccuracies,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_defaultCarbsRatioAccuracy = value;
|
_defaultCarbsRatioAccuracy = value;
|
||||||
|
_defaultCarbsRatioAccuracyController.text =
|
||||||
|
(_defaultCarbsRatioAccuracy ?? '').toString();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<Accuracy>(
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
_defaultCarbsRatioAccuracy == null
|
||||||
|
? const AccuracyDetailScreen()
|
||||||
|
: AccuracyDetailScreen(
|
||||||
|
id: _defaultCarbsRatioAccuracy!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
setState(() {
|
||||||
|
_defaultCarbsRatioAccuracy = result?[1];
|
||||||
|
_defaultCarbsRatioAccuracyController.text =
|
||||||
|
(_defaultCarbsRatioAccuracy ?? '').toString();
|
||||||
|
});
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_defaultCarbsRatioAccuracy == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<Accuracy>(
|
||||||
selectedItem: _defaultPortionSizeAccuracy,
|
selectedItem: _defaultPortionSizeAccuracy,
|
||||||
|
controller: _defaultPortionSizeAccuracyController,
|
||||||
label: 'Default Portion Size Accuracy',
|
label: 'Default Portion Size Accuracy',
|
||||||
items: _portionSizeAccuracies,
|
items: _portionSizeAccuracies,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_defaultPortionSizeAccuracy = value;
|
_defaultPortionSizeAccuracy = value;
|
||||||
|
_defaultPortionSizeAccuracyController.text =
|
||||||
|
(_defaultPortionSizeAccuracy ?? '')
|
||||||
|
.toString();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<MealCategory>(
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
_defaultPortionSizeAccuracy == null
|
||||||
|
? const AccuracyDetailScreen()
|
||||||
|
: AccuracyDetailScreen(
|
||||||
|
id: _defaultPortionSizeAccuracy!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
setState(() {
|
||||||
|
_defaultPortionSizeAccuracy = result?[1];
|
||||||
|
_defaultPortionSizeAccuracyController.text =
|
||||||
|
(_defaultPortionSizeAccuracy ?? '')
|
||||||
|
.toString();
|
||||||
|
});
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_defaultPortionSizeAccuracy == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<MealCategory>(
|
||||||
selectedItem: _defaultMealCategory,
|
selectedItem: _defaultMealCategory,
|
||||||
|
controller: _defaultMealCategoryController,
|
||||||
label: 'Default Meal Category',
|
label: 'Default Meal Category',
|
||||||
items: _mealCategories,
|
items: _mealCategories,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_defaultMealCategory = value;
|
_defaultMealCategory = value;
|
||||||
|
_defaultMealCategoryController.text =
|
||||||
|
(_defaultMealCategory ?? '').toString();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<MealPortionType>(
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _defaultMealCategory == null
|
||||||
|
? const MealCategoryDetailScreen()
|
||||||
|
: MealCategoryDetailScreen(
|
||||||
|
id: _defaultMealCategory!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
setState(() {
|
||||||
|
_defaultMealCategory = result?[1];
|
||||||
|
_defaultMealCategoryController.text =
|
||||||
|
(_defaultMealCategory ?? '').toString();
|
||||||
|
});
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_defaultMealCategory == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AutoCompleteDropdownButton<MealPortionType>(
|
||||||
selectedItem: _defaultMealPortionType,
|
selectedItem: _defaultMealPortionType,
|
||||||
|
controller: _defaultMealPortionTypeController,
|
||||||
label: 'Default Meal Portion Type',
|
label: 'Default Meal Portion Type',
|
||||||
items: _mealPortionTypes,
|
items: _mealPortionTypes,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_defaultMealPortionType = value;
|
_defaultMealPortionType = value;
|
||||||
|
_defaultMealPortionTypeController.text =
|
||||||
|
(_defaultMealPortionType ?? '').toString();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
_defaultMealPortionType == null
|
||||||
|
? const MealPortionTypeDetailScreen()
|
||||||
|
: MealPortionTypeDetailScreen(
|
||||||
|
id: _defaultMealPortionType!.id),
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
setState(() {
|
||||||
|
_defaultMealPortionType = result?[1];
|
||||||
|
_defaultMealPortionTypeController.text =
|
||||||
|
(_defaultMealPortionType ?? '').toString();
|
||||||
|
});
|
||||||
|
reload(message: result?[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(_defaultMealPortionType == null
|
||||||
|
? Icons.add
|
||||||
|
: Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Notes',
|
labelText: 'Notes',
|
||||||
alignLabelWithHint: true,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 2,
|
||||||
|
maxLines: 5,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
|
@ -17,6 +17,8 @@ class MealSourceListScreen extends StatefulWidget {
|
|||||||
class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
||||||
List<MealSource> _mealSources = [];
|
List<MealSource> _mealSources = [];
|
||||||
|
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -71,13 +73,17 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _mealSources.isNotEmpty ? ListView.builder(
|
child: _mealSources.isNotEmpty ? Scrollbar(
|
||||||
padding: const EdgeInsets.only(top: 10.0),
|
controller: _scrollController,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
itemCount: _mealSources.length,
|
itemCount: _mealSources.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final mealSource = _mealSources[index];
|
final mealSource = _mealSources[index];
|
||||||
|
|
||||||
return ListTile(
|
return Card(
|
||||||
|
child: ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
@ -86,10 +92,12 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
id: mealSource.id,
|
id: mealSource.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
title: Text(mealSource.value),
|
title: Text(
|
||||||
subtitle: Text(mealSource.notes ?? ''),
|
mealSource.value.toUpperCase(),
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -104,8 +112,10 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
),
|
||||||
) : const Center(
|
) : const Center(
|
||||||
child: Text('You have not created any Meal Sources yet!'),
|
child: Text('You have not created any Meal Sources yet!'),
|
||||||
),
|
),
|
||||||
@ -119,7 +129,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const MealSourceDetailScreen(),
|
builder: (context) => const MealSourceDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -17,8 +17,8 @@ class SettingsScreen extends StatefulWidget {
|
|||||||
class _SettingsScreenState extends State<SettingsScreen> {
|
class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
late Settings _settings;
|
late Settings _settings;
|
||||||
|
|
||||||
late String _nutritionMeasurementLabel;
|
final TextEditingController _nutritionMeasurementLabelController = TextEditingController(text: '');
|
||||||
late String _glucoseMeasurementLabel;
|
final TextEditingController _glucoseMeasurementLabelController = TextEditingController(text: '');
|
||||||
|
|
||||||
late bool _onlyDisplayActiveGlucoseMeasurement;
|
late bool _onlyDisplayActiveGlucoseMeasurement;
|
||||||
late bool _displayBothGlucoseMeasurementsInDetailView;
|
late bool _displayBothGlucoseMeasurementsInDetailView;
|
||||||
@ -44,9 +44,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_settings = Settings.get();
|
_settings = Settings.get();
|
||||||
_nutritionMeasurementLabel =
|
_nutritionMeasurementLabelController.text =
|
||||||
nutritionMeasurementLabels[_settings.nutritionMeasurementIndex];
|
nutritionMeasurementLabels[_settings.nutritionMeasurementIndex];
|
||||||
_glucoseMeasurementLabel =
|
_glucoseMeasurementLabelController.text =
|
||||||
glucoseMeasurementLabels[_settings.glucoseMeasurementIndex];
|
glucoseMeasurementLabels[_settings.glucoseMeasurementIndex];
|
||||||
_onlyDisplayActiveGlucoseMeasurement = _settings.glucoseDisplayModeIndex == GlucoseDisplayMode.activeOnly.index;
|
_onlyDisplayActiveGlucoseMeasurement = _settings.glucoseDisplayModeIndex == GlucoseDisplayMode.activeOnly.index;
|
||||||
_displayBothGlucoseMeasurementsInDetailView =
|
_displayBothGlucoseMeasurementsInDetailView =
|
||||||
@ -93,9 +93,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
Settings.put(Settings(
|
Settings.put(Settings(
|
||||||
id: _settings.id,
|
id: _settings.id,
|
||||||
nutritionMeasurementIndex:
|
nutritionMeasurementIndex:
|
||||||
nutritionMeasurementLabels.indexOf(_nutritionMeasurementLabel),
|
nutritionMeasurementLabels.indexOf(_nutritionMeasurementLabelController.text),
|
||||||
glucoseMeasurementIndex:
|
glucoseMeasurementIndex:
|
||||||
glucoseMeasurementLabels.indexOf(_glucoseMeasurementLabel),
|
glucoseMeasurementLabels.indexOf(_glucoseMeasurementLabelController.text),
|
||||||
glucoseDisplayModeIndex: _onlyDisplayActiveGlucoseMeasurement
|
glucoseDisplayModeIndex: _onlyDisplayActiveGlucoseMeasurement
|
||||||
? GlucoseDisplayMode.activeOnly.index
|
? GlucoseDisplayMode.activeOnly.index
|
||||||
: _displayBothGlucoseMeasurementsInDetailView && _displayBothGlucoseMeasurementsInListView
|
: _displayBothGlucoseMeasurementsInDetailView && _displayBothGlucoseMeasurementsInListView
|
||||||
@ -157,28 +157,49 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'MEASUREMENTS',
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
AutoCompleteDropdownButton<String>(
|
AutoCompleteDropdownButton<String>(
|
||||||
selectedItem: _nutritionMeasurementLabel,
|
controller: _nutritionMeasurementLabelController,
|
||||||
|
selectedItem: _nutritionMeasurementLabelController.text,
|
||||||
label: 'Preferred Nutrition Measurement',
|
label: 'Preferred Nutrition Measurement',
|
||||||
items: nutritionMeasurementLabels,
|
items: nutritionMeasurementLabels,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
_nutritionMeasurementLabel = value;
|
setState(() {
|
||||||
|
_nutritionMeasurementLabelController.text = value;
|
||||||
|
});
|
||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AutoCompleteDropdownButton<String>(
|
Padding(
|
||||||
selectedItem: _glucoseMeasurementLabel,
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: AutoCompleteDropdownButton<String>(
|
||||||
|
controller: _glucoseMeasurementLabelController,
|
||||||
|
selectedItem: _glucoseMeasurementLabelController.text,
|
||||||
label: 'Preferred Glucose Measurement',
|
label: 'Preferred Glucose Measurement',
|
||||||
items: glucoseMeasurementLabels,
|
items: glucoseMeasurementLabels,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
_glucoseMeasurementLabel = value;
|
setState(() {
|
||||||
|
_glucoseMeasurementLabelController.text = value;
|
||||||
|
});
|
||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
BooleanFormField(
|
BooleanFormField(
|
||||||
value: _onlyDisplayActiveGlucoseMeasurement,
|
value: _onlyDisplayActiveGlucoseMeasurement,
|
||||||
label: 'only display active glucose measurement',
|
label: 'only display active glucose measurement',
|
||||||
@ -205,9 +226,18 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
saveSettings();
|
saveSettings();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Padding(
|
const Divider(),
|
||||||
padding: EdgeInsets.only(top: 10.0),
|
Padding(
|
||||||
child: Text('Confirmation prompts'),
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'CONFIRMATION PROMPTS',
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
BooleanFormField(
|
BooleanFormField(
|
||||||
value: _showConfirmationDialogOnCancel,
|
value: _showConfirmationDialogOnCancel,
|
||||||
|
18
pubspec.lock
18
pubspec.lock
@ -422,13 +422,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
objectbox_flutter_libs:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: objectbox_flutter_libs
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.0"
|
|
||||||
objectbox_generator:
|
objectbox_generator:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -436,6 +429,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
objectbox_sync_flutter_libs:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: objectbox_sync_flutter_libs
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -610,7 +610,7 @@ packages:
|
|||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.2.0"
|
||||||
sembast:
|
sembast:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -720,7 +720,7 @@ packages:
|
|||||||
name: sqflite
|
name: sqflite
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0+4"
|
version: "2.0.1"
|
||||||
sqflite_common:
|
sqflite_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -19,7 +19,7 @@ dependencies:
|
|||||||
shared_preferences: ^2.0.8
|
shared_preferences: ^2.0.8
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
objectbox: ^1.2.0
|
objectbox: ^1.2.0
|
||||||
objectbox_flutter_libs: any
|
objectbox_sync_flutter_libs: any
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -31,7 +31,3 @@ dev_dependencies:
|
|||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
fonts:
|
|
||||||
- family: RobotoCondensed
|
|
||||||
fonts:
|
|
||||||
- asset: assets/fonts/RobotoCondensed-Regular.ttf
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "tide",
|
"name": "diameter",
|
||||||
"short_name": "tide",
|
"short_name": "diameter",
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#0175C2",
|
"background_color": "#0175C2",
|
||||||
|
Loading…
Reference in New Issue
Block a user