├── .VSCodeCounter └── 2021-04-28_21-38-47 │ ├── details.md │ ├── results.csv │ ├── results.md │ └── results.txt ├── .gitignore ├── .metadata ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── assets ├── fonts │ ├── MySocialIcons.ttf │ ├── Ubuntu-Bold.ttf │ ├── Ubuntu-Medium.ttf │ └── Ubuntu-Regular.ttf ├── images │ └── logo │ │ ├── logo.png │ │ └── me.png └── resume │ └── resume.pdf ├── deploy.sh ├── lib ├── constants │ ├── colors.dart │ ├── routes.dart │ └── types.dart ├── controllers │ └── main-controller.dart ├── main.dart ├── models │ ├── change-page.dart │ ├── responsive │ │ ├── layout_wrapper.dart │ │ ├── responsive_builder.dart │ │ ├── screen_type_layout.dart │ │ └── sizing_information.dart │ ├── social_icons.dart │ ├── typewriter.dart │ └── url_helper.dart └── screens │ ├── body │ ├── body.dart │ └── mobile-body.dart │ ├── pages │ ├── about │ │ ├── about.dart │ │ ├── about_mobile.dart │ │ └── about_widgets.dart │ ├── home │ │ ├── home.dart │ │ ├── home_mobile.dart │ │ └── home_widgets.dart │ ├── portfolio │ │ ├── portfolio.dart │ │ ├── portfolio_box.dart │ │ ├── portfolio_mobile.dart │ │ └── portfolio_widgets.dart │ └── skills │ │ ├── skill_box.dart │ │ ├── skills.dart │ │ ├── skills_mobile.dart │ │ └── skills_widgets.dart │ └── shared │ ├── drawer.dart │ ├── nav_bar.dart │ ├── navbar │ ├── item.dart │ └── navbar.dart │ └── social_media_bar.dart ├── pubspec.lock ├── pubspec.yaml └── web ├── icons ├── android-icon-144x144.png ├── android-icon-192x192.png ├── android-icon-36x36.png ├── android-icon-48x48.png ├── android-icon-72x72.png ├── android-icon-96x96.png ├── apple-icon-114x114.png ├── apple-icon-120x120.png ├── apple-icon-144x144.png ├── apple-icon-152x152.png ├── apple-icon-180x180.png ├── apple-icon-57x57.png ├── apple-icon-60x60.png ├── apple-icon-72x72.png ├── apple-icon-76x76.png ├── apple-icon-precomposed.png ├── apple-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-96x96.png ├── ms-icon-144x144.png ├── ms-icon-150x150.png ├── ms-icon-310x310.png └── ms-icon-70x70.png ├── index.html ├── manifest.json └── spinkit.css /.VSCodeCounter/2021-04-28_21-38-47/details.md: -------------------------------------------------------------------------------- 1 | # Details 2 | 3 | Date : 2021-04-28 21:38:47 4 | 5 | Directory c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib 6 | 7 | Total : 23 files, 1789 codes, 55 comments, 167 blanks, all 2011 lines 8 | 9 | [summary](results.md) 10 | 11 | ## Files 12 | | filename | language | code | comment | blank | total | 13 | | :--- | :--- | ---: | ---: | ---: | ---: | 14 | | [lib/main.dart](/lib/main.dart) | Dart | 19 | 0 | 3 | 22 | 15 | | [lib/models/social_icons.dart](/lib/models/social_icons.dart) | Dart | 26 | 15 | 4 | 45 | 16 | | [lib/models/typewriter.dart](/lib/models/typewriter.dart) | Dart | 69 | 0 | 14 | 83 | 17 | | [lib/models/url_helper.dart](/lib/models/url_helper.dart) | Dart | 18 | 3 | 5 | 26 | 18 | | [lib/screens/about/about.dart](/lib/screens/about/about.dart) | Dart | 29 | 1 | 3 | 33 | 19 | | [lib/screens/about/about_mobile.dart](/lib/screens/about/about_mobile.dart) | Dart | 22 | 0 | 2 | 24 | 20 | | [lib/screens/about/about_widgets.dart](/lib/screens/about/about_widgets.dart) | Dart | 134 | 1 | 7 | 142 | 21 | | [lib/screens/home/home.dart](/lib/screens/home/home.dart) | Dart | 30 | 0 | 2 | 32 | 22 | | [lib/screens/home/home_mobile.dart](/lib/screens/home/home_mobile.dart) | Dart | 25 | 0 | 2 | 27 | 23 | | [lib/screens/home/home_widgets.dart](/lib/screens/home/home_widgets.dart) | Dart | 209 | 10 | 15 | 234 | 24 | | [lib/screens/main/body.dart](/lib/screens/main/body.dart) | Dart | 32 | 0 | 6 | 38 | 25 | | [lib/screens/main/main_page.dart](/lib/screens/main/main_page.dart) | Dart | 74 | 2 | 15 | 91 | 26 | | [lib/screens/responsive/layout_wrapper.dart](/lib/screens/responsive/layout_wrapper.dart) | Dart | 30 | 0 | 5 | 35 | 27 | | [lib/screens/responsive/responsive_builder.dart](/lib/screens/responsive/responsive_builder.dart) | Dart | 38 | 0 | 11 | 49 | 28 | | [lib/screens/responsive/screen_type_layout.dart](/lib/screens/responsive/screen_type_layout.dart) | Dart | 29 | 1 | 6 | 36 | 29 | | [lib/screens/responsive/sizing_information.dart](/lib/screens/responsive/sizing_information.dart) | Dart | 18 | 0 | 5 | 23 | 30 | | [lib/screens/skills/skill_box.dart](/lib/screens/skills/skill_box.dart) | Dart | 122 | 1 | 9 | 132 | 31 | | [lib/screens/skills/skills.dart](/lib/screens/skills/skills.dart) | Dart | 27 | 2 | 3 | 32 | 32 | | [lib/screens/skills/skills_mobile.dart](/lib/screens/skills/skills_mobile.dart) | Dart | 22 | 0 | 2 | 24 | 33 | | [lib/screens/skills/skills_widgets.dart](/lib/screens/skills/skills_widgets.dart) | Dart | 313 | 0 | 12 | 325 | 34 | | [lib/shared/drawer.dart](/lib/shared/drawer.dart) | Dart | 136 | 9 | 14 | 159 | 35 | | [lib/shared/nav_bar.dart](/lib/shared/nav_bar.dart) | Dart | 270 | 10 | 20 | 300 | 36 | | [lib/shared/social_media_bar.dart](/lib/shared/social_media_bar.dart) | Dart | 97 | 0 | 2 | 99 | 37 | 38 | [summary](results.md) -------------------------------------------------------------------------------- /.VSCodeCounter/2021-04-28_21-38-47/results.csv: -------------------------------------------------------------------------------- 1 | "filename", "language", "Dart", "comment", "blank", "total" 2 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\main.dart", "Dart", 19, 0, 3, 22 3 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\models\social_icons.dart", "Dart", 26, 15, 4, 45 4 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\models\typewriter.dart", "Dart", 69, 0, 14, 83 5 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\models\url_helper.dart", "Dart", 18, 3, 5, 26 6 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\about\about.dart", "Dart", 29, 1, 3, 33 7 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\about\about_mobile.dart", "Dart", 22, 0, 2, 24 8 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\about\about_widgets.dart", "Dart", 134, 1, 7, 142 9 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\home\home.dart", "Dart", 30, 0, 2, 32 10 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\home\home_mobile.dart", "Dart", 25, 0, 2, 27 11 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\home\home_widgets.dart", "Dart", 209, 10, 15, 234 12 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\main\body.dart", "Dart", 32, 0, 6, 38 13 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\main\main_page.dart", "Dart", 74, 2, 15, 91 14 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\responsive\layout_wrapper.dart", "Dart", 30, 0, 5, 35 15 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\responsive\responsive_builder.dart", "Dart", 38, 0, 11, 49 16 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\responsive\screen_type_layout.dart", "Dart", 29, 1, 6, 36 17 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\responsive\sizing_information.dart", "Dart", 18, 0, 5, 23 18 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\skills\skill_box.dart", "Dart", 122, 1, 9, 132 19 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\skills\skills.dart", "Dart", 27, 2, 3, 32 20 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\skills\skills_mobile.dart", "Dart", 22, 0, 2, 24 21 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\skills\skills_widgets.dart", "Dart", 313, 0, 12, 325 22 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\shared\drawer.dart", "Dart", 136, 9, 14, 159 23 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\shared\nav_bar.dart", "Dart", 270, 10, 20, 300 24 | "c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\shared\social_media_bar.dart", "Dart", 97, 0, 2, 99 25 | "Total", "-", 1789, 55, 167, 2011 -------------------------------------------------------------------------------- /.VSCodeCounter/2021-04-28_21-38-47/results.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | Date : 2021-04-28 21:38:47 4 | 5 | Directory c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib 6 | 7 | Total : 23 files, 1789 codes, 55 comments, 167 blanks, all 2011 lines 8 | 9 | [details](details.md) 10 | 11 | ## Languages 12 | | language | files | code | comment | blank | total | 13 | | :--- | ---: | ---: | ---: | ---: | ---: | 14 | | Dart | 23 | 1,789 | 55 | 167 | 2,011 | 15 | 16 | ## Directories 17 | | path | files | code | comment | blank | total | 18 | | :--- | ---: | ---: | ---: | ---: | ---: | 19 | | . | 23 | 1,789 | 55 | 167 | 2,011 | 20 | | models | 3 | 113 | 18 | 23 | 154 | 21 | | screens | 16 | 1,154 | 18 | 105 | 1,277 | 22 | | screens\about | 3 | 185 | 2 | 12 | 199 | 23 | | screens\home | 3 | 264 | 10 | 19 | 293 | 24 | | screens\main | 2 | 106 | 2 | 21 | 129 | 25 | | screens\responsive | 4 | 115 | 1 | 27 | 143 | 26 | | screens\skills | 4 | 484 | 3 | 26 | 513 | 27 | | shared | 3 | 503 | 19 | 36 | 558 | 28 | 29 | [details](details.md) -------------------------------------------------------------------------------- /.VSCodeCounter/2021-04-28_21-38-47/results.txt: -------------------------------------------------------------------------------- 1 | Date : 2021-04-28 21:38:47 2 | Directory : c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib 3 | Total : 23 files, 1789 codes, 55 comments, 167 blanks, all 2011 lines 4 | 5 | Languages 6 | +----------+------------+------------+------------+------------+------------+ 7 | | language | files | code | comment | blank | total | 8 | +----------+------------+------------+------------+------------+------------+ 9 | | Dart | 23 | 1,789 | 55 | 167 | 2,011 | 10 | +----------+------------+------------+------------+------------+------------+ 11 | 12 | Directories 13 | +---------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ 14 | | path | files | code | comment | blank | total | 15 | +---------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ 16 | | . | 23 | 1,789 | 55 | 167 | 2,011 | 17 | | models | 3 | 113 | 18 | 23 | 154 | 18 | | screens | 16 | 1,154 | 18 | 105 | 1,277 | 19 | | screens\about | 3 | 185 | 2 | 12 | 199 | 20 | | screens\home | 3 | 264 | 10 | 19 | 293 | 21 | | screens\main | 2 | 106 | 2 | 21 | 129 | 22 | | screens\responsive | 4 | 115 | 1 | 27 | 143 | 23 | | screens\skills | 4 | 484 | 3 | 26 | 513 | 24 | | shared | 3 | 503 | 19 | 36 | 558 | 25 | +---------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ 26 | 27 | Files 28 | +---------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ 29 | | filename | language | code | comment | blank | total | 30 | +---------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ 31 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\main.dart | Dart | 19 | 0 | 3 | 22 | 32 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\models\social_icons.dart | Dart | 26 | 15 | 4 | 45 | 33 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\models\typewriter.dart | Dart | 69 | 0 | 14 | 83 | 34 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\models\url_helper.dart | Dart | 18 | 3 | 5 | 26 | 35 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\about\about.dart | Dart | 29 | 1 | 3 | 33 | 36 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\about\about_mobile.dart | Dart | 22 | 0 | 2 | 24 | 37 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\about\about_widgets.dart | Dart | 134 | 1 | 7 | 142 | 38 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\home\home.dart | Dart | 30 | 0 | 2 | 32 | 39 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\home\home_mobile.dart | Dart | 25 | 0 | 2 | 27 | 40 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\home\home_widgets.dart | Dart | 209 | 10 | 15 | 234 | 41 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\main\body.dart | Dart | 32 | 0 | 6 | 38 | 42 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\main\main_page.dart | Dart | 74 | 2 | 15 | 91 | 43 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\responsive\layout_wrapper.dart | Dart | 30 | 0 | 5 | 35 | 44 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\responsive\responsive_builder.dart | Dart | 38 | 0 | 11 | 49 | 45 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\responsive\screen_type_layout.dart | Dart | 29 | 1 | 6 | 36 | 46 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\responsive\sizing_information.dart | Dart | 18 | 0 | 5 | 23 | 47 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\skills\skill_box.dart | Dart | 122 | 1 | 9 | 132 | 48 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\skills\skills.dart | Dart | 27 | 2 | 3 | 32 | 49 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\skills\skills_mobile.dart | Dart | 22 | 0 | 2 | 24 | 50 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\screens\skills\skills_widgets.dart | Dart | 313 | 0 | 12 | 325 | 51 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\shared\drawer.dart | Dart | 136 | 9 | 14 | 159 | 52 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\shared\nav_bar.dart | Dart | 270 | 10 | 20 | 300 | 53 | | c:\Users\imanr\Erfan\FlutterProjects\My-Personal-Website\lib\shared\social_media_bar.dart | Dart | 97 | 0 | 2 | 99 | 54 | | Total | | 1,789 | 55 | 167 | 2,011 | 55 | +---------------------------------------------------------------------------------------------------------+----------+------------+------------+------------+------------+ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Non-Web platforms 44 | /android/ 45 | /ios/ 46 | /linux/ 47 | 48 | # Android Studio will place build artifacts here 49 | /android/app/debug 50 | /android/app/profile 51 | /android/app/release 52 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: eefcff900c3cdf4aff43751c1b32a3aa65c91a4d 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "personal_web", 9 | "request": "launch", 10 | "type": "dart" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright [2021] [Erfan Rahmati] 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 👤 My personal website 2 | 3 | > ### Checkout Website at https://ErfanRht.github.io/ 4 | > ### Deployed using [Github Pages](https://pages.github.com/) 5 | 6 | ## Technologies Used 7 | 8 | - #### Flutter 9 | - #### Dart 10 | - #### HTML 11 | - #### Illustration (for images) 12 | 13 | ## Contributing 14 | 15 | Feel free to dive in! [Open an issue](https://github.com/ErfanRht/My-Personal-Website/issues/new) or submit PRs 16 | 17 | ## Issues 18 | 19 | Please file any issues, bugs or feature request [here](https://github.com/ErfanRht/My-Personal-Website/issues). 20 | 21 | ## License 22 | 23 | This project is licensed under the [Apache-2.0 License](https://github.com/ErfanRht/My-Personal-Website/blob/master/LICENSE). 24 | 25 | ## Author 26 | 27 | This Flutter project is developed by [Erfan Rahmati](https://github.com/ErfanRht). 28 | 29 | --- 30 | 31 |
32 | 33 | ### Show some ❤️ by starring 🌟 the repository! 34 | 35 |
36 | -------------------------------------------------------------------------------- /assets/fonts/MySocialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/assets/fonts/MySocialIcons.ttf -------------------------------------------------------------------------------- /assets/fonts/Ubuntu-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/assets/fonts/Ubuntu-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Ubuntu-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/assets/fonts/Ubuntu-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Ubuntu-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/assets/fonts/Ubuntu-Regular.ttf -------------------------------------------------------------------------------- /assets/images/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/assets/images/logo/logo.png -------------------------------------------------------------------------------- /assets/images/logo/me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/assets/images/logo/me.png -------------------------------------------------------------------------------- /assets/resume/resume.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/assets/resume/resume.pdf -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # Get commit message 4 | if [ -z "$1" ] 5 | then 6 | message="Update" 7 | else 8 | message=$1 9 | fi 10 | 11 | # Clean up the build 12 | flutter clean 13 | 14 | # Get the dependencies 15 | pub get 16 | 17 | # Analyze for errors 18 | flutter analyze 19 | 20 | # Run web build 21 | flutter build web 22 | 23 | # Copy the generated files into the Github pages repo directory 24 | cp -R build/web/. ../../ErfanRht.github.io 25 | 26 | echo "Pushing with commit message: $message" 27 | 28 | # Change into the Github pages repo directory, commit the changes and push 29 | cd ../../ErfanRht.github.io 30 | git add . 31 | git commit -m "$message" 32 | git push 33 | -------------------------------------------------------------------------------- /lib/constants/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const kPrimaryColor = Colors.teal; 4 | -------------------------------------------------------------------------------- /lib/constants/routes.dart: -------------------------------------------------------------------------------- 1 | // Desktop routes 2 | const HomeRoute = "/home"; 3 | const AboutRoute = "/about"; 4 | const SkillsRoute = "/skills"; 5 | const PortfolioRoute = "/portfolio"; 6 | const ProjectsRoute = "/projects"; 7 | 8 | // Mobile routes 9 | const HomeMobileRoute = "/home-mobile"; 10 | const AboutMobileRoute = "/about-mobile"; 11 | const SkillsMobileRoute = "/skills-mobile"; 12 | const PortfolioMobileRoute = "/portfolio-mobile"; 13 | -------------------------------------------------------------------------------- /lib/constants/types.dart: -------------------------------------------------------------------------------- 1 | enum Pages { HOME, ABOUT, SKILLS, PORTFOLIO, RESUME } 2 | -------------------------------------------------------------------------------- /lib/controllers/main-controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:personal_web/constants/types.dart'; 3 | 4 | class MainController extends GetxController { 5 | Pages selectedPage = Pages.HOME; 6 | int selectedPageNum = 0; 7 | 8 | updatePage({Pages page, int pageNum}) { 9 | selectedPage = page!=null?page:selectedPage; 10 | selectedPageNum = pageNum!=null?pageNum:selectedPageNum; 11 | update(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:personal_web/constants/colors.dart'; 3 | import 'package:personal_web/constants/routes.dart'; 4 | import 'package:personal_web/constants/types.dart'; 5 | import 'package:personal_web/controllers/main-controller.dart'; 6 | import 'package:personal_web/models/responsive/layout_wrapper.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:personal_web/screens/pages/about/about.dart'; 9 | import 'package:personal_web/screens/pages/about/about_mobile.dart'; 10 | import 'package:personal_web/screens/pages/home/home.dart'; 11 | import 'package:personal_web/screens/pages/home/home_mobile.dart'; 12 | import 'package:personal_web/screens/pages/portfolio/portfolio.dart'; 13 | import 'package:personal_web/screens/pages/portfolio/portfolio_mobile.dart'; 14 | import 'package:personal_web/screens/pages/skills/skills.dart'; 15 | import 'package:personal_web/screens/pages/skills/skills_mobile.dart'; 16 | 17 | void main() { 18 | // ignore: unused_local_variable 19 | MainController mainController = Get.put(MainController()); 20 | runApp(MyApp()); 21 | } 22 | 23 | class MyApp extends StatelessWidget { 24 | @override 25 | Widget build(BuildContext context) { 26 | return GetMaterialApp( 27 | title: 'Erfan Rahmati', 28 | debugShowCheckedModeBanner: false, 29 | theme: ThemeData( 30 | primarySwatch: kPrimaryColor, 31 | fontFamily: 'Ubuntu', 32 | ), 33 | initialRoute: HomeRoute, 34 | routes: { 35 | HomeRoute: (context) => LayoutWrapper( 36 | page: HomeSection(), 37 | mobilePage: HomeSectionMobile(), 38 | selectedPage: Pages.HOME, 39 | ), 40 | AboutRoute: (context) => LayoutWrapper( 41 | page: AboutSection(), 42 | mobilePage: AboutSectionMobile(), 43 | selectedPage: Pages.ABOUT, 44 | ), 45 | SkillsRoute: (context) => LayoutWrapper( 46 | page: SkillsSection(), 47 | mobilePage: SkillsSectionMobile(), 48 | selectedPage: Pages.SKILLS, 49 | ), 50 | PortfolioRoute: (context) => LayoutWrapper( 51 | page: PortfolioSection(), 52 | mobilePage: PortfolioSectionMobile(), 53 | selectedPage: Pages.PORTFOLIO, 54 | ), 55 | }, 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/models/change-page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:personal_web/constants/routes.dart'; 4 | import 'package:personal_web/constants/types.dart'; 5 | import 'package:personal_web/controllers/main-controller.dart'; 6 | 7 | changePage(BuildContext context, Pages page) { 8 | String pageRoute; 9 | int pageNum; 10 | if (page == Pages.HOME) { 11 | pageRoute = HomeRoute; 12 | pageNum = 0; 13 | } else if (page == Pages.ABOUT) { 14 | pageRoute = AboutRoute; 15 | pageNum = 1; 16 | } else if (page == Pages.SKILLS) { 17 | pageRoute = SkillsRoute; 18 | pageNum = 2; 19 | } else if (page == Pages.PORTFOLIO) { 20 | pageRoute = PortfolioRoute; 21 | pageNum = 3; 22 | } 23 | Get.find().updatePage(page: page, pageNum: pageNum); 24 | Navigator.pushReplacementNamed(context, pageRoute); 25 | } 26 | -------------------------------------------------------------------------------- /lib/models/responsive/layout_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/types.dart'; 3 | import 'package:personal_web/models/responsive/screen_type_layout.dart'; 4 | import 'package:personal_web/screens/body/body.dart'; 5 | import 'package:personal_web/screens/body/mobile-body.dart'; 6 | 7 | class LayoutWrapper extends StatelessWidget { 8 | Widget page; 9 | Widget mobilePage; 10 | Pages selectedPage; 11 | LayoutWrapper({@required this.page, @required this.mobilePage,@required this.selectedPage}); 12 | @override 13 | Widget build(BuildContext context) { 14 | return ScreenTypeLayout( 15 | mobile: MobileBody(page: mobilePage, selectedPage: selectedPage), 16 | tablet: PageBody(page: page, selectedPage: selectedPage) 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/models/responsive/responsive_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'screen_type_layout.dart'; 3 | import 'sizing_information.dart'; 4 | 5 | class ResponsiveBuilder extends StatelessWidget { 6 | final Widget Function( 7 | BuildContext context, 8 | SizingInformation sizingInformation, 9 | ) builder; 10 | 11 | const ResponsiveBuilder({ 12 | Key key, 13 | @required this.builder, 14 | }) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | final mediaQuery = MediaQuery.of(context); 19 | 20 | return LayoutBuilder( 21 | builder: (context, constraints) { 22 | final sizingInformation = SizingInformation( 23 | orientation: mediaQuery.orientation, 24 | deviceType: getDeviceType(mediaQuery), 25 | screenSize: mediaQuery.size, 26 | localWidgetSize: Size(constraints.maxWidth, constraints.maxHeight), 27 | ); 28 | 29 | return builder(context, sizingInformation); 30 | }, 31 | ); 32 | } 33 | 34 | DeviceScreenType getDeviceType(MediaQueryData mediaQuery) { 35 | double deviceWidth = mediaQuery.size.shortestSide; 36 | 37 | if (deviceWidth > 950) { 38 | return DeviceScreenType.Desktop; 39 | } 40 | 41 | if (deviceWidth > 600) { 42 | return DeviceScreenType.Tablet; 43 | } 44 | 45 | return DeviceScreenType.Mobile; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/models/responsive/screen_type_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'responsive_builder.dart'; 4 | 5 | enum DeviceScreenType { Mobile, Tablet, Desktop } 6 | 7 | class ScreenTypeLayout extends StatelessWidget { 8 | // Mobile will be returned by default 9 | final Widget mobile; 10 | final Widget tablet; 11 | final Widget desktop; 12 | 13 | const ScreenTypeLayout({ 14 | Key key, 15 | @required this.mobile, 16 | this.tablet, 17 | this.desktop, 18 | }) : super(key: key); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return ResponsiveBuilder( 23 | builder: (context, sizingInformation) { 24 | switch (sizingInformation.deviceType) { 25 | case DeviceScreenType.Tablet: 26 | case DeviceScreenType.Desktop: 27 | return tablet; 28 | break; 29 | default: 30 | return mobile; 31 | } 32 | }, 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/models/responsive/sizing_information.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'screen_type_layout.dart'; 4 | 5 | class SizingInformation { 6 | final Orientation orientation; 7 | final DeviceScreenType deviceType; 8 | final Size screenSize; 9 | final Size localWidgetSize; 10 | 11 | SizingInformation({ 12 | this.orientation, 13 | this.deviceType, 14 | this.screenSize, 15 | this.localWidgetSize, 16 | }); 17 | 18 | @override 19 | String toString() { 20 | return 'Orientation:$orientation DeviceType:$deviceType ScreenSize:$screenSize LocalWidgetSize:$localWidgetSize'; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/models/social_icons.dart: -------------------------------------------------------------------------------- 1 | /// Flutter icons SocialIcons 2 | /// Copyright (C) 2020 by original authors @ fluttericon.com, fontello.com 3 | /// This font was generated by FlutterIcon.com, which is derived from Fontello. 4 | /// 5 | /// To use this font, place it in your fonts/ directory and include the 6 | /// following in your pubspec.yaml 7 | /// 8 | /// flutter: 9 | /// fonts: 10 | /// - family: MySocialIcons 11 | /// fonts: 12 | /// - asset: fonts/MySocialIcons.ttf 13 | /// 14 | /// 15 | /// 16 | import 'package:flutter/widgets.dart'; 17 | 18 | class SocialIcons { 19 | SocialIcons._(); 20 | 21 | static const _kFontFam = 'MySocialIcons'; 22 | static const _kFontPkg = null; 23 | 24 | static const IconData github = 25 | IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg); 26 | static const IconData facebook = 27 | IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg); 28 | static const IconData twitter = 29 | IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg); 30 | static const IconData play_store = 31 | IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg); 32 | static const IconData instagram = 33 | IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg); 34 | static const IconData medium = 35 | IconData(0xe805, fontFamily: _kFontFam, fontPackage: _kFontPkg); 36 | static const IconData linkedin = 37 | IconData(0xe806, fontFamily: _kFontFam, fontPackage: _kFontPkg); 38 | static const IconData whatsapp = 39 | IconData(0xe808, fontFamily: _kFontFam, fontPackage: _kFontPkg); 40 | static const IconData envelope = 41 | IconData(0xe809, fontFamily: _kFontFam, fontPackage: _kFontPkg); 42 | static const IconData cloud_download = 43 | IconData(0xe80a, fontFamily: _kFontFam, fontPackage: _kFontPkg); 44 | } 45 | -------------------------------------------------------------------------------- /lib/models/typewriter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Typewriter extends StatefulWidget { 4 | final String text; 5 | final Curve curve; 6 | final TextStyle textStyle; 7 | final VoidCallback onEnd; 8 | final Duration duration; 9 | final bool animate; 10 | 11 | Typewriter( 12 | this.text, { 13 | this.curve = Curves.easeInOut, 14 | this.textStyle, 15 | this.onEnd, 16 | this.animate = true, 17 | this.duration = const Duration(seconds: 2), 18 | }); 19 | 20 | @override 21 | _TypewriterState createState() => _TypewriterState(); 22 | } 23 | 24 | class _TypewriterState extends State with TickerProviderStateMixin { 25 | Animation _characterCount; 26 | AnimationController controller; 27 | 28 | int _stringIndex; 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | controller = AnimationController( 34 | duration: widget.duration, 35 | vsync: this, 36 | ); 37 | 38 | _stringIndex = _stringIndex == null ? 0 : _stringIndex + 1; 39 | 40 | _characterCount = StepTween(begin: 0, end: widget.text.length).animate( 41 | CurvedAnimation( 42 | parent: controller, 43 | curve: widget.curve, 44 | ), 45 | )..addListener(() { 46 | setState(() {}); 47 | }); 48 | 49 | if (widget.animate) controller.forward(); 50 | 51 | controller.addListener(() { 52 | if (widget.onEnd != null && 53 | controller.status == AnimationStatus.completed) { 54 | widget.onEnd(); 55 | } 56 | }); 57 | } 58 | 59 | @override 60 | void dispose() { 61 | controller.dispose(); 62 | super.dispose(); 63 | } 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | return Container( 68 | child: _characterCount == null 69 | ? null 70 | : AnimatedBuilder( 71 | animation: _characterCount, 72 | builder: (BuildContext context, Widget child) { 73 | String text = !widget.animate 74 | ? widget.text 75 | : widget.text.substring(0, _characterCount.value); 76 | 77 | return Text(text, style: widget.textStyle); 78 | }, 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/models/url_helper.dart: -------------------------------------------------------------------------------- 1 | // ignore: avoid_web_libraries_in_flutter 2 | import 'dart:js' as js; 3 | // ignore: avoid_web_libraries_in_flutter 4 | import 'dart:html' as html; 5 | 6 | import 'package:flutter/services.dart'; 7 | import 'package:url_launcher/url_launcher.dart'; 8 | 9 | class UrlHelper { 10 | static Future launchUrl(String url) async { 11 | if (await canLaunch(url)) { 12 | await launch(url, enableJavaScript: true); 13 | } 14 | } 15 | 16 | static Future downloadResume() async { 17 | final readValue = await rootBundle.load('assets/DKB_CV.pdf'); 18 | 19 | // Call the "saveAs" method from the FileSaver.js libray 20 | js.context.callMethod("saveAs", [ 21 | html.Blob([readValue]), 22 | "DKB Resume.pdf", 23 | ]); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/screens/body/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/colors.dart'; 3 | import 'package:personal_web/constants/types.dart'; 4 | import 'package:personal_web/screens/shared/navbar/navbar.dart'; 5 | import 'package:personal_web/screens/shared/social_media_bar.dart'; 6 | 7 | class PageBody extends StatelessWidget { 8 | Widget page; 9 | Pages selectedPage; 10 | PageBody({@required this.page, @required this.selectedPage}); 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | //backgroundColor: Color(0xfffafafa), 15 | body: Stack( 16 | children: [ 17 | CustomPaint(painter: _BackgroundPainter(), size: Size.infinite), 18 | Column( 19 | children: [Navbar(selectedPage: selectedPage), Expanded(child: page)], 20 | ), 21 | Align(alignment: Alignment.centerLeft, child: SocialMediaBar()), 22 | ], 23 | ), 24 | ); 25 | } 26 | } 27 | 28 | class _BackgroundPainter extends CustomPainter { 29 | @override 30 | void paint(Canvas canvas, Size size) { 31 | final path = Path(); 32 | 33 | path.moveTo(size.width * 0.4, 0); 34 | path.lineTo(size.width, 0); 35 | path.lineTo(size.width, size.height); 36 | path.lineTo(size.width * 0.6, size.height); 37 | path.close(); 38 | 39 | final paint = Paint() 40 | ..color = kPrimaryColor 41 | ..style = PaintingStyle.fill; 42 | 43 | canvas.drawPath(path, paint); 44 | } 45 | 46 | @override 47 | bool shouldRepaint(covariant CustomPainter oldDelegate) => false; 48 | } 49 | -------------------------------------------------------------------------------- /lib/screens/body/mobile-body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/types.dart'; 3 | import 'package:personal_web/screens/shared/drawer.dart'; 4 | import 'package:personal_web/screens/shared/nav_bar.dart'; 5 | import 'package:personal_web/screens/shared/navbar/navbar.dart'; 6 | import 'package:personal_web/screens/shared/social_media_bar.dart'; 7 | 8 | class MobileBody extends StatelessWidget { 9 | Widget page; 10 | Pages selectedPage; 11 | MobileBody({@required this.page, @required this.selectedPage}); 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | appBar: MobileNavbar(), 16 | endDrawer: AppDrawer( 17 | selectedPage: selectedPage 18 | ), 19 | body: page 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /lib/screens/pages/about/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/screens/pages/about/about_widgets.dart'; 3 | 4 | class AboutSection extends StatefulWidget { 5 | @override 6 | _AboutSectionState createState() => _AboutSectionState(); 7 | } 8 | 9 | class _AboutSectionState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return Center( 13 | child: Row( 14 | children: [ 15 | Spacer(flex: 1), 16 | //SizedBox(width: 32), 17 | Expanded( 18 | flex: 7, 19 | child: AboutManImage(), 20 | ), 21 | Spacer(flex: 2), 22 | //SizedBox(width: 36), 23 | Expanded( 24 | flex: 6, 25 | child: AboutContent(), 26 | ), 27 | Spacer(flex: 2), 28 | ], 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/screens/pages/about/about_mobile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/colors.dart'; 3 | import 'package:personal_web/screens/pages/about/about_widgets.dart'; 4 | 5 | class AboutSectionMobile extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return Center( 9 | child: SingleChildScrollView( 10 | padding: const EdgeInsets.fromLTRB(30, 4, 30, 24), 11 | child: Column( 12 | mainAxisAlignment: MainAxisAlignment.center, 13 | mainAxisSize: MainAxisSize.min, 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | Image.asset('assets/images/illustration/about.png'), 17 | SizedBox(height: 32), 18 | AboutContent(color: kPrimaryColor, isMobile: true), 19 | ], 20 | ), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/screens/pages/about/about_widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:ms_undraw/ms_undraw.dart'; 3 | import 'package:personal_web/constants/colors.dart'; 4 | import 'package:personal_web/models/typewriter.dart'; 5 | 6 | class AboutManImage extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return UnDraw( 10 | color: kPrimaryColor, 11 | illustration: UnDrawIllustration.programming, 12 | placeholder: Text( 13 | "Illustration is loading...", 14 | style: TextStyle( 15 | color: kPrimaryColor, 16 | fontSize: 20, 17 | ), 18 | ), 19 | errorWidget: Icon(Icons.error_outline, color: kPrimaryColor, size: 50), 20 | ); 21 | } 22 | } 23 | 24 | class AboutContent extends StatefulWidget { 25 | final Color color; 26 | final bool isMobile; 27 | 28 | const AboutContent({this.color = Colors.white, this.isMobile = false}); 29 | @override 30 | _AboutContentState createState() => _AboutContentState(); 31 | } 32 | 33 | class _AboutContentState extends State 34 | with TickerProviderStateMixin { 35 | static bool showAbout = false; 36 | static bool showStack1 = false; 37 | static bool showStack2 = false; 38 | 39 | static bool whoSeen = false; 40 | static bool aboutSeen = false; 41 | static bool stack1Seen = false; 42 | static bool stack2Seen = false; 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return AnimatedSize( 47 | vsync: this, 48 | duration: kThemeAnimationDuration, 49 | alignment: Alignment.topCenter, 50 | child: Column( 51 | crossAxisAlignment: CrossAxisAlignment.start, 52 | mainAxisAlignment: MainAxisAlignment.center, 53 | mainAxisSize: MainAxisSize.min, 54 | children: [ 55 | Typewriter( 56 | 'Who am I?', 57 | animate: !whoSeen, 58 | duration: const Duration(seconds: 1), 59 | textStyle: TextStyle( 60 | color: widget.color, 61 | fontSize: 24, 62 | fontWeight: FontWeight.w700, 63 | letterSpacing: 1.4, 64 | ), 65 | onEnd: () { 66 | if (mounted) { 67 | setState(() { 68 | showAbout = true; 69 | whoSeen = true; 70 | }); 71 | } 72 | }, 73 | ), 74 | Container( 75 | width: 60, 76 | height: 2, 77 | margin: const EdgeInsets.only(top: 4, bottom: 16), 78 | color: widget.color, 79 | ), 80 | if (showAbout) 81 | Typewriter( 82 | "Hello! I'm Erfan Rahmati, A teen software developer.\n\n" 83 | "I love to create performant and interesting stuff that are beneficial to the community.\n" 84 | "I enjoy learning and exploring new areas in the technologies I work with and even the ones outside my stack.\n\n", 85 | //"Currently I am working with Dart, Python and Framework languages.", 86 | animate: !aboutSeen, 87 | duration: const Duration(seconds: 10), 88 | textStyle: TextStyle( 89 | color: widget.color, 90 | fontSize: 16, 91 | letterSpacing: 1.2, 92 | height: 1.3, 93 | ), 94 | onEnd: () { 95 | if (mounted) { 96 | setState(() { 97 | showStack1 = true; 98 | aboutSeen = true; 99 | }); 100 | } 101 | }, 102 | ), 103 | if (showStack1) ...[ 104 | SizedBox(height: 54), 105 | Typewriter( 106 | 'What things is I use to get stuff done?', 107 | animate: !stack1Seen, 108 | duration: const Duration(seconds: 2), 109 | textStyle: TextStyle( 110 | color: widget.color, 111 | fontSize: 24, 112 | fontWeight: FontWeight.w700, 113 | letterSpacing: 1.4, 114 | ), 115 | onEnd: () { 116 | if (mounted) { 117 | setState(() { 118 | showStack2 = true; 119 | stack1Seen = true; 120 | }); 121 | } 122 | }, 123 | ), 124 | Container( 125 | width: 60, 126 | height: 2, 127 | margin: const EdgeInsets.only(top: 4, bottom: 16), 128 | color: widget.color, 129 | ), 130 | if (showStack2) 131 | Typewriter( 132 | 'OS: Ubuntu 20.04\nBrowser: Chorme Web Browser\nTerminal: ZSH: Oh My Zsh (PowerLevel10k)\nCode Editor: VSCode - The best editor out there.\nTo Stay Updated: Medium, Virgool, Telegram and Twitter.', 133 | animate: !stack2Seen, 134 | duration: const Duration(seconds: 6), 135 | textStyle: TextStyle( 136 | color: widget.color, 137 | fontSize: 16, 138 | letterSpacing: 1.2, 139 | height: 1.3, 140 | ), 141 | onEnd: () { 142 | if (mounted) { 143 | setState(() { 144 | stack2Seen = true; 145 | }); 146 | } 147 | }, 148 | ), 149 | ], 150 | ], 151 | ), 152 | ); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /lib/screens/pages/home/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/screens/pages/home/home_widgets.dart'; 3 | 4 | class HomeSection extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return Center( 8 | child: Row( 9 | children: [ 10 | Spacer(flex: 2), 11 | SizedBox(width: 32), 12 | Expanded( 13 | flex: 6, 14 | child: Introduction(), 15 | ), 16 | SizedBox(width: 36), 17 | Expanded( 18 | flex: 8, 19 | child: Padding( 20 | padding: EdgeInsets.only(left: 150), 21 | child: HeroImage( 22 | widgetColor: Color(0xfffafafa), 23 | ), 24 | ), 25 | ), 26 | Spacer(flex: 2), 27 | ], 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/screens/pages/home/home_mobile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/colors.dart'; 3 | import 'package:personal_web/screens/pages/home/home_widgets.dart'; 4 | 5 | class HomeSectionMobile extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return Center( 9 | child: SingleChildScrollView( 10 | padding: const EdgeInsets.fromLTRB(30, 4, 30, 12), 11 | child: Column( 12 | mainAxisAlignment: MainAxisAlignment.center, 13 | mainAxisSize: MainAxisSize.min, 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | HeroImage( 17 | backgroundColor: kPrimaryColor, 18 | widgetColor: kPrimaryColor 19 | ), 20 | SizedBox(height: 32), 21 | Introduction(), 22 | ], 23 | ), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/screens/pages/home/home_widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/colors.dart'; 3 | import 'package:personal_web/models/typewriter.dart'; 4 | import 'package:personal_web/models/url_helper.dart'; 5 | 6 | /// Introductory texts with the Hire Me button as well 7 | class Introduction extends StatefulWidget { 8 | @override 9 | _IntroductionState createState() => _IntroductionState(); 10 | } 11 | 12 | class _IntroductionState extends State 13 | with SingleTickerProviderStateMixin { 14 | static bool helloSeen = false; 15 | static bool nameSeen = false; 16 | static bool positionSeen = false; 17 | static bool abstractSeen = false; 18 | 19 | static bool showName = false; 20 | static bool showPosition = false; 21 | static bool showAbstract = false; 22 | static bool showHireMe = false; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return AnimatedSize( 27 | vsync: this, 28 | duration: kThemeAnimationDuration, 29 | alignment: Alignment.topCenter, 30 | child: Column( 31 | crossAxisAlignment: CrossAxisAlignment.start, 32 | mainAxisAlignment: MainAxisAlignment.center, 33 | mainAxisSize: MainAxisSize.min, 34 | children: [ 35 | Typewriter( 36 | "Hey! I'm...", 37 | animate: !helloSeen, 38 | textStyle: TextStyle( 39 | color: kPrimaryColor, 40 | fontSize: 24, 41 | fontWeight: FontWeight.w700, 42 | letterSpacing: 1.4, 43 | ), 44 | onEnd: () { 45 | if (mounted) { 46 | setState(() { 47 | showName = true; 48 | helloSeen = true; 49 | }); 50 | } 51 | }, 52 | ), 53 | if (showName) ...[ 54 | SizedBox(height: 16), 55 | Typewriter( 56 | 'Erfan Rahmati', 57 | animate: !nameSeen, 58 | textStyle: TextStyle( 59 | color: Colors.blueGrey[900], 60 | fontSize: 40, 61 | fontWeight: FontWeight.w700, 62 | ), 63 | onEnd: () { 64 | if (mounted) { 65 | setState(() { 66 | showPosition = true; 67 | nameSeen = true; 68 | }); 69 | } 70 | }, 71 | ), 72 | ], 73 | if (showPosition) ...[ 74 | SizedBox(height: 16), 75 | Typewriter( 76 | 'Mobile & Web Developer', 77 | animate: !positionSeen, 78 | textStyle: TextStyle( 79 | color: Colors.blueGrey[900], 80 | fontSize: 20, 81 | fontWeight: FontWeight.w500, 82 | ), 83 | onEnd: () { 84 | if (mounted) { 85 | setState(() { 86 | showAbstract = true; 87 | positionSeen = true; 88 | }); 89 | } 90 | }, 91 | ), 92 | ], 93 | if (showAbstract) ...[ 94 | SizedBox(height: 24), 95 | Typewriter( 96 | "I build neat, cool and scalable mobile apps with Flutter and I'm an aspiring deep learning engineer.\n" 97 | 'I love to learn and build new stuff that are beneficial to the community and cool to work on.\n' 98 | 'I also have great interest in the open source community.', 99 | animate: !abstractSeen, 100 | textStyle: TextStyle( 101 | color: Colors.grey, 102 | fontSize: 16, 103 | letterSpacing: 1.2, 104 | height: 1.3, 105 | ), 106 | onEnd: () { 107 | Future.delayed(Duration(milliseconds: 500), () { 108 | if (mounted) { 109 | setState(() { 110 | showHireMe = true; 111 | abstractSeen = true; 112 | }); 113 | } 114 | }); 115 | }, 116 | ), 117 | ], 118 | if (showHireMe) ...[ 119 | SizedBox(height: 30), 120 | _HireMeButton(), 121 | ], 122 | ], 123 | ), 124 | ); 125 | } 126 | } 127 | 128 | class _HireMeButton extends StatefulWidget { 129 | @override 130 | __HireMeButtonState createState() => __HireMeButtonState(); 131 | } 132 | 133 | class __HireMeButtonState extends State<_HireMeButton> { 134 | bool hovered = false; 135 | 136 | @override 137 | Widget build(BuildContext context) { 138 | return InkWell( 139 | hoverColor: Colors.transparent, 140 | splashColor: Colors.transparent, 141 | onTap: () { 142 | UrlHelper.launchUrl("mailto:ErfanRht1384.com@gmail.com"); 143 | }, 144 | onHover: (value) { 145 | if (mounted) { 146 | setState(() { 147 | hovered = value; 148 | }); 149 | } 150 | }, 151 | child: AnimatedContainer( 152 | height: 50, 153 | width: 160, 154 | duration: kThemeAnimationDuration, 155 | alignment: Alignment.center, 156 | decoration: BoxDecoration( 157 | border: Border.all(width: 1.4, color: kPrimaryColor), 158 | borderRadius: BorderRadius.all(Radius.circular(50)), 159 | color: hovered ? kPrimaryColor.withOpacity(1.0) : Colors.transparent, 160 | ), 161 | child: AnimatedDefaultTextStyle( 162 | duration: kThemeAnimationDuration, 163 | style: TextStyle( 164 | color: hovered ? Colors.white : kPrimaryColor, 165 | fontSize: 17, 166 | fontWeight: FontWeight.w500, 167 | fontFamily: 'Ubuntu', 168 | ), 169 | child: Text( 170 | 'Hire Me', 171 | ), 172 | ), 173 | ), 174 | ); 175 | } 176 | } 177 | 178 | class HeroImage extends StatefulWidget { 179 | final Color borderColor; 180 | final Color backgroundColor; 181 | final Color widgetColor; 182 | 183 | HeroImage({ 184 | this.borderColor = Colors.white, 185 | this.backgroundColor = Colors.transparent, 186 | this.widgetColor = Colors.white, 187 | }); 188 | 189 | @override 190 | _HeroImageState createState() => _HeroImageState(); 191 | } 192 | 193 | class _HeroImageState extends State { 194 | bool showLogo; 195 | 196 | wait() async { 197 | await Future.delayed(Duration(milliseconds: 250)); 198 | setState(() { 199 | showLogo = !showLogo; 200 | }); 201 | } 202 | 203 | @override 204 | void initState() { 205 | super.initState(); 206 | showLogo = false; 207 | wait(); 208 | } 209 | 210 | @override 211 | Widget build(BuildContext context) { 212 | return AnimatedOpacity( 213 | opacity: showLogo ? 1 : 0, 214 | duration: Duration(milliseconds: 250), 215 | child: Container( 216 | width: 300, 217 | height: 300, 218 | child: Image.asset( 219 | 'assets/images/logo/me.png', 220 | color: widget.widgetColor, 221 | ), 222 | ), 223 | ); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /lib/screens/pages/portfolio/portfolio.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/screens/pages/portfolio/portfolio_widgets.dart'; 3 | 4 | class PortfolioSection extends StatefulWidget { 5 | @override 6 | _PortfolioSectionState createState() => _PortfolioSectionState(); 7 | } 8 | 9 | class _PortfolioSectionState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return Center( 13 | child: Row( 14 | children: [ 15 | Spacer(flex: 1), 16 | SizedBox(width: 10), 17 | Expanded( 18 | flex: 10, 19 | child: PortfolioManImage(false), 20 | ), 21 | //SizedBox(width: 36), 22 | Expanded( 23 | flex: 8, 24 | child: PortfolioContent(), 25 | ), 26 | //Spacer(flex: 2), 27 | ], 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/screens/pages/portfolio/portfolio_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/models/typewriter.dart'; 3 | 4 | class PortfolioBox extends StatefulWidget { 5 | final String name; 6 | final Color color; 7 | final bool isMobile, seen; 8 | final double score; 9 | final int waitTime; 10 | 11 | const PortfolioBox( 12 | {this.name, 13 | this.color = Colors.white, 14 | this.isMobile = false, 15 | this.score = 0, 16 | this.seen = false, 17 | this.waitTime = 0}); 18 | @override 19 | _PortfolioBoxState createState() => _PortfolioBoxState(); 20 | } 21 | 22 | class _PortfolioBoxState extends State 23 | with TickerProviderStateMixin { 24 | double widgetWidth, insideWidgetWidth, space; 25 | int showSpeed; 26 | bool start; 27 | @override 28 | void initState() { 29 | super.initState(); 30 | showSpeed = 750; 31 | if (widget.seen) { 32 | start = true; 33 | widgetWidth = 200; 34 | insideWidgetWidth = widget.score * 2; 35 | } else { 36 | start = false; 37 | widgetWidth = 0; 38 | insideWidgetWidth = 0; 39 | } 40 | 41 | if (widget.name == 'Dart ') { 42 | space = 7.0; 43 | } else if (widget.name == 'PhP ') { 44 | space = 8.5; 45 | } else if (widget.name == 'Git ') { 46 | space = 9; 47 | } else if (widget.name == 'MySQL ') { 48 | space = 6.5; 49 | } else if (widget.name == 'Linux ') { 50 | space = 11.5; 51 | } else if (widget.name == 'HTML ') { 52 | space = 8; 53 | } else { 54 | space = 5; 55 | } 56 | 57 | startWidget(); 58 | //updateValues(); 59 | } 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | return Stack( 64 | children: [ 65 | if (start) 66 | Row( 67 | children: [ 68 | Typewriter( 69 | widget.name, 70 | animate: true, 71 | duration: Duration(milliseconds: widget.seen ? 0 : 350), 72 | textStyle: TextStyle( 73 | color: widget.color, 74 | fontSize: 15, 75 | fontWeight: FontWeight.w700, 76 | letterSpacing: 1.4, 77 | ), 78 | onEnd: () async { 79 | if (mounted) { 80 | await Future.delayed(Duration(milliseconds: 250)); 81 | updateValues(); 82 | } 83 | }, 84 | ), 85 | ], 86 | ), 87 | AnimatedContainer( 88 | margin: EdgeInsets.only(left: 70), 89 | duration: Duration(milliseconds: widget.seen ? 0 : showSpeed), 90 | decoration: BoxDecoration( 91 | color: widget.isMobile ? Color(0xffd3d3d3) : Color(0xfffafafa), 92 | borderRadius: BorderRadius.circular(8)), 93 | height: 17.5, 94 | width: widgetWidth, 95 | child: Center( 96 | child: Row( 97 | children: [ 98 | SizedBox( 99 | width: 2.5, 100 | ), 101 | AnimatedContainer( 102 | duration: Duration(milliseconds: widget.seen ? 0 : showSpeed), 103 | decoration: BoxDecoration( 104 | color: Colors.blue, 105 | borderRadius: BorderRadius.circular(6)), 106 | height: 12.5, 107 | width: insideWidgetWidth, 108 | ), 109 | ], 110 | ), 111 | ), 112 | ), 113 | ], 114 | ); 115 | } 116 | 117 | startWidget() async { 118 | if (!widget.seen) { 119 | await Future.delayed(Duration(milliseconds: widget.waitTime)); 120 | setState(() { 121 | start = true; 122 | }); 123 | } 124 | } 125 | 126 | updateValues() async { 127 | setState(() { 128 | widgetWidth = 200; 129 | }); 130 | if (!widget.seen) { 131 | await Future.delayed(Duration(milliseconds: showSpeed)); 132 | setState(() { 133 | insideWidgetWidth = widget.score * 2; 134 | }); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /lib/screens/pages/portfolio/portfolio_mobile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/colors.dart'; 3 | import 'package:personal_web/screens/pages/portfolio/portfolio_widgets.dart'; 4 | 5 | class PortfolioSectionMobile extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return Center( 9 | child: SingleChildScrollView( 10 | padding: const EdgeInsets.fromLTRB(30, 4, 30, 24), 11 | child: Column( 12 | mainAxisAlignment: MainAxisAlignment.center, 13 | mainAxisSize: MainAxisSize.min, 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | PortfolioManImage(true), 17 | SizedBox(height: 32), 18 | PortfolioContent(color: kPrimaryColor, isMobile: true), 19 | ], 20 | ), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/screens/pages/portfolio/portfolio_widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | import 'package:personal_web/models/typewriter.dart'; 4 | 5 | class PortfolioManImage extends StatelessWidget { 6 | final bool isMobile; 7 | const PortfolioManImage(this.isMobile); 8 | @override 9 | Widget build(BuildContext context) { 10 | return Padding( 11 | padding: EdgeInsets.only(right: isMobile ? 0 : 150), 12 | child: Image.asset( 13 | 'assets/images/illustration/Portfolios.png', 14 | height: 450, 15 | ), 16 | ); 17 | } 18 | } 19 | 20 | class PortfolioContent extends StatefulWidget { 21 | final Color color; 22 | final bool isMobile; 23 | 24 | const PortfolioContent({this.color = Colors.white, this.isMobile = false}); 25 | @override 26 | _PortfolioContentState createState() => _PortfolioContentState(); 27 | } 28 | 29 | class _PortfolioContentState extends State 30 | with TickerProviderStateMixin { 31 | static bool showPortfolio = false; 32 | static bool portfolioSeen = false; 33 | static bool whatSeen = false; 34 | static double space, widthSpace; 35 | 36 | List portfolio; 37 | List scores; 38 | int showSpeed, portfolioNumber; 39 | 40 | @override 41 | void initState() { 42 | super.initState(); 43 | space = 30; 44 | widthSpace = 50; 45 | showSpeed = 750; 46 | portfolioNumber = -2; 47 | portfolio = [ 48 | 'Flutter', 49 | 'Python', 50 | 'Dart', 51 | 'PHP', 52 | 'Git', 53 | 'Linux', 54 | 'HTML', 55 | 'MySQL', 56 | 'Regex', 57 | 'JSON' 58 | ]; 59 | 60 | scores = [90.0, 80.0, 85.0, 60.0, 90.0, 85.0, 75.0, 80.0, 75.0, 85]; 61 | endPage(); 62 | } 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | return AnimatedSize( 67 | vsync: this, 68 | duration: kThemeAnimationDuration, 69 | alignment: Alignment.topCenter, 70 | child: Column( 71 | crossAxisAlignment: CrossAxisAlignment.start, 72 | mainAxisAlignment: MainAxisAlignment.center, 73 | mainAxisSize: MainAxisSize.min, 74 | children: [], 75 | ), 76 | ); 77 | } 78 | 79 | endPage() async { 80 | await Future.delayed( 81 | Duration(milliseconds: (portfolio.length + 1) * 3 * showSpeed)); 82 | setState(() { 83 | portfolioSeen = true; 84 | }); 85 | } 86 | 87 | Widget sizedBox(double height, double width) { 88 | return SizedBox( 89 | height: height, 90 | width: width, 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/screens/pages/skills/skill_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/models/typewriter.dart'; 3 | 4 | class SkillBox extends StatefulWidget { 5 | final String name; 6 | final Color color; 7 | final bool isMobile, seen; 8 | final double score; 9 | final int waitTime; 10 | 11 | const SkillBox( 12 | {this.name, 13 | this.color = Colors.white, 14 | this.isMobile = false, 15 | this.score = 0, 16 | this.seen = false, 17 | this.waitTime = 0}); 18 | @override 19 | _SkillBoxState createState() => _SkillBoxState(); 20 | } 21 | 22 | class _SkillBoxState extends State with TickerProviderStateMixin { 23 | double widgetWidth, insideWidgetWidth, space; 24 | int showSpeed; 25 | bool start; 26 | @override 27 | void initState() { 28 | super.initState(); 29 | showSpeed = 750; 30 | if (widget.seen) { 31 | start = true; 32 | widgetWidth = 200; 33 | insideWidgetWidth = widget.score * 2; 34 | } else { 35 | start = false; 36 | widgetWidth = 0; 37 | insideWidgetWidth = 0; 38 | } 39 | 40 | if (widget.name == 'Dart ') { 41 | space = 7.0; 42 | } else if (widget.name == 'PhP ') { 43 | space = 8.5; 44 | } else if (widget.name == 'Git ') { 45 | space = 9; 46 | } else if (widget.name == 'MySQL ') { 47 | space = 6.5; 48 | } else if (widget.name == 'Linux ') { 49 | space = 11.5; 50 | } else if (widget.name == 'HTML ') { 51 | space = 8; 52 | } else { 53 | space = 5; 54 | } 55 | 56 | startWidget(); 57 | //updateValues(); 58 | } 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | return Stack( 63 | children: [ 64 | if (start) 65 | Row( 66 | children: [ 67 | Typewriter( 68 | widget.name, 69 | animate: true, 70 | duration: Duration(milliseconds: widget.seen ? 0 : 350), 71 | textStyle: TextStyle( 72 | color: widget.color, 73 | fontSize: 15, 74 | fontWeight: FontWeight.w700, 75 | letterSpacing: 1.4, 76 | ), 77 | onEnd: () async { 78 | if (mounted) { 79 | await Future.delayed(Duration(milliseconds: 250)); 80 | updateValues(); 81 | } 82 | }, 83 | ), 84 | ], 85 | ), 86 | AnimatedContainer( 87 | margin: EdgeInsets.only(left: 70), 88 | duration: Duration(milliseconds: widget.seen ? 0 : showSpeed), 89 | decoration: BoxDecoration( 90 | color: widget.isMobile ? Color(0xffd3d3d3) : Color(0xfffafafa), 91 | borderRadius: BorderRadius.circular(8)), 92 | height: 17.5, 93 | width: widgetWidth, 94 | child: Center( 95 | child: Row( 96 | children: [ 97 | SizedBox( 98 | width: 2.5, 99 | ), 100 | AnimatedContainer( 101 | duration: Duration(milliseconds: widget.seen ? 0 : showSpeed), 102 | decoration: BoxDecoration( 103 | color: Colors.blue, 104 | borderRadius: BorderRadius.circular(6)), 105 | height: 12.5, 106 | width: insideWidgetWidth, 107 | ), 108 | ], 109 | ), 110 | ), 111 | ), 112 | ], 113 | ); 114 | } 115 | 116 | startWidget() async { 117 | if (!widget.seen) { 118 | await Future.delayed(Duration(milliseconds: widget.waitTime)); 119 | setState(() { 120 | start = true; 121 | }); 122 | } 123 | } 124 | 125 | updateValues() async { 126 | setState(() { 127 | widgetWidth = 200; 128 | }); 129 | if (!widget.seen) { 130 | await Future.delayed(Duration(milliseconds: showSpeed)); 131 | setState(() { 132 | insideWidgetWidth = widget.score * 2; 133 | }); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /lib/screens/pages/skills/skills.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/screens/pages/skills/skills_widgets.dart'; 3 | 4 | class SkillsSection extends StatefulWidget { 5 | @override 6 | _SkillsSectionState createState() => _SkillsSectionState(); 7 | } 8 | 9 | class _SkillsSectionState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return Center( 13 | child: Row( 14 | children: [ 15 | Spacer(flex: 1), 16 | Expanded( 17 | flex: 7, 18 | child: SkillsManImage(false), 19 | ), 20 | Spacer(flex: 2), 21 | Expanded( 22 | flex: 8, 23 | child: SkillsContent(), 24 | ), 25 | //Spacer(flex: 2), 26 | ], 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/screens/pages/skills/skills_mobile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/colors.dart'; 3 | import 'package:personal_web/screens/pages/skills/skills_widgets.dart'; 4 | 5 | class SkillsSectionMobile extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return Center( 9 | child: SingleChildScrollView( 10 | padding: const EdgeInsets.fromLTRB(30, 4, 30, 24), 11 | child: Column( 12 | mainAxisAlignment: MainAxisAlignment.center, 13 | mainAxisSize: MainAxisSize.min, 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | SkillsManImage(true), 17 | SizedBox(height: 32), 18 | SkillsContent(color: kPrimaryColor, isMobile: true), 19 | ], 20 | ), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/screens/pages/skills/skills_widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | import 'package:ms_undraw/ms_undraw.dart'; 4 | import 'package:personal_web/constants/colors.dart'; 5 | import 'package:personal_web/models/typewriter.dart'; 6 | import 'package:personal_web/screens/pages/skills/skill_box.dart'; 7 | 8 | class SkillsManImage extends StatelessWidget { 9 | final bool isMobile; 10 | const SkillsManImage(this.isMobile); 11 | @override 12 | Widget build(BuildContext context) { 13 | return UnDraw( 14 | color: kPrimaryColor, 15 | illustration: UnDrawIllustration.profile, 16 | placeholder: Text( 17 | "Illustration is loading...", 18 | style: TextStyle( 19 | color:kPrimaryColor, 20 | fontSize: 20, 21 | ), 22 | ), 23 | errorWidget: Icon(Icons.error_outline, color: kPrimaryColor, size: 50), 24 | ); 25 | } 26 | } 27 | 28 | class SkillsContent extends StatefulWidget { 29 | final Color color; 30 | final bool isMobile; 31 | 32 | const SkillsContent({this.color = Colors.white, this.isMobile = false}); 33 | @override 34 | _SkillsContentState createState() => _SkillsContentState(); 35 | } 36 | 37 | class _SkillsContentState extends State 38 | with TickerProviderStateMixin { 39 | static bool showSkills = false; 40 | static bool skillsSeen = false; 41 | static bool whatSeen = false; 42 | static double space, widthSpace; 43 | 44 | List skills; 45 | List scores; 46 | int showSpeed, skillNumber; 47 | 48 | @override 49 | void initState() { 50 | super.initState(); 51 | space = 30; 52 | widthSpace = 50; 53 | showSpeed = 750; 54 | skillNumber = -2; 55 | skills = [ 56 | 'Flutter', 57 | 'Python', 58 | 'Dart', 59 | 'PHP', 60 | 'Git', 61 | 'Linux', 62 | 'HTML', 63 | 'MySQL', 64 | 'Regex', 65 | 'JSON' 66 | ]; 67 | 68 | scores = [90.0, 80.0, 85.0, 60.0, 90.0, 85.0, 75.0, 80.0, 75.0, 85]; 69 | endPage(); 70 | } 71 | 72 | @override 73 | Widget build(BuildContext context) { 74 | return AnimatedSize( 75 | vsync: this, 76 | duration: kThemeAnimationDuration, 77 | alignment: Alignment.topCenter, 78 | child: Column( 79 | crossAxisAlignment: CrossAxisAlignment.start, 80 | mainAxisAlignment: MainAxisAlignment.center, 81 | mainAxisSize: MainAxisSize.min, 82 | children: [ 83 | Typewriter( 84 | 'What are my skills?', 85 | animate: !whatSeen, 86 | duration: const Duration(milliseconds: 750), 87 | textStyle: TextStyle( 88 | color: widget.color, 89 | fontSize: 24, 90 | fontWeight: FontWeight.w700, 91 | letterSpacing: 1.4, 92 | ), 93 | onEnd: () { 94 | if (mounted) { 95 | setState(() { 96 | showSkills = true; 97 | whatSeen = true; 98 | }); 99 | } 100 | }, 101 | ), 102 | Container( 103 | width: 60, 104 | height: 2, 105 | margin: const EdgeInsets.only(top: 4, bottom: 16), 106 | color: widget.color, 107 | ), 108 | if (!widget.isMobile) ...[ 109 | if (showSkills) ...[ 110 | sizedBox(space / 2, 0), 111 | Row( 112 | children: [ 113 | skillBoxShower(0), 114 | sizedBox(0, widthSpace), 115 | skillBoxShower(1), 116 | ], 117 | ), 118 | sizedBox(space, 0), 119 | Row( 120 | children: [ 121 | skillBoxShower(2), 122 | sizedBox(0, widthSpace), 123 | skillBoxShower(3), 124 | ], 125 | ), 126 | sizedBox(space, 0), 127 | Row( 128 | children: [ 129 | skillBoxShower(4), 130 | sizedBox(0, widthSpace), 131 | skillBoxShower(5), 132 | ], 133 | ), 134 | sizedBox(space, 0), 135 | Row( 136 | children: [ 137 | skillBoxShower(6), 138 | sizedBox(0, widthSpace), 139 | skillBoxShower(7), 140 | ], 141 | ), 142 | sizedBox(space, 0), 143 | Row( 144 | children: [ 145 | skillBoxShower(8), 146 | sizedBox(0, widthSpace), 147 | skillBoxShower(9), 148 | ], 149 | ), 150 | ] 151 | ] else ...[ 152 | Column( 153 | children: [ 154 | sizedBox(15, 0), 155 | skillBoxShower(0), 156 | sizedBox(15, 0), 157 | skillBoxShower(1), 158 | sizedBox(15, 0), 159 | skillBoxShower(2), 160 | sizedBox(15, 0), 161 | skillBoxShower(3), 162 | sizedBox(15, 0), 163 | skillBoxShower(4), 164 | sizedBox(15, 0), 165 | skillBoxShower(5), 166 | sizedBox(15, 0), 167 | skillBoxShower(6), 168 | sizedBox(15, 0), 169 | skillBoxShower(7), 170 | sizedBox(15, 0), 171 | skillBoxShower(8), 172 | sizedBox(15, 0), 173 | skillBoxShower(9), 174 | sizedBox(15, 0), 175 | ], 176 | ) 177 | ] 178 | ], 179 | ), 180 | ); 181 | } 182 | 183 | endPage() async { 184 | await Future.delayed( 185 | Duration(milliseconds: (skills.length + 1) * 3 * showSpeed)); 186 | setState(() { 187 | skillsSeen = true; 188 | }); 189 | } 190 | 191 | Widget sizedBox(double height, double width) { 192 | return SizedBox( 193 | height: height, 194 | width: width, 195 | ); 196 | } 197 | 198 | Widget skillRow() { 199 | setState(() { 200 | skillNumber++; 201 | skillNumber++; 202 | }); 203 | return Column( 204 | children: [ 205 | Row( 206 | children: [ 207 | skillBoxShower(skillNumber - 1), 208 | sizedBox(0, widthSpace), 209 | skillBoxShower(skillNumber), 210 | ], 211 | ), 212 | sizedBox(space, 0), 213 | ], 214 | ); 215 | } 216 | 217 | Widget skillBoxShower(int index) { 218 | return SkillBox( 219 | name: skills[index], 220 | color: widget.color, 221 | isMobile: widget.isMobile, 222 | score: scores[index], 223 | seen: skillsSeen, 224 | waitTime: index * 3 * showSpeed, 225 | ); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /lib/screens/shared/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/colors.dart'; 3 | import 'package:personal_web/constants/types.dart'; 4 | import 'package:personal_web/models/change-page.dart'; 5 | import 'package:personal_web/screens/shared/nav_bar.dart'; 6 | import 'package:personal_web/models/url_helper.dart'; 7 | 8 | class AppDrawer extends StatelessWidget { 9 | Pages selectedPage; 10 | AppDrawer({@required this.selectedPage}); 11 | 12 | final textStyle = TextStyle( 13 | color: Colors.white, 14 | fontSize: 17, 15 | fontWeight: FontWeight.w700, 16 | ); 17 | 18 | final items = [ 19 | 'Home', 20 | 'About', 21 | 'Skills', 22 | 'Experience', 23 | 'Projects', 24 | 'Resume', 25 | ]; 26 | 27 | final page = [ 28 | Pages.HOME, 29 | Pages.ABOUT, 30 | Pages.SKILLS, 31 | Pages.PORTFOLIO, 32 | Pages.RESUME 33 | ]; 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return Drawer( 38 | child: Container( 39 | color: kPrimaryColor, 40 | child: Stack( 41 | alignment: Alignment.center, 42 | children: [ 43 | Positioned( 44 | left: 8, 45 | top: 8, 46 | child: IconButton( 47 | color: Colors.white, 48 | icon: Icon(Icons.close), 49 | onPressed: () { 50 | Navigator.pop(context); 51 | }, 52 | ), 53 | ), 54 | Column( 55 | children: [ 56 | Container( 57 | height: 100, 58 | width: 100, 59 | margin: EdgeInsets.only(top: 75), 60 | child: Image.asset( 61 | 'assets/images/logo/me.png', 62 | color: Color(0xfffafafa), 63 | ), 64 | ), 65 | ], 66 | ), 67 | Column( 68 | mainAxisAlignment: MainAxisAlignment.center, 69 | mainAxisSize: MainAxisSize.min, 70 | children: items.map((item) { 71 | return InkWell( 72 | onTap: () { 73 | switch (item) { 74 | case 'Home': 75 | if (selectedPage != Pages.HOME) { 76 | changePage(context, Pages.HOME); 77 | } 78 | break; 79 | case 'About': 80 | if (selectedPage != Pages.ABOUT) { 81 | changePage(context, Pages.ABOUT); 82 | } 83 | break; 84 | case 'Skills': 85 | if (selectedPage != Pages.SKILLS) { 86 | changePage(context, Pages.SKILLS); 87 | } 88 | break; 89 | case 'Portfolio': 90 | if (selectedPage != Pages.PORTFOLIO) { 91 | changePage(context, Pages.PORTFOLIO); 92 | } 93 | break; 94 | case 'Resume': 95 | break; 96 | case 'Skills': 97 | break; 98 | default: 99 | 100 | } 101 | 102 | Navigator.pop(context); 103 | }, 104 | child: item == 'Resume' 105 | ? SizedBox(width: 160, child: ResumeButton()) 106 | : Column( 107 | mainAxisAlignment: MainAxisAlignment.center, 108 | children: [ 109 | Text( 110 | item, 111 | style: textStyle.copyWith( 112 | fontWeight: FontWeight.w500, 113 | color: textStyle.color.withOpacity( 114 | selectedPage == page[items.indexOf(item)] 115 | ? 1.0 116 | : 0.75, 117 | ), 118 | ), 119 | ), 120 | SizedBox(height: 4), 121 | if (item != 'Resume') 122 | AnimatedContainer( 123 | duration: Duration(milliseconds: 300), 124 | height: 2, 125 | width: 20, 126 | color: selectedPage == page[items.indexOf(item)] 127 | ? Colors.white 128 | : Colors.transparent, 129 | ), 130 | SizedBox(height: 20), 131 | ], 132 | ), 133 | ); 134 | }).toList(), 135 | ), 136 | ], 137 | ), 138 | ), 139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /lib/screens/shared/nav_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:personal_web/constants/colors.dart'; 4 | import 'package:personal_web/constants/types.dart'; 5 | import 'package:personal_web/controllers/main-controller.dart'; 6 | import 'package:personal_web/models/change-page.dart'; 7 | import 'package:personal_web/models/url_helper.dart'; 8 | 9 | class MobileNavbar extends StatelessWidget implements PreferredSizeWidget { 10 | final textStyle = TextStyle( 11 | color: Colors.white, 12 | fontSize: 17, 13 | fontWeight: FontWeight.w700, 14 | ); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return AppBar( 19 | backgroundColor: Theme.of(context).scaffoldBackgroundColor, 20 | elevation: 0, 21 | actions: [ 22 | IconButton( 23 | color: kPrimaryColor, 24 | highlightColor: Colors.transparent, 25 | hoverColor: Colors.transparent, 26 | splashColor: Colors.transparent, 27 | icon: Icon(Icons.menu), 28 | onPressed: () { 29 | Scaffold.of(context).openEndDrawer(); 30 | }, 31 | ) 32 | ], 33 | title: Row( 34 | mainAxisAlignment: MainAxisAlignment.center, 35 | mainAxisSize: MainAxisSize.min, 36 | children: [ 37 | CircleAvatar( 38 | backgroundColor: kPrimaryColor, 39 | radius: 15, 40 | child: Image.asset( 41 | 'assets/images/logo/logo.png', 42 | height: 20, 43 | //'web/icons/favicon-32x32', 44 | color: Colors.white, 45 | )), 46 | SizedBox(width: 10), 47 | RichText( 48 | textAlign: TextAlign.center, 49 | text: TextSpan( 50 | text: 'Erfan', 51 | style: textStyle.copyWith( 52 | color: Colors.black.withOpacity(0.75), 53 | fontSize: 18, 54 | fontFamily: 'Ubuntu', 55 | ), 56 | children: [ 57 | TextSpan( 58 | text: 'Rahmati', 59 | style: textStyle.copyWith( 60 | color: kPrimaryColor, 61 | fontSize: 18, 62 | fontFamily: 'Ubuntu', 63 | ), 64 | ), 65 | ], 66 | ), 67 | ), 68 | ], 69 | ), 70 | ); 71 | } 72 | 73 | @override 74 | Size get preferredSize => Size.fromHeight(56.0); 75 | } 76 | 77 | class Navbar extends StatefulWidget { 78 | @override 79 | _NavbarState createState() => _NavbarState(); 80 | } 81 | 82 | class _NavbarState extends State { 83 | final MainController mainController = Get.find(); 84 | int selectedIndex; 85 | final textStyle = TextStyle( 86 | color: Colors.white, 87 | fontSize: 17, 88 | fontWeight: FontWeight.w700, 89 | ); 90 | 91 | final items = [ 92 | 'Home', 93 | 'About', 94 | 'Skills', 95 | 'Portfolio', 96 | 'Projects', 97 | 'Resume', 98 | ]; 99 | 100 | @override 101 | void initState() { 102 | super.initState(); 103 | if (mainController.selectedPage == Pages.HOME) { 104 | selectedIndex=0; 105 | } else if (mainController.selectedPage == Pages.ABOUT) { 106 | selectedIndex=1; 107 | } else if (mainController.selectedPage == Pages.SKILLS) { 108 | selectedIndex=2; 109 | } else if (mainController.selectedPage == Pages.PORTFOLIO) { 110 | selectedIndex=3; 111 | } 112 | } 113 | 114 | @override 115 | Widget build(BuildContext context) { 116 | return GetBuilder(builder: (_) { 117 | return Container( 118 | height: 72, 119 | width: double.infinity, 120 | child: Row( 121 | children: [ 122 | Expanded( 123 | child: Row( 124 | mainAxisAlignment: MainAxisAlignment.center, 125 | mainAxisSize: MainAxisSize.min, 126 | children: [ 127 | CircleAvatar( 128 | backgroundColor: kPrimaryColor, 129 | radius: 15, 130 | child: Image.asset( 131 | 'assets/images/logo/logo.png', 132 | height: 20, 133 | color: Colors.white, 134 | )), 135 | SizedBox(width: 8), 136 | RichText( 137 | textAlign: TextAlign.center, 138 | text: TextSpan( 139 | text: 'Erfan', 140 | style: textStyle.copyWith( 141 | color: Colors.black.withOpacity(0.75), 142 | fontSize: 18, 143 | fontFamily: 'Ubuntu', 144 | ), 145 | children: [ 146 | TextSpan( 147 | text: 'Rahmati', 148 | style: textStyle.copyWith( 149 | color: kPrimaryColor, 150 | fontSize: 18, 151 | fontFamily: 'Ubuntu', 152 | ), 153 | ), 154 | ], 155 | ), 156 | ), 157 | ], 158 | ), 159 | ), 160 | Expanded( 161 | child: Row( 162 | mainAxisAlignment: MainAxisAlignment.spaceAround, 163 | children: items.map((item) { 164 | return InkWell( 165 | onTap: () { 166 | switch (item) { 167 | case 'Home': 168 | if (mounted) { 169 | changePage(context, Pages.HOME); 170 | } 171 | setState(() { 172 | selectedIndex = items.indexOf(item); 173 | }); 174 | break; 175 | case 'About': 176 | if (mounted) { 177 | changePage(context, Pages.ABOUT); 178 | } 179 | setState(() { 180 | selectedIndex = items.indexOf(item); 181 | }); 182 | break; 183 | case 'Skills': 184 | if (mounted) { 185 | changePage(context, Pages.SKILLS); 186 | } 187 | setState(() { 188 | selectedIndex = items.indexOf(item); 189 | }); 190 | break; 191 | case 'Portfolio': 192 | if (mounted) { 193 | changePage(context, Pages.PORTFOLIO); 194 | } 195 | setState(() { 196 | selectedIndex = items.indexOf(item); 197 | }); 198 | break; 199 | case 'Resume': 200 | break; 201 | case 'Skills': 202 | // UrlHelper.launchUrl( 203 | // 'https://medium.com/@debrahkwesibuabeng2', 204 | // ); 205 | break; 206 | default: 207 | setState(() { 208 | selectedIndex = items.indexOf(item); 209 | }); 210 | 211 | // if (widget.onItemSelected != null) { 212 | // widget.onItemSelected(selectedIndex); 213 | // } 214 | } 215 | }, 216 | child: item == 'Resume' 217 | ? ResumeButton() 218 | : Column( 219 | mainAxisAlignment: MainAxisAlignment.center, 220 | children: [ 221 | Text( 222 | item, 223 | style: textStyle.copyWith( 224 | fontWeight: FontWeight.w500, 225 | color: textStyle.color.withOpacity( 226 | _.selectedPageNum == items.indexOf(item) 227 | ? 1.0 228 | : 0.75, 229 | ), 230 | ), 231 | ), 232 | SizedBox(height: 4), 233 | if (item != 'Resume') 234 | AnimatedContainer( 235 | duration: Duration(milliseconds: 300), 236 | height: 2, 237 | width: 20, 238 | color: _.selectedPageNum == items.indexOf(item) 239 | ? Colors.white 240 | : Colors.transparent, 241 | ), 242 | ], 243 | ), 244 | ); 245 | }).toList(), 246 | ), 247 | ), 248 | ], 249 | ), 250 | ); 251 | }); 252 | } 253 | } 254 | 255 | class ResumeButton extends StatefulWidget { 256 | @override 257 | _ResumeButtonState createState() => _ResumeButtonState(); 258 | } 259 | 260 | class _ResumeButtonState extends State { 261 | bool hovered = false; 262 | 263 | @override 264 | Widget build(BuildContext context) { 265 | return InkWell( 266 | hoverColor: Colors.transparent, 267 | splashColor: Colors.transparent, 268 | onTap: () { 269 | UrlHelper.downloadResume(); 270 | }, 271 | onHover: (value) { 272 | if (mounted) { 273 | setState(() { 274 | hovered = value; 275 | }); 276 | } 277 | }, 278 | child: AnimatedContainer( 279 | height: 40, 280 | duration: kThemeAnimationDuration, 281 | alignment: Alignment.center, 282 | padding: const EdgeInsets.symmetric( 283 | vertical: 6, 284 | horizontal: 10, 285 | ), 286 | decoration: BoxDecoration( 287 | border: Border.all(color: Colors.white, width: 2), 288 | borderRadius: BorderRadius.all(Radius.circular(30)), 289 | color: hovered ? Colors.white.withOpacity(0.92) : kPrimaryColor, 290 | ), 291 | child: AnimatedDefaultTextStyle( 292 | duration: kThemeAnimationDuration, 293 | style: TextStyle( 294 | color: hovered ? kPrimaryColor : Colors.white, 295 | fontSize: 17, 296 | fontWeight: FontWeight.w500, 297 | fontFamily: 'Ubuntu', 298 | ), 299 | child: Text( 300 | 'Resume', 301 | ), 302 | ), 303 | ), 304 | ); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /lib/screens/shared/navbar/item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/types.dart'; 3 | 4 | class NavbarItem extends StatelessWidget { 5 | 6 | Pages selectedPage, item; 7 | String itemName; 8 | NavbarItem({@required this.item,@required this.itemName ,@required this.selectedPage}); 9 | 10 | final textStyle = TextStyle( 11 | color: Colors.white, 12 | fontSize: 17, 13 | fontWeight: FontWeight.w700, 14 | ); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Column( 19 | mainAxisAlignment: MainAxisAlignment.center, 20 | children: [ 21 | Text( 22 | itemName, 23 | style: textStyle.copyWith( 24 | fontWeight: FontWeight.w500, 25 | color: textStyle.color.withOpacity( 26 | item==selectedPage 27 | ? 1.0 28 | : 0.75, 29 | ), 30 | ), 31 | ), 32 | SizedBox(height: 4), 33 | 34 | AnimatedContainer( 35 | duration: Duration(milliseconds: 300), 36 | height: 2, 37 | width: 20, 38 | color: item==selectedPage 39 | ? Colors.white 40 | : Colors.transparent, 41 | ), 42 | ], 43 | ); 44 | } 45 | } -------------------------------------------------------------------------------- /lib/screens/shared/navbar/navbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:personal_web/constants/colors.dart'; 4 | import 'package:personal_web/constants/types.dart'; 5 | import 'package:personal_web/controllers/main-controller.dart'; 6 | import 'package:personal_web/models/change-page.dart'; 7 | import 'package:personal_web/models/url_helper.dart'; 8 | import 'package:personal_web/screens/shared/navbar/item.dart'; 9 | 10 | class Navbar extends StatelessWidget { 11 | Pages selectedPage; 12 | Navbar({@required this.selectedPage}); 13 | 14 | final textStyle = TextStyle( 15 | color: Colors.white, 16 | fontSize: 17, 17 | fontWeight: FontWeight.w700, 18 | ); 19 | 20 | final items = [ 21 | 'Home', 22 | 'About', 23 | 'Skills', 24 | 'Portfolio', 25 | 'Projects', 26 | 'Resume', 27 | ]; 28 | 29 | final pages = [ 30 | Pages.HOME, 31 | Pages.ABOUT, 32 | Pages.SKILLS, 33 | Pages.PORTFOLIO, 34 | Pages.RESUME 35 | ]; 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return GetBuilder(builder: (_) { 40 | return Container( 41 | height: 72, 42 | width: double.infinity, 43 | child: Row( 44 | children: [ 45 | Expanded( 46 | child: Row( 47 | mainAxisAlignment: MainAxisAlignment.center, 48 | mainAxisSize: MainAxisSize.min, 49 | children: [ 50 | CircleAvatar( 51 | backgroundColor: kPrimaryColor, 52 | radius: 15, 53 | child: Image.asset( 54 | 'assets/images/logo/logo.png', 55 | height: 20, 56 | color: Colors.white, 57 | )), 58 | SizedBox(width: 8), 59 | RichText( 60 | textAlign: TextAlign.center, 61 | text: TextSpan( 62 | text: 'Erfan', 63 | style: textStyle.copyWith( 64 | color: Colors.black.withOpacity(0.75), 65 | fontSize: 18, 66 | fontFamily: 'Ubuntu', 67 | ), 68 | children: [ 69 | TextSpan( 70 | text: 'Rahmati', 71 | style: textStyle.copyWith( 72 | color: kPrimaryColor, 73 | fontSize: 18, 74 | fontFamily: 'Ubuntu', 75 | ), 76 | ), 77 | ], 78 | ), 79 | ), 80 | ], 81 | ), 82 | ), 83 | Expanded( 84 | child: Row( 85 | mainAxisAlignment: MainAxisAlignment.spaceAround, 86 | children: items.map((item) { 87 | return InkWell( 88 | onTap: () { 89 | switch (item) { 90 | case 'Home': 91 | if (selectedPage != Pages.HOME) { 92 | changePage(context, Pages.HOME); 93 | } 94 | break; 95 | case 'About': 96 | if (selectedPage != Pages.ABOUT) { 97 | changePage(context, Pages.ABOUT); 98 | } 99 | break; 100 | case 'Skills': 101 | if (selectedPage != Pages.SKILLS) { 102 | changePage(context, Pages.SKILLS); 103 | } 104 | break; 105 | case 'Portfolio': 106 | if (selectedPage != Pages.PORTFOLIO) { 107 | changePage(context, Pages.PORTFOLIO); 108 | } 109 | break; 110 | case 'Resume': 111 | break; 112 | case 'Skills': 113 | break; 114 | default: 115 | } 116 | }, 117 | child: item == 'Resume' 118 | ? ResumeButton() 119 | : NavbarItem(item: pages[items.indexOf(item)], itemName: item, selectedPage: selectedPage) 120 | ); 121 | }).toList(), 122 | ), 123 | ), 124 | ], 125 | ), 126 | ); 127 | }); 128 | } 129 | } 130 | 131 | 132 | 133 | class ResumeButton extends StatefulWidget { 134 | @override 135 | _ResumeButtonState createState() => _ResumeButtonState(); 136 | } 137 | 138 | class _ResumeButtonState extends State { 139 | bool hovered = false; 140 | 141 | @override 142 | Widget build(BuildContext context) { 143 | return InkWell( 144 | hoverColor: Colors.transparent, 145 | splashColor: Colors.transparent, 146 | onTap: () { 147 | UrlHelper.downloadResume(); 148 | }, 149 | onHover: (value) { 150 | if (mounted) { 151 | setState(() { 152 | hovered = value; 153 | }); 154 | } 155 | }, 156 | child: AnimatedContainer( 157 | height: 40, 158 | duration: kThemeAnimationDuration, 159 | alignment: Alignment.center, 160 | padding: const EdgeInsets.symmetric( 161 | vertical: 6, 162 | horizontal: 10, 163 | ), 164 | decoration: BoxDecoration( 165 | border: Border.all(color: Colors.white, width: 2), 166 | borderRadius: BorderRadius.all(Radius.circular(30)), 167 | color: hovered ? Colors.white.withOpacity(0.92) : kPrimaryColor, 168 | ), 169 | child: AnimatedDefaultTextStyle( 170 | duration: kThemeAnimationDuration, 171 | style: TextStyle( 172 | color: hovered ? kPrimaryColor: Colors.white, 173 | fontSize: 17, 174 | fontWeight: FontWeight.w500, 175 | fontFamily: 'Ubuntu', 176 | ), 177 | child: Text( 178 | 'Resume', 179 | ), 180 | ), 181 | ), 182 | ); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /lib/screens/shared/social_media_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:personal_web/constants/colors.dart'; 3 | import 'package:personal_web/models/social_icons.dart'; 4 | import 'package:personal_web/models/url_helper.dart'; 5 | 6 | class SocialMediaBar extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Container( 10 | padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 12), 11 | margin: const EdgeInsets.only(left: 32), 12 | decoration: BoxDecoration( 13 | border: Border.all(color:kPrimaryColor.withOpacity(0.3), width: 1.4), 14 | borderRadius: BorderRadius.all(Radius.circular(35)), 15 | ), 16 | child: Column( 17 | mainAxisSize: MainAxisSize.min, 18 | children: [ 19 | InkWell( 20 | hoverColor: Colors.transparent, 21 | splashColor: Colors.transparent, 22 | onTap: () { 23 | UrlHelper.launchUrl('https://github.com/ErfanRht'); 24 | }, 25 | child: Icon( 26 | SocialIcons.github, 27 | color: kPrimaryColor.withOpacity(0.75), 28 | ), 29 | ), 30 | SizedBox(height: 30), 31 | InkWell( 32 | hoverColor: Colors.transparent, 33 | splashColor: Colors.transparent, 34 | onTap: () { 35 | UrlHelper.launchUrl( 36 | 'https://www.linkedin.com/in/ErfanRahmati/', 37 | ); 38 | }, 39 | child: Icon( 40 | SocialIcons.linkedin, 41 | color: kPrimaryColor.withOpacity(0.75), 42 | ), 43 | ), 44 | SizedBox(height: 30), 45 | InkWell( 46 | hoverColor: Colors.transparent, 47 | splashColor: Colors.transparent, 48 | onTap: () { 49 | UrlHelper.launchUrl( 50 | "mailto:ErfanRht1384.com@gmail.com?subject=Hello%20DKB", 51 | ); 52 | }, 53 | child: Icon( 54 | SocialIcons.envelope, 55 | color: kPrimaryColor.withOpacity(0.75), 56 | ), 57 | ), 58 | SizedBox(height: 30), 59 | InkWell( 60 | hoverColor: Colors.transparent, 61 | splashColor: Colors.transparent, 62 | onTap: () { 63 | UrlHelper.launchUrl('https://twitter.com/ErfanRht'); 64 | }, 65 | child: Icon( 66 | SocialIcons.twitter, 67 | color: kPrimaryColor.withOpacity(0.75), 68 | ), 69 | ), 70 | SizedBox(height: 30), 71 | InkWell( 72 | hoverColor: Colors.transparent, 73 | splashColor: Colors.transparent, 74 | onTap: () { 75 | UrlHelper.launchUrl('https://www.instagram.com/ErfanRahmatei/'); 76 | }, 77 | child: Icon( 78 | SocialIcons.instagram, 79 | color: kPrimaryColor.withOpacity(0.75), 80 | ), 81 | ), 82 | SizedBox(height: 30), 83 | InkWell( 84 | hoverColor: Colors.transparent, 85 | splashColor: Colors.transparent, 86 | onTap: () { 87 | UrlHelper.launchUrl( 88 | 'https://web.facebook.com/ErfanRahmati', 89 | ); 90 | }, 91 | child: Icon( 92 | SocialIcons.facebook, 93 | color: kPrimaryColor.withOpacity(0.75), 94 | ), 95 | ), 96 | SizedBox(height: 30), 97 | InkWell( 98 | hoverColor: Colors.transparent, 99 | splashColor: Colors.transparent, 100 | onTap: () { 101 | UrlHelper.launchUrl( 102 | 'https://api.whatsapp.com/send?phone=0989397288246'); 103 | }, 104 | child: Icon( 105 | SocialIcons.whatsapp, 106 | color: kPrimaryColor.withOpacity(0.75), 107 | ), 108 | ), 109 | ], 110 | ), 111 | ); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.6.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.2.0" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | cupertino_icons: 47 | dependency: "direct main" 48 | description: 49 | name: cupertino_icons 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.0.0" 53 | fake_async: 54 | dependency: transitive 55 | description: 56 | name: fake_async 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.2.0" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_spinkit: 66 | dependency: "direct main" 67 | description: 68 | name: flutter_spinkit 69 | url: "https://pub.dartlang.org" 70 | source: hosted 71 | version: "5.0.0" 72 | flutter_svg: 73 | dependency: transitive 74 | description: 75 | name: flutter_svg 76 | url: "https://pub.dartlang.org" 77 | source: hosted 78 | version: "0.22.0" 79 | flutter_test: 80 | dependency: "direct dev" 81 | description: flutter 82 | source: sdk 83 | version: "0.0.0" 84 | flutter_web_plugins: 85 | dependency: transitive 86 | description: flutter 87 | source: sdk 88 | version: "0.0.0" 89 | get: 90 | dependency: "direct main" 91 | description: 92 | name: get 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "4.3.8" 96 | http: 97 | dependency: transitive 98 | description: 99 | name: http 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "0.13.2" 103 | http_parser: 104 | dependency: transitive 105 | description: 106 | name: http_parser 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "4.0.0" 110 | js: 111 | dependency: transitive 112 | description: 113 | name: js 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "0.6.3" 117 | matcher: 118 | dependency: transitive 119 | description: 120 | name: matcher 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "0.12.10" 124 | meta: 125 | dependency: transitive 126 | description: 127 | name: meta 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.3.0" 131 | ms_undraw: 132 | dependency: "direct main" 133 | description: 134 | name: ms_undraw 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "3.0.0+0" 138 | page_transition: 139 | dependency: "direct main" 140 | description: 141 | name: page_transition 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.1.7+6" 145 | path: 146 | dependency: transitive 147 | description: 148 | name: path 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.8.0" 152 | path_drawing: 153 | dependency: transitive 154 | description: 155 | name: path_drawing 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "0.5.0" 159 | path_parsing: 160 | dependency: transitive 161 | description: 162 | name: path_parsing 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "0.2.0" 166 | pedantic: 167 | dependency: transitive 168 | description: 169 | name: pedantic 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "1.11.0" 173 | petitparser: 174 | dependency: transitive 175 | description: 176 | name: petitparser 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "4.1.0" 180 | plugin_platform_interface: 181 | dependency: transitive 182 | description: 183 | name: plugin_platform_interface 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "1.0.3" 187 | sky_engine: 188 | dependency: transitive 189 | description: flutter 190 | source: sdk 191 | version: "0.0.99" 192 | source_span: 193 | dependency: transitive 194 | description: 195 | name: source_span 196 | url: "https://pub.dartlang.org" 197 | source: hosted 198 | version: "1.8.1" 199 | stack_trace: 200 | dependency: transitive 201 | description: 202 | name: stack_trace 203 | url: "https://pub.dartlang.org" 204 | source: hosted 205 | version: "1.10.0" 206 | stream_channel: 207 | dependency: transitive 208 | description: 209 | name: stream_channel 210 | url: "https://pub.dartlang.org" 211 | source: hosted 212 | version: "2.1.0" 213 | string_scanner: 214 | dependency: transitive 215 | description: 216 | name: string_scanner 217 | url: "https://pub.dartlang.org" 218 | source: hosted 219 | version: "1.1.0" 220 | term_glyph: 221 | dependency: transitive 222 | description: 223 | name: term_glyph 224 | url: "https://pub.dartlang.org" 225 | source: hosted 226 | version: "1.2.0" 227 | test_api: 228 | dependency: transitive 229 | description: 230 | name: test_api 231 | url: "https://pub.dartlang.org" 232 | source: hosted 233 | version: "0.3.0" 234 | typed_data: 235 | dependency: transitive 236 | description: 237 | name: typed_data 238 | url: "https://pub.dartlang.org" 239 | source: hosted 240 | version: "1.3.0" 241 | url_launcher: 242 | dependency: "direct main" 243 | description: 244 | name: url_launcher 245 | url: "https://pub.dartlang.org" 246 | source: hosted 247 | version: "5.7.10" 248 | url_launcher_linux: 249 | dependency: transitive 250 | description: 251 | name: url_launcher_linux 252 | url: "https://pub.dartlang.org" 253 | source: hosted 254 | version: "0.0.1+4" 255 | url_launcher_macos: 256 | dependency: transitive 257 | description: 258 | name: url_launcher_macos 259 | url: "https://pub.dartlang.org" 260 | source: hosted 261 | version: "0.0.1+9" 262 | url_launcher_platform_interface: 263 | dependency: transitive 264 | description: 265 | name: url_launcher_platform_interface 266 | url: "https://pub.dartlang.org" 267 | source: hosted 268 | version: "1.0.9" 269 | url_launcher_web: 270 | dependency: transitive 271 | description: 272 | name: url_launcher_web 273 | url: "https://pub.dartlang.org" 274 | source: hosted 275 | version: "0.1.5+1" 276 | url_launcher_windows: 277 | dependency: transitive 278 | description: 279 | name: url_launcher_windows 280 | url: "https://pub.dartlang.org" 281 | source: hosted 282 | version: "0.0.1+3" 283 | vector_math: 284 | dependency: transitive 285 | description: 286 | name: vector_math 287 | url: "https://pub.dartlang.org" 288 | source: hosted 289 | version: "2.1.0" 290 | xml: 291 | dependency: transitive 292 | description: 293 | name: xml 294 | url: "https://pub.dartlang.org" 295 | source: hosted 296 | version: "5.1.0" 297 | sdks: 298 | dart: ">=2.12.0 <3.0.0" 299 | flutter: ">=2.0.0" 300 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: personal_web 2 | description: Erfan Rahmati portfolio website. 3 | 4 | publish_to: "none" 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: ">=2.7.0 <3.0.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | cupertino_icons: ^1.0.0 16 | url_launcher: ^5.7.10 17 | ms_undraw: ^3.0.0+0 18 | get: ^4.3.8 19 | flutter_spinkit: ^5.0.0 20 | page_transition: ^1.1.7+6 21 | 22 | dev_dependencies: 23 | flutter_test: 24 | sdk: flutter 25 | 26 | flutter: 27 | uses-material-design: true 28 | 29 | assets: 30 | - assets/resume/ 31 | - assets/images/logo/ 32 | 33 | fonts: 34 | - family: MySocialIcons 35 | fonts: 36 | - asset: assets/fonts/MySocialIcons.ttf 37 | - family: Ubuntu 38 | fonts: 39 | - asset: assets/fonts/Ubuntu-Regular.ttf 40 | - asset: assets/fonts/Ubuntu-Medium.ttf 41 | weight: 500 42 | - asset: assets/fonts/Ubuntu-Bold.ttf 43 | weight: 700 44 | -------------------------------------------------------------------------------- /web/icons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/android-icon-144x144.png -------------------------------------------------------------------------------- /web/icons/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/android-icon-192x192.png -------------------------------------------------------------------------------- /web/icons/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/android-icon-36x36.png -------------------------------------------------------------------------------- /web/icons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/android-icon-48x48.png -------------------------------------------------------------------------------- /web/icons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/android-icon-72x72.png -------------------------------------------------------------------------------- /web/icons/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/android-icon-96x96.png -------------------------------------------------------------------------------- /web/icons/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-114x114.png -------------------------------------------------------------------------------- /web/icons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-120x120.png -------------------------------------------------------------------------------- /web/icons/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-144x144.png -------------------------------------------------------------------------------- /web/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /web/icons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-180x180.png -------------------------------------------------------------------------------- /web/icons/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-57x57.png -------------------------------------------------------------------------------- /web/icons/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-60x60.png -------------------------------------------------------------------------------- /web/icons/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-72x72.png -------------------------------------------------------------------------------- /web/icons/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-76x76.png -------------------------------------------------------------------------------- /web/icons/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon-precomposed.png -------------------------------------------------------------------------------- /web/icons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/apple-icon.png -------------------------------------------------------------------------------- /web/icons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /web/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/favicon-16x16.png -------------------------------------------------------------------------------- /web/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/favicon-32x32.png -------------------------------------------------------------------------------- /web/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/favicon-96x96.png -------------------------------------------------------------------------------- /web/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /web/icons/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/ms-icon-150x150.png -------------------------------------------------------------------------------- /web/icons/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/ms-icon-310x310.png -------------------------------------------------------------------------------- /web/icons/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErfanRht/Flutter-Personal-Website/0b34020b40bc9e1903fd88f0219603d4b98dea81/web/icons/ms-icon-70x70.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Erfan Rahmati 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Erfan Rahmati 52 | 53 | 54 | 100 | 101 | 102 | 103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Erfan Rahmati", 3 | "short_name": "Erfan Rahmati", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "Erfan Rahmati's portfolio website.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "\/android-icon-36x36.png", 14 | "sizes": "36x36", 15 | "type": "image\/png", 16 | "density": "0.75" 17 | }, 18 | { 19 | "src": "\/android-icon-48x48.png", 20 | "sizes": "48x48", 21 | "type": "image\/png", 22 | "density": "1.0" 23 | }, 24 | { 25 | "src": "\/android-icon-72x72.png", 26 | "sizes": "72x72", 27 | "type": "image\/png", 28 | "density": "1.5" 29 | }, 30 | { 31 | "src": "\/android-icon-96x96.png", 32 | "sizes": "96x96", 33 | "type": "image\/png", 34 | "density": "2.0" 35 | }, 36 | { 37 | "src": "\/android-icon-144x144.png", 38 | "sizes": "144x144", 39 | "type": "image\/png", 40 | "density": "3.0" 41 | }, 42 | { 43 | "src": "\/android-icon-192x192.png", 44 | "sizes": "192x192", 45 | "type": "image\/png", 46 | "density": "4.0" 47 | } 48 | ] 49 | } -------------------------------------------------------------------------------- /web/spinkit.css: -------------------------------------------------------------------------------- 1 | /* Config */ 2 | :root { 3 | --sk-size: 90px; 4 | --sk-color: #333; 5 | 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | position: absolute; 10 | top: 50%; 11 | left: 50%; 12 | } 13 | 14 | 15 | /* Utility class for centering */ 16 | .sk-center { margin: auto; } 17 | 18 | 19 | /* Plane 20 |
21 | */ 22 | .sk-plane { 23 | width: var(--sk-size); 24 | height: var(--sk-size); 25 | background-color: var(--sk-color); 26 | animation: sk-plane 1.2s infinite ease-in-out; 27 | } 28 | 29 | @keyframes sk-plane { 30 | 0% { 31 | transform: perspective(120px) rotateX(0deg) rotateY(0deg); 32 | } 50% { 33 | transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); 34 | } 100% { 35 | transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); 36 | } 37 | } 38 | 39 | 40 | /* Chase 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | */ 50 | .sk-chase { 51 | width: var(--sk-size); 52 | height: var(--sk-size); 53 | position: relative; 54 | animation: sk-chase 2.5s infinite linear both; 55 | } 56 | 57 | .sk-chase-dot { 58 | width: 100%; 59 | height: 100%; 60 | position: absolute; 61 | left: 0; 62 | top: 0; 63 | animation: sk-chase-dot 2.0s infinite ease-in-out both; 64 | } 65 | 66 | .sk-chase-dot:before { 67 | content: ''; 68 | display: block; 69 | width: 25%; 70 | height: 25%; 71 | background-color: var(--sk-color); 72 | border-radius: 100%; 73 | animation: sk-chase-dot-before 2.0s infinite ease-in-out both; 74 | } 75 | 76 | .sk-chase-dot:nth-child(1) { animation-delay: -1.1s; } 77 | .sk-chase-dot:nth-child(2) { animation-delay: -1.0s; } 78 | .sk-chase-dot:nth-child(3) { animation-delay: -0.9s; } 79 | .sk-chase-dot:nth-child(4) { animation-delay: -0.8s; } 80 | .sk-chase-dot:nth-child(5) { animation-delay: -0.7s; } 81 | .sk-chase-dot:nth-child(6) { animation-delay: -0.6s; } 82 | .sk-chase-dot:nth-child(1):before { animation-delay: -1.1s; } 83 | .sk-chase-dot:nth-child(2):before { animation-delay: -1.0s; } 84 | .sk-chase-dot:nth-child(3):before { animation-delay: -0.9s; } 85 | .sk-chase-dot:nth-child(4):before { animation-delay: -0.8s; } 86 | .sk-chase-dot:nth-child(5):before { animation-delay: -0.7s; } 87 | .sk-chase-dot:nth-child(6):before { animation-delay: -0.6s; } 88 | 89 | @keyframes sk-chase { 90 | 100% { transform: rotate(360deg); } 91 | } 92 | 93 | @keyframes sk-chase-dot { 94 | 80%, 100% { transform: rotate(360deg); } 95 | } 96 | 97 | @keyframes sk-chase-dot-before { 98 | 50% { 99 | transform: scale(0.4); 100 | } 100%, 0% { 101 | transform: scale(1.0); 102 | } 103 | } 104 | 105 | 106 | /* Bounce 107 |
108 |
109 |
110 |
111 | */ 112 | .sk-bounce { 113 | width: var(--sk-size); 114 | height: var(--sk-size); 115 | position: relative; 116 | } 117 | 118 | .sk-bounce-dot { 119 | width: 100%; 120 | height: 100%; 121 | border-radius: 50%; 122 | background-color: var(--sk-color); 123 | opacity: 0.6; 124 | position: absolute; 125 | top: 0; 126 | left: 0; 127 | animation: sk-bounce 2s infinite cubic-bezier(0.455, 0.03, 0.515, 0.955); 128 | } 129 | 130 | .sk-bounce-dot:nth-child(2) { animation-delay: -1.0s; } 131 | 132 | @keyframes sk-bounce { 133 | 0%, 100% { 134 | transform: scale(0); 135 | } 45%, 55% { 136 | transform: scale(1); 137 | } 138 | } 139 | 140 | 141 | /* Wave 142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | */ 150 | .sk-wave { 151 | width: var(--sk-size); 152 | height: var(--sk-size); 153 | display: flex; 154 | justify-content: space-between; 155 | } 156 | 157 | .sk-wave-rect { 158 | background-color: var(--sk-color); 159 | height: 100%; 160 | width: 15%; 161 | animation: sk-wave 1.2s infinite ease-in-out; 162 | } 163 | 164 | .sk-wave-rect:nth-child(1) { animation-delay: -1.2s; } 165 | .sk-wave-rect:nth-child(2) { animation-delay: -1.1s; } 166 | .sk-wave-rect:nth-child(3) { animation-delay: -1.0s; } 167 | .sk-wave-rect:nth-child(4) { animation-delay: -0.9s; } 168 | .sk-wave-rect:nth-child(5) { animation-delay: -0.8s; } 169 | 170 | @keyframes sk-wave { 171 | 0%, 40%, 100% { 172 | transform: scaleY(0.4); 173 | } 20% { 174 | transform: scaleY(1); 175 | } 176 | } 177 | 178 | 179 | /* Pulse 180 |
181 | */ 182 | .sk-pulse { 183 | width: var(--sk-size); 184 | height: var(--sk-size); 185 | background-color: var(--sk-color); 186 | border-radius: 100%; 187 | animation: sk-pulse 1.2s infinite cubic-bezier(0.455, 0.03, 0.515, 0.955); 188 | } 189 | 190 | @keyframes sk-pulse { 191 | 0% { 192 | transform: scale(0); 193 | } 100% { 194 | transform: scale(1); 195 | opacity: 0; 196 | } 197 | } 198 | 199 | 200 | /* Flow 201 |
202 |
203 |
204 |
205 |
206 | */ 207 | .sk-flow { 208 | width: calc(var(--sk-size) * 1.3); 209 | height: calc(var(--sk-size) * 1.3); 210 | display: flex; 211 | justify-content: space-between; 212 | } 213 | 214 | .sk-flow-dot { 215 | width: 25%; 216 | height: 25%; 217 | background-color: var(--sk-color); 218 | border-radius: 50%; 219 | animation: sk-flow 1.4s cubic-bezier(0.455, 0.03, 0.515, 0.955) 0s infinite both; 220 | } 221 | 222 | .sk-flow-dot:nth-child(1) { animation-delay: -0.30s; } 223 | .sk-flow-dot:nth-child(2) { animation-delay: -0.15s; } 224 | 225 | @keyframes sk-flow { 226 | 0%, 80%, 100% { 227 | transform: scale(0.3); } 228 | 40% { 229 | transform: scale(1); 230 | } 231 | } 232 | 233 | 234 | /* Swing 235 |
236 |
237 |
238 |
239 | */ 240 | .sk-swing { 241 | width: var(--sk-size); 242 | height: var(--sk-size); 243 | position: relative; 244 | animation: sk-swing 1.8s infinite linear; 245 | } 246 | 247 | .sk-swing-dot { 248 | width: 45%; 249 | height: 45%; 250 | position: absolute; 251 | top: 0; 252 | left: 0; 253 | right: 0; 254 | margin: auto; 255 | background-color: var(--sk-color); 256 | border-radius: 100%; 257 | animation: sk-swing-dot 2s infinite ease-in-out; 258 | } 259 | 260 | .sk-swing-dot:nth-child(2) { 261 | top: auto; 262 | bottom: 0; 263 | animation-delay: -1s; 264 | } 265 | 266 | @keyframes sk-swing { 267 | 100% { 268 | transform: rotate(360deg); 269 | } 270 | } 271 | 272 | @keyframes sk-swing-dot { 273 | 0%, 100% { 274 | transform: scale(0.2); } 275 | 50% { 276 | transform: scale(1); 277 | } 278 | } 279 | 280 | 281 | /* Circle 282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 | */ 297 | .sk-circle { 298 | width: var(--sk-size); 299 | height: var(--sk-size); 300 | position: relative; 301 | } 302 | 303 | .sk-circle-dot { 304 | width: 100%; 305 | height: 100%; 306 | position: absolute; 307 | left: 0; 308 | top: 0; 309 | } 310 | 311 | .sk-circle-dot:before { 312 | content: ''; 313 | display: block; 314 | width: 15%; 315 | height: 15%; 316 | background-color: var(--sk-color); 317 | border-radius: 100%; 318 | animation: sk-circle 1.2s infinite ease-in-out both; 319 | } 320 | 321 | .sk-circle-dot:nth-child(1) { transform: rotate(30deg); } 322 | .sk-circle-dot:nth-child(2) { transform: rotate(60deg); } 323 | .sk-circle-dot:nth-child(3) { transform: rotate(90deg); } 324 | .sk-circle-dot:nth-child(4) { transform: rotate(120deg); } 325 | .sk-circle-dot:nth-child(5) { transform: rotate(150deg); } 326 | .sk-circle-dot:nth-child(6) { transform: rotate(180deg); } 327 | .sk-circle-dot:nth-child(7) { transform: rotate(210deg); } 328 | .sk-circle-dot:nth-child(8) { transform: rotate(240deg); } 329 | .sk-circle-dot:nth-child(9) { transform: rotate(270deg); } 330 | .sk-circle-dot:nth-child(10) { transform: rotate(300deg); } 331 | .sk-circle-dot:nth-child(11) { transform: rotate(330deg); } 332 | .sk-circle-dot:nth-child(1):before { animation-delay: -1.1s; } 333 | .sk-circle-dot:nth-child(2):before { animation-delay: -1s; } 334 | .sk-circle-dot:nth-child(3):before { animation-delay: -0.9s; } 335 | .sk-circle-dot:nth-child(4):before { animation-delay: -0.8s; } 336 | .sk-circle-dot:nth-child(5):before { animation-delay: -0.7s; } 337 | .sk-circle-dot:nth-child(6):before { animation-delay: -0.6s; } 338 | .sk-circle-dot:nth-child(7):before { animation-delay: -0.5s; } 339 | .sk-circle-dot:nth-child(8):before { animation-delay: -0.4s; } 340 | .sk-circle-dot:nth-child(9):before { animation-delay: -0.3s; } 341 | .sk-circle-dot:nth-child(10):before { animation-delay: -0.2s; } 342 | .sk-circle-dot:nth-child(11):before { animation-delay: -0.1s; } 343 | 344 | @keyframes sk-circle { 345 | 0%, 80%, 100% { 346 | transform: scale(0); } 347 | 40% { 348 | transform: scale(1); 349 | } 350 | } 351 | 352 | 353 | /* Circle Fade 354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 | */ 369 | .sk-circle-fade { 370 | width: var(--sk-size); 371 | height: var(--sk-size); 372 | position: relative; 373 | } 374 | 375 | .sk-circle-fade-dot { 376 | width: 100%; 377 | height: 100%; 378 | position: absolute; 379 | left: 0; 380 | top: 0; 381 | } 382 | 383 | .sk-circle-fade-dot:before { 384 | content: ''; 385 | display: block; 386 | width: 15%; 387 | height: 15%; 388 | background-color: var(--sk-color); 389 | border-radius: 100%; 390 | animation: sk-circle-fade 1.2s infinite ease-in-out both; 391 | } 392 | 393 | .sk-circle-fade-dot:nth-child(1) { transform: rotate(30deg); } 394 | .sk-circle-fade-dot:nth-child(2) { transform: rotate(60deg); } 395 | .sk-circle-fade-dot:nth-child(3) { transform: rotate(90deg); } 396 | .sk-circle-fade-dot:nth-child(4) { transform: rotate(120deg); } 397 | .sk-circle-fade-dot:nth-child(5) { transform: rotate(150deg); } 398 | .sk-circle-fade-dot:nth-child(6) { transform: rotate(180deg); } 399 | .sk-circle-fade-dot:nth-child(7) { transform: rotate(210deg); } 400 | .sk-circle-fade-dot:nth-child(8) { transform: rotate(240deg); } 401 | .sk-circle-fade-dot:nth-child(9) { transform: rotate(270deg); } 402 | .sk-circle-fade-dot:nth-child(10) { transform: rotate(300deg); } 403 | .sk-circle-fade-dot:nth-child(11) { transform: rotate(330deg); } 404 | .sk-circle-fade-dot:nth-child(1):before { animation-delay: -1.1s; } 405 | .sk-circle-fade-dot:nth-child(2):before { animation-delay: -1.0s; } 406 | .sk-circle-fade-dot:nth-child(3):before { animation-delay: -0.9s; } 407 | .sk-circle-fade-dot:nth-child(4):before { animation-delay: -0.8s; } 408 | .sk-circle-fade-dot:nth-child(5):before { animation-delay: -0.7s; } 409 | .sk-circle-fade-dot:nth-child(6):before { animation-delay: -0.6s; } 410 | .sk-circle-fade-dot:nth-child(7):before { animation-delay: -0.5s; } 411 | .sk-circle-fade-dot:nth-child(8):before { animation-delay: -0.4s; } 412 | .sk-circle-fade-dot:nth-child(9):before { animation-delay: -0.3s; } 413 | .sk-circle-fade-dot:nth-child(10):before { animation-delay: -0.2s; } 414 | .sk-circle-fade-dot:nth-child(11):before { animation-delay: -0.1s; } 415 | 416 | @keyframes sk-circle-fade { 417 | 0%, 39%, 100% { 418 | opacity: 0; 419 | transform: scale(0.6); 420 | } 40% { 421 | opacity: 1; 422 | transform: scale(1); 423 | } 424 | } 425 | 426 | 427 | /* Grid 428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 | */ 440 | .sk-grid { 441 | width: var(--sk-size); 442 | height: var(--sk-size); 443 | /* Cube positions 444 | * 1 2 3 445 | * 4 5 6 446 | * 7 8 9 447 | */ 448 | } 449 | .sk-grid-cube { 450 | width: 33.33%; 451 | height: 33.33%; 452 | background-color: var(--sk-color); 453 | float: left; 454 | animation: sk-grid 1.3s infinite ease-in-out; 455 | } 456 | .sk-grid-cube:nth-child(1) { animation-delay: 0.2s; } 457 | .sk-grid-cube:nth-child(2) { animation-delay: 0.3s; } 458 | .sk-grid-cube:nth-child(3) { animation-delay: 0.4s; } 459 | .sk-grid-cube:nth-child(4) { animation-delay: 0.1s; } 460 | .sk-grid-cube:nth-child(5) { animation-delay: 0.2s; } 461 | .sk-grid-cube:nth-child(6) { animation-delay: 0.3s; } 462 | .sk-grid-cube:nth-child(7) { animation-delay: 0.0s; } 463 | .sk-grid-cube:nth-child(8) { animation-delay: 0.1s; } 464 | .sk-grid-cube:nth-child(9) { animation-delay: 0.2s; } 465 | 466 | @keyframes sk-grid { 467 | 0%, 70%, 100% { 468 | transform: scale3D(1, 1, 1); 469 | } 35% { 470 | transform: scale3D(0, 0, 1); 471 | } 472 | } 473 | 474 | 475 | /* Fold 476 |
477 |
478 |
479 |
480 |
481 |
482 | */ 483 | .sk-fold { 484 | width: var(--sk-size); 485 | height: var(--sk-size); 486 | position: relative; 487 | transform: rotateZ(45deg); 488 | } 489 | .sk-fold-cube { 490 | float: left; 491 | width: 50%; 492 | height: 50%; 493 | position: relative; 494 | transform: scale(1.1); 495 | } 496 | 497 | .sk-fold-cube:before { 498 | content: ''; 499 | position: absolute; 500 | top: 0; 501 | left: 0; 502 | width: 100%; 503 | height: 100%; 504 | background-color: var(--sk-color); 505 | animation: sk-fold 2.4s infinite linear both; 506 | transform-origin: 100% 100%; 507 | } 508 | .sk-fold-cube:nth-child(2) { transform: scale(1.1) rotateZ(90deg); } 509 | .sk-fold-cube:nth-child(4) { transform: scale(1.1) rotateZ(180deg); } 510 | .sk-fold-cube:nth-child(3) { transform: scale(1.1) rotateZ(270deg); } 511 | .sk-fold-cube:nth-child(2):before { animation-delay: 0.3s; } 512 | .sk-fold-cube:nth-child(4):before { animation-delay: 0.6s; } 513 | .sk-fold-cube:nth-child(3):before { animation-delay: 0.9s; } 514 | 515 | @keyframes sk-fold { 516 | 0%, 10% { 517 | transform: perspective(140px) rotateX(-180deg); 518 | opacity: 0; 519 | } 25%, 75% { 520 | transform: perspective(140px) rotateX(0deg); 521 | opacity: 1; 522 | } 90%, 100% { 523 | transform: perspective(140px) rotateY(180deg); 524 | opacity: 0; 525 | } 526 | } 527 | 528 | 529 | /* Wander 530 |
531 |
532 |
533 |
534 |
535 |
536 | */ 537 | .sk-wander { 538 | width: var(--sk-size); 539 | height: var(--sk-size); 540 | position: relative; 541 | } 542 | 543 | .sk-wander-cube { 544 | background-color: var(--sk-color); 545 | width: 20%; 546 | height: 20%; 547 | position: absolute; 548 | top: 0; 549 | left: 0; 550 | --sk-wander-distance: calc(var(--sk-size) * 0.75); 551 | animation: sk-wander 2.0s ease-in-out -2.0s infinite both; 552 | } 553 | .sk-wander-cube:nth-child(2) { animation-delay: -0.5s; } 554 | .sk-wander-cube:nth-child(3) { animation-delay: -1.0s; } 555 | 556 | @keyframes sk-wander { 557 | 0% { 558 | transform: rotate(0deg); 559 | } 25% { 560 | transform: translateX(var(--sk-wander-distance)) rotate(-90deg) scale(0.6); 561 | } 50% { /* Make FF rotate in the right direction */ 562 | transform: translateX(var(--sk-wander-distance)) translateY(var(--sk-wander-distance)) rotate(-179deg); 563 | } 50.1% { 564 | transform: translateX(var(--sk-wander-distance)) translateY(var(--sk-wander-distance)) rotate(-180deg); 565 | } 75% { 566 | transform: translateX(0) translateY(var(--sk-wander-distance)) rotate(-270deg) scale(0.6); 567 | } 100% { 568 | transform: rotate(-360deg); 569 | } 570 | } 571 | 572 | .sk-folding-cube { 573 | margin: 20px auto; 574 | width: 40px; 575 | height: 40px; 576 | position: relative; 577 | -webkit-transform: rotateZ(45deg); 578 | transform: rotateZ(45deg); 579 | } 580 | 581 | .sk-folding-cube .sk-cube { 582 | float: left; 583 | width: 50%; 584 | height: 50%; 585 | position: relative; 586 | -webkit-transform: scale(1.1); 587 | -ms-transform: scale(1.1); 588 | transform: scale(1.1); 589 | } 590 | .sk-folding-cube .sk-cube:before { 591 | content: ''; 592 | position: absolute; 593 | top: 0; 594 | left: 0; 595 | width: 100%; 596 | height: 100%; 597 | background-color: #333; 598 | -webkit-animation: sk-foldCubeAngle 2.4s infinite linear both; 599 | animation: sk-foldCubeAngle 2.4s infinite linear both; 600 | -webkit-transform-origin: 100% 100%; 601 | -ms-transform-origin: 100% 100%; 602 | transform-origin: 100% 100%; 603 | } 604 | .sk-folding-cube .sk-cube2 { 605 | -webkit-transform: scale(1.1) rotateZ(90deg); 606 | transform: scale(1.1) rotateZ(90deg); 607 | } 608 | .sk-folding-cube .sk-cube3 { 609 | -webkit-transform: scale(1.1) rotateZ(180deg); 610 | transform: scale(1.1) rotateZ(180deg); 611 | } 612 | .sk-folding-cube .sk-cube4 { 613 | -webkit-transform: scale(1.1) rotateZ(270deg); 614 | transform: scale(1.1) rotateZ(270deg); 615 | } 616 | .sk-folding-cube .sk-cube2:before { 617 | -webkit-animation-delay: 0.3s; 618 | animation-delay: 0.3s; 619 | } 620 | .sk-folding-cube .sk-cube3:before { 621 | -webkit-animation-delay: 0.6s; 622 | animation-delay: 0.6s; 623 | } 624 | .sk-folding-cube .sk-cube4:before { 625 | -webkit-animation-delay: 0.9s; 626 | animation-delay: 0.9s; 627 | } 628 | @-webkit-keyframes sk-foldCubeAngle { 629 | 0%, 10% { 630 | -webkit-transform: perspective(140px) rotateX(-180deg); 631 | transform: perspective(140px) rotateX(-180deg); 632 | opacity: 0; 633 | } 25%, 75% { 634 | -webkit-transform: perspective(140px) rotateX(0deg); 635 | transform: perspective(140px) rotateX(0deg); 636 | opacity: 1; 637 | } 90%, 100% { 638 | -webkit-transform: perspective(140px) rotateY(180deg); 639 | transform: perspective(140px) rotateY(180deg); 640 | opacity: 0; 641 | } 642 | } 643 | 644 | @keyframes sk-foldCubeAngle { 645 | 0%, 10% { 646 | -webkit-transform: perspective(140px) rotateX(-180deg); 647 | transform: perspective(140px) rotateX(-180deg); 648 | opacity: 0; 649 | } 25%, 75% { 650 | -webkit-transform: perspective(140px) rotateX(0deg); 651 | transform: perspective(140px) rotateX(0deg); 652 | opacity: 1; 653 | } 90%, 100% { 654 | -webkit-transform: perspective(140px) rotateY(180deg); 655 | transform: perspective(140px) rotateY(180deg); 656 | opacity: 0; 657 | } 658 | } --------------------------------------------------------------------------------