├── .github
└── workflows
│ └── deploy.yaml
├── .gitignore
├── .metadata
├── LICENSE.md
├── Makefile
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle.kts
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── dev
│ │ │ │ └── aladdine
│ │ │ │ └── portfolio
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-hdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-mdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-night-hdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-night-mdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-night-v21
│ │ │ ├── background.png
│ │ │ └── launch_background.xml
│ │ │ ├── drawable-night-xhdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-night-xxhdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-night-xxxhdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-night
│ │ │ ├── background.png
│ │ │ └── launch_background.xml
│ │ │ ├── drawable-v21
│ │ │ ├── background.png
│ │ │ └── launch_background.xml
│ │ │ ├── drawable-xhdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-xxhdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ └── splash.png
│ │ │ ├── drawable
│ │ │ ├── background.png
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values-night-v31
│ │ │ └── styles.xml
│ │ │ ├── values-night
│ │ │ └── styles.xml
│ │ │ ├── values-v31
│ │ │ └── styles.xml
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle.kts
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle.kts
├── assets
├── fonts
│ ├── BoxIcons.ttf
│ ├── FontAwesome.ttf
│ ├── Nunito-Bold.ttf
│ ├── Nunito-Regular.ttf
│ └── OctIcons.ttf
├── icons
│ ├── flags
│ │ ├── france.svg
│ │ └── united-states-of-america.svg
│ ├── other
│ │ ├── bootstrap.svg
│ │ ├── dart.svg
│ │ ├── express-js.svg
│ │ └── firebase.svg
│ └── software-development
│ │ ├── css3.svg
│ │ ├── docker.svg
│ │ ├── flask.svg
│ │ ├── flutter.svg
│ │ ├── graphql.svg
│ │ ├── html-5.svg
│ │ ├── javascript.svg
│ │ ├── mariadb.svg
│ │ ├── mongodb.svg
│ │ ├── nodejs.svg
│ │ ├── postgresql.svg
│ │ ├── python.svg
│ │ └── typescript.svg
├── images
│ ├── gsb-frais.png
│ ├── gsb-rv-dr.png
│ ├── gsb-rv-visiteur-serveur.png
│ ├── learnflow-api.png
│ ├── learnflow-backoffice.png
│ ├── logo.png
│ ├── logo.svg
│ ├── portfolio.png
│ ├── spinner-dark.apng
│ └── spinner-light.apng
└── translations
│ ├── en.json
│ └── fr.json
├── build.yaml
├── docs
├── images
│ ├── github-actions-enable-workflows.png
│ ├── github-pages-branch-gh-pages.png
│ ├── github-pages-branch-none.png
│ ├── mockups-1.png
│ ├── mockups-2.png
│ └── screenshot-desktop.png
├── packages-in-use.md
└── translation-template.md
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
├── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon-production.appiconset
│ │ │ ├── AppIcon-production-1024x1024@1x.png
│ │ │ ├── AppIcon-production-20x20@1x.png
│ │ │ ├── AppIcon-production-20x20@2x.png
│ │ │ ├── AppIcon-production-20x20@3x.png
│ │ │ ├── AppIcon-production-29x29@1x.png
│ │ │ ├── AppIcon-production-29x29@2x.png
│ │ │ ├── AppIcon-production-29x29@3x.png
│ │ │ ├── AppIcon-production-40x40@1x.png
│ │ │ ├── AppIcon-production-40x40@2x.png
│ │ │ ├── AppIcon-production-40x40@3x.png
│ │ │ ├── AppIcon-production-50x50@1x.png
│ │ │ ├── AppIcon-production-50x50@2x.png
│ │ │ ├── AppIcon-production-57x57@1x.png
│ │ │ ├── AppIcon-production-57x57@2x.png
│ │ │ ├── AppIcon-production-60x60@2x.png
│ │ │ ├── AppIcon-production-60x60@3x.png
│ │ │ ├── AppIcon-production-72x72@1x.png
│ │ │ ├── AppIcon-production-72x72@2x.png
│ │ │ ├── AppIcon-production-76x76@1x.png
│ │ │ ├── AppIcon-production-76x76@2x.png
│ │ │ ├── AppIcon-production-83.5x83.5@2x.png
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-50x50@1x.png
│ │ │ ├── Icon-App-50x50@2x.png
│ │ │ ├── Icon-App-57x57@1x.png
│ │ │ ├── Icon-App-57x57@2x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-72x72@1x.png
│ │ │ ├── Icon-App-72x72@2x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ ├── LaunchBackground.imageset
│ │ │ ├── Contents.json
│ │ │ ├── background.png
│ │ │ └── darkbackground.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── LaunchImageDark.png
│ │ │ ├── LaunchImageDark@2x.png
│ │ │ ├── LaunchImageDark@3x.png
│ │ │ └── README.md
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
└── RunnerTests
│ └── RunnerTests.swift
├── lib
├── main.dart
└── src
│ ├── app.dart
│ ├── app_startup.dart
│ ├── common
│ ├── data
│ │ └── language_repository.dart
│ ├── domain
│ │ ├── icon.dart
│ │ ├── language.dart
│ │ ├── link.dart
│ │ └── technology.dart
│ ├── provider
│ │ └── shared_preferences_provider.dart
│ └── widgets
│ │ ├── animated_fade_slide.dart
│ │ ├── icon.dart
│ │ ├── link.dart
│ │ ├── responsive.dart
│ │ ├── selection_area.dart
│ │ ├── technology_chip.dart
│ │ ├── technology_wrap_chips.dart
│ │ └── wrap_links.dart
│ ├── constants
│ ├── sizes.dart
│ ├── themes.dart
│ └── transparent_image.dart
│ ├── features
│ ├── about
│ │ └── presentation
│ │ │ ├── about_desktop.dart
│ │ │ └── about_section.dart
│ ├── experience
│ │ ├── data
│ │ │ └── experience_repository.dart
│ │ ├── domain
│ │ │ └── experience.dart
│ │ └── presentation
│ │ │ ├── experience_desktop.dart
│ │ │ ├── experience_section.dart
│ │ │ └── widgets
│ │ │ ├── experience_card.dart
│ │ │ └── experience_date_text.dart
│ ├── general
│ │ ├── presentation
│ │ │ ├── general_section.dart
│ │ │ ├── general_section_desktop.dart
│ │ │ ├── general_section_tablet.dart
│ │ │ └── widgets
│ │ │ │ ├── app_bar.dart
│ │ │ │ ├── app_bar_button.dart
│ │ │ │ ├── bottom_banner.dart
│ │ │ │ ├── dark_mode_switch.dart
│ │ │ │ ├── drawer_button.dart
│ │ │ │ ├── end_drawer.dart
│ │ │ │ ├── locale_button.dart
│ │ │ │ ├── safe_area.dart
│ │ │ │ └── sliver_app_bar.dart
│ │ └── provider
│ │ │ ├── brightness_controller.dart
│ │ │ ├── dark_mode_controller.dart
│ │ │ ├── scroll_controller.dart
│ │ │ └── section_key_provider.dart
│ ├── personal_info
│ │ ├── data
│ │ │ └── personal_info_repository.dart
│ │ ├── domain
│ │ │ ├── contact.dart
│ │ │ └── resume.dart
│ │ └── presentation
│ │ │ ├── personal_info_desktop.dart
│ │ │ ├── personal_info_mobile.dart
│ │ │ ├── personal_info_section.dart
│ │ │ ├── personal_info_tablet.dart
│ │ │ └── widgets
│ │ │ ├── contact_bar.dart
│ │ │ ├── contact_icon_button.dart
│ │ │ ├── resume_button.dart
│ │ │ ├── resume_language_dialog.dart
│ │ │ └── resume_language_dialog_tile.dart
│ └── project
│ │ ├── data
│ │ └── project_repository.dart
│ │ ├── domain
│ │ └── project.dart
│ │ └── presentation
│ │ ├── project_desktop.dart
│ │ ├── project_section.dart
│ │ └── widgets
│ │ ├── project_card.dart
│ │ ├── project_description.dart
│ │ ├── project_icon.dart
│ │ └── project_image.dart
│ ├── localization
│ ├── app_localizations.dart
│ ├── json_list_translation.dart
│ └── locale_controller.dart
│ └── utils
│ ├── launch_url_helper.dart
│ ├── localized_date_extension.dart
│ ├── scaffold_messenger_helper.dart
│ └── string_extension.dart
├── linux
├── .gitignore
├── CMakeLists.txt
├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
├── main.cc
├── my_application.cc
├── my_application.h
└── runner
│ ├── CMakeLists.txt
│ ├── main.cc
│ ├── my_application.cc
│ └── my_application.h
├── macos
├── .gitignore
├── Flutter
│ ├── Flutter-Debug.xcconfig
│ ├── Flutter-Release.xcconfig
│ └── GeneratedPluginRegistrant.swift
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── app_icon_1024.png
│ │ │ ├── app_icon_128.png
│ │ │ ├── app_icon_16.png
│ │ │ ├── app_icon_256.png
│ │ │ ├── app_icon_32.png
│ │ │ ├── app_icon_512.png
│ │ │ └── app_icon_64.png
│ ├── Base.lproj
│ │ └── MainMenu.xib
│ ├── Configs
│ │ ├── AppInfo.xcconfig
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── Warnings.xcconfig
│ ├── DebugProfile.entitlements
│ ├── Info.plist
│ ├── MainFlutterWindow.swift
│ └── Release.entitlements
└── RunnerTests
│ └── RunnerTests.swift
├── pubspec.lock
├── pubspec.yaml
├── test
└── widget_test.dart
├── web
├── favicon.png
├── icons
│ ├── Icon-192.png
│ ├── Icon-512.png
│ ├── Icon-maskable-192.png
│ └── Icon-maskable-512.png
├── index.html
├── manifest.json
└── splash
│ └── img
│ ├── dark-1x.png
│ ├── dark-2x.png
│ ├── dark-3x.png
│ ├── dark-4x.png
│ ├── light-1x.png
│ ├── light-2x.png
│ ├── light-3x.png
│ └── light-4x.png
└── windows
├── .gitignore
├── CMakeLists.txt
├── flutter
├── CMakeLists.txt
├── generated_plugin_registrant.cc
├── generated_plugin_registrant.h
└── generated_plugins.cmake
└── runner
├── CMakeLists.txt
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── resources
└── app_icon.ico
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
/.github/workflows/deploy.yaml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths-ignore:
8 | - '**.md'
9 | workflow_dispatch:
10 |
11 | jobs:
12 | deploy:
13 | runs-on: ubuntu-24.04
14 | permissions:
15 | contents: write
16 | concurrency:
17 | group: ${{ github.workflow }}-${{ github.ref }}
18 | steps:
19 | - uses: actions/checkout@v3
20 |
21 | - uses: subosito/flutter-action@v2
22 | with:
23 | channel: 'stable'
24 | flutter-version: 3.32.0
25 | cache: true
26 |
27 | - name: Install dependencies
28 | run: flutter pub get
29 |
30 | - name: Code generation
31 | run: |
32 | dart run build_runner build -d
33 | dart run easy_localization:generate -S assets/translations -f json -O lib/src/localization/generated -o locale_json.g.dart
34 | dart run easy_localization:generate -S assets/translations -f keys -O lib/src/localization/generated -o locale_keys.g.dart
35 |
36 | - name: Set base-href
37 | id: set_base_href
38 | run: |
39 | if [[ -z "${{ vars.CNAME }}" ]]; then
40 | echo "base_href=/${{ github.event.repository.name }}/" >> $GITHUB_OUTPUT
41 | else
42 | echo "base_href=/" >> $GITHUB_OUTPUT
43 | fi
44 |
45 | - name: Build
46 | run: flutter build web --wasm --release --no-tree-shake-icons --base-href "${{ steps.set_base_href.outputs.base_href }}"
47 |
48 | - name: Deploy
49 | uses: peaceiris/actions-gh-pages@v3
50 | if: github.ref == 'refs/heads/main'
51 | with:
52 | github_token: ${{ secrets.GITHUB_TOKEN }}
53 | publish_dir: ./build/web
54 | cname: ${{ vars.CNAME }}
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .build/
9 | .buildlog/
10 | .history
11 | .svn/
12 | .swiftpm/
13 | migrate_working_dir/
14 |
15 | # IntelliJ related
16 | *.iml
17 | *.ipr
18 | *.iws
19 | .idea/
20 |
21 | # The .vscode folder contains launch configuration and tasks you configure in
22 | # VS Code which you may wish to be included in version control, so this line
23 | # is commented out by default.
24 | #.vscode/
25 |
26 | # Flutter/Dart/Pub related
27 | **/doc/api/
28 | **/ios/Flutter/.last_build_id
29 | .dart_tool/
30 | .flutter-plugins
31 | .flutter-plugins-dependencies
32 | .packages
33 | .pub-cache/
34 | .pub/
35 | /build/
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
48 | # Dart generated files
49 | *.g.dart
50 | *.freezed.dart
51 |
--------------------------------------------------------------------------------
/.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: "b25305a8832cfc6ba632a7f87ad455e319dccce8"
8 | channel: "stable"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
17 | base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
18 | - platform: android
19 | create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
20 | base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
21 | - platform: ios
22 | create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
23 | base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
24 | - platform: linux
25 | create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
26 | base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
27 | - platform: macos
28 | create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
29 | base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
30 | - platform: web
31 | create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
32 | base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
33 | - platform: windows
34 | create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
35 | base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
36 |
37 | # User provided section
38 |
39 | # List of Local paths (relative to this file) that should be
40 | # ignored by the migrate tool.
41 | #
42 | # Files that are not part of the templates will be ignored by default.
43 | unmanaged_files:
44 | - 'lib/main.dart'
45 | - 'ios/Runner.xcodeproj/project.pbxproj'
46 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Aladdine Abdou
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | localization:
2 | dart run easy_localization:generate -S assets/translations -f json -O lib/src/localization/generated -o locale_json.g.dart
3 | dart run easy_localization:generate -S assets/translations -f keys -O lib/src/localization/generated -o locale_keys.g.dart
4 |
5 | launcher_icons:
6 | dart run flutter_launcher_icons
7 |
8 | native_splash:
9 | dart run flutter_native_splash:create
10 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | analyzer:
13 | exclude:
14 | - "**/*.g.dart"
15 | - "**/*.freezed.dart"
16 | errors:
17 | invalid_annotation_target: ignore
18 | plugins:
19 | - custom_lint
20 |
21 | linter:
22 | # The lint rules applied to this project can be customized in the
23 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
24 | # included above or to enable additional rules. A list of all available lints
25 | # and their documentation is published at
26 | # https://dart-lang.github.io/linter/lints/index.html.
27 | #
28 | # Instead of disabling a lint rule for the entire project in the
29 | # section below, it can also be suppressed for a single line of code
30 | # or a specific dart file by using the `// ignore: name_of_lint` and
31 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
32 | # producing the lint.
33 | rules:
34 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
35 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
36 |
37 | # Additional information about this file can be found at
38 | # https://dart.dev/guides/language/analysis-options
39 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 | .cxx/
9 |
10 | # Remember to never publicly share your keystore.
11 | # See https://flutter.dev/to/reference-keystore
12 | key.properties
13 | **/*.keystore
14 | **/*.jks
15 |
--------------------------------------------------------------------------------
/android/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("kotlin-android")
4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5 | id("dev.flutter.flutter-gradle-plugin")
6 | }
7 |
8 | android {
9 | namespace = "dev.aladdine.portfolio"
10 | compileSdk = flutter.compileSdkVersion
11 | ndkVersion = "27.0.12077973"
12 |
13 | compileOptions {
14 | sourceCompatibility = JavaVersion.VERSION_11
15 | targetCompatibility = JavaVersion.VERSION_11
16 | }
17 |
18 | kotlinOptions {
19 | jvmTarget = JavaVersion.VERSION_11.toString()
20 | }
21 |
22 | defaultConfig {
23 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
24 | applicationId = "dev.aladdine.portfolio"
25 | // You can update the following values to match your application needs.
26 | // For more information, see: https://flutter.dev/to/review-gradle-config.
27 | minSdk = flutter.minSdkVersion
28 | targetSdk = flutter.targetSdkVersion
29 | versionCode = flutter.versionCode
30 | versionName = flutter.versionName
31 | }
32 |
33 | buildTypes {
34 | release {
35 | // TODO: Add your own signing config for the release build.
36 | // Signing with the debug keys for now, so `flutter run --release` works.
37 | signingConfig = signingConfigs.getByName("debug")
38 | }
39 | }
40 | }
41 |
42 | flutter {
43 | source = "../.."
44 | }
45 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
35 |
38 |
39 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/dev/aladdine/portfolio/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package dev.aladdine.portfolio
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity : FlutterActivity()
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-hdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-mdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-hdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-night-hdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-mdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-night-mdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-v21/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-night-v21/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-xhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-night-xhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-xxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-night-xxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-xxxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-night-xxxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-night/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-v21/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-xhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-xxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable-xxxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/drawable/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night-v31/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
19 |
22 |
23 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-v31/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
19 |
22 |
23 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle.kts:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
9 | rootProject.layout.buildDirectory.value(newBuildDir)
10 |
11 | subprojects {
12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
13 | project.layout.buildDirectory.value(newSubprojectBuildDir)
14 | }
15 | subprojects {
16 | project.evaluationDependsOn(":app")
17 | }
18 |
19 | tasks.register("clean") {
20 | delete(rootProject.layout.buildDirectory)
21 | }
22 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | val flutterSdkPath = run {
3 | val properties = java.util.Properties()
4 | file("local.properties").inputStream().use { properties.load(it) }
5 | val flutterSdkPath = properties.getProperty("flutter.sdk")
6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
7 | flutterSdkPath
8 | }
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0"
21 | id("com.android.application") version "8.7.3" apply false
22 | id("org.jetbrains.kotlin.android") version "2.1.0" apply false
23 | }
24 |
25 | include(":app")
26 |
--------------------------------------------------------------------------------
/assets/fonts/BoxIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/fonts/BoxIcons.ttf
--------------------------------------------------------------------------------
/assets/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/assets/fonts/Nunito-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/fonts/Nunito-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/Nunito-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/fonts/Nunito-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/OctIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/fonts/OctIcons.ttf
--------------------------------------------------------------------------------
/assets/icons/flags/france.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/flags/united-states-of-america.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/other/bootstrap.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/other/dart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/other/express-js.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/other/firebase.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/css3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/docker.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/flutter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/graphql.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/html-5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/javascript.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/mariadb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/mongodb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/nodejs.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/python.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/software-development/typescript.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/gsb-frais.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/images/gsb-frais.png
--------------------------------------------------------------------------------
/assets/images/gsb-rv-dr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/images/gsb-rv-dr.png
--------------------------------------------------------------------------------
/assets/images/gsb-rv-visiteur-serveur.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/images/gsb-rv-visiteur-serveur.png
--------------------------------------------------------------------------------
/assets/images/learnflow-api.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/images/learnflow-api.png
--------------------------------------------------------------------------------
/assets/images/learnflow-backoffice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/images/learnflow-backoffice.png
--------------------------------------------------------------------------------
/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/images/logo.png
--------------------------------------------------------------------------------
/assets/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/images/portfolio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/images/portfolio.png
--------------------------------------------------------------------------------
/assets/images/spinner-dark.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/images/spinner-dark.apng
--------------------------------------------------------------------------------
/assets/images/spinner-light.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/assets/images/spinner-light.apng
--------------------------------------------------------------------------------
/build.yaml:
--------------------------------------------------------------------------------
1 | targets:
2 | $default:
3 | builders:
4 | json_serializable:
5 | options:
6 | # Options configure how source code is generated for every
7 | # `@JsonSerializable`-annotated class in the package.
8 | #
9 | # The default value for each is listed.
10 | # any_map: false
11 | # checked: false
12 | # constructor: ""
13 | # create_factory: true
14 | # create_field_map: false
15 | # create_per_field_to_json: false
16 | # create_to_json: true
17 | # disallow_unrecognized_keys: false
18 | explicit_to_json: true
19 | # generic_argument_factories: false
20 | # ignore_unannotated: false
21 | # include_if_null: true
--------------------------------------------------------------------------------
/docs/images/github-actions-enable-workflows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/docs/images/github-actions-enable-workflows.png
--------------------------------------------------------------------------------
/docs/images/github-pages-branch-gh-pages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/docs/images/github-pages-branch-gh-pages.png
--------------------------------------------------------------------------------
/docs/images/github-pages-branch-none.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/docs/images/github-pages-branch-none.png
--------------------------------------------------------------------------------
/docs/images/mockups-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/docs/images/mockups-1.png
--------------------------------------------------------------------------------
/docs/images/mockups-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/docs/images/mockups-2.png
--------------------------------------------------------------------------------
/docs/images/screenshot-desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/docs/images/screenshot-desktop.png
--------------------------------------------------------------------------------
/docs/packages-in-use.md:
--------------------------------------------------------------------------------
1 | ## Packages in use 📦
2 |
3 | These are the main packages used in the app:
4 |
5 | - [Hooks Riverpod](https://pub.dev/packages/hooks_riverpod) for state-management
6 | - [Flutter Hooks](https://pub.dev/packages/flutter_hooks) to reduce boilerplate code of stateful widgets and increase code reusability
7 | - [Freezed](https://pub.dev/packages/freezed) to reduce boilerplate code in model classes
8 | - [Flex Color Scheme](https://pub.dev/packages/flex_color_scheme) to make AWESOME Flutter Material Design themes
9 | - [Google Fonts](https://pub.dev/packages/google_fonts) to use fonts from https://fonts.google.com
10 | - [Icons Plus](https://pub.dev/packages/icons_plus) to use a collection of attractive icons from different packs
11 | - [Easy Localization](https://pub.dev/packages/easy_localization) for... easy localization
12 | - [Intl](https://pub.dev/packages/intl) for date formatting
13 | - [Shared Preferences](https://pub.dev/packages/shared_preferences) for persistent storage (used to store theme state)
14 |
15 | See the [`pubspec.yaml`](pubspec.yaml) file for the complete list
16 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '12.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | target 'RunnerTests' do
36 | inherit! :search_paths
37 | end
38 | end
39 |
40 | post_install do |installer|
41 | installer.pods_project.targets.each do |target|
42 | flutter_additional_ios_build_settings(target)
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_native_splash (2.4.3):
4 | - Flutter
5 | - path_provider_foundation (0.0.1):
6 | - Flutter
7 | - FlutterMacOS
8 | - shared_preferences_foundation (0.0.1):
9 | - Flutter
10 | - FlutterMacOS
11 | - url_launcher_ios (0.0.1):
12 | - Flutter
13 |
14 | DEPENDENCIES:
15 | - Flutter (from `Flutter`)
16 | - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
17 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
18 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
19 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
20 |
21 | EXTERNAL SOURCES:
22 | Flutter:
23 | :path: Flutter
24 | flutter_native_splash:
25 | :path: ".symlinks/plugins/flutter_native_splash/ios"
26 | path_provider_foundation:
27 | :path: ".symlinks/plugins/path_provider_foundation/darwin"
28 | shared_preferences_foundation:
29 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
30 | url_launcher_ios:
31 | :path: ".symlinks/plugins/url_launcher_ios/ios"
32 |
33 | SPEC CHECKSUMS:
34 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
35 | flutter_native_splash: f71420956eb811e6d310720fee915f1d42852e7a
36 | path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
37 | shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
38 | url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
39 |
40 | PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
41 |
42 | COCOAPODS: 1.12.1
43 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @main
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-50x50@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-50x50@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-50x50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-50x50@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-57x57@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-57x57@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-57x57@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-57x57@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-72x72@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-72x72@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-72x72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-72x72@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/AppIcon-production-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon-production.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {"images":[{"size":"20x20","idiom":"iphone","filename":"AppIcon-production-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"AppIcon-production-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-production-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-production-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"AppIcon-production-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-production-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"AppIcon-production-40x40@3x.png","scale":"3x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-production-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"AppIcon-production-50x50@2x.png","scale":"2x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-production-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"AppIcon-production-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-production-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"AppIcon-production-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-production-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"AppIcon-production-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-production-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"AppIcon-production-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-production-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"AppIcon-production-40x40@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-production-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"AppIcon-production-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-production-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"AppIcon-production-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"AppIcon-production-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppIcon-production-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "background.png",
5 | "idiom" : "universal"
6 | },
7 | {
8 | "appearances" : [
9 | {
10 | "appearance" : "luminosity",
11 | "value" : "dark"
12 | }
13 | ],
14 | "filename" : "darkbackground.png",
15 | "idiom" : "universal"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "LaunchImage.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "appearances" : [
10 | {
11 | "appearance" : "luminosity",
12 | "value" : "dark"
13 | }
14 | ],
15 | "filename" : "LaunchImageDark.png",
16 | "idiom" : "universal",
17 | "scale" : "1x"
18 | },
19 | {
20 | "filename" : "LaunchImage@2x.png",
21 | "idiom" : "universal",
22 | "scale" : "2x"
23 | },
24 | {
25 | "appearances" : [
26 | {
27 | "appearance" : "luminosity",
28 | "value" : "dark"
29 | }
30 | ],
31 | "filename" : "LaunchImageDark@2x.png",
32 | "idiom" : "universal",
33 | "scale" : "2x"
34 | },
35 | {
36 | "filename" : "LaunchImage@3x.png",
37 | "idiom" : "universal",
38 | "scale" : "3x"
39 | },
40 | {
41 | "appearances" : [
42 | {
43 | "appearance" : "luminosity",
44 | "value" : "dark"
45 | }
46 | ],
47 | "filename" : "LaunchImageDark@3x.png",
48 | "idiom" : "universal",
49 | "scale" : "3x"
50 | }
51 | ],
52 | "info" : {
53 | "author" : "xcode",
54 | "version" : 1
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AladdineDev/portfolio/6cc5a771e35292c14ee1e8c63cedf4ea42db2296/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleDisplayName
10 | Portfolio
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | portfolio
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | $(FLUTTER_BUILD_NAME)
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | $(FLUTTER_BUILD_NUMBER)
27 | LSRequiresIPhoneOS
28 |
29 | UIApplicationSupportsIndirectInputEvents
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UIStatusBarHidden
36 |
37 | UISupportedInterfaceOrientations
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationLandscapeLeft
41 | UIInterfaceOrientationLandscapeRight
42 |
43 | UISupportedInterfaceOrientations~ipad
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationPortraitUpsideDown
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 | UIViewControllerBasedStatusBarAppearance
51 |
52 | LSApplicationQueriesSchemes
53 |
54 | sms
55 | tel
56 | mailto
57 | https
58 | file
59 |
60 | CFBundleLocalizations
61 |
62 | en
63 | fr
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_localization/easy_localization.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 | import 'package:google_fonts/google_fonts.dart';
5 | import 'package:flutter_web_plugins/url_strategy.dart';
6 | import 'package:portfolio/src/app.dart';
7 | import 'package:portfolio/src/app_startup.dart';
8 | import 'package:portfolio/src/localization/app_localizations.dart';
9 | import 'package:portfolio/src/localization/locale_controller.dart';
10 |
11 | void main() async {
12 | WidgetsFlutterBinding.ensureInitialized();
13 | EasyLocalization.logger.enableBuildModes = [];
14 | usePathUrlStrategy();
15 | GoogleFonts.config.allowRuntimeFetching = false;
16 | final supportedLocales = await AppLocalizations.supportedLocales();
17 | runApp(
18 | ProviderScope(
19 | child: AppStartupWidget(
20 | onLoaded: (context) {
21 | return Consumer(
22 | builder: (context, ref, child) {
23 | ref.watch(localeControllerProvider);
24 | return EasyLocalization(
25 | supportedLocales: supportedLocales,
26 | path: AppLocalizations.translationsPath,
27 | fallbackLocale: supportedLocales.first,
28 | child: const MyApp(),
29 | );
30 | },
31 | );
32 | },
33 | ),
34 | ),
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/lib/src/app.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/constants/themes.dart' as themes;
4 | import 'package:portfolio/src/features/general/presentation/general_section.dart';
5 | import 'package:portfolio/src/features/general/provider/dark_mode_controller.dart';
6 | import 'package:easy_localization/easy_localization.dart';
7 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
8 |
9 | class MyApp extends ConsumerWidget {
10 | const MyApp({super.key});
11 |
12 | @override
13 | Widget build(BuildContext context, WidgetRef ref) {
14 | return MaterialApp(
15 | debugShowCheckedModeBanner: false,
16 | onGenerateTitle: (_) => tr(LocaleKeys.name),
17 | localizationsDelegates: context.localizationDelegates,
18 | supportedLocales: context.supportedLocales,
19 | locale: context.locale,
20 | theme: themes.lightTheme,
21 | darkTheme: themes.darkTheme,
22 | themeMode: ref.watch(darkModeProvider).maybeWhen(
23 | data: (darkMode) => darkMode ? ThemeMode.dark : ThemeMode.light,
24 | orElse: () => ThemeMode.system,
25 | ),
26 | home: const GeneralSection(),
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/src/app_startup.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_localization/easy_localization.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:google_fonts/google_fonts.dart';
4 | import 'package:hooks_riverpod/hooks_riverpod.dart';
5 | import 'package:portfolio/src/constants/sizes.dart';
6 | import 'package:riverpod_annotation/riverpod_annotation.dart';
7 |
8 | part 'app_startup.g.dart';
9 |
10 | @Riverpod(keepAlive: true)
11 | Future appStartup(Ref ref) async {
12 | await Future.wait([
13 | EasyLocalization.ensureInitialized(),
14 | GoogleFonts.pendingFonts([GoogleFonts.nunito()]),
15 | ]);
16 | }
17 |
18 | class AppStartupWidget extends ConsumerWidget {
19 | const AppStartupWidget({
20 | super.key,
21 | required this.onLoaded,
22 | });
23 |
24 | final WidgetBuilder onLoaded;
25 |
26 | @override
27 | Widget build(BuildContext context, WidgetRef ref) {
28 | final appStartupState = ref.watch(appStartupProvider);
29 | return appStartupState.when(
30 | data: (_) => onLoaded(context),
31 | loading: () => const AppStartupLoadingWidget(),
32 | error: (error, stackTrace) => AppStartupErrorWidget(
33 | message: error.toString(),
34 | onRetry: () => ref.invalidate(appStartupProvider),
35 | ),
36 | );
37 | }
38 | }
39 |
40 | class AppStartupLoadingWidget extends StatelessWidget {
41 | const AppStartupLoadingWidget({super.key});
42 |
43 | @override
44 | Widget build(BuildContext context) {
45 | return const SizedBox.shrink();
46 | }
47 | }
48 |
49 | class AppStartupErrorWidget extends StatelessWidget {
50 | const AppStartupErrorWidget({
51 | super.key,
52 | required this.message,
53 | required this.onRetry,
54 | });
55 |
56 | final String message;
57 | final VoidCallback onRetry;
58 |
59 | @override
60 | Widget build(BuildContext context) {
61 | return MaterialApp(
62 | home: Scaffold(
63 | body: Center(
64 | child: Column(
65 | mainAxisSize: MainAxisSize.min,
66 | children: [
67 | Text(
68 | message,
69 | style: Theme.of(context).textTheme.headlineSmall,
70 | ),
71 | gapH16,
72 | ElevatedButton(
73 | onPressed: onRetry,
74 | child: const Icon(Icons.restart_alt),
75 | ),
76 | ],
77 | ),
78 | ),
79 | ),
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/lib/src/common/data/language_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:hooks_riverpod/hooks_riverpod.dart';
2 | import 'package:portfolio/src/common/domain/language.dart';
3 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
4 | import 'package:portfolio/src/localization/json_list_translation.dart';
5 | import 'package:portfolio/src/localization/locale_controller.dart';
6 | import 'package:riverpod_annotation/riverpod_annotation.dart';
7 |
8 | part 'language_repository.g.dart';
9 |
10 | @riverpod
11 | LanguageRepository languageRepository(Ref ref) {
12 | return LanguageRepository(ref);
13 | }
14 |
15 | class LanguageRepository {
16 | LanguageRepository(this._ref);
17 |
18 | final Ref _ref;
19 |
20 | List getLanguages() {
21 | final locale = _ref.watch(localeControllerProvider).requireValue.locale;
22 | final jsonLanguages = trList(locale, LocaleKeys.languages);
23 | final languages = jsonLanguages.map((jsonLanguage) {
24 | return Language.fromJson(jsonLanguage);
25 | }).toList();
26 | return languages;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/common/domain/icon.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'icon.freezed.dart';
4 | part 'icon.g.dart';
5 |
6 | @freezed
7 | class IconModel with _$IconModel {
8 | const factory IconModel({
9 | String? assetName,
10 | String? codePoint,
11 | String? fontFamily,
12 | String? color,
13 | }) = _IconModel;
14 |
15 | factory IconModel.fromJson(Map json) =>
16 | _$IconModelFromJson(json);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/src/common/domain/language.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | import 'package:portfolio/src/common/domain/icon.dart';
3 |
4 | part 'language.freezed.dart';
5 | part 'language.g.dart';
6 |
7 | @freezed
8 | class Language with _$Language {
9 | const factory Language({
10 | String? code,
11 | String? name,
12 | String? nativeName,
13 | IconModel? icon,
14 | }) = _Language;
15 |
16 | factory Language.fromJson(Map json) =>
17 | _$LanguageFromJson(json);
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/common/domain/link.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'link.freezed.dart';
4 | part 'link.g.dart';
5 |
6 | @freezed
7 | class Link with _$Link {
8 | const factory Link({
9 | String? url,
10 | String? display,
11 | }) = _Link;
12 |
13 | factory Link.fromJson(Map json) => _$LinkFromJson(json);
14 | }
15 |
--------------------------------------------------------------------------------
/lib/src/common/domain/technology.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | import 'package:portfolio/src/common/domain/icon.dart';
3 |
4 | part 'technology.freezed.dart';
5 | part 'technology.g.dart';
6 |
7 | @freezed
8 | class Technology with _$Technology {
9 | const factory Technology({
10 | String? name,
11 | IconModel? icon,
12 | }) = _Technology;
13 |
14 | factory Technology.fromJson(Map json) =>
15 | _$TechnologyFromJson(json);
16 | }
17 |
--------------------------------------------------------------------------------
/lib/src/common/provider/shared_preferences_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:hooks_riverpod/hooks_riverpod.dart';
2 | import 'package:riverpod_annotation/riverpod_annotation.dart';
3 | import 'package:shared_preferences/shared_preferences.dart';
4 |
5 | part 'shared_preferences_provider.g.dart';
6 |
7 | @riverpod
8 | FutureOr sharedPreferences(Ref ref) async {
9 | return await SharedPreferences.getInstance();
10 | }
11 |
--------------------------------------------------------------------------------
/lib/src/common/widgets/animated_fade_slide.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:flutter_hooks/flutter_hooks.dart';
4 |
5 | class AnimatedFadeSlide extends HookConsumerWidget {
6 | const AnimatedFadeSlide({
7 | super.key,
8 | this.delay = const Duration(milliseconds: 0),
9 | this.duration = const Duration(milliseconds: 500),
10 | this.offset = const Offset(0, -64),
11 | required this.child,
12 | });
13 |
14 | final Duration delay;
15 | final Duration duration;
16 | final Offset offset;
17 | final Widget child;
18 |
19 | @override
20 | Widget build(BuildContext context, WidgetRef ref) {
21 | final controller = useAnimationController(duration: duration);
22 | final curveAnimation = CurvedAnimation(
23 | parent: controller,
24 | curve: Curves.decelerate,
25 | );
26 | final dxAnimation = useAnimation(
27 | Tween(begin: offset.dx, end: 0.0).animate(curveAnimation),
28 | );
29 | final dyAnimation = useAnimation(
30 | Tween(begin: offset.dy, end: 0.0).animate(curveAnimation),
31 | );
32 |
33 | useEffect(() {
34 | Future.delayed(delay, () {
35 | if (context.mounted) {
36 | controller.forward();
37 | }
38 | });
39 | return null;
40 | }, [controller]);
41 |
42 | return AnimatedBuilder(
43 | animation: controller,
44 | builder: (_, child) {
45 | return Opacity(
46 | opacity: controller.value,
47 | child: Transform.translate(
48 | offset: Offset(dxAnimation, dyAnimation),
49 | child: child,
50 | ),
51 | );
52 | },
53 | child: child,
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/src/common/widgets/icon.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_svg/svg.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 | import 'package:portfolio/src/common/domain/icon.dart';
5 |
6 | class MyIcon extends ConsumerWidget {
7 | const MyIcon({
8 | super.key,
9 | this.icon,
10 | this.placeholder = const SizedBox.shrink(),
11 | this.size = 24,
12 | this.padding,
13 | });
14 |
15 | final IconModel? icon;
16 | final double? size;
17 | final Widget placeholder;
18 | final EdgeInsetsGeometry? padding;
19 |
20 | @override
21 | Widget build(BuildContext context, WidgetRef ref) {
22 | final iconAssetName = icon?.assetName;
23 | final iconCodePoint = icon?.codePoint;
24 | final iconFontFamily = icon?.fontFamily;
25 | final iconColor = icon?.color;
26 | Color? color;
27 | if (iconColor != null) {
28 | final colorHex = int.tryParse(iconColor);
29 | if (colorHex != null) {
30 | color = Color(colorHex);
31 | }
32 | }
33 | if (iconCodePoint != null && iconFontFamily != null) {
34 | final iconCodePointHexa = int.tryParse(iconCodePoint);
35 | if (iconCodePointHexa != null) {
36 | final iconData = IconData(
37 | iconCodePointHexa,
38 | fontFamily: iconFontFamily,
39 | );
40 | return Padding(
41 | padding: const EdgeInsets.all(2),
42 | child: FittedBox(
43 | child: Icon(
44 | iconData,
45 | color: color,
46 | size: size,
47 | ),
48 | ),
49 | );
50 | }
51 | } else if (iconAssetName != null) {
52 | return SvgPicture.asset(
53 | iconAssetName,
54 | width: size,
55 | colorFilter:
56 | color == null ? null : ColorFilter.mode(color, BlendMode.srcIn),
57 | );
58 | }
59 | return placeholder;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/src/common/widgets/responsive.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 |
4 | class Responsive extends ConsumerWidget {
5 | final Widget desktop;
6 | final Widget? tablet;
7 | final Widget? mobile;
8 |
9 | const Responsive({
10 | super.key,
11 | required this.desktop,
12 | this.tablet,
13 | this.mobile,
14 | });
15 |
16 | static bool isDesktop(BuildContext context) =>
17 | MediaQuery.sizeOf(context).width >= 1024;
18 |
19 | static bool isTablet(BuildContext context) =>
20 | MediaQuery.sizeOf(context).width < 1024 &&
21 | MediaQuery.sizeOf(context).width >= 640;
22 |
23 | static bool isMobile(BuildContext context) =>
24 | MediaQuery.sizeOf(context).width < 640;
25 |
26 | @override
27 | Widget build(BuildContext context, WidgetRef ref) {
28 | final maxWidth = MediaQuery.sizeOf(context).width;
29 | if (maxWidth < 640 && mobile != null) {
30 | return mobile!;
31 | } else if (maxWidth < 1024 && tablet != null) {
32 | return tablet!;
33 | } else {
34 | return desktop;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/src/common/widgets/selection_area.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 |
4 | class MySelectionArea extends ConsumerWidget {
5 | const MySelectionArea({
6 | super.key,
7 | required this.child,
8 | this.mouseCursor = WidgetStateMouseCursor.textable,
9 | });
10 |
11 | final Widget child;
12 | final MouseCursor mouseCursor;
13 |
14 | @override
15 | Widget build(BuildContext context, WidgetRef ref) {
16 | return DefaultSelectionStyle(
17 | selectionColor: Theme.of(context).colorScheme.tertiary,
18 | mouseCursor: mouseCursor,
19 | child: SelectionArea(
20 | child: child,
21 | ),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/common/widgets/technology_chip.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:portfolio/src/common/domain/technology.dart';
3 | import 'package:portfolio/src/common/widgets/icon.dart';
4 | import 'package:portfolio/src/constants/sizes.dart';
5 |
6 | class TechnologyChip extends StatelessWidget {
7 | const TechnologyChip({super.key, required this.technology});
8 |
9 | final Technology technology;
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Container(
14 | padding: const EdgeInsets.symmetric(horizontal: 8),
15 | clipBehavior: Clip.hardEdge,
16 | decoration: BoxDecoration(
17 | color: Theme.of(context).colorScheme.secondary,
18 | borderRadius: const BorderRadius.all(Radius.circular(32)),
19 | ),
20 | height: 32,
21 | child: _buildTechnologyChip(context: context, technology: technology),
22 | );
23 | }
24 |
25 | Widget? _buildTechnologyChip({
26 | required BuildContext context,
27 | required Technology technology,
28 | }) {
29 | final technologyName = technology.name;
30 | final technologyIcon = technology.icon;
31 | if (technologyName == null) return null;
32 | return Row(
33 | mainAxisSize: MainAxisSize.min,
34 | children: [
35 | if (technologyIcon == null)
36 | gapW4
37 | else
38 | Padding(
39 | padding: const EdgeInsets.fromLTRB(2, 4, 4, 4),
40 | child: MyIcon(
41 | icon: technologyIcon,
42 | size: 32,
43 | ),
44 | ),
45 | Text(
46 | technologyName,
47 | style: Theme.of(context).textTheme.bodySmall,
48 | ),
49 | gapW4
50 | ],
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/src/common/widgets/technology_wrap_chips.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/common/domain/technology.dart';
4 | import 'package:portfolio/src/common/widgets/technology_chip.dart';
5 |
6 | class TechnologyWrapChips extends ConsumerWidget {
7 | const TechnologyWrapChips({
8 | super.key,
9 | required this.technologies,
10 | });
11 |
12 | final List technologies;
13 |
14 | @override
15 | Widget build(BuildContext context, WidgetRef ref) {
16 | return Wrap(
17 | spacing: 8,
18 | runSpacing: 8,
19 | children: technologies.map((technology) {
20 | return TechnologyChip(technology: technology);
21 | }).toList(),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/common/widgets/wrap_links.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/common/domain/link.dart';
4 | import 'package:portfolio/src/common/widgets/link.dart';
5 |
6 | class WrapLinks extends ConsumerWidget {
7 | const WrapLinks({
8 | super.key,
9 | required this.links,
10 | });
11 |
12 | final List links;
13 |
14 | @override
15 | Widget build(BuildContext context, WidgetRef ref) {
16 | return Wrap(
17 | spacing: 16,
18 | runSpacing: 4,
19 | children: links.where((link) => link.url != null).map((link) {
20 | final projectLinkUrl = link.url;
21 | final projectLinkDisplay = link.display;
22 | if (projectLinkUrl == null) return const SizedBox.shrink();
23 | return MyLink(
24 | url: projectLinkUrl,
25 | displayLink: projectLinkDisplay ?? projectLinkUrl,
26 | displayLeadingIcon: true,
27 | );
28 | }).toList(),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/src/constants/sizes.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class Sizes {
4 | static const p4 = 4.0;
5 | static const p8 = 8.0;
6 | static const p12 = 12.0;
7 | static const p16 = 16.0;
8 | static const p20 = 20.0;
9 | static const p24 = 24.0;
10 | static const p32 = 32.0;
11 | static const p40 = 40.0;
12 | static const p48 = 48.0;
13 | static const p64 = 64.0;
14 | static const p80 = 80.0;
15 | static const p100 = 100.0;
16 | static const p120 = 120.0;
17 | }
18 |
19 | const gapW4 = SizedBox(width: Sizes.p4);
20 | const gapW8 = SizedBox(width: Sizes.p8);
21 | const gapW12 = SizedBox(width: Sizes.p12);
22 | const gapW16 = SizedBox(width: Sizes.p16);
23 | const gapW20 = SizedBox(width: Sizes.p20);
24 | const gapW24 = SizedBox(width: Sizes.p24);
25 | const gapW32 = SizedBox(width: Sizes.p32);
26 | const gapW40 = SizedBox(width: Sizes.p40);
27 | const gapW48 = SizedBox(width: Sizes.p48);
28 | const gapW64 = SizedBox(width: Sizes.p64);
29 | const gapW80 = SizedBox(width: Sizes.p80);
30 | const gapW100 = SizedBox(width: Sizes.p100);
31 | const gapW120 = SizedBox(width: Sizes.p120);
32 |
33 | const gapH4 = SizedBox(height: Sizes.p4);
34 | const gapH8 = SizedBox(height: Sizes.p8);
35 | const gapH12 = SizedBox(height: Sizes.p12);
36 | const gapH16 = SizedBox(height: Sizes.p16);
37 | const gapH20 = SizedBox(height: Sizes.p20);
38 | const gapH24 = SizedBox(height: Sizes.p24);
39 | const gapH32 = SizedBox(height: Sizes.p32);
40 | const gapH40 = SizedBox(height: Sizes.p40);
41 | const gapH48 = SizedBox(height: Sizes.p48);
42 | const gapH64 = SizedBox(height: Sizes.p64);
43 | const gapH80 = SizedBox(height: Sizes.p80);
44 | const gapH100 = SizedBox(height: Sizes.p100);
45 | const gapH120 = SizedBox(height: Sizes.p120);
46 |
--------------------------------------------------------------------------------
/lib/src/constants/transparent_image.dart:
--------------------------------------------------------------------------------
1 | import 'dart:typed_data';
2 |
3 | final Uint8List transparentImage = Uint8List.fromList([
4 | 0x89,
5 | 0x50,
6 | 0x4E,
7 | 0x47,
8 | 0x0D,
9 | 0x0A,
10 | 0x1A,
11 | 0x0A,
12 | 0x00,
13 | 0x00,
14 | 0x00,
15 | 0x0D,
16 | 0x49,
17 | 0x48,
18 | 0x44,
19 | 0x52,
20 | 0x00,
21 | 0x00,
22 | 0x00,
23 | 0x01,
24 | 0x00,
25 | 0x00,
26 | 0x00,
27 | 0x01,
28 | 0x08,
29 | 0x06,
30 | 0x00,
31 | 0x00,
32 | 0x00,
33 | 0x1F,
34 | 0x15,
35 | 0xC4,
36 | 0x89,
37 | 0x00,
38 | 0x00,
39 | 0x00,
40 | 0x0A,
41 | 0x49,
42 | 0x44,
43 | 0x41,
44 | 0x54,
45 | 0x78,
46 | 0x9C,
47 | 0x63,
48 | 0x00,
49 | 0x01,
50 | 0x00,
51 | 0x00,
52 | 0x05,
53 | 0x00,
54 | 0x01,
55 | 0x0D,
56 | 0x0A,
57 | 0x2D,
58 | 0xB4,
59 | 0x00,
60 | 0x00,
61 | 0x00,
62 | 0x00,
63 | 0x49,
64 | 0x45,
65 | 0x4E,
66 | 0x44,
67 | 0xAE,
68 | 0x42,
69 | 0x60,
70 | 0x82,
71 | ]);
72 |
--------------------------------------------------------------------------------
/lib/src/features/about/presentation/about_desktop.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_localization/easy_localization.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
5 |
6 | class AboutDesktop extends ConsumerWidget {
7 | const AboutDesktop({super.key});
8 |
9 | @override
10 | Widget build(BuildContext context, WidgetRef ref) {
11 | return Column(
12 | crossAxisAlignment: CrossAxisAlignment.start,
13 | children: [
14 | Padding(
15 | padding: const EdgeInsets.only(bottom: 32),
16 | child: Text(
17 | tr(LocaleKeys.aboutSectionTitleAlt),
18 | style: Theme.of(context).textTheme.titleLarge,
19 | ),
20 | ),
21 | Text(
22 | tr(LocaleKeys.aboutDescription),
23 | style: Theme.of(context).textTheme.bodyLarge,
24 | ),
25 | ],
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/features/about/presentation/about_section.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/features/about/presentation/about_desktop.dart';
4 | import 'package:portfolio/src/common/widgets/responsive.dart';
5 |
6 | class AboutSection extends ConsumerWidget {
7 | const AboutSection({super.key});
8 |
9 | @override
10 | Widget build(BuildContext context, WidgetRef ref) {
11 | return const Responsive(
12 | desktop: AboutDesktop(),
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/features/experience/data/experience_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:hooks_riverpod/hooks_riverpod.dart';
2 | import 'package:portfolio/src/features/experience/domain/experience.dart';
3 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
4 | import 'package:portfolio/src/localization/json_list_translation.dart';
5 | import 'package:portfolio/src/localization/locale_controller.dart';
6 | import 'package:riverpod_annotation/riverpod_annotation.dart';
7 |
8 | part 'experience_repository.g.dart';
9 |
10 | @riverpod
11 | ExperienceRepository experienceRepository(Ref ref) {
12 | return ExperienceRepository(ref);
13 | }
14 |
15 | class ExperienceRepository {
16 | ExperienceRepository(this._ref);
17 |
18 | final Ref _ref;
19 |
20 | List getExperiences() {
21 | final locale = _ref.watch(localeControllerProvider).requireValue.locale;
22 | final jsonExperiences = trList(locale, LocaleKeys.experiences);
23 | final experiences = jsonExperiences.map((jsonExperience) {
24 | return Experience.fromJson(jsonExperience);
25 | }).toList();
26 | return experiences;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/features/experience/domain/experience.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | import 'package:portfolio/src/common/domain/link.dart';
3 | import 'package:portfolio/src/common/domain/technology.dart';
4 |
5 | part 'experience.freezed.dart';
6 | part 'experience.g.dart';
7 |
8 | @freezed
9 | class Experience with _$Experience {
10 | const factory Experience({
11 | String? role,
12 | String? company,
13 | String? description,
14 | String? url,
15 | bool? isPresent,
16 | int? startYear,
17 | int? startMonth,
18 | int? endYear,
19 | int? endMonth,
20 | List? technologies,
21 | List? links,
22 | }) = _Experience;
23 |
24 | factory Experience.fromJson(Map json) =>
25 | _$ExperienceFromJson(json);
26 | }
27 |
--------------------------------------------------------------------------------
/lib/src/features/experience/presentation/experience_desktop.dart:
--------------------------------------------------------------------------------
1 | import 'package:collection/collection.dart';
2 | import 'package:easy_localization/easy_localization.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:hooks_riverpod/hooks_riverpod.dart';
5 | import 'package:portfolio/src/constants/sizes.dart';
6 | import 'package:portfolio/src/features/experience/data/experience_repository.dart';
7 | import 'package:portfolio/src/features/experience/presentation/widgets/experience_card.dart';
8 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
9 |
10 | class ExperienceDesktop extends ConsumerWidget {
11 | const ExperienceDesktop({super.key});
12 |
13 | @override
14 | Widget build(BuildContext context, WidgetRef ref) {
15 | final experiences =
16 | ref.watch(experienceRepositoryProvider).getExperiences();
17 |
18 | return Column(
19 | crossAxisAlignment: CrossAxisAlignment.start,
20 | children: [
21 | Padding(
22 | padding: const EdgeInsets.only(left: 12, bottom: 20),
23 | child: Text(
24 | tr(LocaleKeys.experienceSectionTitle),
25 | style: Theme.of(context).textTheme.titleLarge,
26 | ),
27 | ),
28 | ...experiences.mapIndexed((index, experience) {
29 | return Column(
30 | children: [
31 | ExperienceCard(experience: experience),
32 | if (index != experiences.length - 1) gapH24,
33 | ],
34 | );
35 | }),
36 | ],
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/src/features/experience/presentation/experience_section.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/features/experience/presentation/experience_desktop.dart';
4 | import 'package:portfolio/src/common/widgets/responsive.dart';
5 |
6 | class ExperienceSection extends ConsumerWidget {
7 | const ExperienceSection({super.key});
8 |
9 | @override
10 | Widget build(BuildContext context, WidgetRef ref) {
11 | return const Responsive(
12 | desktop: ExperienceDesktop(),
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/features/experience/presentation/widgets/experience_date_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_localization/easy_localization.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 | import 'package:portfolio/src/features/experience/domain/experience.dart';
5 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
6 | import 'package:portfolio/src/utils/localized_date_extension.dart';
7 | import 'package:portfolio/src/utils/string_extension.dart';
8 |
9 | class ExperienceDateText extends ConsumerWidget {
10 | const ExperienceDateText({super.key, required this.experience});
11 |
12 | final Experience experience;
13 |
14 | @override
15 | Widget build(BuildContext context, WidgetRef ref) {
16 | final locale = context.locale;
17 | final startMonth = experience.startMonth?.localizedMonth(locale) ?? "";
18 | final startYear = experience.startYear?.localizedYear(locale);
19 | final startDate = startMonth.isEmpty ? startYear : "$startMonth $startYear";
20 | final endMonth = experience.endMonth?.localizedMonth(locale) ?? "";
21 | final endYear = experience.endYear?.localizedYear(locale);
22 | String? endDate;
23 | if (experience.isPresent == true) {
24 | endDate = tr(LocaleKeys.present);
25 | } else {
26 | endDate = endMonth.isEmpty ? endYear : "$endMonth $endYear";
27 | }
28 | if (startDate == null || endDate == null) return const Text("");
29 | return Text(
30 | "${startDate.capitalize()} - ${endDate.capitalize()}",
31 | style: Theme.of(context).textTheme.bodyMedium,
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/src/features/general/presentation/general_section.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/features/general/presentation/general_section_desktop.dart';
4 | import 'package:portfolio/src/features/general/presentation/general_section_tablet.dart';
5 | import 'package:portfolio/src/features/general/presentation/widgets/bottom_banner.dart';
6 | import 'package:portfolio/src/features/general/presentation/widgets/end_drawer.dart';
7 | import 'package:portfolio/src/features/general/presentation/widgets/safe_area.dart';
8 | import 'package:portfolio/src/common/widgets/responsive.dart';
9 |
10 | class GeneralSection extends ConsumerWidget {
11 | const GeneralSection({super.key});
12 |
13 | @override
14 | Widget build(BuildContext context, WidgetRef ref) {
15 | return Scaffold(
16 | backgroundColor: Theme.of(context).colorScheme.secondary,
17 | endDrawer: const MySafeArea(
18 | child: EndDrawer(),
19 | ),
20 | body: const MySafeArea(
21 | child: Stack(
22 | children: [
23 | Responsive(
24 | desktop: GeneralDesktop(),
25 | tablet: GeneralTablet(),
26 | ),
27 | Align(
28 | alignment: Alignment.bottomCenter,
29 | child: BottomBanner(),
30 | )
31 | ],
32 | ),
33 | ),
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/src/features/general/presentation/widgets/app_bar_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 |
4 | class AppBarButton extends ConsumerWidget {
5 | const AppBarButton({super.key, this.onPressed, required this.title});
6 |
7 | final VoidCallback? onPressed;
8 | final String title;
9 |
10 | @override
11 | Widget build(BuildContext context, WidgetRef ref) {
12 | return MaterialButton(
13 | hoverColor: Theme.of(context).colorScheme.primary,
14 | splashColor: Theme.of(context).colorScheme.tertiary,
15 | shape: const Border(),
16 | onPressed: onPressed,
17 | child: SizedBox(
18 | height: kToolbarHeight,
19 | child: Center(
20 | child: Text(
21 | title,
22 | style: Theme.of(context).textTheme.titleMedium,
23 | ),
24 | ),
25 | ),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/features/general/presentation/widgets/dark_mode_switch.dart:
--------------------------------------------------------------------------------
1 | import 'package:animated_toggle_switch/animated_toggle_switch.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 | import 'package:portfolio/src/features/general/provider/dark_mode_controller.dart';
5 |
6 | class DarkModeSwitch extends ConsumerWidget {
7 | const DarkModeSwitch({super.key});
8 |
9 | @override
10 | Widget build(BuildContext context, WidgetRef ref) {
11 | return SelectionContainer.disabled(
12 | child: AnimatedToggleSwitch.dual(
13 | current: _getDarkMode(ref),
14 | onChanged: (_) => ref.read(darkModeProvider.notifier).updateTheme(),
15 | first: false,
16 | second: true,
17 | spacing: 8,
18 | height: 36,
19 | indicatorSize: const Size.square(32),
20 | animationCurve: Curves.decelerate,
21 | style: ToggleStyle(
22 | backgroundColor: Theme.of(context).colorScheme.primary,
23 | borderColor: Colors.transparent,
24 | ),
25 | styleBuilder: (_) => ToggleStyle(
26 | indicatorColor: _getDarkMode(ref)
27 | ? Theme.of(context).colorScheme.onPrimary
28 | : Theme.of(context).switchTheme.thumbColor?.resolve({}),
29 | ),
30 | iconBuilder: (darkMode) {
31 | return _buildSwitchIcon(
32 | ref: ref,
33 | context: context,
34 | darkMode: darkMode,
35 | );
36 | },
37 | textBuilder: (darkMode) {
38 | return _buildSwitchIcon(
39 | ref: ref,
40 | context: context,
41 | darkMode: !darkMode,
42 | );
43 | },
44 | ),
45 | );
46 | }
47 |
48 | bool _getDarkMode(WidgetRef ref) {
49 | return ref.watch(darkModeProvider).maybeWhen(
50 | data: (darkMode) => darkMode,
51 | orElse: () => ThemeMode.system == ThemeMode.dark,
52 | );
53 | }
54 |
55 | Icon _buildSwitchIcon({
56 | required WidgetRef ref,
57 | required BuildContext context,
58 | required bool darkMode,
59 | }) {
60 | if (darkMode) {
61 | if (_getDarkMode(ref)) {
62 | return Icon(
63 | Icons.mode_night_outlined,
64 | color: Theme.of(context).colorScheme.onInverseSurface,
65 | );
66 | }
67 | return const Icon(Icons.mode_night_outlined);
68 | }
69 | if (!_getDarkMode(ref)) {
70 | return Icon(
71 | Icons.wb_sunny_outlined,
72 | color: Theme.of(context).colorScheme.onInverseSurface,
73 | );
74 | }
75 | return const Icon(Icons.wb_sunny_outlined);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/lib/src/features/general/presentation/widgets/drawer_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_hooks/flutter_hooks.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 |
5 | class MyDrawerButton extends StatefulHookConsumerWidget {
6 | const MyDrawerButton({
7 | super.key,
8 | required this.title,
9 | required this.sectionKey,
10 | });
11 |
12 | final String title;
13 | final GlobalKey sectionKey;
14 |
15 | @override
16 | ConsumerState createState() => _MyDrawerButtonState();
17 | }
18 |
19 | class _MyDrawerButtonState extends ConsumerState {
20 | late ColorTween _colorTween;
21 |
22 | @override
23 | void didChangeDependencies() {
24 | _colorTween = ColorTween(
25 | begin: Theme.of(context).colorScheme.inverseSurface,
26 | end: Theme.of(context).colorScheme.onSecondary,
27 | );
28 | super.didChangeDependencies();
29 | }
30 |
31 | @override
32 | Widget build(BuildContext context) {
33 | final isHovered = useState(false);
34 | final controller = useAnimationController(
35 | duration: const Duration(milliseconds: 200),
36 | );
37 | final colorAnimation = useAnimation(_colorTween.animate(controller));
38 |
39 | return DefaultTextStyle(
40 | style: Theme.of(context).textTheme.headlineMedium!,
41 | child: MouseRegion(
42 | cursor: SystemMouseCursors.click,
43 | onEnter: (_) {
44 | if (!isHovered.value) {
45 | isHovered.value = true;
46 | controller.forward();
47 | }
48 | },
49 | onExit: (_) {
50 | if (isHovered.value) {
51 | isHovered.value = false;
52 | controller.reverse();
53 | }
54 | },
55 | child: GestureDetector(
56 | onTap: () => _onTap(context),
57 | child: Text(
58 | widget.title,
59 | style: TextStyle(color: colorAnimation),
60 | ),
61 | ),
62 | ),
63 | );
64 | }
65 |
66 | void _onTap(BuildContext context) {
67 | final sectionKeyCurrentContext = widget.sectionKey.currentContext;
68 | if (sectionKeyCurrentContext != null) {
69 | Scrollable.ensureVisible(
70 | sectionKeyCurrentContext,
71 | duration: const Duration(milliseconds: 500),
72 | curve: Curves.decelerate,
73 | );
74 | }
75 | Navigator.of(context).pop();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/lib/src/features/general/presentation/widgets/locale_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:collection/collection.dart';
2 | import 'package:easy_localization/easy_localization.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:hooks_riverpod/hooks_riverpod.dart';
5 | import 'package:portfolio/src/common/data/language_repository.dart';
6 | import 'package:portfolio/src/common/widgets/icon.dart';
7 | import 'package:portfolio/src/constants/sizes.dart';
8 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
9 | import 'package:portfolio/src/common/domain/language.dart';
10 | import 'package:portfolio/src/localization/locale_controller.dart';
11 |
12 | class LocaleButton extends ConsumerWidget {
13 | const LocaleButton({super.key});
14 |
15 | @override
16 | Widget build(BuildContext context, WidgetRef ref) {
17 | final languages = ref.watch(languageRepositoryProvider).getLanguages();
18 |
19 | return DropdownButton(
20 | padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
21 | value: context.locale,
22 | dropdownColor: Theme.of(context).colorScheme.primary,
23 | focusNode: FocusNode(canRequestFocus: false),
24 | focusColor: Colors.transparent,
25 | underline: const SizedBox.shrink(),
26 | items: languages.mapIndexed((index, language) {
27 | return DropdownMenuItem(
28 | value: Locale(language.code ?? ""),
29 | child: Row(
30 | children: [
31 | MyIcon(
32 | icon: language.icon,
33 | placeholder: const Icon(Icons.translate),
34 | ),
35 | gapW8,
36 | Text(_getLanguageName(language)),
37 | ],
38 | ),
39 | );
40 | }).toList(),
41 | onChanged: (locale) async {
42 | await _onLocaleChanged(context, ref, locale: locale);
43 | },
44 | );
45 | }
46 |
47 | Future _onLocaleChanged(
48 | BuildContext context,
49 | WidgetRef ref, {
50 | required Locale? locale,
51 | }) async {
52 | if (locale != null) {
53 | await context.setLocale(locale);
54 | await ref.read(localeControllerProvider).requireValue.setLocale(locale);
55 | }
56 | }
57 |
58 | String _getLanguageName(Language language) {
59 | final languageName = language.name;
60 | final languageNativeName = language.nativeName;
61 | if (languageNativeName != null) return languageNativeName;
62 | if (languageName != null) return languageName;
63 | return tr(LocaleKeys.unknownLanguageError);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/lib/src/features/general/presentation/widgets/safe_area.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 |
4 | class MySafeArea extends ConsumerWidget {
5 | const MySafeArea({super.key, required this.child});
6 |
7 | final Widget child;
8 |
9 | @override
10 | Widget build(BuildContext context, WidgetRef ref) {
11 | return SafeArea(
12 | left: false,
13 | right: false,
14 | bottom: false,
15 | child: child,
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/features/general/presentation/widgets/sliver_app_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/features/general/presentation/widgets/app_bar.dart';
4 |
5 | class MySliverAppBar extends ConsumerWidget {
6 | const MySliverAppBar({super.key});
7 |
8 | @override
9 | Widget build(BuildContext context, WidgetRef ref) {
10 | return SliverPersistentHeader(
11 | delegate: _AppBarDelegate(),
12 | floating: true,
13 | );
14 | }
15 | }
16 |
17 | class _AppBarDelegate extends SliverPersistentHeaderDelegate {
18 | @override
19 | Widget build(
20 | BuildContext context,
21 | double shrinkOffset,
22 | bool overlapsContent,
23 | ) {
24 | return const MyAppBar();
25 | }
26 |
27 | @override
28 | double get minExtent => kToolbarHeight;
29 |
30 | @override
31 | double get maxExtent => kToolbarHeight;
32 |
33 | @override
34 | bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
35 | return false;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/src/features/general/provider/brightness_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:portfolio/src/common/provider/shared_preferences_provider.dart';
2 | import 'package:riverpod_annotation/riverpod_annotation.dart';
3 | import 'dart:ui';
4 |
5 | part 'brightness_controller.g.dart';
6 |
7 | @riverpod
8 | class BrightnessController extends _$BrightnessController {
9 | static const _brightnessKey = "brightness";
10 |
11 | @override
12 | FutureOr build() async => _getBrightness();
13 |
14 | Future updateBrightness() async {
15 | state = const AsyncValue.loading();
16 | state = await AsyncValue.guard(() async {
17 | final brightness = await _getBrightness();
18 | final Brightness newBrightness;
19 | if (brightness.name == Brightness.dark.name) {
20 | newBrightness = Brightness.light;
21 | } else {
22 | newBrightness = Brightness.dark;
23 | }
24 | await _setBrightness(newBrightness);
25 | return newBrightness;
26 | });
27 | }
28 |
29 | Future _getBrightness() async {
30 | final sharedPreferences = await ref.watch(sharedPreferencesProvider.future);
31 | final brightnessValue = sharedPreferences.getString(_brightnessKey);
32 | if (brightnessValue == Brightness.dark.name) {
33 | return Brightness.dark;
34 | } else if (brightnessValue == Brightness.light.name) {
35 | return Brightness.light;
36 | } else {
37 | // first build
38 | final platformDispatcher = PlatformDispatcher.instance;
39 | final platformBrightness = platformDispatcher.platformBrightness;
40 | await _setBrightness(platformBrightness);
41 | return platformBrightness;
42 | }
43 | }
44 |
45 | Future _setBrightness(Brightness brightness) async {
46 | ref.watch(sharedPreferencesProvider).whenData((sharedPreferences) async {
47 | await sharedPreferences.setString(_brightnessKey, brightness.name);
48 | });
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/src/features/general/provider/dark_mode_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | import 'package:portfolio/src/features/general/provider/brightness_controller.dart';
4 | import 'package:riverpod_annotation/riverpod_annotation.dart';
5 |
6 | part 'dark_mode_controller.g.dart';
7 |
8 | @riverpod
9 | class DarkMode extends _$DarkMode {
10 | @override
11 | FutureOr build() async {
12 | final brightness = await ref.watch(brightnessControllerProvider.future);
13 | return brightness == Brightness.dark;
14 | }
15 |
16 | Future updateTheme() async {
17 | await ref.watch(brightnessControllerProvider.notifier).updateBrightness();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/src/features/general/provider/scroll_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 |
4 | final scrollControllerProvider = Provider((ref) {
5 | return ScrollController();
6 | });
7 |
--------------------------------------------------------------------------------
/lib/src/features/general/provider/section_key_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:riverpod_annotation/riverpod_annotation.dart';
4 |
5 | part 'section_key_provider.g.dart';
6 |
7 | @riverpod
8 | GlobalKey homeSectionKey(Ref ref) {
9 | return GlobalKey();
10 | }
11 |
12 | @riverpod
13 | GlobalKey aboutSectionKey(Ref ref) {
14 | return GlobalKey();
15 | }
16 |
17 | @riverpod
18 | GlobalKey experienceSectionKey(Ref ref) {
19 | return GlobalKey();
20 | }
21 |
22 | @riverpod
23 | GlobalKey projectSectionKey(Ref ref) {
24 | return GlobalKey();
25 | }
26 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/data/personal_info_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:hooks_riverpod/hooks_riverpod.dart';
2 | import 'package:portfolio/src/features/personal_info/domain/contact.dart';
3 | import 'package:portfolio/src/features/personal_info/domain/resume.dart';
4 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
5 | import 'package:portfolio/src/localization/json_list_translation.dart';
6 | import 'package:portfolio/src/localization/locale_controller.dart';
7 | import 'package:riverpod_annotation/riverpod_annotation.dart';
8 |
9 | part 'personal_info_repository.g.dart';
10 |
11 | @riverpod
12 | PersonalInfoRepository personalInfoRepository(Ref ref) {
13 | return PersonalInfoRepository(ref);
14 | }
15 |
16 | class PersonalInfoRepository {
17 | PersonalInfoRepository(this._ref);
18 |
19 | final Ref _ref;
20 |
21 | List getResumes() {
22 | final locale = _ref.watch(localeControllerProvider).requireValue.locale;
23 | final jsonResumes = trList(locale, LocaleKeys.resumes);
24 | final resumes = jsonResumes.map((jsonResume) {
25 | return Resume.fromJson(jsonResume);
26 | }).toList();
27 | return resumes;
28 | }
29 |
30 | List getContacts() {
31 | final locale = _ref.watch(localeControllerProvider).requireValue.locale;
32 | final jsonContacts = trList(locale, LocaleKeys.contacts);
33 | final contacts = jsonContacts.map((jsonContact) {
34 | return Contact.fromJson(jsonContact);
35 | }).toList();
36 | return contacts;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/domain/contact.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | import 'package:portfolio/src/common/domain/icon.dart';
3 |
4 | part 'contact.freezed.dart';
5 | part 'contact.g.dart';
6 |
7 | @freezed
8 | class Contact with _$Contact {
9 | const factory Contact({
10 | String? tooltip,
11 | String? url,
12 | IconModel? icon,
13 | }) = _Contact;
14 |
15 | factory Contact.fromJson(Map json) =>
16 | _$ContactFromJson(json);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/domain/resume.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'resume.freezed.dart';
4 | part 'resume.g.dart';
5 |
6 | @freezed
7 | class Resume with _$Resume {
8 | const factory Resume({
9 | String? languageCode,
10 | String? language,
11 | String? url,
12 | }) = _Resume;
13 |
14 | factory Resume.fromJson(Map json) => _$ResumeFromJson(json);
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/presentation/personal_info_desktop.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_localization/easy_localization.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 | import 'package:portfolio/src/constants/sizes.dart';
5 | import 'package:portfolio/src/features/personal_info/data/personal_info_repository.dart';
6 | import 'package:portfolio/src/features/personal_info/domain/resume.dart';
7 | import 'package:portfolio/src/features/personal_info/presentation/widgets/contact_bar.dart';
8 | import 'package:portfolio/src/features/personal_info/presentation/widgets/resume_button.dart';
9 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
10 |
11 | class PersonalInfoDesktop extends ConsumerWidget {
12 | const PersonalInfoDesktop({super.key});
13 |
14 | @override
15 | Widget build(BuildContext context, WidgetRef ref) {
16 | final resumes = ref.watch(personalInfoRepositoryProvider).getResumes();
17 | final contacts = ref.watch(personalInfoRepositoryProvider).getContacts();
18 |
19 | return Column(
20 | crossAxisAlignment: CrossAxisAlignment.start,
21 | children: [
22 | Text(
23 | tr(LocaleKeys.name),
24 | style: Theme.of(context).textTheme.displayLarge,
25 | ),
26 | gapH4,
27 | Text(
28 | tr(LocaleKeys.description),
29 | style: Theme.of(context).textTheme.titleLarge,
30 | ),
31 | gapH8,
32 | Text(
33 | tr(LocaleKeys.subDescription),
34 | style: Theme.of(context).textTheme.bodyLarge,
35 | ),
36 | _buildResumeButton(ref, resumes: resumes.toList()),
37 | const Spacer(),
38 | gapH8,
39 | ContactBar(contacts: contacts.toList()),
40 | ],
41 | );
42 | }
43 |
44 | Widget _buildResumeButton(WidgetRef ref, {required List resumes}) {
45 | if (resumes.isEmpty) return const SizedBox.shrink();
46 | return Column(
47 | children: [
48 | gapH40,
49 | Padding(
50 | padding: const EdgeInsets.symmetric(vertical: 24),
51 | child: ResumeButton(resumes: resumes),
52 | ),
53 | ],
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/presentation/personal_info_mobile.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_localization/easy_localization.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 | import 'package:portfolio/src/constants/sizes.dart';
5 | import 'package:portfolio/src/features/personal_info/data/personal_info_repository.dart';
6 | import 'package:portfolio/src/features/personal_info/domain/resume.dart';
7 | import 'package:portfolio/src/features/personal_info/presentation/widgets/contact_bar.dart';
8 | import 'package:portfolio/src/features/personal_info/presentation/widgets/resume_button.dart';
9 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
10 |
11 | class PersonalInfoMobile extends ConsumerWidget {
12 | const PersonalInfoMobile({super.key});
13 |
14 | @override
15 | Widget build(BuildContext context, WidgetRef ref) {
16 | final resumes = ref.watch(personalInfoRepositoryProvider).getResumes();
17 | final contacts = ref.watch(personalInfoRepositoryProvider).getContacts();
18 |
19 | return Column(
20 | crossAxisAlignment: CrossAxisAlignment.start,
21 | children: [
22 | Text(
23 | tr(LocaleKeys.name),
24 | style: Theme.of(context).textTheme.displayMedium,
25 | ),
26 | gapH4,
27 | Text(
28 | tr(LocaleKeys.description),
29 | style: Theme.of(context).textTheme.titleLarge?.copyWith(fontSize: 20),
30 | ),
31 | gapH8,
32 | Text(
33 | tr(LocaleKeys.subDescription),
34 | style: Theme.of(context).textTheme.bodyLarge,
35 | ),
36 | _buildResumeButton(ref, resumes: resumes.toList()),
37 | gapH8,
38 | ContactBar(contacts: contacts.toList()),
39 | ],
40 | );
41 | }
42 |
43 | Widget _buildResumeButton(WidgetRef ref, {required List resumes}) {
44 | if (resumes.isEmpty) return const SizedBox.shrink();
45 | return Padding(
46 | padding: const EdgeInsets.symmetric(vertical: 28),
47 | child: ResumeButton(resumes: resumes),
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/presentation/personal_info_section.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/features/personal_info/presentation/personal_info_desktop.dart';
4 | import 'package:portfolio/src/features/personal_info/presentation/personal_info_mobile.dart';
5 | import 'package:portfolio/src/features/personal_info/presentation/personal_info_tablet.dart';
6 | import 'package:portfolio/src/common/widgets/responsive.dart';
7 |
8 | class PersonalInfoSection extends ConsumerWidget {
9 | const PersonalInfoSection({super.key});
10 |
11 | @override
12 | Widget build(BuildContext context, WidgetRef ref) {
13 | return const Responsive(
14 | mobile: PersonalInfoMobile(),
15 | tablet: PersonalInfoTablet(),
16 | desktop: PersonalInfoDesktop(),
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/presentation/personal_info_tablet.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_localization/easy_localization.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 | import 'package:portfolio/src/constants/sizes.dart';
5 | import 'package:portfolio/src/features/personal_info/data/personal_info_repository.dart';
6 | import 'package:portfolio/src/features/personal_info/domain/resume.dart';
7 | import 'package:portfolio/src/features/personal_info/presentation/widgets/contact_bar.dart';
8 | import 'package:portfolio/src/features/personal_info/presentation/widgets/resume_button.dart';
9 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
10 |
11 | class PersonalInfoTablet extends ConsumerWidget {
12 | const PersonalInfoTablet({super.key});
13 |
14 | @override
15 | Widget build(BuildContext context, WidgetRef ref) {
16 | final resumes = ref.watch(personalInfoRepositoryProvider).getResumes();
17 | final contacts = ref.watch(personalInfoRepositoryProvider).getContacts();
18 |
19 | return Column(
20 | crossAxisAlignment: CrossAxisAlignment.start,
21 | children: [
22 | Text(
23 | tr(LocaleKeys.name),
24 | style: Theme.of(context).textTheme.displayLarge,
25 | ),
26 | gapH4,
27 | Text(
28 | tr(LocaleKeys.description),
29 | style: Theme.of(context).textTheme.titleLarge,
30 | ),
31 | gapH8,
32 | Text(
33 | tr(LocaleKeys.subDescription),
34 | style: Theme.of(context).textTheme.bodyLarge,
35 | ),
36 | _buildResumeButton(ref, resumes: resumes.toList()),
37 | gapH8,
38 | ContactBar(contacts: contacts.toList()),
39 | ],
40 | );
41 | }
42 |
43 | Widget _buildResumeButton(WidgetRef ref, {required List resumes}) {
44 | if (resumes.isEmpty) return const SizedBox.shrink();
45 | return Padding(
46 | padding: const EdgeInsets.symmetric(vertical: 36),
47 | child: ResumeButton(resumes: resumes),
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/presentation/widgets/contact_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/features/personal_info/presentation/widgets/contact_icon_button.dart';
4 | import 'package:portfolio/src/features/personal_info/domain/contact.dart';
5 |
6 | class ContactBar extends ConsumerWidget {
7 | const ContactBar({super.key, required this.contacts});
8 |
9 | final List contacts;
10 |
11 | @override
12 | Widget build(BuildContext context, WidgetRef ref) {
13 | return Wrap(
14 | children: contacts.map((contact) {
15 | return ContactIconButton(contact: contact);
16 | }).toList(),
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/presentation/widgets/contact_icon_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/common/widgets/icon.dart';
4 | import 'package:portfolio/src/features/personal_info/domain/contact.dart';
5 | import 'package:portfolio/src/utils/launch_url_helper.dart';
6 | import 'package:portfolio/src/utils/scaffold_messenger_helper.dart';
7 |
8 | class ContactIconButton extends ConsumerWidget {
9 | const ContactIconButton({super.key, required this.contact});
10 |
11 | final Contact contact;
12 |
13 | @override
14 | Widget build(BuildContext context, WidgetRef ref) {
15 | final contactUrl = contact.url;
16 | final icon = _buildIcon(contact: contact);
17 | if (contactUrl == null || icon == null) {
18 | return const SizedBox.shrink();
19 | }
20 | return IconButton(
21 | tooltip: contact.tooltip,
22 | icon: icon,
23 | onPressed: () async {
24 | try {
25 | await LaunchUrlHelper.launchURL(contactUrl);
26 | } catch (e) {
27 | if (context.mounted) {
28 | ScaffoldMessengerHelper.showLaunchUrlError(
29 | context,
30 | url: contactUrl,
31 | );
32 | }
33 | }
34 | },
35 | );
36 | }
37 |
38 | Widget? _buildIcon({required Contact contact}) {
39 | final contactTooltip = contact.tooltip;
40 | return MyIcon(
41 | icon: contact.icon,
42 | placeholder: contactTooltip == null
43 | ? const SizedBox.shrink()
44 | : Row(
45 | mainAxisSize: MainAxisSize.min,
46 | children: [
47 | const Icon(Icons.link),
48 | const SizedBox(width: 4),
49 | Text(contactTooltip),
50 | ],
51 | ),
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/presentation/widgets/resume_language_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:collection/collection.dart';
2 | import 'package:easy_localization/easy_localization.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:hooks_riverpod/hooks_riverpod.dart';
5 | import 'package:portfolio/src/common/widgets/selection_area.dart';
6 | import 'package:portfolio/src/features/personal_info/domain/resume.dart';
7 | import 'package:portfolio/src/features/personal_info/presentation/widgets/resume_language_dialog_tile.dart';
8 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
9 |
10 | class ResumeLanguageDialog extends ConsumerWidget {
11 | const ResumeLanguageDialog({super.key, required this.resumes});
12 |
13 | final List resumes;
14 |
15 | @override
16 | Widget build(BuildContext context, WidgetRef ref) {
17 | return SimpleDialog(
18 | title: MySelectionArea(
19 | child: Center(
20 | child: Text(tr(LocaleKeys.downloadResume)),
21 | ),
22 | ),
23 | titlePadding: const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 8.0),
24 | contentPadding: const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 40.0),
25 | children: [
26 | const Divider(height: 0),
27 | ...resumes.mapIndexed((index, resume) {
28 | return Column(
29 | children: [
30 | ResumeLanguageDialogTile(resume: resume),
31 | if (index != resumes.length) const Divider(height: 0)
32 | ],
33 | );
34 | }),
35 | ],
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/src/features/personal_info/presentation/widgets/resume_language_dialog_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_localization/easy_localization.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 | import 'package:portfolio/src/common/data/language_repository.dart';
5 | import 'package:portfolio/src/common/widgets/icon.dart';
6 | import 'package:portfolio/src/constants/sizes.dart';
7 | import 'package:portfolio/src/features/personal_info/domain/resume.dart';
8 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
9 | import 'package:portfolio/src/utils/launch_url_helper.dart';
10 | import 'package:portfolio/src/utils/scaffold_messenger_helper.dart';
11 |
12 | class ResumeLanguageDialogTile extends ConsumerWidget {
13 | const ResumeLanguageDialogTile({super.key, required this.resume});
14 | final Resume resume;
15 |
16 | @override
17 | Widget build(BuildContext context, WidgetRef ref) {
18 | return InkWell(
19 | onTap: () {
20 | final resumeUrl = resume.url;
21 | if (resumeUrl == null) return;
22 | _onPressed(context, resumeUrl: resumeUrl);
23 | },
24 | child: Row(
25 | mainAxisAlignment: MainAxisAlignment.center,
26 | children: [
27 | Padding(
28 | padding: const EdgeInsets.symmetric(vertical: 16),
29 | child: _buildResumeLanguageTileContent(
30 | context,
31 | ref,
32 | resume: resume,
33 | ),
34 | ),
35 | ],
36 | ),
37 | );
38 | }
39 | }
40 |
41 | Widget _buildResumeLanguageTileContent(
42 | BuildContext context,
43 | WidgetRef ref, {
44 | required Resume resume,
45 | }) {
46 | final languages = ref.watch(languageRepositoryProvider).getLanguages();
47 | final resumeLanguageCode = resume.languageCode;
48 | if (resumeLanguageCode == null) {
49 | return Text(
50 | tr(LocaleKeys.unknownLanguageError),
51 | style: Theme.of(context).textTheme.titleMedium,
52 | );
53 | }
54 | final language = languages.firstWhere((language) {
55 | return language.code == resumeLanguageCode;
56 | });
57 | return Row(
58 | children: [
59 | MyIcon(
60 | icon: language.icon,
61 | placeholder: const Icon(Icons.translate),
62 | ),
63 | gapW8,
64 | Text(
65 | language.name ?? tr(LocaleKeys.unknownLanguageError),
66 | style: Theme.of(context).textTheme.titleMedium,
67 | )
68 | ],
69 | );
70 | }
71 |
72 | void _onPressed(BuildContext context, {required String resumeUrl}) async {
73 | try {
74 | await LaunchUrlHelper.launchURL(resumeUrl);
75 | } catch (e) {
76 | if (context.mounted) {
77 | ScaffoldMessengerHelper.showLaunchUrlError(context, url: resumeUrl);
78 | }
79 | }
80 | if (context.mounted) {
81 | Navigator.of(context).pop();
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/lib/src/features/project/data/project_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:hooks_riverpod/hooks_riverpod.dart';
2 | import 'package:portfolio/src/features/project/domain/project.dart';
3 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
4 | import 'package:portfolio/src/localization/json_list_translation.dart';
5 | import 'package:portfolio/src/localization/locale_controller.dart';
6 | import 'package:riverpod_annotation/riverpod_annotation.dart';
7 |
8 | part 'project_repository.g.dart';
9 |
10 | @riverpod
11 | ProjectRepository projectRepository(Ref ref) {
12 | return ProjectRepository(ref);
13 | }
14 |
15 | class ProjectRepository {
16 | ProjectRepository(this._ref);
17 |
18 | final Ref _ref;
19 |
20 | List getProjects() {
21 | final locale = _ref.watch(localeControllerProvider).requireValue.locale;
22 | final jsonProjects = trList(locale, LocaleKeys.projects);
23 | final projects = jsonProjects.map((jsonProject) {
24 | return Project.fromJson(jsonProject);
25 | }).toList();
26 | return projects;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/features/project/domain/project.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | import 'package:portfolio/src/common/domain/icon.dart';
3 | import 'package:portfolio/src/common/domain/link.dart';
4 | import 'package:portfolio/src/common/domain/technology.dart';
5 |
6 | part 'project.freezed.dart';
7 | part 'project.g.dart';
8 |
9 | @freezed
10 | class Project with _$Project {
11 | const factory Project({
12 | String? name,
13 | String? description,
14 | String? url,
15 | IconModel? icon,
16 | String? screenshotPath,
17 | List? technologies,
18 | List? links,
19 | }) = _Project;
20 |
21 | factory Project.fromJson(Map json) =>
22 | _$ProjectFromJson(json);
23 | }
24 |
--------------------------------------------------------------------------------
/lib/src/features/project/presentation/project_desktop.dart:
--------------------------------------------------------------------------------
1 | import 'package:collection/collection.dart';
2 | import 'package:easy_localization/easy_localization.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:hooks_riverpod/hooks_riverpod.dart';
5 | import 'package:portfolio/src/constants/sizes.dart';
6 | import 'package:portfolio/src/features/project/data/project_repository.dart';
7 | import 'package:portfolio/src/features/project/presentation/widgets/project_card.dart';
8 | import 'package:portfolio/src/localization/generated/locale_keys.g.dart';
9 |
10 | class ProjectDesktop extends ConsumerWidget {
11 | const ProjectDesktop({super.key});
12 |
13 | @override
14 | Widget build(BuildContext context, WidgetRef ref) {
15 | final projects = ref.watch(projectRepositoryProvider).getProjects();
16 |
17 | return Column(
18 | crossAxisAlignment: CrossAxisAlignment.start,
19 | children: [
20 | Padding(
21 | padding: const EdgeInsets.only(left: 12, bottom: 20),
22 | child: Text(
23 | tr(LocaleKeys.projectsSectionTitle),
24 | style: Theme.of(context).textTheme.titleLarge,
25 | ),
26 | ),
27 | ...projects.mapIndexed((index, project) {
28 | return Column(
29 | children: [
30 | ProjectCard(project: project),
31 | if (index != projects.length - 1) gapH24,
32 | ],
33 | );
34 | }),
35 | ],
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/src/features/project/presentation/project_section.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/features/project/presentation/project_desktop.dart';
4 | import 'package:portfolio/src/common/widgets/responsive.dart';
5 |
6 | class ProjectSection extends ConsumerWidget {
7 | const ProjectSection({super.key});
8 |
9 | @override
10 | Widget build(BuildContext context, WidgetRef ref) {
11 | return const Responsive(
12 | desktop: ProjectDesktop(),
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/features/project/presentation/widgets/project_description.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 | import 'package:portfolio/src/common/widgets/technology_wrap_chips.dart';
4 | import 'package:portfolio/src/common/widgets/wrap_links.dart';
5 | import 'package:portfolio/src/constants/sizes.dart';
6 | import 'package:portfolio/src/features/project/domain/project.dart';
7 |
8 | class ProjectDescription extends ConsumerWidget {
9 | const ProjectDescription({super.key, required this.project});
10 |
11 | final Project project;
12 |
13 | @override
14 | Widget build(BuildContext context, WidgetRef ref) {
15 | final theme = Theme.of(context);
16 | final projectName = project.name;
17 |
18 | return Column(
19 | crossAxisAlignment: CrossAxisAlignment.start,
20 | children: [
21 | Row(
22 | children: [
23 | Text(
24 | projectName != null ? "$projectName " : "",
25 | style: theme.textTheme.titleMedium
26 | ?.copyWith(fontWeight: FontWeight.bold),
27 | ),
28 | const Icon(Icons.open_in_new),
29 | ],
30 | ),
31 | gapH8,
32 | Row(
33 | children: [
34 | Expanded(
35 | child: Text(
36 | project.description ?? "",
37 | style: theme.textTheme.bodyMedium,
38 | ),
39 | ),
40 | ],
41 | ),
42 | gapH12,
43 | Column(
44 | crossAxisAlignment: CrossAxisAlignment.start,
45 | children: [
46 | _buildLinks(context),
47 | if (project.links?.isNotEmpty == true) gapH12 else gapH4,
48 | _buildChips(),
49 | ],
50 | ),
51 | ],
52 | );
53 | }
54 |
55 | Widget _buildChips() {
56 | final projectTechnologies = project.technologies;
57 | if (projectTechnologies == null) return const SizedBox.shrink();
58 | return TechnologyWrapChips(technologies: projectTechnologies);
59 | }
60 |
61 | Widget _buildLinks(BuildContext context) {
62 | final projectLinks = project.links;
63 | if (projectLinks == null) return const SizedBox.shrink();
64 | return WrapLinks(links: projectLinks);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/lib/src/features/project/presentation/widgets/project_icon.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hooks_riverpod/hooks_riverpod.dart';
3 |
4 | class ProjectIcon extends ConsumerWidget {
5 | const ProjectIcon({super.key, required this.iconData});
6 |
7 | final IconData iconData;
8 |
9 | @override
10 | Widget build(BuildContext context, WidgetRef ref) {
11 | return Icon(
12 | color: Colors.white,
13 | size: 32,
14 | iconData,
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/src/localization/app_localizations.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/services.dart';
4 | import 'package:flutter/widgets.dart';
5 | import 'package:path/path.dart' as path;
6 |
7 | class AppLocalizations {
8 | static const translationsPath = "assets/translations";
9 |
10 | static Future> supportedLocales() async {
11 | final manifestContent = await rootBundle.loadString('AssetManifest.json');
12 | final Map manifestMap = json.decode(manifestContent);
13 | List assets = manifestMap.keys.where((String key) {
14 | return key.startsWith(translationsPath);
15 | }).toList();
16 | List fileNames = assets.map((String asset) {
17 | final filenameLanguageCode = path.basenameWithoutExtension(asset);
18 | return Locale(filenameLanguageCode);
19 | }).toList();
20 | return fileNames;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/src/localization/json_list_translation.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | import 'package:portfolio/src/localization/generated/locale_json.g.dart';
4 |
5 | List