down with daos, mild refactoring, moving to pulling from real data

This commit is contained in:
Joshua Burman
2024-12-15 22:21:23 -05:00
parent 54d47245ae
commit 67d7a374d4
22 changed files with 731 additions and 528 deletions

View File

@ -1,14 +1,16 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/widgets/activity_view.dart';
class ActivityCard extends StatefulWidget {
final ActivityModel activity;
final Activity data;
const ActivityCard({super.key, required this.activity});
const ActivityCard({super.key, required this.activity, required this.data});
@override
State<ActivityCard> createState() => ActivityCardState();
@ -34,28 +36,28 @@ class ActivityCardState extends State<ActivityCard> {
: Theme.of(context).colorScheme.surfaceContainerLow,
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () => showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: const Duration(milliseconds: 220),
transitionBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
Animation<Offset> custom = Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, 0.0))
.animate(animation);
return SlideTransition(
position: custom,
child: Dialog.fullscreen(
child: ActivityView(activity: widget.activity)));
},
barrierDismissible: true,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {
return Container();
}),
// onTap: () => showGeneralDialog(
// barrierColor: Colors.black.withOpacity(0.5),
// transitionDuration: const Duration(milliseconds: 220),
// transitionBuilder: (BuildContext context,
// Animation<double> animation,
// Animation<double> secondaryAnimation,
// Widget child) {
// Animation<Offset> custom = Tween<Offset>(
// begin: const Offset(0.0, 1.0),
// end: const Offset(0.0, 0.0))
// .animate(animation);
// return SlideTransition(
// position: custom,
// child: Dialog.fullscreen(
// child: ActivityView(activity: widget.activity)));
// },
// barrierDismissible: true,
// barrierLabel: '',
// context: context,
// pageBuilder: (context, animation1, animation2) {
// return Container();
// }),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
@ -79,13 +81,13 @@ class ActivityCardState extends State<ActivityCard> {
if (atm.activity?.id == widget.activity.id) {
return Text(
maxLines: 1,
"${widget.activity.title} (${formattedTime(atm.totalTime)})");
"${widget.data.title} (${formattedTime(atm.totalTime)})");
} else {
return Text(maxLines: 1, widget.activity.title);
return Text(maxLines: 1, widget.data.title);
}
},
),
subtitle: Text(maxLines: 2, widget.activity.description),
subtitle: Text(maxLines: 2, widget.data.description),
trailing: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(Icons.close_rounded),

View File

@ -193,22 +193,23 @@ class ActivityViewMedia extends StatelessWidget {
}
}
List<Widget> mediaCards =
List.generate(media.length, (i) => MediaCard(media: media[i]));
return Text("media!");
// List<Widget> mediaCards =
// List.generate(media.length, (i) => MediaCard(media: media[i]));
return Column(
children: [
SizedBox(
width: double.infinity,
height: 100,
child: GridView.count(
padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
scrollDirection: Axis.horizontal,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
crossAxisCount: 1,
children: mediaCards))
],
);
// return Column(
// children: [
// SizedBox(
// width: double.infinity,
// height: 100,
// child: GridView.count(
// padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
// scrollDirection: Axis.horizontal,
// crossAxisSpacing: 5,
// mainAxisSpacing: 5,
// crossAxisCount: 1,
// children: mediaCards))
// ],
// );
}
}

View File

