Compare commits

..

10 Commits

Author SHA1 Message Date
cf79defe49 added simple profile 2024-12-02 19:18:15 +01:00
9982c3396a initial start page 2024-12-02 19:05:36 +01:00
e22fe2e58f changed title style 2024-12-02 18:29:28 +01:00
f91c8100be added breakpoints 2024-12-02 18:29:15 +01:00
7f9374a768 fixed type issue 2024-12-02 18:21:26 +01:00
7bb7037618 added start and end date 2024-12-02 18:21:17 +01:00
06da803497 Minor alignment changes 2024-12-02 17:55:08 +01:00
7a1bbfe365 created content boxes 2024-12-02 17:50:31 +01:00
d715b4b201 Added ContentProvider 2024-12-02 16:39:34 +01:00
bacf168d09 added content.json 2024-12-02 16:38:32 +01:00
10 changed files with 371 additions and 69 deletions

20
.gitignore vendored
View File

@@ -46,6 +46,23 @@ analysis_benchmark.json
# packages file containing multi-root paths
.packages.generated
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
/dev/docs/api_docs.zip
/dev/docs/flutter.docs.zip
/dev/docs/lib/
/dev/docs/pubspec.yaml
/dev/integration_tests/**/xcuserdata
/dev/integration_tests/**/Pods
/packages/flutter/coverage/
version
analysis_benchmark.json
# packages file containing multi-root paths
.packages.generated
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
@@ -137,3 +154,6 @@ app.*.symbols
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
!.vscode/settings.json
# Custom
content.json

BIN
assets/profile.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View File

@@ -1,11 +1,43 @@
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';
class LandingPage extends StatelessWidget {
class LandingPage extends StatefulWidget {
const LandingPage({super.key});
static const String routeName = '/';
@override
State<LandingPage> createState() => _LandingPageState();
}
class _LandingPageState extends State<LandingPage> {
bool loadingDone = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await ContentProvider.init();
setState(() => loadingDone = true);
});
}
double _getPageWidth() {
final width = MediaQuery.of(context).size.width;
// if (width < Breakpoints.sm) return width;
if (width < Breakpoints.md) return width * 0.95;
if (width < Breakpoints.lg) return width * 0.8;
if (width < Breakpoints.xl) return width * 0.7;
if (width < Breakpoints.xl2) return width * 0.6;
return width * 0.5;
}
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: const Text('Landing'),
@@ -13,6 +45,77 @@ class LandingPage extends StatelessWidget {
TextButton(onPressed: () {}, child: const Text('Source Code')),
],
),
body: !loadingDone
// While the content is being loaded from JSON, show a LoadingIndicator
? const Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
Padding(padding: EdgeInsets.symmetric(vertical: 10)),
Text('Loading...')
],
),
)
: SingleChildScrollView(
child: Stack(
children: [
Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: (MediaQuery.of(context).size.width -
_getPageWidth()) /
2,
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 50),
child: Profile(),
),
),
),
Align(
alignment: Alignment.topRight,
child: SizedBox(
width: (MediaQuery.of(context).size.width -
_getPageWidth()) /
2,
child: screenWidth >= Breakpoints.xl2
? Padding(
padding:
const EdgeInsets.symmetric(horizontal: 50),
child: ContentBox(
title: 'Fähigkeiten',
content: ContentProvider.skills,
contentType: ContentType.skills,
),
)
: null,
),
),
Center(
child: SizedBox(
width: _getPageWidth(),
child: Column(
children: [
ContentBox(
title: 'Arbeitserfahrung',
content: ContentProvider.experience,
contentType: ContentType.experience,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 15)),
ContentBox(
title: 'Bildungsweg',
content: ContentProvider.education,
contentType: ContentType.education,
),
],
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,7 @@
class Breakpoints {
static const sm = 640;
static const md = 768;
static const lg = 1024;
static const xl = 1280;
static const xl2 = 1536;
}

View File

@@ -0,0 +1,27 @@
import 'dart:convert';
import 'package:flutter/services.dart';
class ContentProvider {
ContentProvider._();
static const String _jsonPath = 'assets/content.json';
static Future<bool> init() async {
try {
String file = await rootBundle.loadString(_jsonPath);
_content = json.decode(file);
} catch (e) {
return false;
}
return true;
}
static Map<String, dynamic> _content = {};
static List<dynamic> get experience => _content['experience'];
static List<dynamic> get education => _content['education'];
static List<dynamic> get skills => _content['skills'];
}

View File

@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:resume/services/breakpoints.dart';
class ContentListTile extends StatelessWidget {
const ContentListTile(
{super.key,
this.name,
this.location,
this.title,
this.description,
this.startDate,
this.endDate});
final String? name;
final String? location;
final String? title;
final String? description;
final String? startDate;
final String? endDate;
@override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return ListTile(
contentPadding: const EdgeInsets.all(0),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (name != null) Text(name!),
if (location != null) Text(', $location'),
if (title != null && Breakpoints.xl < width) Text(' - $title'),
],
),
if (title != null && Breakpoints.xl >= width) Text('$title'),
],
),
titleAlignment: ListTileTitleAlignment.titleHeight,
subtitle: description != null ? Text(description!) : null,
trailing: startDate != null && endDate != null
? Text(_getTimeString(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}';
}
static const months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
}

View File

@@ -0,0 +1,69 @@
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,
),
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 }

40
lib/widgets/profile.dart Normal file
View File

@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
class Profile extends StatelessWidget {
const Profile({super.key});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
constraints: const BoxConstraints(maxWidth: 200),
child: ClipRRect(
borderRadius: BorderRadius.circular(180),
child: Image.asset('assets/profile.jpg'),
),
),
const Padding(padding: EdgeInsets.symmetric(vertical: 5)),
Text(
'Marco Skupin',
style: Theme.of(context).textTheme.displayMedium,
),
const Padding(padding: EdgeInsets.symmetric(vertical: 5)),
Text(
'Master of Science',
style: Theme.of(context).textTheme.titleLarge,
),
const Padding(padding: EdgeInsets.symmetric(vertical: 5)),
Text(
'marco@skup.in',
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
);
}
}

View File

@@ -0,0 +1,31 @@
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)),
if (percentage != null)
Expanded(
flex: 5,
child: LinearProgressIndicator(
value: double.parse(percentage!) / 100,
),
),
],
),
);
}
}

View File

@@ -1,90 +1,26 @@
name: resume
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: ^3.5.4
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^4.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "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: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
assets:
- assets/content.json
- assets/profile.jpg