252 lines
7.8 KiB
Dart
252 lines
7.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:resume/providers/locale_provider.dart';
|
|
import 'package:resume/services/breakpoints.dart';
|
|
import 'package:resume/widgets/language_dropdown.dart';
|
|
import 'package:resume/widgets/profile.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
import 'package:resume/constants.dart' show ContentType;
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
|
|
import '../providers/content_provider.dart';
|
|
import '../widgets/content_block.dart';
|
|
|
|
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;
|
|
late ContentProvider contentProvider;
|
|
late Locale currentLocale;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
await Provider.of<ContentProvider>(context, listen: false)
|
|
.loadContent(context);
|
|
setState(() => loadingDone = true);
|
|
});
|
|
currentLocale = context.read<LocaleProvider>().locale;
|
|
}
|
|
|
|
double _getMainContentWidth() {
|
|
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;
|
|
}
|
|
|
|
double _getSidebarWidth() =>
|
|
(MediaQuery.of(context).size.width - _getMainContentWidth()) / 2;
|
|
|
|
Widget _getSideBar() {
|
|
return Column(
|
|
children: [
|
|
ContentBlock(
|
|
blockTitle: AppLocalizations.of(context)!.skills,
|
|
contentType: ContentType.skills,
|
|
content: contentProvider.getContent(ContentType.skills),
|
|
),
|
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
|
ContentBlock(
|
|
blockTitle: AppLocalizations.of(context)!.languages,
|
|
contentType: ContentType.language,
|
|
content: contentProvider.getContent(ContentType.language),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _getMainContent() {
|
|
return Column(
|
|
children: [
|
|
ContentBlock(
|
|
blockTitle: AppLocalizations.of(context)!.work_experience,
|
|
contentType: ContentType.experience,
|
|
content: contentProvider.getContent(ContentType.experience),
|
|
),
|
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
|
ContentBlock(
|
|
blockTitle: AppLocalizations.of(context)!.education,
|
|
contentType: ContentType.education,
|
|
content: contentProvider.getContent(ContentType.education),
|
|
),
|
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
|
ContentBlock(
|
|
blockTitle: AppLocalizations.of(context)!.additional_skills,
|
|
contentType: ContentType.generalSkills,
|
|
content: contentProvider.getContent(ContentType.generalSkills),
|
|
),
|
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
|
ContentBlock(
|
|
blockTitle: AppLocalizations.of(context)!.about_me,
|
|
contentType: ContentType.text,
|
|
content: contentProvider.getContent(ContentType.text),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildLayout(BuildContext context, BoxConstraints constraints) {
|
|
if (constraints.maxWidth > Breakpoints.xl2) {
|
|
return Stack(
|
|
children: [
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: _getSidebarWidth(),
|
|
child: const Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 25),
|
|
child: Profile(),
|
|
),
|
|
),
|
|
),
|
|
Align(
|
|
alignment: Alignment.topRight,
|
|
child: SizedBox(
|
|
width: _getSidebarWidth(),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 25),
|
|
child: _getSideBar(),
|
|
),
|
|
),
|
|
),
|
|
Center(
|
|
child: SizedBox(
|
|
width: _getMainContentWidth(),
|
|
child: _getMainContent(),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
} else if (constraints.maxWidth > Breakpoints.xl) {
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
children: [
|
|
const Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 25),
|
|
child: Profile(),
|
|
),
|
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 25),
|
|
child: _getSideBar(),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(
|
|
width: 900,
|
|
child: _getMainContent(),
|
|
),
|
|
const Padding(padding: EdgeInsets.only(right: 25)),
|
|
],
|
|
);
|
|
} else if (constraints.maxWidth > Breakpoints.lg) {
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 25),
|
|
child: Column(
|
|
children: [
|
|
const Profile(),
|
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
|
_getSideBar(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
SizedBox(
|
|
width: 650,
|
|
child: _getMainContent(),
|
|
),
|
|
const Padding(padding: EdgeInsets.only(right: 25)),
|
|
],
|
|
);
|
|
} else {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 25),
|
|
child: Column(
|
|
children: [
|
|
const Profile(),
|
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
|
_getMainContent(),
|
|
const Padding(padding: EdgeInsets.only(bottom: 25)),
|
|
_getSideBar(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
contentProvider = context.read<ContentProvider>();
|
|
|
|
if (currentLocale != context.read<LocaleProvider>().locale) {
|
|
loadingDone = false;
|
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
await Provider.of<ContentProvider>(context, listen: false)
|
|
.loadContent(context);
|
|
setState(() {
|
|
currentLocale = context.read<LocaleProvider>().locale;
|
|
loadingDone = true;
|
|
});
|
|
});
|
|
}
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(AppLocalizations.of(context)!.resume),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: _launchURL,
|
|
child: Text(AppLocalizations.of(context)!.source_code)),
|
|
const LanguageDropdown(),
|
|
],
|
|
),
|
|
body: !loadingDone
|
|
? const Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
CircularProgressIndicator(),
|
|
Padding(padding: EdgeInsets.symmetric(vertical: 10)),
|
|
Text('Loading...')
|
|
],
|
|
),
|
|
)
|
|
: SingleChildScrollView(
|
|
child: LayoutBuilder(
|
|
builder: _buildLayout,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
void _launchURL() async {
|
|
final Uri url = Uri.parse('https://git.skup.in/marco/resume');
|
|
await launchUrl(url);
|
|
}
|