@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database/database.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
class MediaCard extends StatelessWidget {
const MediaCard({super.key, required this.media});
final Media media;
final MediaItem media;
@override
Widget build(BuildContext context) {
@ -14,22 +14,22 @@ class MediaCard extends StatelessWidget {
flags: const YoutubePlayerFlags(
autoPlay: false, mute: true, showLiveFullscreenButton: false));
DecorationImage mediaImage(Media media) {
DecorationImage mediaImage(MediaItem media) {
String image = '';
if (media.type == "image") {
if (media.type == MediaType.image) {
image = media.reference;
} else if (media.type == "youtube") {
} else if (media.type == MediaType.youtube) {
image = 'https://img.youtube.com/vi/${media.reference}/0.jpg';
}
return DecorationImage(image: NetworkImage(image), fit: BoxFit.cover);
}
Widget mediaItem(Media media) {
if (media.type == "image") {
Widget mediaItem(MediaItem media) {
if (media.type == MediaType.image) {
return Image(image: NetworkImage(media.reference));
} else if (media.type == "youtube") {
} else if (media.type == MediaType.youtube) {
return YoutubePlayer(
controller: controller,
aspectRatio: 16 / 9,
@ -62,7 +62,7 @@ class MediaCard extends StatelessWidget {
mediaItem(media),
const SizedBox(height: 15),
Text(
'${media.description}',
media.description,
style: const TextStyle(fontSize: 20),
),
const Divider(

View File

@ -3,7 +3,7 @@ import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database.dart' hide ActivityAction;
import 'package:sendtrain/database/database.dart' hide ActivityAction;
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/session_model.dart';
import 'package:sendtrain/widgets/session_view.dart';

View File

@ -1,165 +1,103 @@
import 'package:drift/drift.dart' hide Column;
import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:sendtrain/database/daos/activities_dao.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/models/session_model.dart';
import 'package:sendtrain/widgets/activity_card.dart';
import 'package:sendtrain/widgets/media_card.dart';
import 'package:sendtrain/widgets/session_view_achievements.dart';
import 'package:sendtrain/widgets/session_view_activities.dart';
import 'package:sendtrain/widgets/session_view_media.dart';
class SessionView extends StatelessWidget {
const SessionView({super.key, required this.data, required this.session});
SessionView({super.key, required this.data, required this.session});
final SessionModel data;
final Session session;
final AppDatabase database = AppDatabase();
@override
Widget build(BuildContext context) {
initializeDateFormatting('en');
final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
return Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
distance: 70,
type: ExpandableFabType.up,
overlayStyle: ExpandableFabOverlayStyle(
color: Colors.black.withOpacity(0.5),
blur: 10,
),
children: [
FloatingActionButton.extended(
icon: const Icon(Icons.history_outlined),
label: Text('Restart'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.done_all_outlined),
label: Text('Done'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.edit_outlined),
label: Text('Edit'),
onPressed: () {},
),
]),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AppBar(
centerTitle: true,
title: Text('Session @ ${dateFormat.format(session.date as DateTime)}',
style: const TextStyle(fontSize: 15)),
),
Padding(
padding: const EdgeInsets.only(
left: 15, right: 20, top: 15, bottom: 10),
child: Text(
maxLines: 1,
style: const TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
session.title)),
SessionViewAchievements(achievements: data.achievements),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child:
Text(style: const TextStyle(fontSize: 15), session.content)),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
'Media:')),
SessionViewMedia(media: data.media),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
'Activites:')),
SessionViewActivities(activities: data.activities),
],
));
}
}
class SessionViewActivities extends StatelessWidget {
const SessionViewActivities({super.key, this.activities});
final List<ActivityModel>? activities;
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
// shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: activities?.length,
itemBuilder: (BuildContext context, int index) {
return ActivityCard(activity: activities![index]);
},
));
}
}
class SessionViewAchievements extends StatelessWidget {
const SessionViewAchievements({super.key, this.achievements});
final List<String>? achievements;
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: SizedBox(
height: 40,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: achievements?.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(right: 5),
child: ActionChip(
visualDensity: VisualDensity.compact,
avatar: const Icon(Icons.check_circle_outline),
label: Text(maxLines: 1, '${achievements?[index]}'),
onPressed: () {},
));
},
))),
],
);
}
}
class SessionViewMedia extends StatelessWidget {
const SessionViewMedia({super.key, this.media});
final List<Media>? media;
@override
Widget build(BuildContext context) {
List<Widget> mediaCards = List.generate((media != null) ? media!.length : 0,
(i) => MediaCard(media: media![i]));
return Column(
children: [
SizedBox(
width: double.infinity,
height: 100,
child: GridView.count(
padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
scrollDirection: Axis.horizontal,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
crossAxisCount: 1,
children: mediaCards))
],
);
return FutureBuilder<List<Activity>>(
future: ActivitiesDao(database).sessionActivities(session.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
final activities = snapshot.data!;
database.close();
return Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
distance: 70,
type: ExpandableFabType.up,
overlayStyle: ExpandableFabOverlayStyle(
color: Colors.black.withOpacity(0.5),
blur: 10,
),
children: [
FloatingActionButton.extended(
icon: const Icon(Icons.history_outlined),
label: Text('Restart'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.done_all_outlined),
label: Text('Done'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.edit_outlined),
label: Text('Edit'),
onPressed: () {},
),
]),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AppBar(
centerTitle: true,
title: Text(
'Session @ ${dateFormat.format(session.date as DateTime)}',
style: const TextStyle(fontSize: 15)),
),
Padding(
padding: const EdgeInsets.only(
left: 15, right: 20, top: 15, bottom: 10),
child: Text(
maxLines: 1,
style: const TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
session.title)),
SessionViewAchievements(session: session),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: Text(
style: const TextStyle(fontSize: 15),
session.content)),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Media:')),
SessionViewMedia(session: session, media: data.media),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Activites:')),
SessionViewActivities(
activities: data.activities, data: activities),
],
));
} else {
return const CircularProgressIndicator();
}
});
}
}

