Skip to content

Commit

Permalink
feat: speaker detail screen
Browse files Browse the repository at this point in the history
  • Loading branch information
glitchedmob committed Oct 9, 2024
1 parent 86380f6 commit 91127aa
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 75 deletions.
Binary file removed assets/AppIcons.ttf
Binary file not shown.
24 changes: 24 additions & 0 deletions lib/providers/schedule_provider.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import 'dart:convert';

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:method_conf_app/data/get_schedule_grid.dart';
import 'package:method_conf_app/data/get_schedule_items.dart';
import 'package:method_conf_app/data/umbraco/models/api_content_model_base.dart';
import 'package:method_conf_app/data/umbraco/models/session.dart';
import 'package:method_conf_app/data/umbraco/models/speaker.dart';
import 'package:method_conf_app/data/umbraco/models/track.dart';
import 'package:method_conf_app/providers/conference_provider.dart';
import 'package:method_conf_app/providers/schedule_state_provider.dart';
import 'package:method_conf_app/utils/iterable_extensions.dart';
import 'package:shared_preferences/shared_preferences.dart';

const _scheduleItemsStorageKey = 'app-schedule-items';
Expand All @@ -33,6 +37,11 @@ class ScheduleProvider extends ChangeNotifier {
List<Session> get sessions =>
schedule?.$1.whereType<Session>().toList() ?? [];

List<Speaker> get speakers => sessions
.expand((session) => session.properties?.speakers ?? <Speaker>[])
.distinctBy((speaker) => speaker.id)
.toList();

ScheduleProvider({required this.conferenceProvider});

Future<void> init() async {
Expand Down Expand Up @@ -118,4 +127,19 @@ class ScheduleProvider extends ChangeNotifier {

await store(newSchedule);
}

List<Session> getSessionsForSpeaker(Speaker? speaker) {
if (speaker == null) {
return [];
}

return sessions
.where(
(session) =>
session.properties?.speakers.firstWhereOrNull(
(sessionSpeaker) => sessionSpeaker.id == speaker.id) !=
null,
)
.toList();
}
}
105 changes: 59 additions & 46 deletions lib/screens/speaker_detail_screen.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:method_conf_app/widgets/session_expansion_tile.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:method_conf_app/data/umbraco/image_url.dart';
import 'package:method_conf_app/providers/schedule_provider.dart';
import 'package:provider/provider.dart';

import 'package:method_conf_app/models/speaker.dart';
import 'package:method_conf_app/data/umbraco/models/speaker.dart';
import 'package:method_conf_app/widgets/session_expansion_tile.dart';
import 'package:method_conf_app/screens/not_found_screen.dart';
import 'package:method_conf_app/providers/session_provider.dart';
import 'package:method_conf_app/theme.dart';
import 'package:method_conf_app/utils/app_icons.dart';
import 'package:method_conf_app/utils/utils.dart';
import 'package:method_conf_app/widgets/app_html.dart';
import 'package:method_conf_app/widgets/app_screen.dart';
Expand All @@ -18,13 +19,16 @@ class SpeakerDetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
var speaker = ModalRoute.of(context)!.settings.arguments as Speaker?;
var session =
Provider.of<SessionProvider>(context).getSessionForSpeaker(speaker);
var sessions =
Provider.of<ScheduleProvider>(context).getSessionsForSpeaker(speaker);

if (speaker == null) {
return const NotFoundScreen();
}

final bio = speaker.properties?.bio?.markup;
final profileImageUrl = speaker?.properties?.profileImage?.url;

return AppScreen(
title: 'Speaker',
body: ListView(
Expand All @@ -37,36 +41,43 @@ class SpeakerDetailScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
speaker.name,
speaker.name ?? '',
textAlign: TextAlign.left,
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
Text(
speaker.properties?.jobTitle ?? '',
textAlign: TextAlign.left,
style: const TextStyle(fontSize: 18),
),
const SizedBox(height: 15),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Flexible(
child: CachedNetworkImage(
imageUrl: speaker.image!,
placeholder: (context, url) {
return const CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(Colors.transparent),
);
},
errorWidget: (context, url, error) =>
const Icon(Icons.error),
if (profileImageUrl != null)
Flexible(
child: CachedNetworkImage(
imageUrl: imageUrl(profileImageUrl,
width: 300, height: 300),
placeholder: (context, url) {
return const CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(Colors.transparent),
);
},
errorWidget: (context, url, error) =>
const Icon(Icons.error),
),
),
),
const SizedBox(width: 20),
Flexible(
child: _buildSocialIcons(speaker),
),
],
),
AppHtml(
markup: speaker.bio,
markup: bio,
)
],
),
Expand All @@ -77,12 +88,15 @@ class SpeakerDetailScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text(
'Speaking Sessions & Workshops',
'Sessions',
style: TextStyle(fontSize: 24),
),
const SizedBox(height: 15),
// SessionExpansionTile(
// session: session!, disableSpeakerTap: true),
...sessions.expand((session) => [
SessionExpansionTile(
session: session, disableSpeakerTap: true),
const SizedBox(height: 15),
])
],
),
)
Expand All @@ -94,44 +108,43 @@ class SpeakerDetailScreen extends StatelessWidget {
Widget _buildSocialIcons(Speaker speaker) {
var icons = <Widget>[];

if (speaker.twitterUrl != null) {
icons.add(InkWell(
// padding: EdgeInsets.all(0),
onTap: () => launchUrl(speaker.twitterUrl!),
child:
const Icon(AppIcons.twitterLogo, color: AppColors.accent, size: 35),
));
}
final xTwitterUrl = speaker.properties?.xTwitterUrl;

if (speaker.twitter2Url != null) {
if (xTwitterUrl != null) {
icons.add(InkWell(
onTap: () => launchUrl(speaker.twitter2Url!),
child:
const Icon(AppIcons.twitterLogo, color: AppColors.accent, size: 35),
onTap: () => launchUrl(xTwitterUrl),
child: const FaIcon(FontAwesomeIcons.xTwitter,
color: AppColors.accent, size: 35),
));
}

if (speaker.linkedinUrl != null) {
final linkedInUrl = speaker.properties?.linkedInUrl;

if (linkedInUrl != null) {
icons.add(InkWell(
onTap: () => launchUrl(speaker.linkedinUrl!),
child: const Icon(AppIcons.linkedInLogo,
onTap: () => launchUrl(linkedInUrl),
child: const FaIcon(FontAwesomeIcons.linkedin,
color: AppColors.accent, size: 35),
));
}

if (speaker.githubUrl != null) {
final instagramUrl = speaker.properties?.instagramUrl;

if (instagramUrl != null) {
icons.add(InkWell(
onTap: () => launchUrl(speaker.githubUrl!),
child:
const Icon(AppIcons.githubLogo, color: AppColors.accent, size: 35),
onTap: () => launchUrl(instagramUrl),
child: const FaIcon(FontAwesomeIcons.instagram,
color: AppColors.accent, size: 35),
));
}

if (speaker.websiteURL != null) {
final websiteUrl = speaker.properties?.websiteUrl;

if (websiteUrl != null) {
icons.add(InkWell(
onTap: () => launchUrl(speaker.websiteURL!),
child:
const Icon(AppIcons.websiteLogo, color: AppColors.accent, size: 35),
onTap: () => launchUrl(websiteUrl),
child: const FaIcon(FontAwesomeIcons.globe,
color: AppColors.accent, size: 35),
));
}

Expand Down
5 changes: 3 additions & 2 deletions lib/screens/speakers_screen.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:method_conf_app/providers/schedule_provider.dart';
import 'package:method_conf_app/widgets/app_list_item.dart';
import 'package:method_conf_app/widgets/app_navigator.dart';
import 'package:provider/provider.dart';
Expand All @@ -11,7 +12,7 @@ class SpeakersScreen extends StatelessWidget {

@override
Widget build(BuildContext context) {
var speakersProvider = Provider.of<SpeakerProvider>(context);
var speakersProvider = Provider.of<ScheduleProvider>(context);

return AppScreen(
title: 'Speakers',
Expand All @@ -20,7 +21,7 @@ class SpeakersScreen extends StatelessWidget {
children: <Widget>[
...speakersProvider.speakers.map((speaker) {
return AppListItem(
text: speaker.name,
text: speaker.name ?? '',
onTap: () {
AppNavigator.pushNamed(
'/more/speakers/detail',
Expand Down
15 changes: 0 additions & 15 deletions lib/utils/app_icons.dart

This file was deleted.

13 changes: 13 additions & 0 deletions lib/utils/iterable_extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
extension IterableExtension<T> on Iterable<T> {
Iterable<T> distinctBy(Object Function(T e) getCompareValue) {
var idSet = <Object>{};
var distinct = <T>[];
for (var d in this) {
if (idSet.add(getCompareValue(d))) {
distinct.add(d);
}
}

return distinct;
}
}
5 changes: 4 additions & 1 deletion lib/utils/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ Future<void> launchUrl(String url) async {
try {
final parsed = Uri.parse(url);
if (await url_launcher.canLaunchUrl(parsed)) {
await url_launcher.launchUrl(parsed);
await url_launcher.launchUrl(
parsed,
mode: url_launcher.LaunchMode.externalApplication,
);
return;
}
} catch (e) {
Expand Down
11 changes: 9 additions & 2 deletions lib/widgets/app_html.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,21 @@ class AppHtml extends StatelessWidget {
child: Html(
data: markup,
style: {
'body': Style(color: Colors.black, margin: Margins.all(0)),
'a': Style(
textDecoration: TextDecoration.underline,
color: AppColors.accent,
),
'p': Style(color: Colors.black)
'li': Style(color: Colors.black, padding: HtmlPaddings.all(0)),
'ol': Style(
padding: HtmlPaddings.symmetric(horizontal: 15, vertical: 0)),
'ul': Style(
padding: HtmlPaddings.symmetric(horizontal: 15, vertical: 0)),
},
onLinkTap: (url, _, __) {
launchUrl(url!);
if (url != null) {
launchUrl(url);
}
},
),
);
Expand Down
10 changes: 5 additions & 5 deletions lib/widgets/app_navigation.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

import 'package:method_conf_app/theme.dart';
import 'package:method_conf_app/utils/app_icons.dart';
import 'package:method_conf_app/widgets/app_navigator.dart';
import 'package:method_conf_app/screens/more_screen.dart';
import 'package:method_conf_app/screens/not_found_screen.dart';
Expand Down Expand Up @@ -38,15 +38,15 @@ class _AppNavigationState extends State<AppNavigation> {
return [
_NavigationItem(
route: '/schedule',
item: _buildNavigationBarItem(AppIcons.schedule, 'Schedule'),
item: _buildNavigationBarItem(FontAwesomeIcons.calendar, 'Schedule'),
),
_NavigationItem(
route: '/partners',
item: _buildNavigationBarItem(AppIcons.piggyBank, 'Sponsors'),
item: _buildNavigationBarItem(FontAwesomeIcons.piggyBank, 'Sponsors'),
),
_NavigationItem(
route: '/more',
item: _buildNavigationBarItem(Icons.more_horiz, 'More'),
item: _buildNavigationBarItem(FontAwesomeIcons.ellipsis, 'More'),
),
];
}
Expand Down Expand Up @@ -83,7 +83,7 @@ class _AppNavigationState extends State<AppNavigation> {

BottomNavigationBarItem _buildNavigationBarItem(IconData icon, String text) {
return BottomNavigationBarItem(
icon: Icon(icon, size: 25),
icon: FaIcon(icon, size: 25),
label: text,
);
}
Expand Down
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.2.8"
font_awesome_flutter:
dependency: "direct main"
description:
name: font_awesome_flutter
sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f"
url: "https://pub.dev"
source: hosted
version: "10.7.0"
frontend_server_client:
dependency: transitive
description:
Expand Down
5 changes: 1 addition & 4 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ dependencies:
http: ^1.2.2
timezone: ^0.9.4
sticky_headers: ^0.3.0+2
font_awesome_flutter: ^10.7.0

dev_dependencies:
flutter_test:
Expand Down Expand Up @@ -93,10 +94,6 @@ flutter:
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
fonts:
- family: AppIcons
fonts:
- asset: assets/AppIcons.ttf
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package

0 comments on commit 91127aa

Please sign in to comment.