Refactoring
This commit is contained in:
17
lib/constants.dart
Normal file
17
lib/constants.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
/// List of months 0-11
|
||||
const months = [
|
||||
'Januar',
|
||||
'Februar',
|
||||
'März',
|
||||
'April',
|
||||
'Mai',
|
||||
'Juni',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'Oktober',
|
||||
'November',
|
||||
'Dezember'
|
||||
];
|
||||
|
||||
enum ContentType { experience, education, skills, language, text }
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:resume/services/breakpoints.dart';
|
||||
import 'package:resume/services/content_provider.dart';
|
||||
import 'package:resume/widgets/content_widget.dart';
|
||||
import 'package:resume/widgets/profile.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:resume/constants.dart' show ContentType;
|
||||
|
||||
import '../services/content_provider.dart';
|
||||
import '../widgets/content_block.dart';
|
||||
|
||||
class LandingPage extends StatefulWidget {
|
||||
const LandingPage({super.key});
|
||||
@@ -40,25 +42,31 @@ class _LandingPageState extends State<LandingPage> {
|
||||
(MediaQuery.of(context).size.width - _getMainContentWidth()) / 2;
|
||||
|
||||
Widget _getSideBar() {
|
||||
return ContentBox(
|
||||
title: 'Fähigkeiten',
|
||||
content: ContentProvider.skills,
|
||||
return const Column(
|
||||
children: [
|
||||
ContentBlock(
|
||||
blockTitle: 'Fähigkeiten',
|
||||
contentType: ContentType.skills,
|
||||
),
|
||||
Padding(padding: EdgeInsets.only(bottom: 25)),
|
||||
ContentBlock(
|
||||
blockTitle: 'Sprachen',
|
||||
contentType: ContentType.language,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getMainContent() {
|
||||
return Column(
|
||||
return const Column(
|
||||
children: [
|
||||
ContentBox(
|
||||
title: 'Arbeitserfahrung',
|
||||
content: ContentProvider.experience,
|
||||
ContentBlock(
|
||||
blockTitle: 'Arbeitserfahrung',
|
||||
contentType: ContentType.experience,
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
||||
ContentBox(
|
||||
title: 'Bildungsweg',
|
||||
content: ContentProvider.education,
|
||||
Padding(padding: EdgeInsets.only(bottom: 25)),
|
||||
ContentBlock(
|
||||
blockTitle: 'Bildungsweg',
|
||||
contentType: ContentType.education,
|
||||
),
|
||||
],
|
||||
@@ -74,7 +82,7 @@ class _LandingPageState extends State<LandingPage> {
|
||||
child: SizedBox(
|
||||
width: _getSidebarWidth(),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 50),
|
||||
padding: EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Profile(),
|
||||
),
|
||||
),
|
||||
@@ -84,7 +92,7 @@ class _LandingPageState extends State<LandingPage> {
|
||||
child: SizedBox(
|
||||
width: _getSidebarWidth(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: _getSideBar(),
|
||||
),
|
||||
),
|
||||
@@ -107,11 +115,12 @@ class _LandingPageState extends State<LandingPage> {
|
||||
child: Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 50),
|
||||
padding: EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Profile(),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: _getSideBar(),
|
||||
),
|
||||
],
|
||||
@@ -121,7 +130,7 @@ class _LandingPageState extends State<LandingPage> {
|
||||
width: 900,
|
||||
child: _getMainContent(),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(right: 50)),
|
||||
const Padding(padding: EdgeInsets.only(right: 25)),
|
||||
],
|
||||
);
|
||||
} else if (constraints.maxWidth > Breakpoints.lg) {
|
||||
@@ -136,6 +145,7 @@ class _LandingPageState extends State<LandingPage> {
|
||||
child: Column(
|
||||
children: [
|
||||
const Profile(),
|
||||
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
||||
_getSideBar(),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:resume/constants.dart';
|
||||
|
||||
class ContentProvider {
|
||||
ContentProvider._();
|
||||
@@ -17,11 +18,25 @@ class ContentProvider {
|
||||
return true;
|
||||
}
|
||||
|
||||
static Map<String, dynamic> _content = {};
|
||||
static Map<String, dynamic> _content = {
|
||||
'experience': <List<dynamic>>[],
|
||||
'education': <List<dynamic>>[],
|
||||
'skills': <List<dynamic>>[],
|
||||
'text': <String>[],
|
||||
};
|
||||
|
||||
static List<dynamic> get experience => _content['experience'];
|
||||
|
||||
static List<dynamic> get education => _content['education'];
|
||||
|
||||
static List<dynamic> get skills => _content['skills'];
|
||||
static T getContent<T>(ContentType contentType) {
|
||||
switch (contentType) {
|
||||
case ContentType.experience:
|
||||
return _content['experience'] as T;
|
||||
case ContentType.education:
|
||||
return _content['education'] as T;
|
||||
case ContentType.skills:
|
||||
return _content['skills'] as T;
|
||||
case ContentType.text:
|
||||
return _content['text'] as T;
|
||||
default:
|
||||
return [] as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
lib/services/tools.dart
Normal file
12
lib/services/tools.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:resume/constants.dart' show months;
|
||||
|
||||
class Tools {
|
||||
/// builds timespan-String out of two date-Strings.
|
||||
/// Date has to be formatted as yyyy-MM
|
||||
static buildTimeString(String startDate, String endDate) {
|
||||
if (startDate.isEmpty || endDate.isEmpty) return '';
|
||||
final firstDate = DateTime.parse('$startDate-01');
|
||||
final secondDate = DateTime.parse('$endDate-01');
|
||||
return '${months[firstDate.month - 1]} ${firstDate.year} - ${months[secondDate.month - 1]} ${secondDate.year}';
|
||||
}
|
||||
}
|
||||
83
lib/widgets/content_block.dart
Normal file
83
lib/widgets/content_block.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:resume/widgets/content_list_tile.dart';
|
||||
import 'package:resume/widgets/language_widget.dart';
|
||||
import 'package:resume/constants.dart' show ContentType;
|
||||
|
||||
import '../services/content_provider.dart';
|
||||
|
||||
class ContentBlock extends StatelessWidget {
|
||||
const ContentBlock({
|
||||
super.key,
|
||||
required this.blockTitle,
|
||||
required this.contentType,
|
||||
});
|
||||
|
||||
final String blockTitle;
|
||||
final ContentType contentType;
|
||||
|
||||
Widget get _getContentWidget {
|
||||
if (contentType == ContentType.language) {
|
||||
return const LanguageWidget();
|
||||
} else if (contentType == ContentType.text) {
|
||||
final content = ContentProvider.getContent<String>(ContentType.text);
|
||||
return Text(content);
|
||||
}
|
||||
// List-based content-blocks
|
||||
List<dynamic> content =
|
||||
ContentProvider.getContent<List<dynamic>>(contentType);
|
||||
List<Widget> widgets = [];
|
||||
for (var item in content) {
|
||||
widgets.add(_buildListTile(item));
|
||||
}
|
||||
return Column(
|
||||
children: widgets,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
blockTitle,
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(bottom: 8)),
|
||||
_getContentWidget,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildListTile(Map data) {
|
||||
if (contentType == ContentType.experience) {
|
||||
return ContentListTile.experience(
|
||||
name: data['name'],
|
||||
location: data['location'],
|
||||
title: data['title'],
|
||||
description: data['description'],
|
||||
startDate: data['startDate'],
|
||||
endDate: data['endDate'],
|
||||
);
|
||||
} else if (contentType == ContentType.education) {
|
||||
return ContentListTile.education(
|
||||
name: data['name'],
|
||||
location: data['location'],
|
||||
title: data['title'],
|
||||
startDate: data['startDate'],
|
||||
endDate: data['endDate'],
|
||||
);
|
||||
} else {
|
||||
return ContentListTile.skills(
|
||||
name: data['name'],
|
||||
percentage: data['percentage'],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,72 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:resume/services/breakpoints.dart';
|
||||
import 'package:resume/constants.dart' show ContentType;
|
||||
|
||||
import '../services/tools.dart';
|
||||
|
||||
class ContentListTile extends StatelessWidget {
|
||||
const ContentListTile(
|
||||
const ContentListTile.education(
|
||||
{super.key,
|
||||
this.name,
|
||||
this.location,
|
||||
this.title,
|
||||
this.description,
|
||||
this.startDate,
|
||||
this.endDate});
|
||||
required this.name,
|
||||
required this.location,
|
||||
required this.title,
|
||||
required this.startDate,
|
||||
required this.endDate})
|
||||
: description = '',
|
||||
percentage = '',
|
||||
_contentType = ContentType.education;
|
||||
|
||||
final String? name;
|
||||
final String? location;
|
||||
final String? title;
|
||||
final String? description;
|
||||
final String? startDate;
|
||||
final String? endDate;
|
||||
const ContentListTile.experience(
|
||||
{super.key,
|
||||
required this.name,
|
||||
required this.location,
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.startDate,
|
||||
required this.endDate})
|
||||
: percentage = '',
|
||||
_contentType = ContentType.experience;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
const ContentListTile.skills(
|
||||
{super.key, required this.name, required this.percentage})
|
||||
: description = '',
|
||||
title = ',',
|
||||
location = '',
|
||||
startDate = '',
|
||||
endDate = '',
|
||||
_contentType = ContentType.skills;
|
||||
|
||||
return ListTile(
|
||||
final String description;
|
||||
final String endDate;
|
||||
final String location;
|
||||
final String name;
|
||||
final String percentage;
|
||||
final String startDate;
|
||||
final String title;
|
||||
|
||||
final ContentType _contentType;
|
||||
|
||||
Widget get _skillsListTile => ListTile(
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(flex: 2, child: Text(name)),
|
||||
const Padding(padding: EdgeInsets.only(bottom: 8)),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: LinearProgressIndicator(
|
||||
value: double.tryParse(percentage) ?? 1 / 100,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _getEducationListTile(BuildContext context, double screenWidth) =>
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -30,61 +74,83 @@ class ContentListTile extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
if (name != null)
|
||||
Text(
|
||||
name!,
|
||||
name,
|
||||
),
|
||||
if (location != null) Text(', $location'),
|
||||
if (title != null && Breakpoints.xl < width) Text(' - $title'),
|
||||
Text(', $location'),
|
||||
if (Breakpoints.xl < screenWidth) Text(' - $title'),
|
||||
],
|
||||
),
|
||||
if (title != null && Breakpoints.xl >= width) Text('$title'),
|
||||
if (Breakpoints.xl >= screenWidth) Text(title),
|
||||
],
|
||||
),
|
||||
titleAlignment: ListTileTitleAlignment.titleHeight,
|
||||
subtitle: description != null
|
||||
? Column(
|
||||
subtitle: Breakpoints.sm >= screenWidth
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
Tools.buildTimeString(startDate, endDate),
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
trailing: Breakpoints.sm < screenWidth
|
||||
? Text(Tools.buildTimeString(startDate, endDate))
|
||||
: null,
|
||||
);
|
||||
|
||||
Widget _getExperienceListTile(BuildContext context, double screenWidth) =>
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
name,
|
||||
),
|
||||
Text(', $location'),
|
||||
if (Breakpoints.xl < screenWidth) Text(' - $title'),
|
||||
],
|
||||
),
|
||||
if (Breakpoints.xl >= screenWidth) Text(title),
|
||||
],
|
||||
),
|
||||
titleAlignment: ListTileTitleAlignment.titleHeight,
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(padding: EdgeInsets.only(top: 8)),
|
||||
if (startDate != null &&
|
||||
endDate != null &&
|
||||
Breakpoints.sm >= width)
|
||||
if (Breakpoints.sm >= screenWidth)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
_getTimeString(startDate!, endDate!),
|
||||
Tools.buildTimeString(startDate, endDate),
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
),
|
||||
),
|
||||
Text(description!),
|
||||
Text(description),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
trailing: startDate != null && endDate != null && Breakpoints.sm < width
|
||||
? Text(_getTimeString(startDate!, endDate!))
|
||||
),
|
||||
trailing: Breakpoints.sm < screenWidth
|
||||
? Text(Tools.buildTimeString(startDate, endDate))
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
String _getTimeString(String startDate, String endDate) {
|
||||
final firstDate = DateTime.parse('$startDate-01');
|
||||
final secondDate = DateTime.parse('$endDate-01');
|
||||
return '${months[firstDate.month - 1]} ${firstDate.year} - ${months[secondDate.month - 1]} ${secondDate.year}';
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
|
||||
static const months = [
|
||||
'Januar',
|
||||
'Februar',
|
||||
'März',
|
||||
'April',
|
||||
'Mai',
|
||||
'Juni',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'Oktober',
|
||||
'November',
|
||||
'Dezember'
|
||||
];
|
||||
if (_contentType == ContentType.skills) {
|
||||
return _skillsListTile;
|
||||
} else if (_contentType == ContentType.education) {
|
||||
return _getEducationListTile(context, width);
|
||||
} else if (_contentType == ContentType.experience) {
|
||||
return _getExperienceListTile(context, width);
|
||||
} else {
|
||||
return const Placeholder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:resume/widgets/content_list_tile.dart';
|
||||
import 'package:resume/widgets/skill_list_tile.dart';
|
||||
|
||||
class ContentBox extends StatelessWidget {
|
||||
const ContentBox({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.content,
|
||||
required this.contentType,
|
||||
});
|
||||
|
||||
final ContentType contentType;
|
||||
final List<dynamic> content;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(bottom: 8)),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: content.length,
|
||||
itemBuilder: (context, index) => _buildListTile(content[index]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildListTile(Map data) {
|
||||
switch (contentType) {
|
||||
case ContentType.experience:
|
||||
return ContentListTile(
|
||||
name: data['name'],
|
||||
location: data['location'],
|
||||
title: data['title'],
|
||||
description: data['description'],
|
||||
startDate: data['startDate'],
|
||||
endDate: data['endDate'],
|
||||
);
|
||||
case ContentType.education:
|
||||
return ContentListTile(
|
||||
name: data['name'],
|
||||
location: data['location'],
|
||||
title: data['title'],
|
||||
startDate: data['startDate'],
|
||||
endDate: data['endDate'],
|
||||
);
|
||||
case ContentType.skills:
|
||||
return SkillListTile(
|
||||
name: data['name'],
|
||||
percentage: data['percentage'],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ContentType { experience, education, skills }
|
||||
@@ -1,35 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SkillListTile extends StatelessWidget {
|
||||
const SkillListTile({
|
||||
super.key,
|
||||
required this.name,
|
||||
this.percentage,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String? percentage;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(flex: 2, child: Text(name)),
|
||||
const Padding(padding: EdgeInsets.only(bottom: 8)),
|
||||
if (percentage != null)
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: LinearProgressIndicator(
|
||||
value: double.parse(percentage!) / 100,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user