View File

@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/database/daos/session_activities_dao.dart';
import 'package:sendtrain/database/database.dart';
class SessionViewAchievements extends StatelessWidget {
SessionViewAchievements({super.key, required this.session});
final Session session;
final AppDatabase database = AppDatabase();
List<String> getAchievements(List<SessionActivity> sessionActivities) {
List<String> achievements = [];
for (int i = 0; i < sessionActivities.length; i++) {
final SessionActivity sessionActivity = sessionActivities[i];
final List? saAchievments = sessionActivity.achievements?.split(',');
if (saAchievments != null) {
saAchievments.forEach((achievement) => achievements.add(achievement));
}
}
return achievements;
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<SessionActivity>>(
future: SessionActivitiesDao(database).sessionActivitiesBySessionId(session.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
final sessionActivities = snapshot.data!;
final achievements = getAchievements(sessionActivities);
database.close();
return Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: SizedBox(
height: 40,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: achievements.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(right: 5),
child: ActionChip(
visualDensity: VisualDensity.compact,
avatar:
const Icon(Icons.check_circle_outline),
label: Text(maxLines: 1, achievements[index]),
onPressed: () {},
));
},
))),
],
);
} else {
return const CircularProgressIndicator();
}
});
}
}

View File

@ -0,0 +1,28 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/widgets/activity_card.dart';
class SessionViewActivities extends StatelessWidget {
const SessionViewActivities({super.key, this.activities, required this.data});
final List<ActivityModel>? activities;
final List<Activity> data;
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
// shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return ActivityCard(
activity: activities![Random().nextInt(activities!.length)],
data: data[index]);
},
));
}
}

View File

@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database/daos/media_items_dao.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/widgets/media_card.dart';
class SessionViewMedia extends StatelessWidget {
SessionViewMedia({super.key, this.media, required this.session});
final List<Media>? media;
final Session session;
final AppDatabase database = AppDatabase();
@override
Widget build(BuildContext context) {
return FutureBuilder<List<MediaItem>>(
future: MediaItemsDao(database).mediaItemsFromSession(session),
builder: (context, snapshot) {
if (snapshot.hasData) {
final mediaItems = snapshot.data!;
database.close();
List<Widget> mediaCards = List.generate(
mediaItems.length, (i) => MediaCard(media: mediaItems[i]));
return Column(
children: [
SizedBox(
width: double.infinity,
height: 100,
child: GridView.count(
padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
scrollDirection: Axis.horizontal,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
crossAxisCount: 1,
children: mediaCards))
],
);
} else {
return CircularProgressIndicator();
}
});
}
}