Initial (redacted) commit.
This commit is contained in:
commit
655f8a036a
368 changed files with 20949 additions and 0 deletions
42
app/lib/infrastructure/bottom_navigation.dart
Normal file
42
app/lib/infrastructure/bottom_navigation.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class ScaffoldWithBottomNavigationBar extends StatelessWidget {
|
||||
const ScaffoldWithBottomNavigationBar({
|
||||
required this.navigationShell,
|
||||
Key? key,
|
||||
}) : super(key: key ?? const ValueKey<String>('ScaffoldWithNavBar'));
|
||||
|
||||
final StatefulNavigationShell navigationShell;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(child: navigationShell),
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
fixedColor: Theme.of(context).colorScheme.onPrimary,
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
items: const <BottomNavigationBarItem>[
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.list),
|
||||
label: 'Widget Wall',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.dashboard),
|
||||
label: 'Dashboard',
|
||||
),
|
||||
],
|
||||
currentIndex: navigationShell.currentIndex,
|
||||
onTap: (int index) => _onTap(context, index),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onTap(BuildContext context, int index) {
|
||||
navigationShell.goBranch(
|
||||
index,
|
||||
initialLocation: index == navigationShell.currentIndex,
|
||||
);
|
||||
}
|
||||
}
|
57
app/lib/infrastructure/routing.dart
Normal file
57
app/lib/infrastructure/routing.dart
Normal file
|
@ -0,0 +1,57 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'package:habitrack_app/infrastructure/bottom_navigation.dart';
|
||||
import 'package:habitrack_app/pages/dashboard_page.dart';
|
||||
import 'package:habitrack_app/pages/widget_page.dart';
|
||||
|
||||
// navigators, root and each destination of bottom navigation bar
|
||||
final _rootNavigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
final _shellNavigatorWidgetWallKey =
|
||||
GlobalKey<NavigatorState>(debugLabel: 'widgetWall');
|
||||
|
||||
final _shellNavigatorDashboardKey =
|
||||
GlobalKey<NavigatorState>(debugLabel: 'dashboard');
|
||||
|
||||
const String _widgetWallPath = '/widgetWall';
|
||||
const String _dashboardPath = '/dashboard';
|
||||
|
||||
final goRouter = GoRouter(
|
||||
initialLocation: _widgetWallPath,
|
||||
navigatorKey: _rootNavigatorKey,
|
||||
debugLogDiagnostics: true,
|
||||
routes: [
|
||||
StatefulShellRoute.indexedStack(
|
||||
builder: (context, state, navigationShell) {
|
||||
return ScaffoldWithBottomNavigationBar(
|
||||
navigationShell: navigationShell,
|
||||
);
|
||||
},
|
||||
branches: [
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _shellNavigatorWidgetWallKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: _widgetWallPath,
|
||||
pageBuilder: (context, state) => const NoTransitionPage(
|
||||
child: WidgetPage(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
navigatorKey: _shellNavigatorDashboardKey,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: _dashboardPath,
|
||||
pageBuilder: (context, state) => const NoTransitionPage(
|
||||
child: DashboardPage(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
27
app/lib/infrastructure/widget_wall/add_widget_button.dart
Normal file
27
app/lib/infrastructure/widget_wall/add_widget_button.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:habitrack_app/infrastructure/widget_wall/add_widget_menu.dart';
|
||||
|
||||
class AddWidgetButton extends StatelessWidget {
|
||||
const AddWidgetButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
onPressed: () => {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute<dynamic>(
|
||||
builder: (context) => const AddWidgetMenu(),
|
||||
),
|
||||
),
|
||||
},
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
150
app/lib/infrastructure/widget_wall/add_widget_menu.dart
Normal file
150
app/lib/infrastructure/widget_wall/add_widget_menu.dart
Normal file
|
@ -0,0 +1,150 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:habitrack_app/infrastructure/widget_wall/items_controller.dart';
|
||||
import 'package:habitrack_app/main.dart';
|
||||
import 'package:habitrack_app/sembast/hydration.dart';
|
||||
import 'package:habitrack_app/sembast/tasks_list.dart';
|
||||
import 'package:habitrack_app/sembast/timer.dart';
|
||||
|
||||
//Add Widget to List- Button Class ######################################
|
||||
class _AddWidgetToList extends ConsumerWidget {
|
||||
const _AddWidgetToList({
|
||||
required this.toAdd,
|
||||
required this.buttonText,
|
||||
required this.iconData,
|
||||
});
|
||||
|
||||
final dynamic toAdd;
|
||||
final String buttonText;
|
||||
final IconData iconData;
|
||||
|
||||
void _buttonFunc(BuildContext context, WidgetRef ref) {
|
||||
//ref.read(widgetListNotifierProvider.notifier).addWidget(toAdd());
|
||||
//ref.read(homeControllerProvider).add()
|
||||
ref.watch(homeControllerProvider).add(toAdd);
|
||||
|
||||
Navigator.pop(context);
|
||||
logger.i('Button Func Called');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0x00000000).withOpacity(0.25),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 2,
|
||||
),
|
||||
],
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
width: 300,
|
||||
child: TextButton(
|
||||
onPressed: () => {_buttonFunc(context, ref)},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
iconData,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
Text(
|
||||
buttonText,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// AddWidgetMenu ##########################################################
|
||||
class AddWidgetMenu extends StatelessWidget {
|
||||
const AddWidgetMenu({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//AddWidgetToList - Button-Instances #######################################
|
||||
final addWaterWidget = _AddWidgetToList(
|
||||
toAdd: Hydration(
|
||||
createdOn: DateTime.now().toString(),
|
||||
completedOn: '',
|
||||
isVisible: true,
|
||||
widgetType: 'Hydration',
|
||||
name: AppLocalizations.of(context)!.waterWidget_defaultName,
|
||||
button1Amount: 100,
|
||||
button2Amount: 250,
|
||||
goal: 2500,
|
||||
current: 0,
|
||||
isExpanded: false,
|
||||
),
|
||||
buttonText: AppLocalizations.of(context)!.addWidget_water,
|
||||
iconData: Icons.local_drink,
|
||||
);
|
||||
|
||||
final addCompoundTimerWidget = _AddWidgetToList(
|
||||
toAdd: TimerItem(
|
||||
widgetType: 'Timer',
|
||||
name: AppLocalizations.of(context)!.timerWidget_defaultName,
|
||||
current: 0,
|
||||
goal: 90,
|
||||
isExpanded: false,
|
||||
createdOn: DateTime.now().toString(),
|
||||
completedOn: '',
|
||||
isVisible: true,
|
||||
state: 'initial',
|
||||
),
|
||||
buttonText: AppLocalizations.of(context)!.addWidget_timer,
|
||||
iconData: Icons.timer,
|
||||
);
|
||||
final addTaskWidget = _AddWidgetToList(
|
||||
toAdd: TasksItem(
|
||||
isVisible: true,
|
||||
widgetType: 'TODO',
|
||||
name: AppLocalizations.of(context)!.tasksWidget_defaultName,
|
||||
isExpanded: false,
|
||||
taskList: [],
|
||||
completedTaskList: [],
|
||||
),
|
||||
buttonText: AppLocalizations.of(context)!.addWidget_tasks,
|
||||
iconData: Icons.task,
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
iconTheme: IconThemeData(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.addWidgetHeader,
|
||||
textScaler: const TextScaler.linear(1.5),
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
body: Container(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
addWaterWidget,
|
||||
addTaskWidget,
|
||||
addCompoundTimerWidget,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
109
app/lib/infrastructure/widget_wall/graph_widget.dart
Normal file
109
app/lib/infrastructure/widget_wall/graph_widget.dart
Normal file
|
@ -0,0 +1,109 @@
|
|||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class DataPoint {
|
||||
DataPoint({required this.date, required this.progress});
|
||||
DateTime date;
|
||||
double progress;
|
||||
}
|
||||
|
||||
class GraphWidget extends ConsumerStatefulWidget {
|
||||
GraphWidget({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<ConsumerStatefulWidget> createState() => _GraphWidgetState();
|
||||
|
||||
final entries = <DataPoint>[
|
||||
DataPoint(date: DateTime(2024, 7, 4), progress: 1),
|
||||
DataPoint(date: DateTime(2024, 7, 5), progress: 0.5),
|
||||
DataPoint(date: DateTime(2024, 7, 6), progress: 1.25),
|
||||
DataPoint(date: DateTime(2024, 7, 7), progress: 0.75),
|
||||
];
|
||||
}
|
||||
|
||||
class _GraphWidgetState extends ConsumerState<GraphWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final firstDate = widget.entries[0].date;
|
||||
final flSpots = <FlSpot>[];
|
||||
for (final dataPoint in widget.entries) {
|
||||
final xValue = _daysBetween(firstDate, dataPoint.date).toDouble();
|
||||
final lcbd = FlSpot(xValue, dataPoint.progress);
|
||||
flSpots.add(lcbd);
|
||||
}
|
||||
final df = DateFormat('dd. MMMM');
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.85,
|
||||
height: MediaQuery.of(context).size.height * 0.5,
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
minY: 0,
|
||||
maxY: 1.5,
|
||||
lineBarsData: [
|
||||
LineChartBarData(spots: flSpots, isCurved: true),
|
||||
],
|
||||
gridData: const FlGridData(show: false),
|
||||
extraLinesData: ExtraLinesData(
|
||||
horizontalLines: [
|
||||
HorizontalLine(
|
||||
y: 1,
|
||||
color: Colors.red,
|
||||
),
|
||||
],
|
||||
),
|
||||
titlesData: FlTitlesData(
|
||||
rightTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
reservedSize: 30,
|
||||
interval: 20,
|
||||
),
|
||||
),
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
interval: 20,
|
||||
),
|
||||
),
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
reservedSize: 30,
|
||||
getTitlesWidget: (value, meta) => Text(value.toString()),
|
||||
interval: 0.25,
|
||||
),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
getTitlesWidget: (value, meta) => Text(
|
||||
df.format(
|
||||
DateTime(
|
||||
firstDate.year,
|
||||
firstDate.month,
|
||||
firstDate.day,
|
||||
).add(Duration(days: value.round())),
|
||||
),
|
||||
),
|
||||
showTitles: true,
|
||||
interval: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Text('You worked for X hours this past week'),
|
||||
const Text('Out of a total of Y hours planned'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
int _daysBetween(DateTime from, DateTime to) {
|
||||
final d1 = DateTime(from.year, from.month, from.day);
|
||||
final d2 = DateTime(to.year, to.month, to.day);
|
||||
return (d2.difference(d1).inHours / 24).round();
|
||||
}
|
||||
}
|
27
app/lib/infrastructure/widget_wall/items_controller.dart
Normal file
27
app/lib/infrastructure/widget_wall/items_controller.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:habitrack_app/sembast/item_repository.dart';
|
||||
import 'package:habitrack_app/sembast/sembast_item_repository.dart';
|
||||
|
||||
final homeControllerProvider = Provider(
|
||||
(ref) => HomeController(
|
||||
itemRepository: ref.watch(itemRepositoryProvider),
|
||||
),
|
||||
);
|
||||
|
||||
class HomeController {
|
||||
HomeController({required this.itemRepository});
|
||||
|
||||
final ItemRepository itemRepository;
|
||||
|
||||
Future<void> delete(int id) async {
|
||||
await itemRepository.deleteItem(id);
|
||||
}
|
||||
|
||||
Future<void> edit(dynamic item) async {
|
||||
await itemRepository.updateItem(item);
|
||||
}
|
||||
|
||||
Future<void> add(dynamic newItem) async {
|
||||
await itemRepository.insertItem(newItem);
|
||||
}
|
||||
}
|
6
app/lib/infrastructure/widget_wall/items_state.dart
Normal file
6
app/lib/infrastructure/widget_wall/items_state.dart
Normal file
|
@ -0,0 +1,6 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:habitrack_app/sembast/sembast_item_repository.dart';
|
||||
|
||||
final itemsProvider = StreamProvider(
|
||||
(ref) => ref.watch(itemRepositoryProvider).getAllItemsStream(),
|
||||
);
|
111
app/lib/infrastructure/widget_wall/widget_wall.dart
Normal file
111
app/lib/infrastructure/widget_wall/widget_wall.dart
Normal file
|
@ -0,0 +1,111 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:habitrack_app/function_widgets/compound_widgets/compound_timer_widget.dart';
|
||||
import 'package:habitrack_app/function_widgets/compound_widgets/compound_widget_tasks.dart';
|
||||
import 'package:habitrack_app/function_widgets/compound_widgets/compound_widget_water.dart';
|
||||
import 'package:habitrack_app/infrastructure/widget_wall/add_widget_button.dart';
|
||||
import 'package:habitrack_app/infrastructure/widget_wall/items_controller.dart';
|
||||
import 'package:habitrack_app/infrastructure/widget_wall/items_state.dart';
|
||||
import 'package:habitrack_app/main.dart';
|
||||
import 'package:habitrack_app/sembast/hydration.dart';
|
||||
import 'package:habitrack_app/sembast/tasks_list.dart';
|
||||
import 'package:habitrack_app/sembast/timer.dart';
|
||||
|
||||
/// Displays detailed information about a SampleItem.
|
||||
|
||||
class WidgetWall extends ConsumerStatefulWidget {
|
||||
const WidgetWall({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<WidgetWall> createState() => _WidgetWallState();
|
||||
}
|
||||
|
||||
class _WidgetWallState extends ConsumerState<WidgetWall> {
|
||||
Future<List<Widget>> buildList() async {
|
||||
ref.watch(homeControllerProvider);
|
||||
//var itemCount = 0;
|
||||
|
||||
final items = ref.watch(itemsProvider);
|
||||
|
||||
// ignore: unused_local_variable
|
||||
final val = items.value;
|
||||
|
||||
return <Widget>[];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// this.buildList();
|
||||
final controller = ref.watch(itemsProvider);
|
||||
switch (controller) {
|
||||
case AsyncError(:final error):
|
||||
return Text('Error: $error');
|
||||
case AsyncData(:final value):
|
||||
final allItems = value;
|
||||
final items = <dynamic>[];
|
||||
for (var i = 0; i < allItems.length; i++) {
|
||||
// ignore: avoid_dynamic_calls
|
||||
if (allItems.elementAt(i).isVisible == true) {
|
||||
logger.i('INSERTING VISIBLE ITEM');
|
||||
items.add(allItems.elementAt(i));
|
||||
}
|
||||
}
|
||||
final itemWidgets = <Widget>[];
|
||||
final itemCount = items.length;
|
||||
for (var i = 0; i < itemCount; i++) {
|
||||
final item = items.elementAt(i);
|
||||
if (item is Hydration && item.isVisible) {
|
||||
final itemwidget = CompoundWidgetWater(item: item);
|
||||
itemWidgets.insert(i, itemwidget);
|
||||
} else if (item is TimerItem && item.isVisible) {
|
||||
if (item.isVisible == true) {
|
||||
logger.i('VISIBLE');
|
||||
final itemwidget = CompoundWidgetTimer(item: item);
|
||||
itemWidgets.insert(i, itemwidget);
|
||||
} else {
|
||||
logger.i('IS NOT VISIBLE');
|
||||
}
|
||||
} else if (item is TasksItem && item.isVisible) {
|
||||
final itemwidget = CompoundWidgetTasks(item: item);
|
||||
itemWidgets.insert(i, itemwidget);
|
||||
}
|
||||
}
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
iconTheme: IconThemeData(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Theme.of(context).colorScheme.primary,
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
textScaler: const TextScaler.linear(2),
|
||||
'Habitrack ',
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
// fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
const Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: AddWidgetButton(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: ColoredBox(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(8),
|
||||
children: itemWidgets,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
default:
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue