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:flutter/material.dart';
|
||||||
import 'package:resume/services/breakpoints.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:resume/widgets/profile.dart';
|
||||||
import 'package:url_launcher/url_launcher.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 {
|
class LandingPage extends StatefulWidget {
|
||||||
const LandingPage({super.key});
|
const LandingPage({super.key});
|
||||||
@@ -40,25 +42,31 @@ class _LandingPageState extends State<LandingPage> {
|
|||||||
(MediaQuery.of(context).size.width - _getMainContentWidth()) / 2;
|
(MediaQuery.of(context).size.width - _getMainContentWidth()) / 2;
|
||||||
|
|
||||||
Widget _getSideBar() {
|
Widget _getSideBar() {
|
||||||
return ContentBox(
|
return const Column(
|
||||||
title: 'Fähigkeiten',
|
children: [
|
||||||
content: ContentProvider.skills,
|
ContentBlock(
|
||||||
|
blockTitle: 'Fähigkeiten',
|
||||||
contentType: ContentType.skills,
|
contentType: ContentType.skills,
|
||||||
|
),
|
||||||
|
Padding(padding: EdgeInsets.only(bottom: 25)),
|
||||||
|
ContentBlock(
|
||||||
|
blockTitle: 'Sprachen',
|
||||||
|
contentType: ContentType.language,
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _getMainContent() {
|
Widget _getMainContent() {
|
||||||
return Column(
|
return const Column(
|
||||||
children: [
|
children: [
|
||||||
ContentBox(
|
ContentBlock(
|
||||||
title: 'Arbeitserfahrung',
|
blockTitle: 'Arbeitserfahrung',
|
||||||
content: ContentProvider.experience,
|
|
||||||
contentType: ContentType.experience,
|
contentType: ContentType.experience,
|
||||||
),
|
),
|
||||||
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
Padding(padding: EdgeInsets.only(bottom: 25)),
|
||||||
ContentBox(
|
ContentBlock(
|
||||||
title: 'Bildungsweg',
|
blockTitle: 'Bildungsweg',
|
||||||
content: ContentProvider.education,
|
|
||||||
contentType: ContentType.education,
|
contentType: ContentType.education,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -74,7 +82,7 @@ class _LandingPageState extends State<LandingPage> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: _getSidebarWidth(),
|
width: _getSidebarWidth(),
|
||||||
child: const Padding(
|
child: const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 50),
|
padding: EdgeInsets.symmetric(horizontal: 25),
|
||||||
child: Profile(),
|
child: Profile(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -84,7 +92,7 @@ class _LandingPageState extends State<LandingPage> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: _getSidebarWidth(),
|
width: _getSidebarWidth(),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||||
child: _getSideBar(),
|
child: _getSideBar(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -107,11 +115,12 @@ class _LandingPageState extends State<LandingPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 50),
|
padding: EdgeInsets.symmetric(horizontal: 25),
|
||||||
child: Profile(),
|
child: Profile(),
|
||||||
),
|
),
|
||||||
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||||
child: _getSideBar(),
|
child: _getSideBar(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -121,7 +130,7 @@ class _LandingPageState extends State<LandingPage> {
|
|||||||
width: 900,
|
width: 900,
|
||||||
child: _getMainContent(),
|
child: _getMainContent(),
|
||||||
),
|
),
|
||||||
const Padding(padding: EdgeInsets.only(right: 50)),
|
const Padding(padding: EdgeInsets.only(right: 25)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else if (constraints.maxWidth > Breakpoints.lg) {
|
} else if (constraints.maxWidth > Breakpoints.lg) {
|
||||||
@@ -136,6 +145,7 @@ class _LandingPageState extends State<LandingPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const Profile(),
|
const Profile(),
|
||||||
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
||||||
_getSideBar(),
|
_getSideBar(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:resume/constants.dart';
|
||||||
|
|
||||||
class ContentProvider {
|
class ContentProvider {
|
||||||
ContentProvider._();
|
ContentProvider._();
|
||||||
@@ -17,11 +18,25 @@ class ContentProvider {
|
|||||||
return true;
|
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 T getContent<T>(ContentType contentType) {
|
||||||
|
switch (contentType) {
|
||||||
static List<dynamic> get education => _content['education'];
|
case ContentType.experience:
|
||||||
|
return _content['experience'] as T;
|
||||||
static List<dynamic> get skills => _content['skills'];
|
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:flutter/material.dart';
|
||||||
import 'package:resume/services/breakpoints.dart';
|
import 'package:resume/services/breakpoints.dart';
|
||||||
|
import 'package:resume/constants.dart' show ContentType;
|
||||||
|
|
||||||
|
import '../services/tools.dart';
|
||||||
|
|
||||||
class ContentListTile extends StatelessWidget {
|
class ContentListTile extends StatelessWidget {
|
||||||
const ContentListTile(
|
const ContentListTile.education(
|
||||||
{super.key,
|
{super.key,
|
||||||
this.name,
|
required this.name,
|
||||||
this.location,
|
required this.location,
|
||||||
this.title,
|
required this.title,
|
||||||
this.description,
|
required this.startDate,
|
||||||
this.startDate,
|
required this.endDate})
|
||||||
this.endDate});
|
: description = '',
|
||||||
|
percentage = '',
|
||||||
|
_contentType = ContentType.education;
|
||||||
|
|
||||||
final String? name;
|
const ContentListTile.experience(
|
||||||
final String? location;
|
{super.key,
|
||||||
final String? title;
|
required this.name,
|
||||||
final String? description;
|
required this.location,
|
||||||
final String? startDate;
|
required this.title,
|
||||||
final String? endDate;
|
required this.description,
|
||||||
|
required this.startDate,
|
||||||
|
required this.endDate})
|
||||||
|
: percentage = '',
|
||||||
|
_contentType = ContentType.experience;
|
||||||
|
|
||||||
@override
|
const ContentListTile.skills(
|
||||||
Widget build(BuildContext context) {
|
{super.key, required this.name, required this.percentage})
|
||||||
final width = MediaQuery.of(context).size.width;
|
: 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),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
title: Column(
|
title: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -30,61 +74,83 @@ class ContentListTile extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (name != null)
|
|
||||||
Text(
|
Text(
|
||||||
name!,
|
name,
|
||||||
),
|
),
|
||||||
if (location != null) Text(', $location'),
|
Text(', $location'),
|
||||||
if (title != null && Breakpoints.xl < width) Text(' - $title'),
|
if (Breakpoints.xl < screenWidth) Text(' - $title'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (title != null && Breakpoints.xl >= width) Text('$title'),
|
if (Breakpoints.xl >= screenWidth) Text(title),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
titleAlignment: ListTileTitleAlignment.titleHeight,
|
titleAlignment: ListTileTitleAlignment.titleHeight,
|
||||||
subtitle: description != null
|
subtitle: Breakpoints.sm >= screenWidth
|
||||||
? Column(
|
? 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,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Padding(padding: EdgeInsets.only(top: 8)),
|
const Padding(padding: EdgeInsets.only(top: 8)),
|
||||||
if (startDate != null &&
|
if (Breakpoints.sm >= screenWidth)
|
||||||
endDate != null &&
|
|
||||||
Breakpoints.sm >= width)
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8),
|
padding: const EdgeInsets.only(bottom: 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
_getTimeString(startDate!, endDate!),
|
Tools.buildTimeString(startDate, endDate),
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(description!),
|
Text(description),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
: null,
|
trailing: Breakpoints.sm < screenWidth
|
||||||
trailing: startDate != null && endDate != null && Breakpoints.sm < width
|
? Text(Tools.buildTimeString(startDate, endDate))
|
||||||
? Text(_getTimeString(startDate!, endDate!))
|
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
String _getTimeString(String startDate, String endDate) {
|
@override
|
||||||
final firstDate = DateTime.parse('$startDate-01');
|
Widget build(BuildContext context) {
|
||||||
final secondDate = DateTime.parse('$endDate-01');
|
final width = MediaQuery.of(context).size.width;
|
||||||
return '${months[firstDate.month - 1]} ${firstDate.year} - ${months[secondDate.month - 1]} ${secondDate.year}';
|
|
||||||
}
|
|
||||||
|
|
||||||
static const months = [
|
if (_contentType == ContentType.skills) {
|
||||||
'Januar',
|
return _skillsListTile;
|
||||||
'Februar',
|
} else if (_contentType == ContentType.education) {
|
||||||
'März',
|
return _getEducationListTile(context, width);
|
||||||
'April',
|
} else if (_contentType == ContentType.experience) {
|
||||||
'Mai',
|
return _getExperienceListTile(context, width);
|
||||||
'Juni',
|
} else {
|
||||||
'July',
|
return const Placeholder();
|
||||||
'August',
|
}
|
||||||
'September',
|
}
|
||||||
'Oktober',
|
|
||||||
'November',
|
|
||||||
'Dezember'
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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