diff --git a/.github/capa.png b/.github/capa.png deleted file mode 100644 index c4d117a9..00000000 Binary files a/.github/capa.png and /dev/null differ diff --git a/.github/docs/CHANGELOG.md b/.github/docs/CHANGELOG.md new file mode 100644 index 00000000..0baede24 --- /dev/null +++ b/.github/docs/CHANGELOG.md @@ -0,0 +1,19 @@ +# CHANGELOG + +## Unreleased + +- None. + +## 2.0.0 - 2023-10-08 + +- 2.0.0 version. + +## 1.0.0 - 2019-02-10 + +- 1.0.0 version. + + \ No newline at end of file diff --git a/.github/docs/CONTRIBUTING.md b/.github/docs/CONTRIBUTING.md new file mode 100644 index 00000000..d32144b2 --- /dev/null +++ b/.github/docs/CONTRIBUTING.md @@ -0,0 +1,64 @@ +# 🀝 Contribution Guide + +## πŸ“‹ Topics + +- [🀝 Contribution Guide](#-contribution-guide) + - [πŸ“‹ Topics](#-topics) + - [🎯 About](#-about) + - [πŸ† Important details](#-important-details) + - [πŸ› Issues Reporting](#-issues-reporting) + - [πŸ™ Thanks](#-thanks) + +## 🎯 About + +Hello, I'm glad to see you're interested in contributing to this project. + +Some settings you may have already seen in the README.md file, but it doesn't hurt to repeat them here. + +**TL;DR:** In a simple and *generic* way, you can contribute in this way below: + +- Fork this repository; +- Create a branch with your feature: git checkout -b my-feature; +- Commit your changes: git commit -m 'feat: My new feature'; +- Push your branch: git push origin my-feature. +- Make a pull request, describing the changes you made and await approval. +- You can also make suggestions by opening an issue, reporting bugs or requesting new features. + +## πŸ† Important details + +Details make all the difference. Especially for attentive souls. For people who seek excellence. I will not treat this project differently. + +So let's go! + +I ask you to follow the following steps: + +- Use the [Conventional Commits](https://www.conventionalcommits.org/); +- Use the [GitFlow Workflow](https://www.atlassian.com/br/git/tutorials/comparing-workflows/gitflow-workflow); +- Follow style guides: + - [Dart Style Guide](https://dart.dev/guides/language/effective-dart/style). + - [Flutter Style Guide](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo). +- Don't forget to follow [flutter_lints](https://pub.dev/packages/flutter_lints) rules. +- Be aware of [dart_code_metrics](https://pub.dev/packages/dart_code_metrics) rules. +- Follow good naming principles, [`Naming Cheatsheet`](https://github.com/kettanaito/naming-cheatsheet), e.g. +- Tests: + - It's always beautiful to receive testable code and even more beautiful with tests. +- Make small commits and pull requests, it's easier to review and understand what you did. +- Read the Code of Conduct before contributing (I like [this](https://github.com/flutter/flutter/blob/master/CODE_OF_CONDUCT.md)). +- Read recommendations for [Contributing to Flutter](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md). + +## πŸ› Issues Reporting + +If you found a bug or have a suggestion, feel free to open an issue. But before doing that, please read the following: + +- Check if the issue was not reported, you can see in [Issues](https://github.com/felipecastrosales/site/issues) tab; +- Provide a clear and consice description of the issue; +- Include screenshots or related files; +- Indicate the expected behavior in comparison to the current behavior; +- Indicate the steps to reproduce the issue; +- Give suggestions on how we can solve this, it's always nice to have new ideas. + +## πŸ™ Thanks + +Just seeing your interest in contributing already got me excited. + +Know that even giving a star ⭐️ you will already be collaborating with the project. diff --git a/.github/docs/CONTRIBUTORS.md b/.github/docs/CONTRIBUTORS.md new file mode 100644 index 00000000..add271fd --- /dev/null +++ b/.github/docs/CONTRIBUTORS.md @@ -0,0 +1,9 @@ +# πŸŽ–οΈ Contributors + +This is a special area for contributors. + +![GitHub Contributors Image](https://contrib.rocks/image?repo=felipecastrosales/site) + +--- + +[![Contributors over time](https://contributor-graph-api.apiseven.com/contributors-svg?chart=contributorOverTime&repo=felipecastrosales/site)](https://www.apiseven.com/en/contributor-graph?chart=contributorOverTime&repo=felipecastrosales/site) diff --git a/.github/docs/WANTTODO.md b/.github/docs/WANTTODO.md new file mode 100644 index 00000000..3e675136 --- /dev/null +++ b/.github/docs/WANTTODO.md @@ -0,0 +1,45 @@ +# 🀩 Want to do + +Before I even openly release officially this to community, I already shared it with a few people, as well as already hosted it; and some of them asked me if they could use the model for their own use. And of course, that's why I'm making a point of explaining better how you can make this project yours. + +First, Follow the [πŸ€” How to Use](./README.md#-how-to-use) steps. + +## Figma + +- See file [here](https://www.figma.com/file/Dgq4C5dLgtWK2sb0KebmEZ/%40felipecastrosales---Portfolio?type=design&node-id=0%3A1&mode=design&t=RTugDZN5S2knn3Nk-1) +- Open project, and duplicate it. +- Change all datas to yours (as images), and export files substituting the originals. + +## Project Datas + +- If you want to change the tokens (colors etc), you can do it in the `lib/app/core/tokens/app_colors.dart` file. +- To change the texts datas: + - Open `lib/app/core/l10n/templates` and change the `.arb` files to yours - or update the existing ones. + - Tip: after you change, you can use the [Chat GPT](https://chat.openai.com/) to convert to other languages - but it's always worth checking the translation. + - After that, discomment the `synthetic-package: false`, inside the [l10n.yaml](l10n.yaml) file. + - Now, is only run the command `flutter gen-l10n` to generate the Dart files. + - And finally, comment the `synthetic-package: false` again, inside the [l10n.yaml](l10n.yaml) file. + +## Contact Form + +- Related to Contact Form: + - Create your account inside `emailjs` and make your changes. + - You can see [this video](https://www.youtube.com/watch?v=9HW3MZ_tsdo), to help you on practice. + +## Firebase + +- Create your Firebase project, and make your necessary changes. +- Configure all, see the [Firebase CLI](https://firebase.flutter.dev/docs/cli/) to help you with productivity. + +### Remote Config + +I put the informations of contact form inside the Remote Config, so create this keys with your information inside your Firebase project: + +- `service_id` +- `template_id` +- `user_id` +- `to_email` + +## Others + +If you want more information or others specific changes, you can open an issue or contact me. I will be happy to help you. diff --git a/.github/images/cover.png b/.github/images/cover.png new file mode 100644 index 00000000..164496b4 Binary files /dev/null and b/.github/images/cover.png differ diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml new file mode 100644 index 00000000..66bf5de7 --- /dev/null +++ b/.github/workflows/ci-cd.yaml @@ -0,0 +1,70 @@ +name: CI/CD + +on: + push: + tags: + - v* + +jobs: + flutter_test: + name: Run Flutter Analyze and Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v1 + with: + java-version: "12.x" + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + - run: flutter pub get + - run: flutter analyze + - run: dart format --set-exit-if-changed lib + - run: dart format --set-exit-if-changed test + - run: flutter test --coverage + - run: flutter test --machine > test-results.json + - uses: dorny/test-reporter@v1 + with: + name: test-results + path: test-results.json + reporter: flutter-json + - uses: VeryGoodOpenSource/very_good_coverage@v2 + with: + min_coverage: 0 + + build_ios: + name: Build Flutter (iOS) + needs: [flutter_test] + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: "12.x" + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + - run: flutter pub get + - run: flutter clean + - run: flutter build ios --release --no-codesign + + build_appbundle: + name: Build Flutter (Android) + needs: [flutter_test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v1 + with: + java-version: "12.x" + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + - run: flutter pub get + - run: flutter clean + - run: flutter build appbundle + - name: Upload appbundle + uses: actions/upload-artifact@v3 + with: + name: appbundle + path: build/app/outputs/bundle/release/app-release.aab diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..29267048 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,44 @@ +name: CI + +on: + push: + branches: + - develop + - master + pull_request: + branches: + - master + - develop + - feature/* + +jobs: + flutter_test: + name: Run Flutter Analyze and Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v1 + with: + java-version: "12.x" + - uses: subosito/flutter-action@v2 + with: + channel: stable + - run: flutter pub get + - run: flutter analyze + - run: dart format --set-exit-if-changed lib + - run: dart format --set-exit-if-changed test + - run: flutter test --coverage + - run: flutter test --machine > test-results.json + - uses: dorny/test-reporter@v1 + with: + name: test-results + path: test-results.json + reporter: flutter-json + - uses: VeryGoodOpenSource/very_good_coverage@v2 + with: + min_coverage: 70 + - uses: codecov/codecov-action@v3 + with: + # token: ${{ secrets.CODECOV_TOKEN }} not required for public repos + file: ./coverage/lcov.info + name: Codecov diff --git a/.gitignore b/.gitignore index e4644db1..f5ee8928 100644 --- a/.gitignore +++ b/.gitignore @@ -13,9 +13,7 @@ .pub/ build/ coverage/ -lib/generated_plugin_registrant.dart .metadata -/test/ site.iml # For library packages, don’t commit the pubspec.lock file. # Regenerating the pubspec.lock file lets you test your package against the latest compatible versions of its dependencies. @@ -77,4 +75,4 @@ package-lock.json firebase.json firebase-config.js .firebase -package.json \ No newline at end of file +package.json diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 00000000..a436a983 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,9 @@ +{ + "MD033": { + "allowed_elements": [ + "a", + "sup", + "nobr" + ] + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE index 8da0e6fd..7fb594b1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Felipe Sales +Copyright (c) 2022-2023 Felipe Sales Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 39c30461..df940c20 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,159 @@ -

My Website πŸ”₯

+

My Portfolio πŸ”₯

--- -

Layout 🎨

+
+ +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](.github/docs/CONTRIBUTING.md) +[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) +[![codecov](https://codecov.io/gh/felipecastrosales/site/branch/feature/new_version/graph/badge.svg?token=3T6SS801FW)](https://codecov.io/gh/felipecastrosales/site) + +
+ +## πŸ“‹ Topics + +- [πŸ“‹ Topics](#-topics) +- [πŸ“‹ About](#-about) +- [🎨 Layout](#-layout) +- [πŸš€ Technologies and Tools](#-technologies-and-tools) +- [🀯 Challenges and Learnings](#-challenges-and-learnings) +- [πŸ€” How to Use](#-how-to-use) +- [🀩 Want to do](#-want-to-do) +- [🀝 Contributing](#-contributing) +- [πŸ“ License](#-license) + +--- + +## πŸ“‹ About + +With a single codebase, you can access this example from mobile, web and even desktop. + +I'm sure this will be one of the best examples of the Flutter Web project in a completely open-source way and with the amout of features that exist. + +--- + +## 🎨 Layout + +

+ You can access the website at felipecastrosales.com and see the result of the project; it is adapted for mobiles, tablets and desktops.

- Website Demo + Website Demo

- The layout was developed by Karol de Paula, and you can access it on Figma. πŸ”₯ + The layout was developed by Karol de Paula and me, and you can access it on Figma.

--- -

How to Use πŸ€”

+## πŸš€ Technologies and Tools + +- Extremely Responsive Design using [`LayoutBuilder`](https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html) and other various strategies; +- Dependency Injection with [`GetIt`](https://pub.dev/packages/get_it); +- Use of [`scrollable_positioned_list`](https://pub.dev/packages/scrollable_positioned_list) to perform the "scroll to index" feature; +- Fancy and custom pre-load, using HTML and CSS; +- Firebase ecosystem: + - [`firebase_analytics`](https://pub.dev/packages/firebase_analytics); + - [`firebase_core`](https://pub.dev/packages/firebase_core); + - [`firebase_remote_config`](https://pub.dev/packages/firebase_remote_config); + - [`firebase_core_platform_interface`](https://pub.dev/packages/firebase_core_platform_interface) - for tests. +- Tests: + - Unitary, Widgets and Integration Test; + - The current coverage is 79.5% (PRs are welcome to increase this value); + - All of them using [`mocktail`](https://pub.dev/packages/mocktail). +- Internationalization: + - With support to 3 languages: English, Portuguese and Spanish; +- Feature to send an email to the user using [`emailjs API`](https://www.emailjs.com/); +- Settings: + - Firebase Hosting; + - Google Domains; + - Google Analytics; + - Firebase Remote Config. +- Use of good practices in general, such as: + - [`Clean Code`](https://www.google.com/search?q=Clean+Code+book&rlz=1C5CHFA_enBR1041BR1041&sxsrf=AJOqlzWiUKnodTrErtwjEw0mr60aKlAQ9A%3A1679360561304&ei=MQIZZICZEpDM1sQPqrSdsAI&ved=0ahUKEwjA9dLN6ev9AhUQppUCHSpaByYQ4dUDCA8&uact=5&oq=Clean+Code+book&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCC4QgAQyBQgAEIAEMgYIABAHEB4yBggAEAcQHjIGCAAQBxAeMgYIABAHEB4yBggAEAcQHjIICAAQBxAeEAoyBggAEAcQHjIGCAAQBxAeOgoIABBHENYEELADOgcIABCwAxBDOg0IABDkAhDWBBCwAxgBOgwILhDIAxCwAxBDGAJKBAhBGABQnQZYnQZg7wdoAXABeACAAaMBiAGjAZIBAzAuMZgBAKABAqABAcgBEcABAdoBBggBEAEYCdoBBggCEAEYCA&sclient=gws-wiz-serp); + - [`Clean Architecture`](https://www.google.com/search?q=Clean+Architecture+book&rlz=1C5CHFA_enBR1041BR1041&sxsrf=AJOqlzWTbx1VkaRMx9Y0pkUmhUFVrdPbew%3A1679360551842&ei=JwIZZKnyMuLf1sQP0-y2MA&ved=0ahUKEwjpoZHJ6ev9AhXir5UCHVO2DQYQ4dUDCA8&uact=5&oq=Clean+Architecture+book&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCC4QgAQyCAgAEIAEEMsBMggIABCABBDLATIICAAQgAQQywEyCAgAEIAEEMsBMggIABCABBDLATIGCAAQFhAeMgYIABAWEB4yBggAEBYQHjIGCAAQFhAeOgoIABBHENYEELADOgcIABCwAxBDOgUIABCABEoECEEYAFC5BVi2CmC4DGgCcAF4AIABnwGIAeAEkgEDMC40mAEAoAEByAEJwAEB&sclient=gws-wiz-serp); + - [`SOLID principles`](https://www.google.com/search?q=SOLID+principles&rlz=1C5CHFA_enBR1041BR1041&oq=SOLID+principles&aqs=chrome..69i57j69i60j69i61.152j0j4&sourceid=chrome&ie=UTF-8); + - [`Naming Cheatsheet`](https://github.com/kettanaito/naming-cheatsheet); + - [`dart_code_metrics`](https://pub.dev/packages/dart_code_metrics) - for code analysis; + - [`flutter_lints`](https://pub.dev/packages/flutter_lints) - for linting. +- CI / CD configurations inside [`workflows` folder](.github/workflows/). +- Among other various packages and internal resources. πŸ”₯ + +--- + +## 🀯 Challenges and Learnings + +Challenges are always an opportunity for growth, and in this project that became even more real. Making something from scratch, where development was perfected from start to finish - with lots of continual tweaks, for the better. One of the main ones was precisely to adjust all these behaviours for the most varied platforms, to work with breakpoints and to guarantee the best UI and UX for the project. + +One thing I realized was that Flutter Web still has a lot to evolve, but id does very well it proposes ([see here](https://docs.flutter.dev/development/platform-integration/web/faq#what-scenarios-are-ideal-for-flutter-on-the-web)). One of the main ones that still annoys me a little is the loading and rendering speed of the elements (there are some pre-load strategies, but that could be better and clearer IMHO. + +I feel that the project can evolve a lot, and for that reason I was very carefull with its development. For it to be simple, scalable and capable of any developer, of any level, being able to use and understand it. Also, I'll always be on the lookout for issues and PRs to improve it. πŸš€ + +This project took me out of my comfort zone, and I'm very happy with the result. Also, releasing it to the community did me a lot of good. Let's grow together, because the **Forge is Daily**. πŸ† + +--- + +## πŸ€” How to Use - ``` - First of all, correctly configure the Flutter development environment on your machine, see https://flutter.dev/docs/get-started/install - - - Clone this repository: - $ git clone https://github.com/felipecastrosales/site +First of all, configure the Flutter development environments, see . - - Enter in directory: - $ cd site +- Clone this repository: +`$ git clone https://github.com/felipecastrosales/site` - - For install dependencies: - $ flutter pub get +- Enter in directory: +`$ cd site` - - Run the app: - $ flutter run - ``` +- Open in your favorite editor (e.g. VSCode): +`$ code .` + +- For install dependencies: +`$ flutter pub get` + +- Run the app: +`$ flutter run` --- -

License πŸ“

+## 🀩 Want to do + +If you want to do this project with yours informations and datas, see the [Want To Do](.github/docs/WANTTODO.md). + +--- + +## 🀝 Contributing + +- Fork this repository; +- Create a branch with your feature: git checkout -b my-feature; +- Commit your changes: git commit -m 'feat: My new feature'; +- Push your branch: git push origin my-feature. +- Make a pull request, describing the changes you made and await approval. +- You can also make suggestions by opening an issue, reporting bugs or requesting new features. + +If you plan to contribute even more, see the [Contribution Guide](.github/docs/CONTRIBUTING.md). + +If you want to know more about the Contributors section, see the [Contributors](.github/docs/CONTRIBUTORS.md). + +See [Changelog](.github/docs/CHANGELOG.md). + +--- + +## πŸ“ License

- This repository is under MIT license. You can see the LICENSE file for more details. πŸ˜‰ + This repository is under MIT license. You can see the LICENSE file for more details.

--- ->This project was developed with ❀️ by **[@Felipe Sales](https://www.linkedin.com/in/felipecastrosales/)**, and the designer [Karol de Paula](https://www.behance.net/karolbarroso).
-If it helped you, give ⭐, contribute, it will help me too πŸ˜‰ +>This project was developed with ❀️ by **[@Felipe Sales](https://www.linkedin.com/in/felipecastrosales/)**, with [Karol de Paula](https://www.instagram.com/karoldepaulasm/). + +If it helped you, give ⭐, contribute, it will help me too. --- -
+
- [![Linkedin Badge](https://img.shields.io/badge/-Felipe%20Sales-292929?style=flat-square&logo=Linkedin&logoColor=white&link=https://www.linkedin.com/in/felipecastrosales/)](https://www.linkedin.com/in/felipecastrosales/) +[![Linkedin Badge](https://img.shields.io/badge/-Felipe%20Sales-292929?style=flat-square&logo=Linkedin&logoColor=white&link=https://www.linkedin.com/in/felipecastrosales/)](https://www.linkedin.com/in/felipecastrosales/) -
+
diff --git a/analysis_options.yaml b/analysis_options.yaml index 0bd999bf..01e5e464 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,55 @@ include: package:flutter_lints/flutter.yaml +analyzer: + plugins: + - dart_code_metrics + exclude: + - lib/generated_plugin_registrant.dart + - test/** + - lib/app/core/l10n/localizations/** # Generated file + - lib/data/services/firebase/firebase_set_defaults.dart # To use dynamic type (because really need it) + linter: rules: + - always_declare_return_types + - always_use_package_imports + - avoid_dynamic_calls + - avoid_relative_lib_imports + - implementation_imports + - require_trailing_commas + - sized_box_for_whitespace + - sized_box_shrink_expand + - sort_child_properties_last + - sort_constructors_first + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_literals_to_create_immutables + - prefer_const_declarations + - prefer_final_fields + - prefer_single_quotes + +dart_code_metrics: + metrics: + cyclomatic-complexity: 20 + number-of-parameters: 4 + maximum-nesting-level: 5 + metrics-exclude: + - test/** + rules: + - avoid-dynamic + - avoid-redundant-async + - avoid-passing-async-when-sync-expected + - avoid-redundant-async + - avoid-unnecessary-type-assertions + - avoid-unnecessary-type-casts + - avoid-unrelated-type-assertions + - avoid-unused-parameters + - avoid-nested-conditional-expressions + - newline-before-return + - no-boolean-literal-compare + - no-empty-block + - prefer-trailing-comma + - prefer-conditional-expressions + - no-equal-then-else + - prefer-moving-to-variable + - prefer-match-file-name \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 11cb1aca..de4b9e7a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -22,6 +22,9 @@ if (flutterVersionName == null) { } apply plugin: 'com.android.application' +// START: FlutterFire Configuration +apply plugin: 'com.google.gms.google-services' +// END: FlutterFire Configuration apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" @@ -44,10 +47,11 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.felipecastrosales.site" - minSdkVersion flutter.minSdkVersion + minSdkVersion 19 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -64,6 +68,9 @@ flutter { } dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation platform('com.google.firebase:firebase-bom:26.5.0') implementation 'com.google.firebase:firebase-analytics-ktx' diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 00000000..a5f26795 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,46 @@ +{ + "project_info": { + "project_number": "884743546828", + "project_id": "site-96dd0", + "storage_bucket": "site-96dd0.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:884743546828:android:ea09ba7ee1ed93aeae9f69", + "android_client_info": { + "package_name": "com.felipecastrosales.site" + } + }, + "oauth_client": [ + { + "client_id": "884743546828-9ea2sk2c4p812dkp6nfqh8nmrljho9t6.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAgfqTS-d712MTUDUC0fOXoKSHGuw6CNoM" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "884743546828-9ea2sk2c4p812dkp6nfqh8nmrljho9t6.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "884743546828-fjfun3totngd3bcog9jeopevkri03lob.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.felipecastrosales.site" + } + } + ] + } + } + } + ], + "configuration_version": "1" +} diff --git a/android/app/src/androidTest/java/site/siteapp/MainActivityTest.java b/android/app/src/androidTest/java/site/siteapp/MainActivityTest.java new file mode 100644 index 00000000..57a1182f --- /dev/null +++ b/android/app/src/androidTest/java/site/siteapp/MainActivityTest.java @@ -0,0 +1,12 @@ +package com.felipecastrosales.site; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterTestRunner.class) +public class MainActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class, true, false); +} diff --git a/android/build.gradle b/android/build.gradle index 24047dce..4e8eb2b8 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,12 +1,15 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.21' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.1.3' + // START: FlutterFire Configuration + classpath 'com.google.gms:google-services:4.3.3' + // END: FlutterFire Configuration classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index bc6a58af..6b665338 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/assets/components/contact/horizontal-texture.png b/assets/components/contact/horizontal-texture.png new file mode 100644 index 00000000..37f1d095 Binary files /dev/null and b/assets/components/contact/horizontal-texture.png differ diff --git a/assets/components/contact/vertical-texture.png b/assets/components/contact/vertical-texture.png new file mode 100644 index 00000000..3ec56287 Binary files /dev/null and b/assets/components/contact/vertical-texture.png differ diff --git a/assets/components/experience/abstract-right.png b/assets/components/experience/abstract-right.png new file mode 100644 index 00000000..0c5642b7 Binary files /dev/null and b/assets/components/experience/abstract-right.png differ diff --git a/assets/components/experience/champ.png b/assets/components/experience/champ.png new file mode 100644 index 00000000..7bd42390 Binary files /dev/null and b/assets/components/experience/champ.png differ diff --git a/assets/components/mouse-down.json b/assets/components/mouse-down.json new file mode 100644 index 00000000..623a71da --- /dev/null +++ b/assets/components/mouse-down.json @@ -0,0 +1 @@ +{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":30.0339965820313,"ip":0,"op":74.9999914647514,"w":797,"h":448,"nm":"ScrollDown","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Camada de forma 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[398.5,224,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[21.5,21.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Caminho da elipse 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"TraΓ§ado 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078012504,0.996078012504,0.996078012504,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Preenchimento 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.386,"y":1},"o":{"x":0.616,"y":0},"t":7,"s":[19.25,-76.75],"to":[0,14.833],"ti":[0,-14.833]},{"t":36.9999957892774,"s":[19.25,12.25]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[0,0]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":10,"s":[100,100]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":34,"s":[100,100]},{"t":42.9999951064575,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[100]},{"t":39.9999954478674,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Elipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":149.999982929503,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Camada de forma 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[398.5,224,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[150,280],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":101,"ix":4},"nm":"Caminho do retΓ’ngulo 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":38,"s":[100]},{"t":56.9999935132111,"s":[0]}],"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"d":[{"n":"d","nm":"dash","v":{"a":1,"k":[{"i":{"x":[0.227],"y":[1]},"o":{"x":[0.575],"y":[0.745]},"t":0,"s":[0]},{"i":{"x":[0.786],"y":[-8.547]},"o":{"x":[0.333],"y":[0]},"t":28,"s":[791]},{"i":{"x":[0.705],"y":[0.641]},"o":{"x":[0.467],"y":[0]},"t":36,"s":[791]},{"t":53.999993854621,"s":[0]}],"ix":1}},{"n":"g","nm":"gap","v":{"a":0,"k":700,"ix":2}},{"n":"o","nm":"offset","v":{"a":0,"k":0,"ix":7}}],"nm":"TraΓ§ado 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078012504,0.996078012504,0.996078012504,1],"ix":4},"o":{"a":0,"k":0,"ix":5},"r":1,"bm":0,"nm":"Preenchimento 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[20.5,10],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"RetΓ’ngulo 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":149.999982929503,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Animations","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":1,"y":0.226},"t":10,"s":[398.5,224,0],"to":[0,5,0],"ti":[0,-2.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.895,"y":0},"t":29,"s":[398.5,254,0],"to":[0,2.333,0],"ti":[0,5,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":48,"s":[398.5,238,0],"to":[0,-5,0],"ti":[0,2.333,0]},{"t":69.999992033768,"s":[398.5,224,0]}],"ix":2},"a":{"a":0,"k":[398.5,224,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":797,"h":448,"ip":0,"op":149.999982929503,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/components/presentation/presentation-mobile.png b/assets/components/presentation/presentation-mobile.png new file mode 100644 index 00000000..f39db6b2 Binary files /dev/null and b/assets/components/presentation/presentation-mobile.png differ diff --git a/assets/components/presentation/presentation-web.png b/assets/components/presentation/presentation-web.png new file mode 100644 index 00000000..12ccf179 Binary files /dev/null and b/assets/components/presentation/presentation-web.png differ diff --git a/assets/components/presentation/texture-background.png b/assets/components/presentation/texture-background.png new file mode 100644 index 00000000..327a4681 Binary files /dev/null and b/assets/components/presentation/texture-background.png differ diff --git a/assets/components/presentation/texture-large.png b/assets/components/presentation/texture-large.png new file mode 100644 index 00000000..07d3d410 Binary files /dev/null and b/assets/components/presentation/texture-large.png differ diff --git a/assets/components/projects/large-abstract.png b/assets/components/projects/large-abstract.png new file mode 100644 index 00000000..38aa0728 Binary files /dev/null and b/assets/components/projects/large-abstract.png differ diff --git a/assets/components/projects/mobile-abstract.png b/assets/components/projects/mobile-abstract.png new file mode 100644 index 00000000..3c5fa9ad Binary files /dev/null and b/assets/components/projects/mobile-abstract.png differ diff --git a/assets/components/projects/mockup.png b/assets/components/projects/mockup.png new file mode 100644 index 00000000..da8d59ff Binary files /dev/null and b/assets/components/projects/mockup.png differ diff --git a/assets/components/social/abstract-large.png b/assets/components/social/abstract-large.png new file mode 100644 index 00000000..3db5349e Binary files /dev/null and b/assets/components/social/abstract-large.png differ diff --git a/assets/components/social/abstract.png b/assets/components/social/abstract.png new file mode 100644 index 00000000..da4e69eb Binary files /dev/null and b/assets/components/social/abstract.png differ diff --git a/assets/components/social/discord.svg b/assets/components/social/discord.svg new file mode 100644 index 00000000..5d59a65b --- /dev/null +++ b/assets/components/social/discord.svg @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/assets/components/social/github.svg b/assets/components/social/github.svg new file mode 100644 index 00000000..4973d5fb --- /dev/null +++ b/assets/components/social/github.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/components/social/instagram.svg b/assets/components/social/instagram.svg new file mode 100644 index 00000000..32d14814 --- /dev/null +++ b/assets/components/social/instagram.svg @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/assets/components/social/linkedin.svg b/assets/components/social/linkedin.svg new file mode 100644 index 00000000..1dbd701b --- /dev/null +++ b/assets/components/social/linkedin.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/components/social/stack-overflow.svg b/assets/components/social/stack-overflow.svg new file mode 100644 index 00000000..c25580fc --- /dev/null +++ b/assets/components/social/stack-overflow.svg @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/assets/components/social/udemy.svg b/assets/components/social/udemy.svg new file mode 100644 index 00000000..58b20111 --- /dev/null +++ b/assets/components/social/udemy.svg @@ -0,0 +1,3 @@ + + Udemy \ No newline at end of file diff --git a/assets/discord.png b/assets/discord.png deleted file mode 100644 index bc8cc60f..00000000 Binary files a/assets/discord.png and /dev/null differ diff --git a/assets/facebook.png b/assets/facebook.png deleted file mode 100644 index e5e26802..00000000 Binary files a/assets/facebook.png and /dev/null differ diff --git a/assets/fonts/Montserrat/Montserrat-Bold.ttf b/assets/fonts/Montserrat/Montserrat-Bold.ttf new file mode 100644 index 00000000..efddc834 Binary files /dev/null and b/assets/fonts/Montserrat/Montserrat-Bold.ttf differ diff --git a/assets/fonts/Montserrat/Montserrat-Light.ttf b/assets/fonts/Montserrat/Montserrat-Light.ttf new file mode 100644 index 00000000..c5dfdb76 Binary files /dev/null and b/assets/fonts/Montserrat/Montserrat-Light.ttf differ diff --git a/assets/fonts/Montserrat/Montserrat-Medium.ttf b/assets/fonts/Montserrat/Montserrat-Medium.ttf new file mode 100644 index 00000000..dfc7e2fc Binary files /dev/null and b/assets/fonts/Montserrat/Montserrat-Medium.ttf differ diff --git a/assets/fonts/Montserrat/Montserrat-Regular.ttf b/assets/fonts/Montserrat/Montserrat-Regular.ttf new file mode 100644 index 00000000..aa9033a8 Binary files /dev/null and b/assets/fonts/Montserrat/Montserrat-Regular.ttf differ diff --git a/assets/fonts/Montserrat/Montserrat-SemiBold.ttf b/assets/fonts/Montserrat/Montserrat-SemiBold.ttf new file mode 100644 index 00000000..cbf44db9 Binary files /dev/null and b/assets/fonts/Montserrat/Montserrat-SemiBold.ttf differ diff --git a/assets/fonts/Poppins/Poppins-Regular.ttf b/assets/fonts/Poppins/Poppins-Regular.ttf new file mode 100644 index 00000000..9f0c71b7 Binary files /dev/null and b/assets/fonts/Poppins/Poppins-Regular.ttf differ diff --git a/assets/github.png b/assets/github.png deleted file mode 100644 index 877a8e0b..00000000 Binary files a/assets/github.png and /dev/null differ diff --git a/assets/header-background.png b/assets/header-background.png deleted file mode 100644 index 1939661c..00000000 Binary files a/assets/header-background.png and /dev/null differ diff --git a/assets/instagram.png b/assets/instagram.png deleted file mode 100644 index 24619028..00000000 Binary files a/assets/instagram.png and /dev/null differ diff --git a/assets/linkedin.png b/assets/linkedin.png deleted file mode 100644 index ef43fc24..00000000 Binary files a/assets/linkedin.png and /dev/null differ diff --git a/assets/rocketseat.png b/assets/rocketseat.png deleted file mode 100644 index 053fc212..00000000 Binary files a/assets/rocketseat.png and /dev/null differ diff --git a/assets/stackoverflow.png b/assets/stackoverflow.png deleted file mode 100644 index f0c00b8f..00000000 Binary files a/assets/stackoverflow.png and /dev/null differ diff --git a/assets/udemy.png b/assets/udemy.png deleted file mode 100644 index d3dc8177..00000000 Binary files a/assets/udemy.png and /dev/null differ diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart new file mode 100644 index 00000000..d265a9d9 --- /dev/null +++ b/integration_test/app_test.dart @@ -0,0 +1,26 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'package:site/app/app_widget.dart'; +import 'package:site/app/features/home/home_page.dart'; +import 'package:site/main.dart' as app; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('App', () { + testWidgets('Widget', (tester) async { + await app.main(); + await tester.pump(); + expect(find.byType(AppWidget), findsOneWidget); + }); + + group('HomePage', () { + testWidgets('Widget', (tester) async { + await app.main(); + await tester.pump(); + expect(find.byType(HomePage), findsOneWidget); + }); + }); + }); +} diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 8d4492f9..9625e105 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/ios/Podfile b/ios/Podfile index 1e8c3c90..88359b22 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 00000000..9924a96d --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,169 @@ +PODS: + - Firebase/Analytics (10.3.0): + - Firebase/Core + - Firebase/Core (10.3.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 10.3.0) + - Firebase/CoreOnly (10.3.0): + - FirebaseCore (= 10.3.0) + - Firebase/RemoteConfig (10.3.0): + - Firebase/CoreOnly + - FirebaseRemoteConfig (~> 10.3.0) + - firebase_analytics (10.1.3): + - Firebase/Analytics (= 10.3.0) + - firebase_core + - Flutter + - firebase_core (2.6.1): + - Firebase/CoreOnly (= 10.3.0) + - Flutter + - firebase_remote_config (3.0.12): + - Firebase/RemoteConfig (= 10.3.0) + - firebase_core + - Flutter + - FirebaseABTesting (10.5.0): + - FirebaseCore (~> 10.0) + - FirebaseAnalytics (10.3.0): + - FirebaseAnalytics/AdIdSupport (= 10.3.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseAnalytics/AdIdSupport (10.3.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleAppMeasurement (= 10.3.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseCore (10.3.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/Logger (~> 7.8) + - FirebaseCoreInternal (10.5.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseInstallations (10.5.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - PromisesObjC (~> 2.1) + - FirebaseRemoteConfig (10.3.0): + - FirebaseABTesting (~> 10.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - Flutter (1.0.0) + - GoogleAppMeasurement (10.3.0): + - GoogleAppMeasurement/AdIdSupport (= 10.3.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (10.3.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.3.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (10.3.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleUtilities/AppDelegateSwizzler (7.11.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.11.0): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.11.0): + - GoogleUtilities/Environment + - GoogleUtilities/MethodSwizzler (7.11.0): + - GoogleUtilities/Logger + - GoogleUtilities/Network (7.11.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.11.0)" + - GoogleUtilities/Reachability (7.11.0): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (7.11.0): + - GoogleUtilities/Logger + - nanopb (2.30909.0): + - nanopb/decode (= 2.30909.0) + - nanopb/encode (= 2.30909.0) + - nanopb/decode (2.30909.0) + - nanopb/encode (2.30909.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - PromisesObjC (2.1.1) + - url_launcher_ios (0.0.1): + - Flutter + +DEPENDENCIES: + - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_remote_config (from `.symlinks/plugins/firebase_remote_config/ios`) + - Flutter (from `Flutter`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + +SPEC REPOS: + trunk: + - Firebase + - FirebaseABTesting + - FirebaseAnalytics + - FirebaseCore + - FirebaseCoreInternal + - FirebaseInstallations + - FirebaseRemoteConfig + - GoogleAppMeasurement + - GoogleUtilities + - nanopb + - PromisesObjC + +EXTERNAL SOURCES: + firebase_analytics: + :path: ".symlinks/plugins/firebase_analytics/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_remote_config: + :path: ".symlinks/plugins/firebase_remote_config/ios" + Flutter: + :path: Flutter + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + +SPEC CHECKSUMS: + Firebase: f92fc551ead69c94168d36c2b26188263860acd9 + firebase_analytics: 649edaf7b1f891d4f04e5e3d52c051d8d73432f8 + firebase_core: 6242f038aefa4582e028536e71312f7e3a41e6bb + firebase_remote_config: 35192f08b5748a4db82901eadc6ab9b1d0c3c60c + FirebaseABTesting: 8cb5cc4e395c8dce8a2820a6a329020ead56fe2f + FirebaseAnalytics: 036232b6a1e2918e5f67572417be1173576245f3 + FirebaseCore: 988754646ab3bd4bdcb740f1bfe26b9f6c0d5f2a + FirebaseCoreInternal: e463f41bb935cd049505bf7e9a5bdd7dcea90df6 + FirebaseInstallations: 935bc4abb6f7a035cab7a0c31cb777b2be3dd254 + FirebaseRemoteConfig: c24f767c17b0440ee63c7e93380d599173556113 + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + GoogleAppMeasurement: c7d6fff39bf2d829587d74088d582e32d75133c3 + GoogleUtilities: c2bdc4cf2ce786c4d2e6b3bcfd599a25ca78f06f + nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 + path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 + PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb + url_launcher_ios: fb12c43172927bb5cf75aeebd073f883801f1993 + +PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 + +COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 2f58ac84..faae624d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,16 +3,18 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 48E59AAAD4795D30F5CBC6C2 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A95F90F1F0D0DB3A268FD89B /* GoogleService-Info.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + A89DA8DE9906C27B34279FB6 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80782BA4BE279BAA412B1169 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -31,10 +33,14 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3446C2D904BDD149A9F61E6C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 3888DEC613C7D6DC32EFD3AC /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 80782BA4BE279BAA412B1169 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8EB7DBEC44B9A13BB81136E6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -42,6 +48,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A95F90F1F0D0DB3A268FD89B /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,12 +56,32 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A89DA8DE9906C27B34279FB6 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 22424A109E99620500D2F74E /* Frameworks */ = { + isa = PBXGroup; + children = ( + 80782BA4BE279BAA412B1169 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 4C80944473CCDCB8C69A3E34 /* Pods */ = { + isa = PBXGroup; + children = ( + 3888DEC613C7D6DC32EFD3AC /* Pods-Runner.debug.xcconfig */, + 3446C2D904BDD149A9F61E6C /* Pods-Runner.release.xcconfig */, + 8EB7DBEC44B9A13BB81136E6 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +99,9 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 4C80944473CCDCB8C69A3E34 /* Pods */, + 22424A109E99620500D2F74E /* Frameworks */, + A95F90F1F0D0DB3A268FD89B /* GoogleService-Info.plist */, ); sourceTree = ""; }; @@ -105,12 +135,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 8EFEDB9F9F8096A5DCCB8A21 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 27D3798770BBECC413219CE5 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -163,14 +195,33 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + 48E59AAAD4795D30F5CBC6C2 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 27D3798770BBECC413219CE5 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -183,8 +234,31 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 8EFEDB9F9F8096A5DCCB8A21 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -254,6 +328,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -272,7 +347,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -325,6 +400,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -349,7 +425,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -380,6 +456,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -398,7 +475,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 00000000..33e09312 --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 884743546828-fjfun3totngd3bcog9jeopevkri03lob.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.884743546828-fjfun3totngd3bcog9jeopevkri03lob + API_KEY + AIzaSyDrl7u3LFGcDrk_o6XR571OHJjLYxHlqN4 + GCM_SENDER_ID + 884743546828 + PLIST_VERSION + 1 + BUNDLE_ID + com.felipecastrosales.site + PROJECT_ID + site-96dd0 + STORAGE_BUCKET + site-96dd0.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:884743546828:ios:f4ddf7bc0168bb74ae9f69 + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 7f0caad7..b067921d 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -43,5 +43,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/ios/firebase_app_id_file.json b/ios/firebase_app_id_file.json new file mode 100644 index 00000000..ff2e60ac --- /dev/null +++ b/ios/firebase_app_id_file.json @@ -0,0 +1,7 @@ +{ + "file_generated_by": "FlutterFire CLI", + "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", + "GOOGLE_APP_ID": "1:884743546828:ios:f4ddf7bc0168bb74ae9f69", + "FIREBASE_PROJECT_ID": "site-96dd0", + "GCM_SENDER_ID": "884743546828" +} \ No newline at end of file diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 00000000..f86dba80 --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,7 @@ +arb-dir: lib/app/core/l10n/templates +output-dir: lib/app/core/l10n/localizations +template-arb-file: app_pt.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations +### Uncomment when generating. +# synthetic-package: false \ No newline at end of file diff --git a/lib/app/app_widget.dart b/lib/app/app_widget.dart index 580e02e9..1aca53b8 100644 --- a/lib/app/app_widget.dart +++ b/lib/app/app_widget.dart @@ -1,17 +1,36 @@ - import 'package:flutter/material.dart'; -import 'home/home_page.dart'; +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:http/http.dart' as http; + +import 'package:site/app/core/injections/injections.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/themes/app_theme.dart'; +import 'package:site/app/features/home/home_page.dart'; class AppWidget extends StatelessWidget { - const AppWidget({Key? key}) : super(key: key); + AppWidget({ + super.key, + FirebaseRemoteConfig? firebaseRemoteConfig, + http.Client? httpClient, + }) : _firebaseRemoteConfig = firebaseRemoteConfig ?? getIt(), + _httpClient = httpClient ?? getIt(); + + final FirebaseRemoteConfig _firebaseRemoteConfig; + final http.Client _httpClient; @override Widget build(BuildContext context) { - return const MaterialApp( - title: 'Felipe Sales | Social Links', + return MaterialApp( + onGenerateTitle: (context) => AppTexts.get(context).projectTitle, debugShowCheckedModeBanner: false, - home: HomePage(), + theme: AppTheme.theme, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: HomePage( + firebaseRemoteConfig: _firebaseRemoteConfig, + httpClient: _httpClient, + ), ); } } diff --git a/lib/app/core/app_colors.dart b/lib/app/core/app_colors.dart deleted file mode 100644 index 1f1aedb0..00000000 --- a/lib/app/core/app_colors.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter/material.dart'; - -class AppColors { - static const primary = Color(0xff4361EE); - static const background = Color(0xff072227); - static const shadow = Color(0xff323232); - static const light = Color(0xffEBE9E9); -} diff --git a/lib/app/core/extensions/extensions.dart b/lib/app/core/extensions/extensions.dart new file mode 100644 index 00000000..20051838 --- /dev/null +++ b/lib/app/core/extensions/extensions.dart @@ -0,0 +1 @@ +export 'media_query_ext.dart'; diff --git a/lib/app/core/extensions/media_query_ext.dart b/lib/app/core/extensions/media_query_ext.dart new file mode 100644 index 00000000..1ec1aa0d --- /dev/null +++ b/lib/app/core/extensions/media_query_ext.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; + +extension MediaQueryExt on BuildContext { + Size get mediaQuerySize => MediaQuery.of(this).size; + + double get height => mediaQuerySize.height; + + double get width => mediaQuerySize.width; +} diff --git a/lib/app/core/injections/injections.dart b/lib/app/core/injections/injections.dart new file mode 100644 index 00000000..a48d7dd0 --- /dev/null +++ b/lib/app/core/injections/injections.dart @@ -0,0 +1,34 @@ +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:get_it/get_it.dart'; +import 'package:http/http.dart' as http; + +import 'package:site/data/repositories/contact/contact.dart'; +import 'package:site/data/services/firebase/firebase.dart'; + +final getIt = GetIt.I; + +void configureDependencies() { + if (!getIt.isRegistered()) { + getIt.registerSingleton( + http.Client(), + ); + } + if (!getIt.isRegistered()) { + getIt.registerSingleton( + FirebaseRemoteConfig.instance, + ); + } + if (!getIt.isRegistered()) { + getIt.registerSingleton( + FirebaseServiceImpl(), + ); + } + if (!getIt.isRegistered()) { + getIt.registerSingleton( + ContactRepositoryImpl( + firebaseRemoteConfig: getIt(), + httpClient: getIt(), + ), + ); + } +} diff --git a/lib/app/core/l10n/l10n.dart b/lib/app/core/l10n/l10n.dart new file mode 100644 index 00000000..e29dbb29 --- /dev/null +++ b/lib/app/core/l10n/l10n.dart @@ -0,0 +1 @@ +export 'localizations/localizations.dart'; diff --git a/lib/app/core/l10n/localizations/app_localizations.dart b/lib/app/core/l10n/localizations/app_localizations.dart new file mode 100644 index 00000000..ad25b13e --- /dev/null +++ b/lib/app/core/l10n/localizations/app_localizations.dart @@ -0,0 +1,488 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'package:site/app/core/l10n/l10n.dart'; + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'localizations/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('en'), + Locale('es'), + Locale('pt') + ]; + + /// No description provided for @projectTitle. + /// + /// In pt, this message translates to: + /// **'Felipe Sales | Social Links'** + String get projectTitle; + + /// No description provided for @felipeSales. + /// + /// In pt, this message translates to: + /// **'Felipe Sales'** + String get felipeSales; + + /// No description provided for @flutterDeveloperInstructor. + /// + /// In pt, this message translates to: + /// **'Flutter Developer Instructor'** + String get flutterDeveloperInstructor; + + /// No description provided for @hiIAmFelipeSales. + /// + /// In pt, this message translates to: + /// **'OlΓ‘, sou Felipe Sales'** + String get hiIAmFelipeSales; + + /// No description provided for @developerFocused. + /// + /// In pt, this message translates to: + /// **'Desenvolvedor focado em aplicar seus conhecimentos em prΓ‘tica e construir coisas incrΓ­veis atravΓ©s de linhas de cΓ³digo.'** + String get developerFocused; + + /// No description provided for @appsDeveloper. + /// + /// In pt, this message translates to: + /// **'> Desenvolvedor de Apps'** + String get appsDeveloper; + + /// No description provided for @applicationsDeveloper. + /// + /// In pt, this message translates to: + /// **'> Desenvolvedor de Aplicativos'** + String get applicationsDeveloper; + + /// No description provided for @home. + /// + /// In pt, this message translates to: + /// **'Home'** + String get home; + + /// No description provided for @deepCodeWayCode. + /// + /// In pt, this message translates to: + /// **'Deep Code. Way Code.'** + String get deepCodeWayCode; + + /// No description provided for @deep. + /// + /// In pt, this message translates to: + /// **'Deep '** + String get deep; + + /// No description provided for @code. + /// + /// In pt, this message translates to: + /// **'Code. '** + String get code; + + /// No description provided for @way. + /// + /// In pt, this message translates to: + /// **'Way '** + String get way; + + /// No description provided for @projects. + /// + /// In pt, this message translates to: + /// **'Projetos'** + String get projects; + + /// No description provided for @seeProjectsUpper. + /// + /// In pt, this message translates to: + /// **'VER PROJETOS'** + String get seeProjectsUpper; + + /// No description provided for @projectAreInMyGitHub. + /// + /// In pt, this message translates to: + /// **'Grande parte dos projetos que jΓ‘ desenvolvi estΓ£o no meu GitHub de forma completamente open-source.'** + String get projectAreInMyGitHub; + + /// No description provided for @experience. + /// + /// In pt, this message translates to: + /// **'ExperiΓͺncia'** + String get experience; + + /// No description provided for @workSuaMusica. + /// + /// In pt, this message translates to: + /// **'Sua MΓΊsica | 2022 - Atualmente'** + String get workSuaMusica; + + /// No description provided for @workRocketseat. + /// + /// In pt, this message translates to: + /// **'Rocketseat | 2021 - 2022'** + String get workRocketseat; + + /// No description provided for @workUdemy. + /// + /// In pt, this message translates to: + /// **'Udemy | 2020 - 2021'** + String get workUdemy; + + /// No description provided for @workPersonalProjects. + /// + /// In pt, this message translates to: + /// **'Projetos Pessoais | Desde 2020'** + String get workPersonalProjects; + + /// No description provided for @flutterDeveloper. + /// + /// In pt, this message translates to: + /// **'Desenvolvedor Flutter'** + String get flutterDeveloper; + + /// No description provided for @developerInstructor. + /// + /// In pt, this message translates to: + /// **'Desenvolvedor Instrutor'** + String get developerInstructor; + + /// No description provided for @teachingAssistent. + /// + /// In pt, this message translates to: + /// **'Auxiliar de Ensino'** + String get teachingAssistent; + + /// No description provided for @payngPriceEveryday. + /// + /// In pt, this message translates to: + /// **'Pagando o preΓ§o todos os dias'** + String get payngPriceEveryday; + + /// No description provided for @socialLinks. + /// + /// In pt, this message translates to: + /// **'Social Links'** + String get socialLinks; + + /// No description provided for @gitHub. + /// + /// In pt, this message translates to: + /// **'GitHub'** + String get gitHub; + + /// No description provided for @linkedIn. + /// + /// In pt, this message translates to: + /// **'LinkedIn'** + String get linkedIn; + + /// No description provided for @stackOverFlow. + /// + /// In pt, this message translates to: + /// **'Stack OverFlow'** + String get stackOverFlow; + + /// No description provided for @discord. + /// + /// In pt, this message translates to: + /// **'Discord'** + String get discord; + + /// No description provided for @udemy. + /// + /// In pt, this message translates to: + /// **'Udemy'** + String get udemy; + + /// No description provided for @instagram. + /// + /// In pt, this message translates to: + /// **'Instagram'** + String get instagram; + + /// No description provided for @followMeOnMySocialNetworks. + /// + /// In pt, this message translates to: + /// **'Me acompanhe nas minhas redes'** + String get followMeOnMySocialNetworks; + + /// No description provided for @contact. + /// + /// In pt, this message translates to: + /// **'Contato'** + String get contact; + + /// No description provided for @name. + /// + /// In pt, this message translates to: + /// **'Nome'** + String get name; + + /// No description provided for @email. + /// + /// In pt, this message translates to: + /// **'E-mail'** + String get email; + + /// No description provided for @title. + /// + /// In pt, this message translates to: + /// **'TΓ­tulo'** + String get title; + + /// No description provided for @text. + /// + /// In pt, this message translates to: + /// **'Texto'** + String get text; + + /// No description provided for @sendEmailUpper. + /// + /// In pt, this message translates to: + /// **'ENVIAR E-MAIL'** + String get sendEmailUpper; + + /// No description provided for @emailSendedWithSuccess. + /// + /// In pt, this message translates to: + /// **'E-mail enviado com sucesso!'** + String get emailSendedWithSuccess; + + /// No description provided for @letsChatCallMe. + /// + /// In pt, this message translates to: + /// **'Vamos bater um papo, me chame:'** + String get letsChatCallMe; + + /// No description provided for @hyphen. + /// + /// In pt, this message translates to: + /// **' - '** + String get hyphen; + + /// No description provided for @username. + /// + /// In pt, this message translates to: + /// **'@felipecastrosales'** + String get username; + + /// No description provided for @dot. + /// + /// In pt, this message translates to: + /// **'.'** + String get dot; + + /// No description provided for @flutterProjectOpenSource. + /// + /// In pt, this message translates to: + /// **'Um projeto Flutter OpenSource.'** + String get flutterProjectOpenSource; + + /// No description provided for @seeInGitHub. + /// + /// In pt, this message translates to: + /// **'Veja no GitHub'** + String get seeInGitHub; + + /// No description provided for @seeInFigma. + /// + /// In pt, this message translates to: + /// **'Veja no Figma'** + String get seeInFigma; + + /// No description provided for @deepCode. + /// + /// In pt, this message translates to: + /// **'Deep Code.'** + String get deepCode; + + /// No description provided for @createdByFelipeCastroSales. + /// + /// In pt, this message translates to: + /// **'Criado por @felipecastrosalesβ„’'** + String get createdByFelipeCastroSales; + + /// No description provided for @insertValidName. + /// + /// In pt, this message translates to: + /// **'Insira um nome vΓ‘lido.'** + String get insertValidName; + + /// No description provided for @insertABiggerName. + /// + /// In pt, this message translates to: + /// **'Insira um nome maior.'** + String get insertABiggerName; + + /// No description provided for @thisNameIsNotValid. + /// + /// In pt, this message translates to: + /// **'Este nome nΓ£o Γ© vΓ‘lido.'** + String get thisNameIsNotValid; + + /// No description provided for @insertValidEmail. + /// + /// In pt, this message translates to: + /// **'Insira um e-mail vΓ‘lido.'** + String get insertValidEmail; + + /// No description provided for @thisEmailIsNotValid. + /// + /// In pt, this message translates to: + /// **'Este e-mail nΓ£o Γ© vΓ‘lido.'** + String get thisEmailIsNotValid; + + /// No description provided for @insertValidMessage. + /// + /// In pt, this message translates to: + /// **'Insira uma mensagem vΓ‘lida.'** + String get insertValidMessage; + + /// No description provided for @insertABiggerMessage. + /// + /// In pt, this message translates to: + /// **'Insira uma mensagem maior.'** + String get insertABiggerMessage; + + /// No description provided for @thisMessageIsNotValid. + /// + /// In pt, this message translates to: + /// **'Esta mensagem nΓ£o Γ© vΓ‘lida.'** + String get thisMessageIsNotValid; + + /// No description provided for @insertValidSubject. + /// + /// In pt, this message translates to: + /// **'Insira um assunto vΓ‘lido.'** + String get insertValidSubject; + + /// No description provided for @insertABiggerSubject. + /// + /// In pt, this message translates to: + /// **'Insira um assunto maior.'** + String get insertABiggerSubject; + + /// No description provided for @thisSubjectIsNotValid. + /// + /// In pt, this message translates to: + /// **'Este assunto nΓ£o Γ© vΓ‘lido.'** + String get thisSubjectIsNotValid; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en', 'es', 'pt'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + case 'es': + return AppLocalizationsEs(); + case 'pt': + return AppLocalizationsPt(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); +} diff --git a/lib/app/core/l10n/localizations/app_localizations_en.dart b/lib/app/core/l10n/localizations/app_localizations_en.dart new file mode 100644 index 00000000..001fd694 --- /dev/null +++ b/lib/app/core/l10n/localizations/app_localizations_en.dart @@ -0,0 +1,185 @@ +import 'package:site/app/core/l10n/l10n.dart'; + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get projectTitle => 'Felipe Sales | Social Links'; + + @override + String get felipeSales => 'Felipe Sales'; + + @override + String get flutterDeveloperInstructor => 'Flutter Developer Instructor'; + + @override + String get hiIAmFelipeSales => 'Hi, I\'m Felipe Sales'; + + @override + String get developerFocused => + 'Developer focused on applying his knowledge in practice and building amazing things through lines of code.'; + + @override + String get appsDeveloper => '> Apps Developer'; + + @override + String get applicationsDeveloper => '> Applications Developer'; + + @override + String get home => 'Home'; + + @override + String get deepCodeWayCode => 'Deep Code. Way Code.'; + + @override + String get deep => 'Deep '; + + @override + String get code => 'Code. '; + + @override + String get way => 'Way '; + + @override + String get projects => 'Projects'; + + @override + String get seeProjectsUpper => 'SEE PROJECTS'; + + @override + String get projectAreInMyGitHub => + 'Most of the projects I have developed are completely open-source on my GitHub.'; + + @override + String get experience => 'Experience'; + + @override + String get workSuaMusica => 'Sua MΓΊsica | 2022 - Present'; + + @override + String get workRocketseat => 'Rocketseat | 2021 - 2022'; + + @override + String get workUdemy => 'Udemy | 2020 - 2021'; + + @override + String get workPersonalProjects => 'Personal Projects | Since 2020'; + + @override + String get flutterDeveloper => 'Flutter Developer'; + + @override + String get developerInstructor => 'Developer Instructor'; + + @override + String get teachingAssistent => 'Teaching Assistant'; + + @override + String get payngPriceEveryday => 'Paying the price every day'; + + @override + String get socialLinks => 'Social Links'; + + @override + String get gitHub => 'GitHub'; + + @override + String get linkedIn => 'LinkedIn'; + + @override + String get stackOverFlow => 'Stack Overflow'; + + @override + String get discord => 'Discord'; + + @override + String get udemy => 'Udemy'; + + @override + String get instagram => 'Instagram'; + + @override + String get followMeOnMySocialNetworks => 'Follow me on my social networks'; + + @override + String get contact => 'Contact'; + + @override + String get name => 'Name'; + + @override + String get email => 'E-mail'; + + @override + String get title => 'Title'; + + @override + String get text => 'Text'; + + @override + String get sendEmailUpper => 'SEND EMAIL'; + + @override + String get emailSendedWithSuccess => 'Email sent successfully!'; + + @override + String get letsChatCallMe => 'Let\'s chat, call me:'; + + @override + String get hyphen => ' - '; + + @override + String get username => '@felipecastrosales'; + + @override + String get dot => '.'; + + @override + String get flutterProjectOpenSource => 'An open-source Flutter project.'; + + @override + String get seeInGitHub => 'See on GitHub'; + + @override + String get seeInFigma => 'See on Figma'; + + @override + String get deepCode => 'Deep Code.'; + + @override + String get createdByFelipeCastroSales => 'Created by @felipecastrosalesβ„’'; + + @override + String get insertValidName => 'Please enter a valid name.'; + + @override + String get insertABiggerName => 'Please enter a longer name.'; + + @override + String get thisNameIsNotValid => 'This name is not valid.'; + + @override + String get insertValidEmail => 'Please enter a valid email address.'; + + @override + String get thisEmailIsNotValid => 'This email address is not valid.'; + + @override + String get insertValidMessage => 'Please enter a valid message.'; + + @override + String get insertABiggerMessage => 'Please enter a longer message.'; + + @override + String get thisMessageIsNotValid => 'This message is not valid.'; + + @override + String get insertValidSubject => 'Please enter a valid subject.'; + + @override + String get insertABiggerSubject => 'Please enter a longer subject.'; + + @override + String get thisSubjectIsNotValid => 'This subject is not valid.'; +} diff --git a/lib/app/core/l10n/localizations/app_localizations_es.dart b/lib/app/core/l10n/localizations/app_localizations_es.dart new file mode 100644 index 00000000..8345d538 --- /dev/null +++ b/lib/app/core/l10n/localizations/app_localizations_es.dart @@ -0,0 +1,187 @@ +import 'package:site/app/core/l10n/l10n.dart'; + +/// The translations for Spanish Castilian (`es`). +class AppLocalizationsEs extends AppLocalizations { + AppLocalizationsEs([String locale = 'es']) : super(locale); + + @override + String get projectTitle => 'Felipe Sales | Enlaces sociales'; + + @override + String get felipeSales => 'Felipe Sales'; + + @override + String get flutterDeveloperInstructor => + 'Instructor de Desarrollador Flutter'; + + @override + String get hiIAmFelipeSales => 'Hola, soy Felipe Sales'; + + @override + String get developerFocused => + 'Desarrollador enfocado en aplicar sus conocimientos en la prΓ‘ctica y construir cosas increΓ­bles a travΓ©s de lΓ­neas de cΓ³digo.'; + + @override + String get appsDeveloper => '> Desarrollador de aplicaciones'; + + @override + String get applicationsDeveloper => '> Desarrollador de aplicaciones'; + + @override + String get home => 'Inicio'; + + @override + String get deepCodeWayCode => 'Deep Code. Way Code.'; + + @override + String get deep => 'Deep '; + + @override + String get code => 'Code. '; + + @override + String get way => 'Way '; + + @override + String get projects => 'Proyectos'; + + @override + String get seeProjectsUpper => 'VER PROYECTOS'; + + @override + String get projectAreInMyGitHub => + 'La mayorΓ­a de los proyectos que he desarrollado son completamente de cΓ³digo abierto en mi GitHub.'; + + @override + String get experience => 'Experiencia'; + + @override + String get workSuaMusica => 'Sua MΓΊsica | 2022 - Actualidad'; + + @override + String get workRocketseat => 'Rocketseat | 2021 - 2022'; + + @override + String get workUdemy => 'Udemy | 2020 - 2021'; + + @override + String get workPersonalProjects => 'Proyectos Personales | Desde 2020'; + + @override + String get flutterDeveloper => 'Desarrollador Flutter'; + + @override + String get developerInstructor => 'Instructor de Desarrollo'; + + @override + String get teachingAssistent => 'Asistente de EnseΓ±anza'; + + @override + String get payngPriceEveryday => 'Pagando el precio todos los dΓ­as'; + + @override + String get socialLinks => 'Enlaces Sociales'; + + @override + String get gitHub => 'GitHub'; + + @override + String get linkedIn => 'LinkedIn'; + + @override + String get stackOverFlow => 'Stack Overflow'; + + @override + String get discord => 'Discord'; + + @override + String get udemy => 'Udemy'; + + @override + String get instagram => 'Instagram'; + + @override + String get followMeOnMySocialNetworks => 'SΓ­gueme en mis redes sociales'; + + @override + String get contact => 'Contacto'; + + @override + String get name => 'Nombre'; + + @override + String get email => 'Email'; + + @override + String get title => 'TΓ­tulo'; + + @override + String get text => 'Texto'; + + @override + String get sendEmailUpper => 'ENVIAR EMAIL'; + + @override + String get emailSendedWithSuccess => 'Β‘Email enviado exitosamente!'; + + @override + String get letsChatCallMe => 'Hablemos, llΓ‘mame:'; + + @override + String get hyphen => ' - '; + + @override + String get username => '@felipecastrosales'; + + @override + String get dot => '.'; + + @override + String get flutterProjectOpenSource => + 'Un proyecto Flutter de cΓ³digo abierto.'; + + @override + String get seeInGitHub => 'Ver en GitHub'; + + @override + String get seeInFigma => 'Ver en Figma'; + + @override + String get deepCode => 'Deep Code.'; + + @override + String get createdByFelipeCastroSales => 'Creado por @felipecastrosalesβ„’'; + + @override + String get insertValidName => 'Ingrese un nombre vΓ‘lido.'; + + @override + String get insertABiggerName => 'Ingrese un nombre mΓ‘s largo.'; + + @override + String get thisNameIsNotValid => 'Este nombre no es vΓ‘lido.'; + + @override + String get insertValidEmail => 'Ingrese un email vΓ‘lido.'; + + @override + String get thisEmailIsNotValid => 'Este email no es vΓ‘lido.'; + + @override + String get insertValidMessage => 'Ingrese un mensaje vΓ‘lido.'; + + @override + String get insertABiggerMessage => 'Ingrese un mensaje mΓ‘s largo.'; + + @override + String get thisMessageIsNotValid => 'Este mensaje no es vΓ‘lido.'; + + @override + String get insertValidSubject => 'Ingrese un asunto vΓ‘lido.'; + + @override + String get insertABiggerSubject => 'Ingrese un asunto mΓ‘s largo.'; + + @override + String get thisSubjectIsNotValid => 'Este asunto no es vΓ‘lido.'; +} diff --git a/lib/app/core/l10n/localizations/app_localizations_pt.dart b/lib/app/core/l10n/localizations/app_localizations_pt.dart new file mode 100644 index 00000000..dc316a7c --- /dev/null +++ b/lib/app/core/l10n/localizations/app_localizations_pt.dart @@ -0,0 +1,185 @@ +import 'package:site/app/core/l10n/l10n.dart'; + +/// The translations for Portuguese (`pt`). +class AppLocalizationsPt extends AppLocalizations { + AppLocalizationsPt([String locale = 'pt']) : super(locale); + + @override + String get projectTitle => 'Felipe Sales | Social Links'; + + @override + String get felipeSales => 'Felipe Sales'; + + @override + String get flutterDeveloperInstructor => 'Flutter Developer Instructor'; + + @override + String get hiIAmFelipeSales => 'OlΓ‘, sou Felipe Sales'; + + @override + String get developerFocused => + 'Desenvolvedor focado em aplicar seus conhecimentos em prΓ‘tica e construir coisas incrΓ­veis atravΓ©s de linhas de cΓ³digo.'; + + @override + String get appsDeveloper => '> Desenvolvedor de Apps'; + + @override + String get applicationsDeveloper => '> Desenvolvedor de Aplicativos'; + + @override + String get home => 'Home'; + + @override + String get deepCodeWayCode => 'Deep Code. Way Code.'; + + @override + String get deep => 'Deep '; + + @override + String get code => 'Code. '; + + @override + String get way => 'Way '; + + @override + String get projects => 'Projetos'; + + @override + String get seeProjectsUpper => 'VER PROJETOS'; + + @override + String get projectAreInMyGitHub => + 'Grande parte dos projetos que jΓ‘ desenvolvi estΓ£o no meu GitHub de forma completamente open-source.'; + + @override + String get experience => 'ExperiΓͺncia'; + + @override + String get workSuaMusica => 'Sua MΓΊsica | 2022 - Atualmente'; + + @override + String get workRocketseat => 'Rocketseat | 2021 - 2022'; + + @override + String get workUdemy => 'Udemy | 2020 - 2021'; + + @override + String get workPersonalProjects => 'Projetos Pessoais | Desde 2020'; + + @override + String get flutterDeveloper => 'Desenvolvedor Flutter'; + + @override + String get developerInstructor => 'Desenvolvedor Instrutor'; + + @override + String get teachingAssistent => 'Auxiliar de Ensino'; + + @override + String get payngPriceEveryday => 'Pagando o preΓ§o todos os dias'; + + @override + String get socialLinks => 'Social Links'; + + @override + String get gitHub => 'GitHub'; + + @override + String get linkedIn => 'LinkedIn'; + + @override + String get stackOverFlow => 'Stack OverFlow'; + + @override + String get discord => 'Discord'; + + @override + String get udemy => 'Udemy'; + + @override + String get instagram => 'Instagram'; + + @override + String get followMeOnMySocialNetworks => 'Me acompanhe nas minhas redes'; + + @override + String get contact => 'Contato'; + + @override + String get name => 'Nome'; + + @override + String get email => 'E-mail'; + + @override + String get title => 'TΓ­tulo'; + + @override + String get text => 'Texto'; + + @override + String get sendEmailUpper => 'ENVIAR E-MAIL'; + + @override + String get emailSendedWithSuccess => 'E-mail enviado com sucesso!'; + + @override + String get letsChatCallMe => 'Vamos bater um papo, me chame:'; + + @override + String get hyphen => ' - '; + + @override + String get username => '@felipecastrosales'; + + @override + String get dot => '.'; + + @override + String get flutterProjectOpenSource => 'Um projeto Flutter OpenSource.'; + + @override + String get seeInGitHub => 'Veja no GitHub'; + + @override + String get seeInFigma => 'Veja no Figma'; + + @override + String get deepCode => 'Deep Code.'; + + @override + String get createdByFelipeCastroSales => 'Criado por @felipecastrosalesβ„’'; + + @override + String get insertValidName => 'Insira um nome vΓ‘lido.'; + + @override + String get insertABiggerName => 'Insira um nome maior.'; + + @override + String get thisNameIsNotValid => 'Este nome nΓ£o Γ© vΓ‘lido.'; + + @override + String get insertValidEmail => 'Insira um e-mail vΓ‘lido.'; + + @override + String get thisEmailIsNotValid => 'Este e-mail nΓ£o Γ© vΓ‘lido.'; + + @override + String get insertValidMessage => 'Insira uma mensagem vΓ‘lida.'; + + @override + String get insertABiggerMessage => 'Insira uma mensagem maior.'; + + @override + String get thisMessageIsNotValid => 'Esta mensagem nΓ£o Γ© vΓ‘lida.'; + + @override + String get insertValidSubject => 'Insira um assunto vΓ‘lido.'; + + @override + String get insertABiggerSubject => 'Insira um assunto maior.'; + + @override + String get thisSubjectIsNotValid => 'Este assunto nΓ£o Γ© vΓ‘lido.'; +} diff --git a/lib/app/core/l10n/localizations/app_texts.dart b/lib/app/core/l10n/localizations/app_texts.dart new file mode 100644 index 00000000..1c71d1d5 --- /dev/null +++ b/lib/app/core/l10n/localizations/app_texts.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; + +class AppTexts { + static AppLocalizations get(BuildContext context) { + return Localizations.of(context, AppLocalizations)!; + } +} diff --git a/lib/app/core/l10n/localizations/localizations.dart b/lib/app/core/l10n/localizations/localizations.dart new file mode 100644 index 00000000..c6038373 --- /dev/null +++ b/lib/app/core/l10n/localizations/localizations.dart @@ -0,0 +1,5 @@ +export 'app_localizations_en.dart'; +export 'app_localizations_es.dart'; +export 'app_localizations_pt.dart'; +export 'app_localizations.dart'; +export 'app_texts.dart'; diff --git a/lib/app/core/l10n/templates/app_en.arb b/lib/app/core/l10n/templates/app_en.arb new file mode 100644 index 00000000..808aef1e --- /dev/null +++ b/lib/app/core/l10n/templates/app_en.arb @@ -0,0 +1,61 @@ +{ + "projectTitle": "Felipe Sales | Social Links", + "felipeSales": "Felipe Sales", + "flutterDeveloperInstructor": "Flutter Developer Instructor", + "hiIAmFelipeSales": "Hi, I'm Felipe Sales", + "developerFocused": "Developer focused on applying his knowledge in practice and building amazing things through lines of code.", + "appsDeveloper": "> Apps Developer", + "applicationsDeveloper": "> Applications Developer", + "home": "Home", + "deepCodeWayCode": "Deep Code. Way Code.", + "deep": "Deep ", + "code": "Code. ", + "way": "Way ", + "projects": "Projects", + "seeProjectsUpper": "SEE PROJECTS", + "projectAreInMyGitHub": "Most of the projects I have developed are completely open-source on my GitHub.", + "experience": "Experience", + "workSuaMusica": "Sua MΓΊsica | 2022 - Present", + "workRocketseat": "Rocketseat | 2021 - 2022", + "workUdemy": "Udemy | 2020 - 2021", + "workPersonalProjects": "Personal Projects | Since 2020", + "flutterDeveloper": "Flutter Developer", + "developerInstructor": "Developer Instructor", + "teachingAssistent": "Teaching Assistant", + "payngPriceEveryday": "Paying the price every day", + "socialLinks": "Social Links", + "gitHub": "GitHub", + "linkedIn": "LinkedIn", + "stackOverFlow": "Stack Overflow", + "discord": "Discord", + "udemy": "Udemy", + "instagram": "Instagram", + "followMeOnMySocialNetworks": "Follow me on my social networks", + "contact": "Contact", + "name": "Name", + "email": "E-mail", + "title": "Title", + "text": "Text", + "sendEmailUpper": "SEND EMAIL", + "emailSendedWithSuccess": "Email sent successfully!", + "letsChatCallMe": "Let's chat, call me:", + "hyphen": " - ", + "username": "@felipecastrosales", + "dot": ".", + "flutterProjectOpenSource": "An open-source Flutter project.", + "seeInGitHub": "See on GitHub", + "seeInFigma": "See on Figma", + "deepCode": "Deep Code.", + "createdByFelipeCastroSales": "Created by @felipecastrosalesβ„’", + "insertValidName": "Please enter a valid name.", + "insertABiggerName": "Please enter a longer name.", + "thisNameIsNotValid": "This name is not valid.", + "insertValidEmail": "Please enter a valid email address.", + "thisEmailIsNotValid": "This email address is not valid.", + "insertValidMessage": "Please enter a valid message.", + "insertABiggerMessage": "Please enter a longer message.", + "thisMessageIsNotValid": "This message is not valid.", + "insertValidSubject": "Please enter a valid subject.", + "insertABiggerSubject": "Please enter a longer subject.", + "thisSubjectIsNotValid": "This subject is not valid." +} \ No newline at end of file diff --git a/lib/app/core/l10n/templates/app_es.arb b/lib/app/core/l10n/templates/app_es.arb new file mode 100644 index 00000000..eb8a8468 --- /dev/null +++ b/lib/app/core/l10n/templates/app_es.arb @@ -0,0 +1,61 @@ +{ + "projectTitle": "Felipe Sales | Enlaces sociales", + "felipeSales": "Felipe Sales", + "flutterDeveloperInstructor": "Instructor de Desarrollador Flutter", + "hiIAmFelipeSales": "Hola, soy Felipe Sales", + "developerFocused": "Desarrollador enfocado en aplicar sus conocimientos en la prΓ‘ctica y construir cosas increΓ­bles a travΓ©s de lΓ­neas de cΓ³digo.", + "appsDeveloper": "> Desarrollador de aplicaciones", + "applicationsDeveloper": "> Desarrollador de aplicaciones", + "home": "Inicio", + "deepCodeWayCode": "Deep Code. Way Code.", + "deep": "Deep ", + "code": "Code. ", + "way": "Way ", + "projects": "Proyectos", + "seeProjectsUpper": "VER PROYECTOS", + "projectAreInMyGitHub": "La mayorΓ­a de los proyectos que he desarrollado son completamente de cΓ³digo abierto en mi GitHub.", + "experience": "Experiencia", + "workSuaMusica": "Sua MΓΊsica | 2022 - Actualidad", + "workRocketseat": "Rocketseat | 2021 - 2022", + "workUdemy": "Udemy | 2020 - 2021", + "workPersonalProjects": "Proyectos Personales | Desde 2020", + "flutterDeveloper": "Desarrollador Flutter", + "developerInstructor": "Instructor de Desarrollo", + "teachingAssistent": "Asistente de EnseΓ±anza", + "payngPriceEveryday": "Pagando el precio todos los dΓ­as", + "socialLinks": "Enlaces Sociales", + "gitHub": "GitHub", + "linkedIn": "LinkedIn", + "stackOverFlow": "Stack Overflow", + "discord": "Discord", + "udemy": "Udemy", + "instagram": "Instagram", + "followMeOnMySocialNetworks": "SΓ­gueme en mis redes sociales", + "contact": "Contacto", + "name": "Nombre", + "email": "Email", + "title": "TΓ­tulo", + "text": "Texto", + "sendEmailUpper": "ENVIAR EMAIL", + "emailSendedWithSuccess": "Β‘Email enviado exitosamente!", + "letsChatCallMe": "Hablemos, llΓ‘mame:", + "hyphen": " - ", + "username": "@felipecastrosales", + "dot": ".", + "flutterProjectOpenSource": "Un proyecto Flutter de cΓ³digo abierto.", + "seeInGitHub": "Ver en GitHub", + "seeInFigma": "Ver en Figma", + "deepCode": "Deep Code.", + "createdByFelipeCastroSales": "Creado por @felipecastrosalesβ„’", + "insertValidName": "Ingrese un nombre vΓ‘lido.", + "insertABiggerName": "Ingrese un nombre mΓ‘s largo.", + "thisNameIsNotValid": "Este nombre no es vΓ‘lido.", + "insertValidEmail": "Ingrese un email vΓ‘lido.", + "thisEmailIsNotValid": "Este email no es vΓ‘lido.", + "insertValidMessage": "Ingrese un mensaje vΓ‘lido.", + "insertABiggerMessage": "Ingrese un mensaje mΓ‘s largo.", + "thisMessageIsNotValid": "Este mensaje no es vΓ‘lido.", + "insertValidSubject": "Ingrese un asunto vΓ‘lido.", + "insertABiggerSubject": "Ingrese un asunto mΓ‘s largo.", + "thisSubjectIsNotValid": "Este asunto no es vΓ‘lido." +} \ No newline at end of file diff --git a/lib/app/core/l10n/templates/app_pt.arb b/lib/app/core/l10n/templates/app_pt.arb new file mode 100644 index 00000000..dd3e523a --- /dev/null +++ b/lib/app/core/l10n/templates/app_pt.arb @@ -0,0 +1,61 @@ +{ + "projectTitle": "Felipe Sales | Social Links", + "felipeSales": "Felipe Sales", + "flutterDeveloperInstructor": "Flutter Developer Instructor", + "hiIAmFelipeSales": "OlΓ‘, sou Felipe Sales", + "developerFocused": "Desenvolvedor focado em aplicar seus conhecimentos em prΓ‘tica e construir coisas incrΓ­veis atravΓ©s de linhas de cΓ³digo.", + "appsDeveloper": "> Desenvolvedor de Apps", + "applicationsDeveloper": "> Desenvolvedor de Aplicativos", + "home": "Home", + "deepCodeWayCode": "Deep Code. Way Code.", + "deep": "Deep ", + "code": "Code. ", + "way": "Way ", + "projects": "Projetos", + "seeProjectsUpper": "VER PROJETOS", + "projectAreInMyGitHub": "Grande parte dos projetos que jΓ‘ desenvolvi estΓ£o no meu GitHub de forma completamente open-source.", + "experience": "ExperiΓͺncia", + "workSuaMusica": "Sua MΓΊsica | 2022 - Atualmente", + "workRocketseat": "Rocketseat | 2021 - 2022", + "workUdemy": "Udemy | 2020 - 2021", + "workPersonalProjects": "Projetos Pessoais | Desde 2020", + "flutterDeveloper": "Desenvolvedor Flutter", + "developerInstructor": "Desenvolvedor Instrutor", + "teachingAssistent": "Auxiliar de Ensino", + "payngPriceEveryday": "Pagando o preΓ§o todos os dias", + "socialLinks": "Social Links", + "gitHub": "GitHub", + "linkedIn": "LinkedIn", + "stackOverFlow": "Stack OverFlow", + "discord": "Discord", + "udemy": "Udemy", + "instagram": "Instagram", + "followMeOnMySocialNetworks": "Me acompanhe nas minhas redes", + "contact": "Contato", + "name": "Nome", + "email": "E-mail", + "title": "TΓ­tulo", + "text": "Texto", + "sendEmailUpper": "ENVIAR E-MAIL", + "emailSendedWithSuccess": "E-mail enviado com sucesso!", + "letsChatCallMe": "Vamos bater um papo, me chame:", + "hyphen": " - ", + "username": "@felipecastrosales", + "dot": ".", + "flutterProjectOpenSource": "Um projeto Flutter OpenSource.", + "seeInGitHub": "Veja no GitHub", + "seeInFigma": "Veja no Figma", + "deepCode": "Deep Code.", + "createdByFelipeCastroSales": "Criado por @felipecastrosalesβ„’", + "insertValidName": "Insira um nome vΓ‘lido.", + "insertABiggerName": "Insira um nome maior.", + "thisNameIsNotValid": "Este nome nΓ£o Γ© vΓ‘lido.", + "insertValidEmail": "Insira um e-mail vΓ‘lido.", + "thisEmailIsNotValid": "Este e-mail nΓ£o Γ© vΓ‘lido.", + "insertValidMessage": "Insira uma mensagem vΓ‘lida.", + "insertABiggerMessage": "Insira uma mensagem maior.", + "thisMessageIsNotValid": "Esta mensagem nΓ£o Γ© vΓ‘lida.", + "insertValidSubject": "Insira um assunto vΓ‘lido.", + "insertABiggerSubject": "Insira um assunto maior.", + "thisSubjectIsNotValid": "Este assunto nΓ£o Γ© vΓ‘lido." +} \ No newline at end of file diff --git a/lib/app/core/mixins/mixins.dart b/lib/app/core/mixins/mixins.dart new file mode 100644 index 00000000..2248fc3f --- /dev/null +++ b/lib/app/core/mixins/mixins.dart @@ -0,0 +1,2 @@ +export 'responsive_position_mixin.dart'; +export 'scroll_to_mixin.dart'; diff --git a/lib/app/core/mixins/responsive_position_mixin.dart b/lib/app/core/mixins/responsive_position_mixin.dart new file mode 100644 index 00000000..b31f1f9d --- /dev/null +++ b/lib/app/core/mixins/responsive_position_mixin.dart @@ -0,0 +1,29 @@ +import 'package:site/app/core/responsive/responsive.dart'; + +mixin ResponsivePositionMixin { + double gradientPresentationWidthAlignment(double constraints) { + if (constraints > Breakpoints.kUHD4K) { + return 0.2; + } else if (constraints > Breakpoints.kFullHD) { + return 0.25; + } else { + return 0.32; + } + } + + double gradientExperienceAndProjectsSectionWidthAlignment( + double constraints, + ) { + if (constraints > Breakpoints.kUHD4K) { + return -0.2; + } else if (constraints > Breakpoints.kLessThanKUHD4K) { + return -0.3; + } else if (constraints > Breakpoints.kWXGA) { + return -0.35; + } else if (constraints > Breakpoints.experience) { + return -0.4; + } else { + return constraints * 0.0003; + } + } +} diff --git a/lib/app/core/mixins/scroll_to_mixin.dart b/lib/app/core/mixins/scroll_to_mixin.dart new file mode 100644 index 00000000..d59587cb --- /dev/null +++ b/lib/app/core/mixins/scroll_to_mixin.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +mixin ScrollToMixin { + void scrollTo(int index, ItemScrollController itemScrollController) { + itemScrollController.scrollTo( + index: index, + alignment: 0, + curve: Curves.easeIn, + duration: const Duration(milliseconds: 1500), + ); + } +} diff --git a/lib/app/core/responsive/breakpoints.dart b/lib/app/core/responsive/breakpoints.dart new file mode 100644 index 00000000..aaec7451 --- /dev/null +++ b/lib/app/core/responsive/breakpoints.dart @@ -0,0 +1,16 @@ +class Breakpoints { + static const appBar = 700; + static const presentation = 660; + static const presentationMobileSubtitle = 420; + static const projects = 650; + static const experience = 600; + static const social = 550; + static const contact = 650; + static const footer = 640; + + /// Generic breakpoints + static const kUHD4K = 2100; + static const kLessThanKUHD4K = 2099; + static const kFullHD = 1920; + static const kWXGA = 1300; +} diff --git a/lib/app/core/responsive/responsive.dart b/lib/app/core/responsive/responsive.dart new file mode 100644 index 00000000..fd39e9e8 --- /dev/null +++ b/lib/app/core/responsive/responsive.dart @@ -0,0 +1 @@ +export 'breakpoints.dart'; diff --git a/lib/app/core/shared/app_assets.dart b/lib/app/core/shared/app_assets.dart new file mode 100644 index 00000000..041a65cd --- /dev/null +++ b/lib/app/core/shared/app_assets.dart @@ -0,0 +1,43 @@ +class AppAssets { + static const developer = 'assets/developer.png'; + static const profile = 'assets/profile.png'; + static const mouse = 'assets/components/mouse-down.json'; + + /// Presentation + static const presentationMobile = + 'assets/components/presentation/presentation-mobile.png'; + static const presentationWeb = + 'assets/components/presentation/presentation-web.png'; + static const presentationTextureBackground = + 'assets/components/presentation/texture-background.png'; + static const presentationTextureLarge = + 'assets/components/presentation/texture-large.png'; + + /// Projects + static const mockup = 'assets/components/projects/mockup.png'; + static const abstractFit = 'assets/components/projects/mobile-abstract.png'; + static const abstractLarge = 'assets/components/projects/large-abstract.png'; + + /// Experience + static const champ = 'assets/components/experience/champ.png'; + static const abstractRight = + 'assets/components/experience/abstract-right.png'; + + /// Social + static String socialLogo(String image) => 'assets/components/social/$image'; + static const socialAbstract = 'assets/components/social/abstract.png'; + static const socialAbstractLarge = + 'assets/components/social/abstract-large.png'; + static const gitHub = 'github.svg'; + static const linkedIn = 'linkedin.svg'; + static const stackOverFlow = 'stack-overflow.svg'; + static const discord = 'discord.svg'; + static const udemy = 'udemy.svg'; + static const instagram = 'instagram.svg'; + + /// Contact + static const contactVerticalTexture = + 'assets/components/contact/vertical-texture.png'; + static const contactHorizontalTexture = + 'assets/components/contact/horizontal-texture.png'; +} diff --git a/lib/app/core/shared/app_datas.dart b/lib/app/core/shared/app_datas.dart new file mode 100644 index 00000000..9b0d848e --- /dev/null +++ b/lib/app/core/shared/app_datas.dart @@ -0,0 +1,5 @@ +class AppDatas { + /// Fonts + static const montserrat = 'Montserrat'; + static const poppins = 'Poppins'; +} diff --git a/lib/app/core/shared/app_keys.dart b/lib/app/core/shared/app_keys.dart new file mode 100644 index 00000000..8e8b388d --- /dev/null +++ b/lib/app/core/shared/app_keys.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +/// This class is to centralize the keys used in the application. +/// To avoid the repetition of the same and to group in a unique way. + +class AppKeys { + static const presentation = Key('presentation'); + static const projects = Key('projects'); + static const experience = Key('experience'); + static const social = Key('social'); + static const contact = Key('contact'); + static const customFooter = Key('custom_footer'); + + /// Social + static const socialItemGitHub = Key('social_list_git_hub'); + static const socialItemLinkedIn = Key('social_list_linked_in'); + static const socialItemStackOverFlow = Key('social_list_stack_over_flow'); + static const socialItemDiscord = Key('social_list_discord'); + static const socialItemUdemy = Key('social_list_udemy'); + static const socialItemInstagram = Key('social_list_instagram'); + + /// Testing keys + static const snackBarKey = Key('tap_snack_bar'); +} diff --git a/lib/app/core/shared/app_urls.dart b/lib/app/core/shared/app_urls.dart new file mode 100644 index 00000000..132913b0 --- /dev/null +++ b/lib/app/core/shared/app_urls.dart @@ -0,0 +1,14 @@ +class AppUrls { + static const gitHub = 'https://github.com/felipecastrosales'; + static const linkedIn = 'https://linkedin.com/in/felipecastrosales'; + static const stackOverFlow = + 'https://stackoverflow.com/users/13096514/felipe-sales'; + static const discord = 'https://discordapp.com/users/406074089011281921'; + static const udemy = 'https://udemy.com/user/luis-felipe-de-castro-sales/'; + static const instagram = 'https://instagram.com/felipecastrosales'; + + /// Project + static const gitHubProject = 'https://github.com/felipecastrosales/site'; + static const figmaProject = + 'https://www.figma.com/file/Dgq4C5dLgtWK2sb0KebmEZ/%40felipecastrosales---Portfolio?type=design&node-id=0%3A1&mode=design&t=RTugDZN5S2knn3Nk-1'; +} diff --git a/lib/app/core/shared/shared.dart b/lib/app/core/shared/shared.dart new file mode 100644 index 00000000..07d8ef82 --- /dev/null +++ b/lib/app/core/shared/shared.dart @@ -0,0 +1,4 @@ +export 'app_assets.dart'; +export 'app_datas.dart'; +export 'app_keys.dart'; +export 'app_urls.dart'; diff --git a/lib/app/core/themes/app_theme.dart b/lib/app/core/themes/app_theme.dart new file mode 100644 index 00000000..6ea3dce4 --- /dev/null +++ b/lib/app/core/themes/app_theme.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/core/tokens/tokens.dart'; + +class AppTheme { + static ThemeData get theme => ThemeData( + useMaterial3: true, + scaffoldBackgroundColor: AppColors.background, + textSelectionTheme: const TextSelectionThemeData( + cursorColor: AppColors.primary, + ), + colorScheme: ThemeData().colorScheme.copyWith( + primary: AppColors.primary, + secondary: AppColors.primary, + ), + appBarTheme: const AppBarTheme( + backgroundColor: AppColors.black, + elevation: 0, + iconTheme: IconThemeData(color: AppColors.primary), + ), + fontFamily: AppDatas.montserrat, + ); +} diff --git a/lib/app/core/tokens/app_borders.dart b/lib/app/core/tokens/app_borders.dart new file mode 100644 index 00000000..a3fbabf8 --- /dev/null +++ b/lib/app/core/tokens/app_borders.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +class AppBorders { + static OutlineInputBorder primaryBorder(Color color) => OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: color, width: 1.5), + ); +} diff --git a/lib/app/core/tokens/app_colors.dart b/lib/app/core/tokens/app_colors.dart new file mode 100644 index 00000000..b268bf70 --- /dev/null +++ b/lib/app/core/tokens/app_colors.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class AppColors { + static const primary = Color(0XFF4A3AFF); + static const primaryDark = Color(0XFF0E0B33); + static const primaryLight = Color(0XFF4361EE); + static const red = Color(0xffef233c); + static const orange = Color(0xfffa642f); + static const mayaBlue = Color(0XFF53D7FF); + static const shadow = Color(0xff323232); + static const background = Color(0XFF070519); + static const shadowLightSocial = Color(0xffc4c4c4); + static const shadowDarkContact = Color(0xff121212); + static const light = Color(0xffEBE9E9); + static const white = Color(0xffffffff); + static const black = Color(0xff000000); + static const blackOpacity = Color(0xff121212); + static const transparent = Colors.transparent; +} diff --git a/lib/app/core/tokens/app_gradients.dart b/lib/app/core/tokens/app_gradients.dart new file mode 100644 index 00000000..bed3b0da --- /dev/null +++ b/lib/app/core/tokens/app_gradients.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class AppGradients { + static const stories = LinearGradient( + colors: AppListColors.stories, + begin: Alignment.bottomLeft, + end: Alignment.topRight, + stops: [0.0, 0.5, 0.9], + ); + + static const divider = LinearGradient( + colors: AppListColors.divider, + ); + + static const button = LinearGradient( + colors: AppListColors.button, + begin: Alignment.centerLeft, + end: Alignment.centerRight, + ); + + static final glassmorphic = LinearGradient( + colors: AppListColors.glassmorphic, + begin: Alignment.centerLeft, + end: Alignment.centerRight, + ); + + static RadialGradient gradientWidget({ + required AlignmentGeometry alignment, + required double radius, + }) { + return RadialGradient( + colors: AppListColors.gradientWidget, + center: alignment, + radius: radius, + ); + } +} diff --git a/lib/app/core/tokens/app_list_colors.dart b/lib/app/core/tokens/app_list_colors.dart new file mode 100644 index 00000000..be6bc5af --- /dev/null +++ b/lib/app/core/tokens/app_list_colors.dart @@ -0,0 +1,40 @@ +import 'package:site/app/core/tokens/tokens.dart'; + +class AppListColors { + static const linear = [ + AppColors.primaryDark, + AppColors.primary, + ]; + + static const stories = [ + AppColors.primary, + AppColors.primaryDark, + AppColors.shadow, + ]; + + static const projectName = [ + AppColors.primary, + AppColors.primaryDark, + ]; + + static const button = [ + AppColors.primary, + AppColors.mayaBlue, + ]; + + static const gradientWidget = [ + AppColors.primary, + AppColors.transparent, + ]; + + static const divider = [ + AppColors.primary, + AppColors.primaryLight, + ]; + + static final glassmorphic = [ + AppColors.shadowLightSocial.withOpacity(.05), + AppColors.shadowLightSocial.withOpacity(.15), + AppColors.shadowLightSocial.withOpacity(.3), + ]; +} diff --git a/lib/app/core/tokens/app_text_styles.dart b/lib/app/core/tokens/app_text_styles.dart new file mode 100644 index 00000000..aa237d17 --- /dev/null +++ b/lib/app/core/tokens/app_text_styles.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:site/app/core/shared/shared.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class AppTextStyles { + static Text appBar(String text) => Text( + text, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: AppColors.white, + ), + ); + + static const presentationTitle = TextStyle( + fontSize: 32, + color: AppColors.primary, + fontWeight: FontWeight.w700, + ); + + static const presentationTitleWeb = TextStyle( + fontSize: 50, + color: AppColors.primary, + fontWeight: FontWeight.w700, + ); + + static const presentationSubtitle = TextStyle( + fontSize: 24, + color: AppColors.white, + fontWeight: FontWeight.w600, + ); + + static const presentationText = TextStyle( + fontSize: 20, + color: AppColors.white, + fontWeight: FontWeight.w600, + ); + + static final phrase = TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + foreground: Paint() + ..style = PaintingStyle.stroke + ..color = AppColors.black + ..strokeWidth = 2, + ); + + static const phraseWhite = TextStyle( + fontSize: 20, + color: AppColors.white, + fontWeight: FontWeight.w600, + ); + + static const phrasePrimary = TextStyle( + fontSize: 20, + color: AppColors.primary, + fontWeight: FontWeight.w600, + ); + + static const hint = TextStyle( + fontSize: 16, + color: AppColors.black, + fontWeight: FontWeight.w500, + ); + + static const textForm = TextStyle( + fontSize: 14, + color: AppColors.background, + fontWeight: FontWeight.w500, + ); + + static const errorForm = TextStyle( + fontSize: 14, + color: AppColors.white, + fontWeight: FontWeight.w300, + ); + + static const buttonTitle = TextStyle( + fontSize: 16, + color: AppColors.white, + fontWeight: FontWeight.w600, + ); + + static const socialTitle = TextStyle( + fontSize: 18, + color: AppColors.light, + fontFamily: AppDatas.poppins, + ); + + static const name = TextStyle( + color: AppColors.light, + fontSize: 24, + ); + + static const office = TextStyle( + color: AppColors.light, + fontSize: 18, + ); + + static const footer = TextStyle( + color: AppColors.light, + fontSize: 12, + ); + + static const footerLink = TextStyle( + color: AppColors.light, + fontSize: 20, + ); + + static const experienceTitle = TextStyle( + color: AppColors.light, + fontSize: 20, + fontWeight: FontWeight.w600, + ); + + static const experienceDescription = TextStyle( + color: AppColors.light, + fontSize: 18, + fontWeight: FontWeight.w600, + ); +} diff --git a/lib/app/core/tokens/tokens.dart b/lib/app/core/tokens/tokens.dart new file mode 100644 index 00000000..6f425d02 --- /dev/null +++ b/lib/app/core/tokens/tokens.dart @@ -0,0 +1,5 @@ +export 'app_borders.dart'; +export 'app_colors.dart'; +export 'app_gradients.dart'; +export 'app_list_colors.dart'; +export 'app_text_styles.dart'; diff --git a/lib/app/features/home/home_page.dart b/lib/app/features/home/home_page.dart new file mode 100644 index 00000000..6cbc51c2 --- /dev/null +++ b/lib/app/features/home/home_page.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; + +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:http/http.dart' as http; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/injections/injections.dart'; +import 'package:site/app/core/responsive/responsive.dart'; +import 'package:site/app/features/home/widgets/contact/contact_widget.dart'; +import 'package:site/app/features/home/widgets/contact/controller/contact_controller.dart'; +import 'package:site/app/features/home/widgets/experience/experience.dart'; +import 'package:site/app/features/home/widgets/footer/footer.dart'; +import 'package:site/app/features/home/widgets/presentation/presentation.dart'; +import 'package:site/app/features/home/widgets/projects/projects.dart'; +import 'package:site/app/features/home/widgets/social/social.dart'; +import 'package:site/app/widgets/app_bar/app_bar.dart'; +import 'package:site/app/widgets/drawer/drawer.dart'; +import 'package:site/data/repositories/contact/contact.dart'; + +class HomePage extends StatefulWidget { + HomePage({ + super.key, + FirebaseRemoteConfig? firebaseRemoteConfig, + http.Client? httpClient, + }) : _firebaseRemoteConfig = firebaseRemoteConfig ?? getIt(), + _httpClient = httpClient ?? getIt(); + + final FirebaseRemoteConfig _firebaseRemoteConfig; + final http.Client _httpClient; + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + late List items; + + final itemScrollController = ItemScrollController(); + final itemPositionsListener = ItemPositionsListener.create(); + + @override + void initState() { + super.initState(); + items = [ + Presentation(itemScrollController), + const Projects(), + const Experience(), + const Social(), + ContactWidget( + contactController: ContactController( + contactRepository: ContactRepositoryImpl( + firebaseRemoteConfig: widget._firebaseRemoteConfig, + httpClient: widget._httpClient, + ), + ), + ), + const CustomFooter(), + ]; + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final isToShowDrawer = constraints.maxWidth < Breakpoints.appBar; + + return Scaffold( + appBar: CustomAppBar(itemScrollController), + drawer: isToShowDrawer ? CustomDrawer(itemScrollController) : null, + body: ScrollablePositionedList.builder( + itemCount: items.length, + itemScrollController: itemScrollController, + itemPositionsListener: itemPositionsListener, + itemBuilder: (context, index) => items[index], + ), + ); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/contact/contact_mobile.dart b/lib/app/features/home/widgets/contact/contact_mobile.dart new file mode 100644 index 00000000..38798deb --- /dev/null +++ b/lib/app/features/home/widgets/contact/contact_mobile.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/images.dart'; +import 'package:site/app/widgets/section/section.dart'; +import 'package:site/app/widgets/utils_widgets/utils_widgets.dart'; + +class ContactMobile extends StatelessWidget { + const ContactMobile( + this.widget, { + super.key, + }); + + final Widget widget; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Positioned( + bottom: 0, + child: ImageAssetWidget( + AppAssets.contactVerticalTexture, + height: context.height, + width: context.width, + ), + ), + Positioned.fill( + child: GradientWidget( + radius: 1, + opacity: 0.6, + height: 400, + width: context.width, + alignment: Alignment.bottomCenter, + ), + ), + SingleChildScrollViewWithoutScroll( + child: Column( + children: [ + MobileBody( + children: [ + SectionTitle( + paddingTop: 50, + paddingBottom: 20, + title: AppTexts.get(context).contact, + ), + SectionText( + paddingTop: 0, + paddingBottom: 45, + title: AppTexts.get(context).letsChatCallMe, + ), + widget, + ], + ), + const SectionDivider(), + ], + ), + ), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/contact/contact_web.dart b/lib/app/features/home/widgets/contact/contact_web.dart new file mode 100644 index 00000000..281e885a --- /dev/null +++ b/lib/app/features/home/widgets/contact/contact_web.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/images.dart'; +import 'package:site/app/widgets/section/section.dart'; +import 'package:site/app/widgets/utils_widgets/utils_widgets.dart'; + +class ContactWeb extends StatelessWidget { + const ContactWeb( + this.widget, { + super.key, + }); + + final Widget widget; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Positioned( + bottom: 0, + child: ImageAssetWidget( + AppAssets.contactHorizontalTexture, + height: context.height, + width: context.width, + ), + ), + Positioned.fill( + left: 0, + child: GradientWidget( + opacity: 0.5, + height: context.height, + alignment: Alignment.centerLeft, + ), + ), + Positioned.fill( + right: 0, + child: GradientWidget( + opacity: 0.5, + height: context.height, + alignment: Alignment.centerRight, + ), + ), + SingleChildScrollViewWithoutScroll( + child: Column( + children: [ + WebBody( + children: [ + SectionTitle( + paddingTop: 50, + paddingBottom: 20, + title: AppTexts.get(context).contact, + ), + SectionText( + paddingTop: 0, + paddingBottom: 45, + title: AppTexts.get(context).letsChatCallMe, + ), + widget, + ], + ), + const SectionDivider(), + ], + ), + ), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/contact/contact_widget.dart b/lib/app/features/home/widgets/contact/contact_widget.dart new file mode 100644 index 00000000..f9c2d8a6 --- /dev/null +++ b/lib/app/features/home/widgets/contact/contact_widget.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/injections/injections.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/responsive/responsive.dart'; +import 'package:site/app/core/shared/app_keys.dart'; +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/features/home/widgets/contact/contact_mobile.dart'; +import 'package:site/app/features/home/widgets/contact/contact_web.dart'; +import 'package:site/app/features/home/widgets/contact/controller/contact_controller.dart'; +import 'package:site/app/features/home/widgets/contact/widgets/widgets.dart'; +import 'package:site/app/widgets/snack_bars/snack_bars.dart'; +import 'package:site/data/models/models.dart' as models; +import 'package:site/data/repositories/contact/contact.dart'; + +class ContactWidget extends StatelessWidget { + ContactWidget({ + super.key, + ContactController? contactController, + }) : _contactController = contactController ?? + ContactController( + contactRepository: ContactRepositoryImpl( + firebaseRemoteConfig: getIt(), + httpClient: getIt(), + ), + ); + + final ContactController? _contactController; + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + final nameController = TextEditingController(); + final emailController = TextEditingController(); + final messageController = TextEditingController(); + final subjectController = TextEditingController(); + + Widget contactForm() { + return CustomForm( + formKey: formKey, + nameController: nameController, + emailController: emailController, + subjectController: subjectController, + messageController: messageController, + onPressed: () { + if (formKey.currentState?.validate() ?? false) { + appShowSnackBar( + context, + text: AppTexts.get(context).emailSendedWithSuccess, + icon: Icons.check, + color: AppColors.primaryDark, + width: 300, + ); + _contactController?.sendMail( + contact: models.Contact( + name: nameController.text, + email: emailController.text, + message: messageController.text, + subject: subjectController.text, + ), + ); + nameController.clear(); + emailController.clear(); + messageController.clear(); + subjectController.clear(); + } + }, + ); + } + + return LayoutBuilder( + key: AppKeys.contact, + builder: (context, constraints) { + final form = contactForm(); + + return constraints.maxWidth < Breakpoints.contact + ? ContactMobile(form) + : ContactWeb(form); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/contact/controller/contact_controller.dart b/lib/app/features/home/widgets/contact/controller/contact_controller.dart new file mode 100644 index 00000000..273e62ff --- /dev/null +++ b/lib/app/features/home/widgets/contact/controller/contact_controller.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:site/app/core/injections/injections.dart'; + +import 'package:site/data/models/models.dart'; +import 'package:site/data/repositories/contact/contact.dart'; + +class ContactController extends ChangeNotifier { + ContactController({ + ContactRepositoryImpl? contactRepository, + }) : _contactRepository = contactRepository ?? getIt(); + + final ContactRepositoryImpl _contactRepository; + + Future sendMail({ + required Contact contact, + }) { + return _contactRepository.sendMail( + contact: contact, + ); + } +} diff --git a/lib/app/features/home/widgets/contact/widgets/custom_form.dart b/lib/app/features/home/widgets/contact/widgets/custom_form.dart new file mode 100644 index 00000000..fa633d08 --- /dev/null +++ b/lib/app/features/home/widgets/contact/widgets/custom_form.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/features/home/widgets/contact/widgets/widgets.dart'; +import 'package:site/app/utils/contact_validators.dart'; +import 'package:site/app/widgets/buttons/buttons.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; + +class CustomForm extends StatelessWidget { + const CustomForm({ + super.key, + required this.formKey, + required this.nameController, + required this.emailController, + required this.subjectController, + required this.messageController, + required this.onPressed, + }); + + final GlobalKey formKey; + final TextEditingController nameController; + final TextEditingController emailController; + final TextEditingController subjectController; + final TextEditingController messageController; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + return Form( + key: formKey, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CustomTextFormField( + controller: nameController, + hintText: AppTexts.get(context).name, + prefixIcon: Icons.account_circle, + validator: (value) => ContactValidators.name(value, context), + ), + const SizedBox(height: 16), + CustomTextFormField( + controller: emailController, + hintText: AppTexts.get(context).email, + validator: (value) => ContactValidators.email(value, context), + prefixIcon: Icons.mail, + keyboardType: TextInputType.emailAddress, + ), + const SizedBox(height: 16), + CustomTextFormField( + controller: subjectController, + hintText: AppTexts.get(context).title, + prefixIcon: Icons.subject, + validator: (value) => ContactValidators.subject(value, context), + ), + const SizedBox(height: 16), + CustomTextFormField( + controller: messageController, + hintText: AppTexts.get(context).text, + prefixIcon: Icons.comment, + validator: (value) => ContactValidators.message(value, context), + maxLines: 2, + ), + const Center( + child: ContactDivider(), + ), + Center( + child: AppTextButton( + text: AppTexts.get(context).sendEmailUpper, + onPressed: onPressed, + ), + ), + const SizedBox(height: 60), + ], + ), + ), + ); + } +} diff --git a/lib/app/features/home/widgets/contact/widgets/custom_text_form_field.dart b/lib/app/features/home/widgets/contact/widgets/custom_text_form_field.dart new file mode 100644 index 00000000..1c2232e6 --- /dev/null +++ b/lib/app/features/home/widgets/contact/widgets/custom_text_form_field.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class CustomTextFormField extends StatelessWidget { + const CustomTextFormField({ + super.key, + required this.hintText, + required this.prefixIcon, + required this.controller, + required this.validator, + this.onChanged, + this.keyboardType = TextInputType.text, + this.maxLines = 1, + }); + + final String hintText; + final TextEditingController controller; + final IconData prefixIcon; + final FormFieldValidator? validator; + final Function(String)? onChanged; + final TextInputType? keyboardType; + final int maxLines; + + @override + Widget build(BuildContext context) { + const customBorder = AppBorders.primaryBorder; + + return SizedBox( + width: 300, + child: Material( + borderRadius: BorderRadius.circular(16), + color: AppColors.transparent, + child: TextFormField( + controller: controller, + validator: validator, + keyboardType: keyboardType, + onChanged: onChanged, + textAlignVertical: TextAlignVertical.center, + maxLines: maxLines, + style: AppTextStyles.textForm, + decoration: InputDecoration( + fillColor: AppColors.white, + filled: true, + isDense: true, + prefixIcon: Icon(prefixIcon, color: AppColors.primary), + hintText: hintText, + hintStyle: AppTextStyles.hint, + errorStyle: AppTextStyles.errorForm, + contentPadding: const EdgeInsets.symmetric(vertical: 16), + border: customBorder(AppColors.primary), + errorBorder: customBorder(AppColors.red), + enabledBorder: customBorder(AppColors.background), + disabledBorder: customBorder(AppColors.background), + focusedBorder: customBorder(AppColors.primary), + focusedErrorBorder: customBorder(AppColors.red), + ), + ), + ), + ); + } +} diff --git a/lib/app/features/home/widgets/contact/widgets/widgets.dart b/lib/app/features/home/widgets/contact/widgets/widgets.dart new file mode 100644 index 00000000..5a4cca33 --- /dev/null +++ b/lib/app/features/home/widgets/contact/widgets/widgets.dart @@ -0,0 +1,2 @@ +export 'custom_form.dart'; +export 'custom_text_form_field.dart'; diff --git a/lib/app/features/home/widgets/experience/experience.dart b/lib/app/features/home/widgets/experience/experience.dart new file mode 100644 index 00000000..0b8501ab --- /dev/null +++ b/lib/app/features/home/widgets/experience/experience.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:site/app/core/shared/app_keys.dart'; + +import 'package:site/app/core/responsive/responsive.dart'; +import 'package:site/app/features/home/widgets/experience/experience_mobile.dart'; +import 'package:site/app/features/home/widgets/experience/experience_web.dart'; + +class Experience extends StatelessWidget { + const Experience({super.key}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + key: AppKeys.experience, + builder: (context, constraints) { + return constraints.maxWidth < Breakpoints.experience + ? const ExperienceMobile() + : const ExperienceWeb(); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/experience/experience_mobile.dart b/lib/app/features/home/widgets/experience/experience_mobile.dart new file mode 100644 index 00000000..74a1c716 --- /dev/null +++ b/lib/app/features/home/widgets/experience/experience_mobile.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/experience/widgets/widgets.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/images.dart'; +import 'package:site/app/widgets/section/section.dart'; + +class ExperienceMobile extends StatelessWidget { + const ExperienceMobile({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Stack( + children: [ + const Positioned( + top: -25, + right: 0, + child: ImageAssetWidget( + AppAssets.abstractRight, + ), + ), + Positioned.fill( + child: GradientWidget( + opacity: 0.7, + height: context.height, + width: context.width, + alignment: Alignment.bottomCenter, + ), + ), + MobileBody( + children: [ + SectionTitle( + paddingTop: 50, + paddingBottom: 20, + title: AppTexts.get(context).experience, + ), + const SectionExperienceTexts(), + Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(top: 24), + child: const ImageAssetWidget( + AppAssets.champ, + height: 185, + ), + ), + const SizedBox(height: 16), + ], + ), + ], + ), + const SectionDivider(), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/experience/experience_web.dart b/lib/app/features/home/widgets/experience/experience_web.dart new file mode 100644 index 00000000..bf8f6657 --- /dev/null +++ b/lib/app/features/home/widgets/experience/experience_web.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/mixins/mixins.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/experience/widgets/widgets.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/images.dart'; +import 'package:site/app/widgets/section/section.dart'; + +class ExperienceWeb extends StatelessWidget with ResponsivePositionMixin { + const ExperienceWeb({super.key}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + return Stack( + children: [ + Positioned.fill( + right: 0, + child: Align( + alignment: Alignment.centerRight, + child: ImageAssetWidget( + AppAssets.abstractRight, + height: context.height, + ), + ), + ), + Positioned.fill( + child: GradientWidget( + radius: 0.5, + width: context.width, + height: context.height, + alignment: Alignment( + gradientExperienceAndProjectsSectionWidthAlignment( + constraints.maxWidth, + ), + 0.18, + ), + ), + ), + Positioned.fill( + child: ImageAssetWidget( + AppAssets.presentationTextureLarge, + alignment: Alignment.topCenter, + height: context.height, + width: context.width, + ), + ), + Column( + children: [ + WebBody( + children: [ + SectionTitle( + isWeb: true, + paddingTop: 50, + paddingBottom: 20, + title: AppLocalizations.of(context)!.experience, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.only( + top: 24, + left: 40, + ), + child: const ImageAssetWidget( + AppAssets.champ, + width: 300, + ), + ), + const SizedBox(height: 87), + ], + ), + ), + const SizedBox(height: 24), + const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SectionExperienceTexts(), + ], + ), + ], + ), + ], + ), + const SectionDivider(), + ], + ), + ], + ); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/experience/widgets/section_experience_texts.dart b/lib/app/features/home/widgets/experience/widgets/section_experience_texts.dart new file mode 100644 index 00000000..c167b839 --- /dev/null +++ b/lib/app/features/home/widgets/experience/widgets/section_experience_texts.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/features/home/widgets/experience/widgets/widgets.dart'; + +class SectionExperienceTexts extends StatelessWidget { + const SectionExperienceTexts({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TitleDescriptionShort( + title: AppTexts.get(context).workSuaMusica, + description: AppTexts.get(context).flutterDeveloper, + ), + TitleDescriptionShort( + title: AppTexts.get(context).workRocketseat, + description: AppTexts.get(context).developerInstructor, + ), + TitleDescriptionShort( + title: AppTexts.get(context).workUdemy, + description: AppTexts.get(context).teachingAssistent, + ), + TitleDescriptionShort( + title: AppTexts.get(context).workPersonalProjects, + description: AppTexts.get(context).payngPriceEveryday, + ), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/experience/widgets/title_description_short.dart b/lib/app/features/home/widgets/experience/widgets/title_description_short.dart new file mode 100644 index 00000000..20ad0d8e --- /dev/null +++ b/lib/app/features/home/widgets/experience/widgets/title_description_short.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class TitleDescriptionShort extends StatelessWidget { + const TitleDescriptionShort({ + required this.title, + required this.description, + super.key, + }); + + final String title; + final String description; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SelectableText( + 'β€’ $title', + style: AppTextStyles.experienceTitle, + ), + const SizedBox(height: 8), + SelectableText( + ' β€” $description', + style: AppTextStyles.experienceDescription, + ), + const SizedBox(height: 8), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/experience/widgets/widgets.dart b/lib/app/features/home/widgets/experience/widgets/widgets.dart new file mode 100644 index 00000000..c9847389 --- /dev/null +++ b/lib/app/features/home/widgets/experience/widgets/widgets.dart @@ -0,0 +1,2 @@ +export 'section_experience_texts.dart'; +export 'title_description_short.dart'; diff --git a/lib/app/features/home/widgets/footer/custom_footer.dart b/lib/app/features/home/widgets/footer/custom_footer.dart new file mode 100644 index 00000000..2b327544 --- /dev/null +++ b/lib/app/features/home/widgets/footer/custom_footer.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:site/app/core/shared/app_keys.dart'; + +import 'package:site/app/core/responsive/responsive.dart'; +import 'package:site/app/features/home/widgets/footer/footer.dart'; + +class CustomFooter extends StatelessWidget { + const CustomFooter({super.key}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + key: AppKeys.customFooter, + builder: (context, constraints) { + return constraints.maxWidth < Breakpoints.footer + ? const FooterMobile() + : const FooterWeb(); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/footer/footer.dart b/lib/app/features/home/widgets/footer/footer.dart new file mode 100644 index 00000000..41d3eb0f --- /dev/null +++ b/lib/app/features/home/widgets/footer/footer.dart @@ -0,0 +1,3 @@ +export 'custom_footer.dart'; +export 'footer_mobile.dart'; +export 'footer_web.dart'; diff --git a/lib/app/features/home/widgets/footer/footer_mobile.dart b/lib/app/features/home/widgets/footer/footer_mobile.dart new file mode 100644 index 00000000..03bf65d8 --- /dev/null +++ b/lib/app/features/home/widgets/footer/footer_mobile.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/features/home/widgets/footer/widgets/rich_text_short.dart'; + +class FooterMobile extends StatelessWidget { + const FooterMobile({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 50, + alignment: Alignment.center, + color: AppColors.black, + child: const RichTextShort(textAlign: TextAlign.center), + ); + } +} diff --git a/lib/app/features/home/widgets/footer/footer_web.dart b/lib/app/features/home/widgets/footer/footer_web.dart new file mode 100644 index 00000000..21ae079e --- /dev/null +++ b/lib/app/features/home/widgets/footer/footer_web.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/features/home/widgets/footer/widgets/widgets.dart'; +import 'package:site/app/widgets/body/body.dart'; + +class FooterWeb extends StatelessWidget { + const FooterWeb({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 107, + color: AppColors.black, + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(vertical: 24), + child: WebBody( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const RichTextShort( + textAlign: TextAlign.start, + ), + SelectableText( + AppTexts.get(context).flutterProjectOpenSource, + style: AppTextStyles.phraseWhite, + ), + ], + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + TextWithLink( + text: AppTexts.get(context).seeInGitHub, + link: AppUrls.gitHubProject, + ), + TextWithLink( + text: AppTexts.get(context).seeInFigma, + link: AppUrls.figmaProject, + ), + ], + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/app/features/home/widgets/footer/widgets/rich_text_short.dart b/lib/app/features/home/widgets/footer/widgets/rich_text_short.dart new file mode 100644 index 00000000..f175f3e9 --- /dev/null +++ b/lib/app/features/home/widgets/footer/widgets/rich_text_short.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/utils/utils.dart'; + +class RichTextShort extends StatelessWidget { + const RichTextShort({ + super.key, + required this.textAlign, + }); + + final TextAlign textAlign; + + @override + Widget build(BuildContext context) { + var year = DateTimeUtils.getYear(DateTime.now()); + + return SelectableText.rich( + textAlign: textAlign, + TextSpan( + children: [ + TextSpan( + text: '$year${AppTexts.get(context).hyphen}', + style: AppTextStyles.phraseWhite, + ), + TextSpan( + text: AppTexts.get(context).username, + style: AppTextStyles.phrasePrimary, + ), + TextSpan( + text: AppTexts.get(context).dot, + style: AppTextStyles.phraseWhite, + ), + ], + ), + ); + } +} diff --git a/lib/app/features/home/widgets/footer/widgets/text_with_link.dart b/lib/app/features/home/widgets/footer/widgets/text_with_link.dart new file mode 100644 index 00000000..eb673a72 --- /dev/null +++ b/lib/app/features/home/widgets/footer/widgets/text_with_link.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/utils/utils.dart'; + +class TextWithLink extends StatelessWidget { + const TextWithLink({ + super.key, + required this.text, + required this.link, + }); + + final String text, link; + + @override + Widget build(BuildContext context) { + return Material( + color: AppColors.transparent, + borderRadius: BorderRadius.circular(8), + clipBehavior: Clip.antiAlias, + child: InkWell( + splashColor: AppColors.primary, + overlayColor: MaterialStateProperty.all( + AppColors.primary.withOpacity(0.25), + ), + onTap: () => LaunchUrls().launchURL(link), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text( + text, + style: AppTextStyles.footerLink, + ), + ), + ), + ); + } +} diff --git a/lib/app/features/home/widgets/footer/widgets/widgets.dart b/lib/app/features/home/widgets/footer/widgets/widgets.dart new file mode 100644 index 00000000..3915e551 --- /dev/null +++ b/lib/app/features/home/widgets/footer/widgets/widgets.dart @@ -0,0 +1,2 @@ +export 'rich_text_short.dart'; +export 'text_with_link.dart'; diff --git a/lib/app/features/home/widgets/headers/header.dart b/lib/app/features/home/widgets/headers/header.dart new file mode 100644 index 00000000..c0e21299 --- /dev/null +++ b/lib/app/features/home/widgets/headers/header.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/widgets/images/images.dart'; + +class Header extends StatelessWidget { + const Header({super.key}); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.center, + child: Stack( + alignment: Alignment.topCenter, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 50), + const SizedBox.square( + dimension: 127, + child: CircleAvatar( + radius: 50, + backgroundColor: AppColors.primary, + child: Padding( + padding: EdgeInsets.all(4), + child: ClipOval( + child: ImageAssetWidget( + AppAssets.profile, + ), + ), + ), + ), + ), + const SizedBox(height: 25), + SelectableText( + AppTexts.get(context).felipeSales, + style: AppTextStyles.name, + ), + const SizedBox(height: 4), + SelectableText( + AppTexts.get(context).flutterDeveloperInstructor, + style: AppTextStyles.office, + ), + const SizedBox(height: 25), + ], + ), + ], + ), + ); + } +} diff --git a/lib/app/features/home/widgets/headers/headers.dart b/lib/app/features/home/widgets/headers/headers.dart new file mode 100644 index 00000000..9c6d2c25 --- /dev/null +++ b/lib/app/features/home/widgets/headers/headers.dart @@ -0,0 +1 @@ +export 'header.dart'; diff --git a/lib/app/features/home/widgets/presentation/presentation.dart b/lib/app/features/home/widgets/presentation/presentation.dart new file mode 100644 index 00000000..7767e9dd --- /dev/null +++ b/lib/app/features/home/widgets/presentation/presentation.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import 'package:site/app/core/shared/app_keys.dart'; + +import 'package:site/app/core/responsive/responsive.dart'; +import 'package:site/app/features/home/widgets/presentation/presentation_mobile.dart'; +import 'package:site/app/features/home/widgets/presentation/presentation_web.dart'; + +class Presentation extends StatelessWidget { + const Presentation( + this.itemScrollController, { + super.key, + }); + + final ItemScrollController itemScrollController; + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + key: AppKeys.presentation, + builder: (context, constraints) { + return constraints.maxWidth < Breakpoints.presentation + ? PresentationMobile(itemScrollController) + : PresentationWeb(itemScrollController); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/presentation/presentation_mobile.dart b/lib/app/features/home/widgets/presentation/presentation_mobile.dart new file mode 100644 index 00000000..99683ad6 --- /dev/null +++ b/lib/app/features/home/widgets/presentation/presentation_mobile.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; + +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/responsive/responsive.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/presentation/widgets/widgets.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/image_asset_widget.dart'; +import 'package:site/app/widgets/section/section.dart'; +import 'package:site/app/widgets/utils_widgets/utils_widgets.dart'; + +class PresentationMobile extends StatelessWidget { + const PresentationMobile( + this.itemScrollController, { + super.key, + }); + + final ItemScrollController itemScrollController; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Positioned.fill( + child: GradientWidget( + radius: 0.9, + height: context.height, + width: context.width, + alignment: Alignment.center, + ), + ), + Positioned.fill( + child: GradientWidget( + opacity: 0.75, + radius: 1, + height: context.height, + width: context.width, + alignment: Alignment.bottomCenter, + ), + ), + Positioned( + bottom: 0, + child: ImageAssetWidget( + AppAssets.presentationTextureBackground, + width: context.width, + height: context.height, + ), + ), + SingleChildScrollViewWithoutScroll( + child: Column( + children: [ + MobileBody( + children: [ + SectionTitle( + paddingTop: 50, + paddingBottom: 16, + title: AppTexts.get(context).hiIAmFelipeSales, + ), + LayoutBuilder( + builder: (context, constraints) { + return constraints.maxWidth < + Breakpoints.presentationMobileSubtitle + ? const SizedBox.shrink() + : SectionSubtitle( + paddingTop: 8, + paddingBottom: 8, + title: + AppTexts.get(context).applicationsDeveloper, + ); + }, + ), + const GradientDivider(), + Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(top: 24), + child: const ImageAssetWidget( + AppAssets.presentationMobile, + ), + ), + Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: SectionText( + paddingTop: 24, + paddingBottom: 8, + isCentered: true, + title: AppTexts.get(context).developerFocused, + ), + ), + ), + Container( + padding: const EdgeInsets.only(top: 8, bottom: 35), + alignment: Alignment.center, + child: const Phrase(), + ), + ], + ), + PresentationDivider(itemScrollController), + ], + ), + ), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/presentation/presentation_web.dart b/lib/app/features/home/widgets/presentation/presentation_web.dart new file mode 100644 index 00000000..1fe516b7 --- /dev/null +++ b/lib/app/features/home/widgets/presentation/presentation_web.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; + +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/mixins/mixins.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/presentation/widgets/widgets.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/images.dart'; +import 'package:site/app/widgets/section/section.dart'; +import 'package:site/app/widgets/utils_widgets/utils_widgets.dart'; + +class PresentationWeb extends StatelessWidget with ResponsivePositionMixin { + const PresentationWeb( + this.itemScrollController, { + super.key, + }); + + final ItemScrollController itemScrollController; + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + return Stack( + children: [ + Positioned.fill( + child: GradientWidget( + radius: 0.7, + height: context.height, + width: context.width, + alignment: Alignment( + gradientPresentationWidthAlignment(constraints.maxWidth), + 0, + ), + ), + ), + Positioned.fill( + child: ImageAssetWidget( + AppAssets.presentationTextureLarge, + height: context.height, + width: context.width, + ), + ), + SingleChildScrollViewWithoutScroll( + child: Column( + children: [ + WebBody( + children: [ + SectionTitle( + isWeb: true, + paddingTop: 50, + paddingBottom: 12, + title: AppTexts.get(context).hiIAmFelipeSales, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SectionSubtitle( + paddingTop: 0, + paddingBottom: 32, + title: AppTexts.get(context).appsDeveloper, + ), + const SizedBox( + width: 400, + child: GradientDivider(), + ), + SizedBox( + child: Center( + child: SectionText( + paddingTop: 32, + paddingBottom: 32, + title: AppTexts.get(context) + .developerFocused, + ), + ), + ), + const Phrase(), + ], + ), + ), + const SizedBox(width: 24), + Expanded( + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(top: 24), + child: const ImageAssetWidget( + AppAssets.presentationWeb, + ), + ), + ), + ], + ), + const SizedBox(height: 60), + ], + ), + PresentationDivider(itemScrollController), + ], + ), + ), + ], + ); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/presentation/widgets/phrase.dart b/lib/app/features/home/widgets/presentation/widgets/phrase.dart new file mode 100644 index 00000000..032319e4 --- /dev/null +++ b/lib/app/features/home/widgets/presentation/widgets/phrase.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/tokens/tokens.dart'; + +class Phrase extends StatelessWidget { + const Phrase({super.key}); + + @override + Widget build(BuildContext context) { + final codeText = AppTexts.get(context).code; + + return Stack( + children: [ + SelectableText( + AppTexts.get(context).deepCodeWayCode, + style: AppTextStyles.phrase, + ), + SelectableText.rich( + TextSpan( + children: [ + TextSpan( + text: AppTexts.get(context).deep, + style: AppTextStyles.phrasePrimary, + ), + TextSpan( + text: codeText, + style: AppTextStyles.phraseWhite, + ), + TextSpan( + text: AppTexts.get(context).way, + style: AppTextStyles.phrasePrimary, + ), + TextSpan( + text: codeText, + style: AppTextStyles.phraseWhite, + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/presentation/widgets/widgets.dart b/lib/app/features/home/widgets/presentation/widgets/widgets.dart new file mode 100644 index 00000000..879743c2 --- /dev/null +++ b/lib/app/features/home/widgets/presentation/widgets/widgets.dart @@ -0,0 +1 @@ +export 'phrase.dart'; diff --git a/lib/app/features/home/widgets/projects/projects.dart b/lib/app/features/home/widgets/projects/projects.dart new file mode 100644 index 00000000..db95d193 --- /dev/null +++ b/lib/app/features/home/widgets/projects/projects.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/shared/app_keys.dart'; +import 'package:site/app/core/responsive/responsive.dart'; +import 'package:site/app/features/home/widgets/projects/projects_mobile.dart'; +import 'package:site/app/features/home/widgets/projects/projects_web.dart'; + +class Projects extends StatelessWidget { + const Projects({super.key}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + key: AppKeys.projects, + builder: (context, constraints) { + return constraints.maxWidth < Breakpoints.projects + ? const ProjectsMobile() + : const ProjectsWeb(); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/projects/projects_mobile.dart b/lib/app/features/home/widgets/projects/projects_mobile.dart new file mode 100644 index 00000000..f3621699 --- /dev/null +++ b/lib/app/features/home/widgets/projects/projects_mobile.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/projects/widgets/widgets.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/images.dart'; +import 'package:site/app/widgets/section/section.dart'; + +class ProjectsMobile extends StatelessWidget { + const ProjectsMobile({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Stack( + children: [ + Positioned( + bottom: 4, + child: Stack( + children: [ + ImageAssetWidget( + AppAssets.abstractFit, + width: context.width, + ), + ], + ), + ), + Positioned.fill( + child: GradientWidget( + radius: 0.6, + width: context.width, + height: context.height, + alignment: const Alignment(0.0, 0.5), + ), + ), + MobileBody( + children: [ + SectionTitle( + paddingTop: 50, + paddingBottom: 20, + title: AppTexts.get(context).projects, + ), + Center( + child: SectionText( + paddingTop: 0, + paddingBottom: 24, + title: AppTexts.get(context).projectAreInMyGitHub, + ), + ), + const AppTextButtonWidget(), + Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(top: 24, bottom: 16), + child: const ImageAssetWidget( + AppAssets.mockup, + width: 324, + ), + ), + ], + ), + ], + ), + const SectionDivider(), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/projects/projects_web.dart b/lib/app/features/home/widgets/projects/projects_web.dart new file mode 100644 index 00000000..fea67b6d --- /dev/null +++ b/lib/app/features/home/widgets/projects/projects_web.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/mixins/mixins.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/projects/widgets/widgets.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/images.dart'; +import 'package:site/app/widgets/section/section.dart'; + +class ProjectsWeb extends StatelessWidget with ResponsivePositionMixin { + const ProjectsWeb({super.key}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + return Stack( + children: [ + Positioned.fill( + bottom: 4, + child: Align( + alignment: Alignment.bottomCenter, + child: ImageAssetWidget( + AppAssets.abstractLarge, + alignment: Alignment.topCenter, + height: 305.7, + width: context.width, + ), + ), + ), + Positioned.fill( + child: GradientWidget( + opacity: 0.8, + radius: 0.75, + width: context.width, + height: context.height, + alignment: Alignment( + -gradientExperienceAndProjectsSectionWidthAlignment( + constraints.maxWidth, + ), + 0.18, + ), + ), + ), + Column( + children: [ + WebBody( + children: [ + SectionTitle( + isWeb: true, + paddingTop: 50, + paddingBottom: 8, + title: AppTexts.get(context).projects, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 400, + child: SectionText( + paddingTop: 42, + paddingBottom: 36, + title: AppTexts.get(context) + .projectAreInMyGitHub, + ), + ), + const AppTextButtonWidget(), + ], + ), + ), + Expanded( + child: Column( + children: [ + Container( + alignment: Alignment.center, + child: const ImageAssetWidget( + AppAssets.mockup, + width: 460, + ), + ), + const SizedBox(height: 60), + ], + ), + ), + ], + ), + ], + ), + const SectionDivider(), + ], + ), + ], + ); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/projects/widgets/app_text_button_widget.dart b/lib/app/features/home/widgets/projects/widgets/app_text_button_widget.dart new file mode 100644 index 00000000..b2cf15d8 --- /dev/null +++ b/lib/app/features/home/widgets/projects/widgets/app_text_button_widget.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/utils/utils.dart'; +import 'package:site/app/widgets/buttons/buttons.dart'; + +class AppTextButtonWidget extends StatelessWidget { + const AppTextButtonWidget({super.key}); + + @override + Widget build(BuildContext context) { + return AppTextButton( + text: AppTexts.get(context).seeProjectsUpper, + onPressed: () => LaunchUrls().launchURL(AppUrls.gitHub), + ); + } +} diff --git a/lib/app/features/home/widgets/projects/widgets/widgets.dart b/lib/app/features/home/widgets/projects/widgets/widgets.dart new file mode 100644 index 00000000..4acbb9ed --- /dev/null +++ b/lib/app/features/home/widgets/projects/widgets/widgets.dart @@ -0,0 +1 @@ +export 'app_text_button_widget.dart'; diff --git a/lib/app/features/home/widgets/social/social.dart b/lib/app/features/home/widgets/social/social.dart new file mode 100644 index 00000000..c762cc21 --- /dev/null +++ b/lib/app/features/home/widgets/social/social.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:site/app/core/shared/app_keys.dart'; + +import 'package:site/app/core/responsive/responsive.dart'; +import 'package:site/app/features/home/widgets/social/social_mobile.dart'; +import 'package:site/app/features/home/widgets/social/social_web.dart'; + +class Social extends StatelessWidget { + const Social({super.key}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + key: AppKeys.social, + builder: (context, constraints) { + return constraints.maxWidth < Breakpoints.social + ? const SocialMobile() + : const SocialWeb(); + }, + ); + } +} diff --git a/lib/app/features/home/widgets/social/social_mobile.dart b/lib/app/features/home/widgets/social/social_mobile.dart new file mode 100644 index 00000000..8c46a7ee --- /dev/null +++ b/lib/app/features/home/widgets/social/social_mobile.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/social/widgets/widgets.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/images.dart'; + +class SocialMobile extends StatelessWidget { + const SocialMobile({super.key}); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Positioned( + bottom: 0, + child: ImageAssetWidget( + AppAssets.socialAbstract, + width: context.width, + ), + ), + Positioned.fill( + child: Opacity( + opacity: 0.7, + child: GradientWidget( + height: 350, + width: context.width, + radius: 0.7, + ), + ), + ), + const SocialAllCardsMobile(), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/social/social_web.dart b/lib/app/features/home/widgets/social/social_web.dart new file mode 100644 index 00000000..0e4c1464 --- /dev/null +++ b/lib/app/features/home/widgets/social/social_web.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/extensions/extensions.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/social/widgets/widgets.dart'; +import 'package:site/app/widgets/gradients/gradients.dart'; +import 'package:site/app/widgets/images/images.dart'; + +class SocialWeb extends StatelessWidget { + const SocialWeb({super.key}); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Positioned( + bottom: 4, + child: ImageAssetWidget( + AppAssets.socialAbstractLarge, + alignment: Alignment.topCenter, + width: context.width, + height: 495, + ), + ), + Positioned.fill( + child: Opacity( + opacity: 0.75, + child: GradientWidget( + height: 400, + width: context.width, + radius: 0.75, + ), + ), + ), + const SocialAllCardsWeb(), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/social/widgets/card_glassmorphism.dart b/lib/app/features/home/widgets/social/widgets/card_glassmorphism.dart new file mode 100644 index 00000000..14e5d796 --- /dev/null +++ b/lib/app/features/home/widgets/social/widgets/card_glassmorphism.dart @@ -0,0 +1,37 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class CardGlassmorphism extends StatelessWidget { + const CardGlassmorphism({ + super.key, + required this.child, + }); + + final Widget child; + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular(16), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 2, sigmaY: 2), + child: Container( + height: 50, + width: 278, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + gradient: AppGradients.glassmorphic, + border: Border.all( + width: 1, + color: AppColors.white.withOpacity(.25), + ), + ), + child: child, + ), + ), + ); + } +} diff --git a/lib/app/features/home/widgets/social/widgets/social_all_cards_mobile.dart b/lib/app/features/home/widgets/social/widgets/social_all_cards_mobile.dart new file mode 100644 index 00000000..068b0ea8 --- /dev/null +++ b/lib/app/features/home/widgets/social/widgets/social_all_cards_mobile.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:site/app/features/home/widgets/social/widgets/widgets.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; + +class SocialAllCardsMobile extends StatelessWidget { + const SocialAllCardsMobile({super.key}); + + @override + Widget build(BuildContext context) { + return const Column( + children: [ + MobileBody( + children: [ + TitleTextList(), + ], + ), + SizedBox(height: 60), + SectionDivider(), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/social/widgets/social_all_cards_web.dart b/lib/app/features/home/widgets/social/widgets/social_all_cards_web.dart new file mode 100644 index 00000000..169d0eef --- /dev/null +++ b/lib/app/features/home/widgets/social/widgets/social_all_cards_web.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:site/app/features/home/widgets/social/widgets/widgets.dart'; +import 'package:site/app/widgets/body/body.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; + +class SocialAllCardsWeb extends StatelessWidget { + const SocialAllCardsWeb({super.key}); + + @override + Widget build(BuildContext context) { + return const SingleChildScrollView( + child: Column( + children: [ + WebBody( + children: [ + TitleTextList(isWeb: true), + ], + ), + SizedBox(height: 60), + SectionDivider(), + ], + ), + ); + } +} diff --git a/lib/app/features/home/widgets/social/widgets/social_item.dart b/lib/app/features/home/widgets/social/widgets/social_item.dart new file mode 100644 index 00000000..966b394c --- /dev/null +++ b/lib/app/features/home/widgets/social/widgets/social_item.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_svg/flutter_svg.dart'; + +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/features/home/widgets/social/widgets/card_glassmorphism.dart'; + +class SocialItem extends StatelessWidget { + const SocialItem({ + super.key, + required this.title, + required this.image, + required this.onTap, + }); + + final String title; + final String image; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return Material( + color: AppColors.transparent, + borderRadius: BorderRadius.circular(16), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(16), + customBorder: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: CardGlassmorphism( + child: Row( + children: [ + const SizedBox(width: 24), + SizedBox( + height: 30, + width: 30, + child: SvgPicture.asset( + AppAssets.socialLogo(image), + ), + ), + const SizedBox(width: 10), + Flexible( + child: Text( + title, + style: AppTextStyles.socialTitle, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/app/features/home/widgets/social/widgets/social_list.dart b/lib/app/features/home/widgets/social/widgets/social_list.dart new file mode 100644 index 00000000..6d480055 --- /dev/null +++ b/lib/app/features/home/widgets/social/widgets/social_list.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/social/widgets/widgets.dart'; +import 'package:site/app/utils/utils.dart'; + +class SocialList extends StatelessWidget { + const SocialList({super.key}); + + @override + Widget build(BuildContext context) { + final launch = LaunchUrls().launchURL; + + return Column( + children: [ + SocialItem( + key: AppKeys.socialItemGitHub, + title: AppTexts.get(context).gitHub, + image: AppAssets.gitHub, + onTap: () => launch(AppUrls.gitHub), + ), + const SizedBox(height: 14), + SocialItem( + key: AppKeys.socialItemLinkedIn, + title: AppTexts.get(context).linkedIn, + image: AppAssets.linkedIn, + onTap: () => launch(AppUrls.linkedIn), + ), + const SizedBox(height: 14), + SocialItem( + key: AppKeys.socialItemStackOverFlow, + title: AppTexts.get(context).stackOverFlow, + image: AppAssets.stackOverFlow, + onTap: () => launch(AppUrls.stackOverFlow), + ), + const SizedBox(height: 14), + SocialItem( + key: AppKeys.socialItemDiscord, + title: AppTexts.get(context).discord, + image: AppAssets.discord, + onTap: () => launch(AppUrls.discord), + ), + const SizedBox(height: 14), + SocialItem( + key: AppKeys.socialItemUdemy, + title: AppTexts.get(context).udemy, + image: AppAssets.udemy, + onTap: () => launch(AppUrls.udemy), + ), + const SizedBox(height: 14), + SocialItem( + key: AppKeys.socialItemInstagram, + title: AppTexts.get(context).instagram, + image: AppAssets.instagram, + onTap: () => launch(AppUrls.instagram), + ), + const SizedBox(height: 14), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/social/widgets/title_text_list.dart b/lib/app/features/home/widgets/social/widgets/title_text_list.dart new file mode 100644 index 00000000..9bac3fc1 --- /dev/null +++ b/lib/app/features/home/widgets/social/widgets/title_text_list.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/features/home/widgets/social/widgets/widgets.dart'; +import 'package:site/app/widgets/section/section.dart'; + +class TitleTextList extends StatelessWidget { + const TitleTextList({ + super.key, + this.isWeb = false, + }); + + final bool isWeb; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SectionTitle( + isWeb: isWeb, + paddingTop: 50, + paddingBottom: 20, + title: AppTexts.get(context).socialLinks, + ), + SectionText( + paddingTop: 0, + paddingBottom: 24, + title: AppTexts.get(context).followMeOnMySocialNetworks, + ), + const Center( + child: SocialList(), + ), + ], + ); + } +} diff --git a/lib/app/features/home/widgets/social/widgets/widgets.dart b/lib/app/features/home/widgets/social/widgets/widgets.dart new file mode 100644 index 00000000..19a65b87 --- /dev/null +++ b/lib/app/features/home/widgets/social/widgets/widgets.dart @@ -0,0 +1,6 @@ +export 'card_glassmorphism.dart'; +export 'social_all_cards_mobile.dart'; +export 'social_all_cards_web.dart'; +export 'social_item.dart'; +export 'social_list.dart'; +export 'title_text_list.dart'; diff --git a/lib/app/home/home_page.dart b/lib/app/home/home_page.dart deleted file mode 100644 index 1d9154e1..00000000 --- a/lib/app/home/home_page.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:site/app/core/app_colors.dart'; -import 'package:site/app/widgets/footer.dart'; -import 'package:site/app/widgets/header.dart'; -import 'package:site/app/widgets/social_buttons_list.dart'; - -class HomePage extends StatelessWidget { - const HomePage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppColors.background, - body: Align( - alignment: Alignment.topCenter, - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 450), - child: ListView( - children: const [ - Header(), - SocialButtonsList(), - ], - ), - ), - ), - bottomNavigationBar: const Footer(), - ); - } -} diff --git a/lib/app/utils/contact_validators.dart b/lib/app/utils/contact_validators.dart new file mode 100644 index 00000000..a9085523 --- /dev/null +++ b/lib/app/utils/contact_validators.dart @@ -0,0 +1,66 @@ +import 'package:flutter/cupertino.dart'; +import 'package:site/app/core/l10n/l10n.dart'; + +class ContactValidators { + static String? name(String? value, [BuildContext? context]) { + if (value!.isEmpty) { + return AppTexts.get(context!).insertValidName; + } + final regexLenght = RegExp(r'^.{3,}$'); + if (!regexLenght.hasMatch(value)) { + return AppTexts.get(context!).insertABiggerName; + } + final regex = RegExp('[a-zA-Z]'); + if (regex.hasMatch(value)) { + return null; + } + + return AppTexts.get(context!).thisNameIsNotValid; + } + + static String? email(String? value, [BuildContext? context]) { + if (value!.isEmpty) { + return AppTexts.get(context!).insertValidEmail; + } + final regex = RegExp( + r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+", + ); + if (regex.hasMatch(value)) { + return null; + } + + return AppTexts.get(context!).thisEmailIsNotValid; + } + + static String? message(String? value, [BuildContext? context]) { + if (value!.isEmpty) { + return AppTexts.get(context!).insertValidMessage; + } + final regexLenght = RegExp(r'^.{10,}$'); + if (!regexLenght.hasMatch(value)) { + return AppTexts.get(context!).insertABiggerMessage; + } + final regex = RegExp('[a-zA-Z]'); + if (regex.hasMatch(value)) { + return null; + } + + return AppTexts.get(context!).thisMessageIsNotValid; + } + + static String? subject(String? value, [BuildContext? context]) { + if (value!.isEmpty) { + return AppTexts.get(context!).insertValidSubject; + } + final regexLenght = RegExp(r'^.{5,}$'); + if (!regexLenght.hasMatch(value)) { + return AppTexts.get(context!).insertABiggerSubject; + } + final regex = RegExp('[a-zA-Z]'); + if (regex.hasMatch(value)) { + return null; + } + + return AppTexts.get(context!).thisSubjectIsNotValid; + } +} diff --git a/lib/app/utils/date_time_utils.dart b/lib/app/utils/date_time_utils.dart new file mode 100644 index 00000000..63eeb93d --- /dev/null +++ b/lib/app/utils/date_time_utils.dart @@ -0,0 +1,5 @@ +class DateTimeUtils { + static String getYear(DateTime date) { + return date.year.toString(); + } +} diff --git a/lib/app/utils/launch_urls.dart b/lib/app/utils/launch_urls.dart new file mode 100644 index 00000000..42551a5b --- /dev/null +++ b/lib/app/utils/launch_urls.dart @@ -0,0 +1,8 @@ +import 'package:url_launcher/url_launcher.dart'; + +class LaunchUrls { + void launchURL(String url) async => await launchUrl( + Uri.parse(url), + webOnlyWindowName: '_blank', + ); +} diff --git a/lib/app/utils/utils.dart b/lib/app/utils/utils.dart new file mode 100644 index 00000000..2c07adab --- /dev/null +++ b/lib/app/utils/utils.dart @@ -0,0 +1,3 @@ +export 'contact_validators.dart'; +export 'date_time_utils.dart'; +export 'launch_urls.dart'; diff --git a/lib/app/widgets/app_bar/app_bar.dart b/lib/app/widgets/app_bar/app_bar.dart new file mode 100644 index 00000000..fd99ce99 --- /dev/null +++ b/lib/app/widgets/app_bar/app_bar.dart @@ -0,0 +1,4 @@ +export 'widgets/widgets.dart'; +export 'custom_app_bar.dart'; +export 'mobile_app_bar.dart'; +export 'web_app_bar.dart'; diff --git a/lib/app/widgets/app_bar/custom_app_bar.dart b/lib/app/widgets/app_bar/custom_app_bar.dart new file mode 100644 index 00000000..1e6bd77f --- /dev/null +++ b/lib/app/widgets/app_bar/custom_app_bar.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/responsive/responsive.dart'; +import 'package:site/app/widgets/app_bar/app_bar.dart'; + +class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { + const CustomAppBar( + this.itemScrollController, { + super.key, + }); + + final ItemScrollController itemScrollController; + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + return constraints.maxWidth < Breakpoints.appBar + ? const MobileAppBar() + : WebAppBar(itemScrollController); + }, + ); + } +} diff --git a/lib/app/widgets/app_bar/mobile_app_bar.dart b/lib/app/widgets/app_bar/mobile_app_bar.dart new file mode 100644 index 00000000..8852276b --- /dev/null +++ b/lib/app/widgets/app_bar/mobile_app_bar.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class MobileAppBar extends StatelessWidget { + const MobileAppBar({super.key}); + + @override + Widget build(BuildContext context) { + return AppBar( + scrolledUnderElevation: 0, + surfaceTintColor: AppColors.transparent, + actions: [ + Flexible( + child: Container( + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 16), + child: const SelectableText( + 'FS', + style: TextStyle( + letterSpacing: 1.5, + color: AppColors.primary, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/app/widgets/app_bar/web_app_bar.dart b/lib/app/widgets/app_bar/web_app_bar.dart new file mode 100644 index 00000000..ce538f7a --- /dev/null +++ b/lib/app/widgets/app_bar/web_app_bar.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/widgets/app_bar/widgets/widgets.dart'; + +class WebAppBar extends StatelessWidget { + const WebAppBar( + this.itemScrollController, { + super.key, + }); + + final ItemScrollController itemScrollController; + + @override + Widget build(BuildContext context) { + return AppBar( + scrolledUnderElevation: 0, + surfaceTintColor: AppColors.transparent, + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + WebAppBarTitle( + title: AppTexts.get(context).home, + index: 0, + itemScrollController: itemScrollController, + ), + const AppBarDivider(), + WebAppBarTitle( + title: AppTexts.get(context).projects, + index: 1, + itemScrollController: itemScrollController, + ), + const AppBarDivider(), + WebAppBarTitle( + title: AppTexts.get(context).experience, + index: 2, + itemScrollController: itemScrollController, + ), + const AppBarDivider(), + WebAppBarTitle( + title: AppTexts.get(context).socialLinks, + index: 3, + itemScrollController: itemScrollController, + ), + const AppBarDivider(), + WebAppBarTitle( + title: AppTexts.get(context).contact, + index: 4, + itemScrollController: itemScrollController, + ), + ], + ), + ); + } +} diff --git a/lib/app/widgets/app_bar/widgets/app_bar_divider.dart b/lib/app/widgets/app_bar/widgets/app_bar_divider.dart new file mode 100644 index 00000000..75514cf7 --- /dev/null +++ b/lib/app/widgets/app_bar/widgets/app_bar_divider.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class AppBarDivider extends StatelessWidget { + const AppBarDivider({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 16, + width: 1, + color: AppColors.primary, + ); + } +} diff --git a/lib/app/widgets/app_bar/widgets/web_app_bar_title.dart b/lib/app/widgets/app_bar/widgets/web_app_bar_title.dart new file mode 100644 index 00000000..7cb7fb68 --- /dev/null +++ b/lib/app/widgets/app_bar/widgets/web_app_bar_title.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/mixins/mixins.dart'; +import 'package:site/app/core/tokens/tokens.dart'; + +class WebAppBarTitle extends StatelessWidget with ScrollToMixin { + const WebAppBarTitle({ + super.key, + required this.index, + required this.title, + required this.itemScrollController, + }); + + final String title; + final int index; + final ItemScrollController itemScrollController; + + @override + Widget build(BuildContext context) { + return Flexible( + child: Material( + color: AppColors.transparent, + borderRadius: BorderRadius.circular(16), + clipBehavior: Clip.antiAlias, + child: InkWell( + splashColor: AppColors.primary, + overlayColor: MaterialStateProperty.all( + AppColors.primary.withOpacity(0.25), + ), + onTap: () => scrollTo(index, itemScrollController), + child: Padding( + padding: const EdgeInsets.all(16), + child: AppTextStyles.appBar(title), + ), + ), + ), + ); + } +} diff --git a/lib/app/widgets/app_bar/widgets/widgets.dart b/lib/app/widgets/app_bar/widgets/widgets.dart new file mode 100644 index 00000000..32ee1762 --- /dev/null +++ b/lib/app/widgets/app_bar/widgets/widgets.dart @@ -0,0 +1,2 @@ +export 'app_bar_divider.dart'; +export 'web_app_bar_title.dart'; diff --git a/lib/app/widgets/body/body.dart b/lib/app/widgets/body/body.dart new file mode 100644 index 00000000..26a1951e --- /dev/null +++ b/lib/app/widgets/body/body.dart @@ -0,0 +1,2 @@ +export 'mobile_body.dart'; +export 'web_body.dart'; diff --git a/lib/app/widgets/body/mobile_body.dart b/lib/app/widgets/body/mobile_body.dart new file mode 100644 index 00000000..1ee3049b --- /dev/null +++ b/lib/app/widgets/body/mobile_body.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class MobileBody extends StatelessWidget { + const MobileBody({ + super.key, + required this.children, + }); + + final List children; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ), + ); + } +} diff --git a/lib/app/widgets/body/web_body.dart b/lib/app/widgets/body/web_body.dart new file mode 100644 index 00000000..ea18ea24 --- /dev/null +++ b/lib/app/widgets/body/web_body.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class WebBody extends StatelessWidget { + const WebBody({ + super.key, + required this.children, + }); + + final List children; + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 1020), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 60), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ), + ), + ); + } +} diff --git a/lib/app/widgets/buttons/app_text_button.dart b/lib/app/widgets/buttons/app_text_button.dart new file mode 100644 index 00000000..af2ca9be --- /dev/null +++ b/lib/app/widgets/buttons/app_text_button.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class AppTextButton extends StatelessWidget { + const AppTextButton({ + super.key, + required this.onPressed, + required this.text, + }); + + final VoidCallback onPressed; + final String text; + + @override + Widget build(BuildContext context) { + return TextButton( + onPressed: onPressed, + style: TextButton.styleFrom( + minimumSize: Size.zero, + padding: EdgeInsets.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 32), + decoration: BoxDecoration( + gradient: AppGradients.button, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + text, + style: AppTextStyles.buttonTitle, + ), + ), + ); + } +} diff --git a/lib/app/widgets/buttons/buttons.dart b/lib/app/widgets/buttons/buttons.dart new file mode 100644 index 00000000..c7157a6f --- /dev/null +++ b/lib/app/widgets/buttons/buttons.dart @@ -0,0 +1 @@ +export 'app_text_button.dart'; diff --git a/lib/app/widgets/dividers/contact_divider.dart b/lib/app/widgets/dividers/contact_divider.dart new file mode 100644 index 00000000..7daad97e --- /dev/null +++ b/lib/app/widgets/dividers/contact_divider.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class ContactDivider extends StatelessWidget { + const ContactDivider({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 2, + width: 100, + margin: const EdgeInsets.symmetric(vertical: 16), + decoration: BoxDecoration( + gradient: AppGradients.divider, + borderRadius: BorderRadius.circular(6), + ), + ); + } +} diff --git a/lib/app/widgets/dividers/custom_divider.dart b/lib/app/widgets/dividers/custom_divider.dart new file mode 100644 index 00000000..db4b5000 --- /dev/null +++ b/lib/app/widgets/dividers/custom_divider.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class CustomDivider extends StatelessWidget { + const CustomDivider( + this.height, + this.color, { + super.key, + this.margin = const EdgeInsets.symmetric(vertical: 8), + }); + + final double height; + final Color color; + final EdgeInsets margin; + + @override + Widget build(BuildContext context) { + return Container( + margin: margin, + height: height, + color: color, + ); + } +} diff --git a/lib/app/widgets/dividers/custom_vertical_divider.dart b/lib/app/widgets/dividers/custom_vertical_divider.dart new file mode 100644 index 00000000..5977102d --- /dev/null +++ b/lib/app/widgets/dividers/custom_vertical_divider.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class CustomVerticalDivider extends StatelessWidget { + const CustomVerticalDivider({ + super.key, + required this.width, + }); + + final double width; + + @override + Widget build(BuildContext context) { + return Container( + width: width, + height: 50, + margin: const EdgeInsets.symmetric(horizontal: 8), + color: AppColors.background, + alignment: Alignment.bottomCenter, + ); + } +} diff --git a/lib/app/widgets/dividers/dividers.dart b/lib/app/widgets/dividers/dividers.dart new file mode 100644 index 00000000..9961e3b3 --- /dev/null +++ b/lib/app/widgets/dividers/dividers.dart @@ -0,0 +1,6 @@ +export 'contact_divider.dart'; +export 'custom_divider.dart'; +export 'custom_vertical_divider.dart'; +export 'gradient_divider.dart'; +export 'presentation_divider.dart'; +export 'section_divider.dart'; diff --git a/lib/app/widgets/dividers/gradient_divider.dart b/lib/app/widgets/dividers/gradient_divider.dart new file mode 100644 index 00000000..fd7aa613 --- /dev/null +++ b/lib/app/widgets/dividers/gradient_divider.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class GradientDivider extends StatelessWidget { + const GradientDivider({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 5, + margin: const EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + gradient: AppGradients.divider, + borderRadius: BorderRadius.circular(6), + ), + ); + } +} diff --git a/lib/app/widgets/dividers/presentation_divider.dart b/lib/app/widgets/dividers/presentation_divider.dart new file mode 100644 index 00000000..b56f4e37 --- /dev/null +++ b/lib/app/widgets/dividers/presentation_divider.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +import 'package:lottie/lottie.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/mixins/mixins.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; + +class PresentationDivider extends StatelessWidget with ScrollToMixin { + const PresentationDivider( + this.itemScrollController, { + super.key, + }); + + final ItemScrollController itemScrollController; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + const Expanded( + child: CustomDivider( + 2, + AppColors.primary, + margin: EdgeInsets.zero, + ), + ), + GestureDetector( + onTap: () => scrollTo(1, itemScrollController), + child: Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Lottie.asset( + AppAssets.mouse, + height: 50, + ), + ), + ), + const Expanded( + child: CustomDivider( + 2, + AppColors.primary, + margin: EdgeInsets.zero, + ), + ), + ], + ); + } +} diff --git a/lib/app/widgets/dividers/section_divider.dart b/lib/app/widgets/dividers/section_divider.dart new file mode 100644 index 00000000..58578f4d --- /dev/null +++ b/lib/app/widgets/dividers/section_divider.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class SectionDivider extends StatelessWidget { + const SectionDivider({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: AppColors.primary, + height: 2, + ); + } +} diff --git a/lib/app/widgets/drawer/custom_drawer.dart b/lib/app/widgets/drawer/custom_drawer.dart new file mode 100644 index 00000000..3a392689 --- /dev/null +++ b/lib/app/widgets/drawer/custom_drawer.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/widgets/dividers/dividers.dart'; +import 'package:site/app/widgets/drawer/drawer.dart'; + +class CustomDrawer extends StatelessWidget { + const CustomDrawer( + this.itemScrollController, { + super.key, + }); + + final ItemScrollController itemScrollController; + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: const BorderRadius.horizontal( + right: Radius.circular(50), + ), + child: Drawer( + backgroundColor: AppColors.background, + surfaceTintColor: AppColors.transparent, + child: SafeArea( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + const CustomDrawerHeader(), + const CustomDivider(.5, AppColors.primaryDark), + DrawerItems(itemScrollController), + const Spacer(), + const DrawerFooter(), + const Footer(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/app/widgets/drawer/custom_drawer_header.dart b/lib/app/widgets/drawer/custom_drawer_header.dart new file mode 100644 index 00000000..0628981d --- /dev/null +++ b/lib/app/widgets/drawer/custom_drawer_header.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/core/tokens/tokens.dart'; + +class CustomDrawerHeader extends StatelessWidget { + const CustomDrawerHeader({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: const EdgeInsets.fromLTRB(16, 24, 16, 8), + width: 120, + height: 120, + padding: const EdgeInsets.all(4), + decoration: const BoxDecoration( + color: AppColors.primary, + shape: BoxShape.circle, + gradient: AppGradients.stories, + ), + child: Container( + decoration: const BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + image: AssetImage(AppAssets.profile), + ), + ), + ), + ), + SelectableText( + AppTexts.get(context).felipeSales, + textAlign: TextAlign.center, + style: AppTextStyles.socialTitle, + ), + const SizedBox(height: 8), + ], + ); + } +} diff --git a/lib/app/widgets/drawer/drawer.dart b/lib/app/widgets/drawer/drawer.dart new file mode 100644 index 00000000..9db5dd63 --- /dev/null +++ b/lib/app/widgets/drawer/drawer.dart @@ -0,0 +1,6 @@ +export 'custom_drawer.dart'; +export 'custom_drawer_header.dart'; +export 'drawer_footer.dart'; +export 'drawer_items.dart'; +export 'drawer_tile.dart'; +export 'footer.dart'; diff --git a/lib/app/widgets/drawer/drawer_footer.dart b/lib/app/widgets/drawer/drawer_footer.dart new file mode 100644 index 00000000..ee2f9a0c --- /dev/null +++ b/lib/app/widgets/drawer/drawer_footer.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/widgets/images/images.dart'; + +class DrawerFooter extends StatelessWidget { + const DrawerFooter({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(20, 10, 0, 20), + child: SizedBox( + height: 30, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const ImageAssetWidget( + AppAssets.developer, + height: 24, + ), + const SizedBox(width: 16), + SelectableText( + AppTexts.get(context).deepCode, + textAlign: TextAlign.center, + style: AppTextStyles.socialTitle, + ), + ], + ), + ), + ); + } +} diff --git a/lib/app/widgets/drawer/drawer_items.dart b/lib/app/widgets/drawer/drawer_items.dart new file mode 100644 index 00000000..161e9054 --- /dev/null +++ b/lib/app/widgets/drawer/drawer_items.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; + +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/widgets/drawer/drawer.dart'; + +class DrawerItems extends StatelessWidget { + const DrawerItems( + this.itemScrollController, { + super.key, + }); + + final ItemScrollController itemScrollController; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + DrawerTile( + title: AppTexts.get(context).home, + leading: Icons.home, + index: 0, + itemScrollController: itemScrollController, + ), + DrawerTile( + title: AppTexts.get(context).projects, + leading: Icons.tips_and_updates, + index: 1, + itemScrollController: itemScrollController, + ), + DrawerTile( + title: AppTexts.get(context).experience, + leading: Icons.receipt_long, + index: 2, + itemScrollController: itemScrollController, + ), + DrawerTile( + title: AppTexts.get(context).socialLinks, + leading: Icons.link, + index: 3, + itemScrollController: itemScrollController, + ), + DrawerTile( + title: AppTexts.get(context).contact, + leading: Icons.contact_mail, + index: 4, + itemScrollController: itemScrollController, + ), + ], + ); + } +} diff --git a/lib/app/widgets/drawer/drawer_tile.dart b/lib/app/widgets/drawer/drawer_tile.dart new file mode 100644 index 00000000..aedcaeb4 --- /dev/null +++ b/lib/app/widgets/drawer/drawer_tile.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/core/mixins/mixins.dart'; +import 'package:site/app/core/tokens/tokens.dart'; + +class DrawerTile extends StatelessWidget with ScrollToMixin { + const DrawerTile({ + super.key, + required this.title, + required this.leading, + required this.index, + required this.itemScrollController, + }); + + final String title; + final IconData leading; + final int index; + final ItemScrollController itemScrollController; + + @override + Widget build(BuildContext context) { + return Material( + color: AppColors.background, + clipBehavior: Clip.antiAlias, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: InkWell( + splashColor: AppColors.blackOpacity, + onTap: () { + scrollTo(index, itemScrollController); + Navigator.pop(context); + }, + child: Padding( + padding: const EdgeInsets.only(left: 8), + child: ListTile( + title: Transform.translate( + offset: const Offset(-12, 0), + child: Text( + title, + style: AppTextStyles.socialTitle, + ), + ), + leading: Icon( + leading, + size: 24, + color: AppColors.primary, + ), + ), + ), + ), + ); + } +} diff --git a/lib/app/widgets/footer.dart b/lib/app/widgets/drawer/footer.dart similarity index 54% rename from lib/app/widgets/footer.dart rename to lib/app/widgets/drawer/footer.dart index e28fdee5..766690fb 100644 --- a/lib/app/widgets/footer.dart +++ b/lib/app/widgets/drawer/footer.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; - -import 'package:site/app/core/app_colors.dart'; +import 'package:site/app/core/l10n/l10n.dart'; +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/widgets/images/images.dart'; class Footer extends StatelessWidget { - const Footer({Key? key}) : super(key: key); + const Footer({super.key}); @override Widget build(BuildContext context) { @@ -17,16 +18,13 @@ class Footer extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - 'Criado por @felipecastrosalesβ„’', - style: GoogleFonts.montserrat( - color: AppColors.light, - fontSize: 16, - ), + SelectableText( + AppTexts.get(context).createdByFelipeCastroSales, + style: AppTextStyles.footer, ), const SizedBox(width: 2), - Image.asset( - 'assets/developer.png', + const ImageAssetWidget( + AppAssets.developer, height: 16, ), ], diff --git a/lib/app/widgets/gradients/gradient_widget.dart b/lib/app/widgets/gradients/gradient_widget.dart new file mode 100644 index 00000000..7335f9e2 --- /dev/null +++ b/lib/app/widgets/gradients/gradient_widget.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:site/app/core/tokens/tokens.dart'; + +class GradientWidget extends StatelessWidget { + const GradientWidget({ + super.key, + this.radius = 0.8, + this.alignment = Alignment.center, + this.opacity = 1, + this.gradient, + this.height, + this.width, + }); + + final double radius, opacity; + final AlignmentGeometry alignment; + final Gradient? gradient; + final double? height, width; + + @override + Widget build(BuildContext context) { + return Opacity( + opacity: opacity, + child: Container( + height: height, + width: width, + decoration: BoxDecoration( + gradient: AppGradients.gradientWidget( + alignment: alignment, + radius: radius, + ), + ), + ), + ); + } +} diff --git a/lib/app/widgets/gradients/gradients.dart b/lib/app/widgets/gradients/gradients.dart new file mode 100644 index 00000000..82648ef9 --- /dev/null +++ b/lib/app/widgets/gradients/gradients.dart @@ -0,0 +1 @@ +export 'gradient_widget.dart'; diff --git a/lib/app/widgets/header.dart b/lib/app/widgets/header.dart deleted file mode 100644 index f225c3e5..00000000 --- a/lib/app/widgets/header.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:google_fonts/google_fonts.dart'; - -import 'package:site/app/core/app_colors.dart'; - -class Header extends StatelessWidget { - const Header({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Align( - alignment: Alignment.center, - child: Stack( - alignment: Alignment.topCenter, - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(16), - child: Image.asset( - 'assets/header-background.png', - fit: BoxFit.cover, - ), - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 80), - SizedBox( - height: 127, - width: 127, - child: CircleAvatar( - radius: 50, - backgroundColor: AppColors.primary, - child: Padding( - padding: const EdgeInsets.all(4), - child: ClipOval( - child: Image.asset('assets/profile.png'), - ), - ), - ), - ), - const SizedBox(height: 25), - Text( - 'Felipe Sales', - style: GoogleFonts.montserrat( - color: AppColors.light, - fontSize: 24, - ), - ), - const SizedBox(height: 4), - Text( - 'Flutter Developer Instructor', - style: GoogleFonts.montserrat( - color: AppColors.light, - fontSize: 18, - ), - ), - const SizedBox(height: 25), - ], - ), - ], - ), - ); - } -} diff --git a/lib/app/widgets/images/image_asset_widget.dart b/lib/app/widgets/images/image_asset_widget.dart new file mode 100644 index 00000000..4f7019d8 --- /dev/null +++ b/lib/app/widgets/images/image_asset_widget.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +class ImageAssetWidget extends StatelessWidget { + const ImageAssetWidget( + this.image, { + super.key, + this.fit = BoxFit.cover, + this.alignment = Alignment.center, + this.filterQuality = FilterQuality.high, + this.height, + this.width, + }); + + final String image; + final BoxFit fit; + final Alignment alignment; + final FilterQuality filterQuality; + final double? height, width; + + @override + Widget build(BuildContext context) { + return Image.asset( + image, + fit: fit, + alignment: alignment, + filterQuality: filterQuality, + height: height, + width: width, + ); + } +} diff --git a/lib/app/widgets/images/images.dart b/lib/app/widgets/images/images.dart new file mode 100644 index 00000000..414ab3fa --- /dev/null +++ b/lib/app/widgets/images/images.dart @@ -0,0 +1 @@ +export 'image_asset_widget.dart'; diff --git a/lib/app/widgets/section/section.dart b/lib/app/widgets/section/section.dart new file mode 100644 index 00000000..0c3740ed --- /dev/null +++ b/lib/app/widgets/section/section.dart @@ -0,0 +1,3 @@ +export 'section_subtitle.dart'; +export 'section_text.dart'; +export 'section_title.dart'; diff --git a/lib/app/widgets/section/section_subtitle.dart b/lib/app/widgets/section/section_subtitle.dart new file mode 100644 index 00000000..1f1104b5 --- /dev/null +++ b/lib/app/widgets/section/section_subtitle.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class SectionSubtitle extends StatelessWidget { + const SectionSubtitle({ + super.key, + required this.title, + required this.paddingTop, + required this.paddingBottom, + }); + + final String title; + final double paddingTop; + final double paddingBottom; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(top: paddingTop, bottom: paddingBottom), + child: SelectableText( + title, + style: AppTextStyles.presentationSubtitle, + ), + ); + } +} diff --git a/lib/app/widgets/section/section_text.dart b/lib/app/widgets/section/section_text.dart new file mode 100644 index 00000000..66f458af --- /dev/null +++ b/lib/app/widgets/section/section_text.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +class SectionText extends StatelessWidget { + const SectionText({ + super.key, + required this.title, + required this.paddingTop, + required this.paddingBottom, + this.isCentered = false, + }); + + final String title; + final double paddingTop; + final double paddingBottom; + final bool isCentered; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(top: paddingTop, bottom: paddingBottom), + child: SelectableText( + title, + textAlign: isCentered ? TextAlign.center : TextAlign.start, + style: AppTextStyles.presentationText, + ), + ); + } +} diff --git a/lib/app/widgets/section/section_title.dart b/lib/app/widgets/section/section_title.dart new file mode 100644 index 00000000..76d7f384 --- /dev/null +++ b/lib/app/widgets/section/section_title.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:site/app/core/tokens/tokens.dart'; + +class SectionTitle extends StatelessWidget { + const SectionTitle({ + super.key, + this.isWeb = false, + required this.title, + required this.paddingTop, + required this.paddingBottom, + }); + + final bool isWeb; + final String title; + final double paddingTop; + final double paddingBottom; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(top: paddingTop, bottom: paddingBottom), + child: SelectableText( + title, + style: isWeb + ? AppTextStyles.presentationTitleWeb + : AppTextStyles.presentationTitle, + ), + ); + } +} diff --git a/lib/app/widgets/snack_bars/app_show_snack_bar.dart b/lib/app/widgets/snack_bars/app_show_snack_bar.dart new file mode 100644 index 00000000..07c7e7d5 --- /dev/null +++ b/lib/app/widgets/snack_bars/app_show_snack_bar.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import 'package:auto_size_text/auto_size_text.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; + +ScaffoldFeatureController appShowSnackBar( + BuildContext context, { + required String text, + required IconData icon, + required Color color, + required double width, +}) { + return ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + elevation: 8, + width: width, + duration: const Duration(seconds: 3), + backgroundColor: color, + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + content: Row( + children: [ + Icon( + icon, + size: 28, + color: AppColors.white, + ), + const SizedBox(width: 8), + Flexible( + child: FittedBox( + fit: BoxFit.contain, + child: AutoSizeText( + text, + maxLines: 1, + style: AppTextStyles.buttonTitle, + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/app/widgets/snack_bars/snack_bars.dart b/lib/app/widgets/snack_bars/snack_bars.dart new file mode 100644 index 00000000..abe0988e --- /dev/null +++ b/lib/app/widgets/snack_bars/snack_bars.dart @@ -0,0 +1 @@ +export 'app_show_snack_bar.dart'; diff --git a/lib/app/widgets/social_buttons_items.dart b/lib/app/widgets/social_buttons_items.dart deleted file mode 100644 index a8df0d43..00000000 --- a/lib/app/widgets/social_buttons_items.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:google_fonts/google_fonts.dart'; - -import 'package:site/app/core/app_colors.dart'; - -class SocialButtonsItems extends StatelessWidget { - const SocialButtonsItems({ - Key? key, - required this.title, - required this.image, - required this.onTap, - }) : super(key: key); - - final String title; - final String image; - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(8), - child: Container( - height: 50, - width: 278, - padding: const EdgeInsets.only(left: 58), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - color: AppColors.primary, - ), - child: Row( - children: [ - Container( - height: 29, - width: 29, - decoration: BoxDecoration( - color: AppColors.light, - borderRadius: BorderRadius.circular(50), - boxShadow: [ - BoxShadow( - color: AppColors.shadow.withOpacity(0.2), - spreadRadius: 4, - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - child: Padding( - padding: const EdgeInsets.all(4), - child: Image.asset('assets/$image'), - ), - ), - const SizedBox(width: 10), - Text( - title, - style: GoogleFonts.inter( - fontSize: 18, - color: AppColors.light, - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/app/widgets/social_buttons_list.dart b/lib/app/widgets/social_buttons_list.dart deleted file mode 100644 index 1921d3f9..00000000 --- a/lib/app/widgets/social_buttons_list.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:url_launcher/url_launcher.dart'; - -import 'package:site/app/widgets/social_buttons_items.dart'; - -class SocialButtonsList extends StatelessWidget { - const SocialButtonsList({Key? key}) : super(key: key); - void launchURL(String url) async => await launchUrl( - Uri.parse('https://$url'), - webOnlyWindowName: '_blank', - ); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - SocialButtonsItems( - title: 'GitHub', - image: 'github.png', - onTap: () => launchURL('github.com/felipecastrosales'), - ), - const SizedBox(height: 14), - SocialButtonsItems( - title: 'LinkedIn', - image: 'linkedin.png', - onTap: () => launchURL('linkedin.com/in/felipecastrosales'), - ), - const SizedBox(height: 14), - SocialButtonsItems( - title: 'Stack OverFlow', - image: 'stackoverflow.png', - onTap: () => - launchURL('stackoverflow.com/users/13096514/felipe-sales'), - ), - const SizedBox(height: 14), - SocialButtonsItems( - title: 'Discord', - image: 'discord.png', - onTap: () => launchURL('discordapp.com/users/406074089011281921'), - ), - const SizedBox(height: 14), - SocialButtonsItems( - title: 'Rocketseat', - image: 'rocketseat.png', - onTap: () => launchURL('app.rocketseat.com.br/me/felipecastrosales'), - ), - const SizedBox(height: 14), - SocialButtonsItems( - title: 'Udemy', - image: 'udemy.png', - onTap: () => launchURL('udemy.com/user/luis-felipe-de-castro-sales/'), - ), - const SizedBox(height: 14), - SocialButtonsItems( - title: 'Instagram', - image: 'instagram.png', - onTap: () => launchURL('instagram.com/felipecastrosales'), - ), - const SizedBox(height: 14), - ], - ); - } -} diff --git a/lib/app/widgets/utils_widgets/single_child_scroll_view_without_scroll.dart b/lib/app/widgets/utils_widgets/single_child_scroll_view_without_scroll.dart new file mode 100644 index 00000000..0185590b --- /dev/null +++ b/lib/app/widgets/utils_widgets/single_child_scroll_view_without_scroll.dart @@ -0,0 +1,29 @@ +/// -> This widget is used to wrap a widget with a SingleChildScrollView without scroll. +/// +/// I make this, because in some cases, +/// the widget test complains that the Column doesn't fit and needs a scroll. +/// +/// e.g: +/// +/// The overflowing RenderFlex has an orientation of Axis.vertical. +/// The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and +/// black striped pattern. This is usually caused by the contents being too big for the RenderFlex. + +import 'package:flutter/material.dart'; + +class SingleChildScrollViewWithoutScroll extends StatelessWidget { + const SingleChildScrollViewWithoutScroll({ + super.key, + required this.child, + }); + + final Widget child; + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + physics: const NeverScrollableScrollPhysics(), + child: child, + ); + } +} diff --git a/lib/app/widgets/utils_widgets/utils_widgets.dart b/lib/app/widgets/utils_widgets/utils_widgets.dart new file mode 100644 index 00000000..eee4867e --- /dev/null +++ b/lib/app/widgets/utils_widgets/utils_widgets.dart @@ -0,0 +1 @@ +export 'single_child_scroll_view_without_scroll.dart'; diff --git a/lib/data/constants/constants_api.dart b/lib/data/constants/constants_api.dart new file mode 100644 index 00000000..88b49a97 --- /dev/null +++ b/lib/data/constants/constants_api.dart @@ -0,0 +1,7 @@ +class ConstantsAPI { + static const baseUrl = 'https://api.emailjs.com/api/v1.0/email/send'; + static const headers = { + 'origin': 'http://localhost', + 'Content-Type': 'application/json', + }; +} diff --git a/lib/data/models/contact.dart b/lib/data/models/contact.dart new file mode 100644 index 00000000..07013d45 --- /dev/null +++ b/lib/data/models/contact.dart @@ -0,0 +1,10 @@ +class Contact { + Contact({ + required this.name, + required this.email, + required this.message, + required this.subject, + }); + + final String name, email, message, subject; +} diff --git a/lib/data/models/models.dart b/lib/data/models/models.dart new file mode 100644 index 00000000..4c72b197 --- /dev/null +++ b/lib/data/models/models.dart @@ -0,0 +1 @@ +export 'contact.dart'; diff --git a/lib/data/repositories/contact/contact.dart b/lib/data/repositories/contact/contact.dart new file mode 100644 index 00000000..dd4eaebc --- /dev/null +++ b/lib/data/repositories/contact/contact.dart @@ -0,0 +1,2 @@ +export 'contact_repository.dart'; +export 'contact_repository_impl.dart'; diff --git a/lib/data/repositories/contact/contact_repository.dart b/lib/data/repositories/contact/contact_repository.dart new file mode 100644 index 00000000..d0dc29b7 --- /dev/null +++ b/lib/data/repositories/contact/contact_repository.dart @@ -0,0 +1,5 @@ +import 'package:site/data/models/models.dart'; + +abstract class ContactRepository { + Future sendMail({required Contact contact}); +} diff --git a/lib/data/repositories/contact/contact_repository_impl.dart b/lib/data/repositories/contact/contact_repository_impl.dart new file mode 100644 index 00000000..d6d70a2a --- /dev/null +++ b/lib/data/repositories/contact/contact_repository_impl.dart @@ -0,0 +1,51 @@ +import 'dart:convert'; + +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:http/http.dart' as http; + +import 'package:site/data/constants/constants_api.dart'; +import 'package:site/data/models/models.dart'; +import 'package:site/data/repositories/contact/contact.dart'; + +class ContactRepositoryImpl implements ContactRepository { + ContactRepositoryImpl({ + required FirebaseRemoteConfig firebaseRemoteConfig, + required http.Client httpClient, + }) : _firebaseRemoteConfig = firebaseRemoteConfig, + _httpClient = httpClient; + + final FirebaseRemoteConfig _firebaseRemoteConfig; + final http.Client _httpClient; + + @override + Future sendMail({ + required Contact contact, + }) async { + final serviceId = _firebaseRemoteConfig.getString('service_id'); + final templateId = _firebaseRemoteConfig.getString('template_id'); + final userId = _firebaseRemoteConfig.getString('user_id'); + final toEmail = _firebaseRemoteConfig.getString('to_email'); + final baseUrl = Uri.parse(ConstantsAPI.baseUrl); + + final response = await _httpClient.post( + baseUrl, + headers: ConstantsAPI.headers, + body: json.encode( + { + 'service_id': serviceId, + 'template_id': templateId, + 'user_id': userId, + 'template_params': { + 'user_name': contact.name, + 'user_email': contact.email, + 'user_subject': contact.email, + 'user_message': contact.message, + 'to_email': toEmail, + }, + }, + ), + ); + + return response; + } +} diff --git a/lib/data/services/firebase/firebase.dart b/lib/data/services/firebase/firebase.dart new file mode 100644 index 00000000..1a8e9723 --- /dev/null +++ b/lib/data/services/firebase/firebase.dart @@ -0,0 +1,4 @@ +export 'firebase_service_impl.dart'; +export 'firebase_service.dart'; +export 'firebase_set_defaults.dart'; +export 'firebase_settings.dart'; diff --git a/lib/data/services/firebase/firebase_service.dart b/lib/data/services/firebase/firebase_service.dart new file mode 100644 index 00000000..d1bf9858 --- /dev/null +++ b/lib/data/services/firebase/firebase_service.dart @@ -0,0 +1,4 @@ +abstract class FirebaseService { + Future setUpInitialization(); + Future setUpRemoteConfig(); +} diff --git a/lib/data/services/firebase/firebase_service_impl.dart b/lib/data/services/firebase/firebase_service_impl.dart new file mode 100644 index 00000000..dea81d34 --- /dev/null +++ b/lib/data/services/firebase/firebase_service_impl.dart @@ -0,0 +1,36 @@ +import 'dart:developer' as developer; + +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_remote_config/firebase_remote_config.dart'; + +import 'package:site/data/services/firebase/firebase.dart'; +import 'package:site/firebase_options.dart'; + +class FirebaseServiceImpl implements FirebaseService { + @override + Future setUpInitialization() async { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + await setUpRemoteConfig(); + } + + @override + Future setUpRemoteConfig() async { + final remoteConfig = FirebaseRemoteConfig.instance; + final remoteConfigKeys = FirebaseSetDefaults().getRemoteConfigKeys(); + final remoteConfigSettings = FirebaseSettings().setSettings; + try { + await remoteConfig.setDefaults(remoteConfigKeys); + await remoteConfig.setConfigSettings(remoteConfigSettings); + await remoteConfig.fetchAndActivate(); + } catch (e, s) { + developer.log( + 'setUpRemoteConfig', + name: 'FirebaseServiceImpl', + error: e, + stackTrace: s, + ); + } + } +} diff --git a/lib/data/services/firebase/firebase_set_defaults.dart b/lib/data/services/firebase/firebase_set_defaults.dart new file mode 100644 index 00000000..f421a794 --- /dev/null +++ b/lib/data/services/firebase/firebase_set_defaults.dart @@ -0,0 +1,8 @@ +class FirebaseSetDefaults { + Map getRemoteConfigKeys() => { + 'service_id': 'service', + 'template_id': 'template', + 'user_id': 'user', + 'to_email': 'to_email', + }; +} diff --git a/lib/data/services/firebase/firebase_settings.dart b/lib/data/services/firebase/firebase_settings.dart new file mode 100644 index 00000000..c30df251 --- /dev/null +++ b/lib/data/services/firebase/firebase_settings.dart @@ -0,0 +1,8 @@ +import 'package:firebase_remote_config/firebase_remote_config.dart'; + +class FirebaseSettings { + final setSettings = RemoteConfigSettings( + fetchTimeout: const Duration(minutes: 1), + minimumFetchInterval: const Duration(hours: 1), + ); +} diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 00000000..db0bd98f --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,86 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +// ignore: prefer-match-file-name +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + case TargetPlatform.windows: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for windows - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'AIzaSyDyClpcHXG5M7twFoC06y1A0fXUZI4GgLQ', + appId: '1:884743546828:web:60fc5493354bb95aae9f69', + messagingSenderId: '884743546828', + projectId: 'site-96dd0', + authDomain: 'site-96dd0.firebaseapp.com', + storageBucket: 'site-96dd0.appspot.com', + measurementId: 'G-QNS1WC1Y4B', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyAgfqTS-d712MTUDUC0fOXoKSHGuw6CNoM', + appId: '1:884743546828:android:ea09ba7ee1ed93aeae9f69', + messagingSenderId: '884743546828', + projectId: 'site-96dd0', + storageBucket: 'site-96dd0.appspot.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyDrl7u3LFGcDrk_o6XR571OHJjLYxHlqN4', + appId: '1:884743546828:ios:f4ddf7bc0168bb74ae9f69', + messagingSenderId: '884743546828', + projectId: 'site-96dd0', + storageBucket: 'site-96dd0.appspot.com', + iosClientId: + '884743546828-fjfun3totngd3bcog9jeopevkri03lob.apps.googleusercontent.com', + iosBundleId: 'com.felipecastrosales.site', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'AIzaSyDrl7u3LFGcDrk_o6XR571OHJjLYxHlqN4', + appId: '1:884743546828:ios:f4ddf7bc0168bb74ae9f69', + messagingSenderId: '884743546828', + projectId: 'site-96dd0', + storageBucket: 'site-96dd0.appspot.com', + iosClientId: + '884743546828-fjfun3totngd3bcog9jeopevkri03lob.apps.googleusercontent.com', + iosBundleId: 'com.felipecastrosales.site', + ); +} diff --git a/lib/main.dart b/lib/main.dart index bece9390..6a2d8627 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,11 +2,16 @@ import 'package:flutter/material.dart'; import 'package:url_strategy/url_strategy.dart'; -import 'app/app_widget.dart'; +import 'package:site/app/app_widget.dart'; +import 'package:site/app/core/injections/injections.dart'; +import 'package:site/data/services/firebase/firebase.dart'; -void main() { +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await FirebaseServiceImpl().setUpInitialization(); setPathUrlStrategy(); + configureDependencies(); runApp( - const AppWidget(), + AppWidget(), ); } diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 00000000..746adbb6 --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..4b81f9b2 --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..5caa9d15 --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..00ced224 --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,18 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import firebase_analytics +import firebase_core +import firebase_remote_config +import url_launcher_macos + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin")) + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseRemoteConfigPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseRemoteConfigPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) +} diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 00000000..049abe29 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..4f4371ea --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,583 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 8F2B99622645F5BCD6EEB795 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = E1E9A0F117E84D1ABF0A2A8E /* GoogleService-Info.plist */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* site.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = site.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + E1E9A0F117E84D1ABF0A2A8E /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + E1E9A0F117E84D1ABF0A2A8E /* GoogleService-Info.plist */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* site.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* site.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + 8F2B99622645F5BCD6EEB795 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..1c83bb05 --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..d53ef643 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 00000000..82b6f9d9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 00000000..13b35eba Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 00000000..0a3f5fa4 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 00000000..bdb57226 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 00000000..f083318e Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 00000000..326c0e72 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 00000000..2f1632cf Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..80e867a4 --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..1928cb0f --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = site + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.felipecastrosales.site + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright Β© 2022 com.felipecastrosales. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..dddb8a30 --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/macos/Runner/GoogleService-Info.plist b/macos/Runner/GoogleService-Info.plist new file mode 100644 index 00000000..33e09312 --- /dev/null +++ b/macos/Runner/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 884743546828-fjfun3totngd3bcog9jeopevkri03lob.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.884743546828-fjfun3totngd3bcog9jeopevkri03lob + API_KEY + AIzaSyDrl7u3LFGcDrk_o6XR571OHJjLYxHlqN4 + GCM_SENDER_ID + 884743546828 + PLIST_VERSION + 1 + BUNDLE_ID + com.felipecastrosales.site + PROJECT_ID + site-96dd0 + STORAGE_BUCKET + site-96dd0.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:884743546828:ios:f4ddf7bc0168bb74ae9f69 + + \ No newline at end of file diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..2722837e --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 00000000..852fa1a4 --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/macos/firebase_app_id_file.json b/macos/firebase_app_id_file.json new file mode 100644 index 00000000..ff2e60ac --- /dev/null +++ b/macos/firebase_app_id_file.json @@ -0,0 +1,7 @@ +{ + "file_generated_by": "FlutterFire CLI", + "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", + "GOOGLE_APP_ID": "1:884743546828:ios:f4ddf7bc0168bb74ae9f69", + "FIREBASE_PROJECT_ID": "site-96dd0", + "GCM_SENDER_ID": "884743546828" +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 930e856e..3504dcbc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,137 +1,245 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.dev" + source: hosted + version: "61.0.0" + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: "5dce45a06d386358334eb1689108db6455d90ceb0d75848d5f4819283d4ee2b8" + url: "https://pub.dev" + source: hosted + version: "1.3.4" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.dev" + source: hosted + version: "5.13.0" + archive: + dependency: transitive + description: + name: archive + sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + url: "https://pub.dev" + source: hosted + version: "3.3.7" + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.8.2" + version: "2.11.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + url: "https://pub.dev" + source: hosted + version: "1.17.1" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.6.3" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.3" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - ffi: - dependency: transitive - description: - name: ffi - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.3.1" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "6.1.4" firebase_analytics: dependency: "direct main" description: name: firebase_analytics - url: "https://pub.dartlang.org" + sha256: d7494294bdb56a58ab6c4cfef377693835c43fa3610707346b98892fd7c4b634 + url: "https://pub.dev" source: hosted - version: "9.1.7" + version: "10.4.4" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - url: "https://pub.dartlang.org" + sha256: bcb32ac53c494bd6deb61dcd02087418ae96d063ce522a4004974a32545c3f2b + url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.6.4" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - url: "https://pub.dartlang.org" + sha256: "2f7d8ab97b46a747730f580b5a38b3e5330efb54c16af48613a6f729cc5f57fa" + url: "https://pub.dev" source: hosted - version: "0.4.0+12" + version: "0.5.4+4" firebase_core: dependency: "direct main" description: name: firebase_core - url: "https://pub.dartlang.org" + sha256: "2e9324f719e90200dc7d3c4f5d2abc26052f9f2b995d3b6626c47a0dfe1c8192" + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "2.15.0" firebase_core_platform_interface: - dependency: transitive + dependency: "direct main" description: name: firebase_core_platform_interface - url: "https://pub.dartlang.org" + sha256: b63e3be6c96ef5c33bdec1aab23c91eb00696f6452f0519401d640938c94cba2 + url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.8.0" firebase_core_web: - dependency: transitive + dependency: "direct main" description: name: firebase_core_web - url: "https://pub.dartlang.org" + sha256: "0fd5c4b228de29b55fac38aed0d9e42514b3d3bd47675de52bf7f8fccaf922fa" + url: "https://pub.dev" source: hosted - version: "1.6.3" + version: "2.6.0" + firebase_remote_config: + dependency: "direct main" + description: + name: firebase_remote_config + sha256: "2883fad38d9536487f649c558c9e902a8ae36676990e9000bc8bd72f914ac9dd" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + firebase_remote_config_platform_interface: + dependency: transitive + description: + name: firebase_remote_config_platform_interface + sha256: "250384e15b4a732d9cca97b8b9ed5a29767f5c77d6301fc653d6bbe8b26b9487" + url: "https://pub.dev" + source: hosted + version: "1.4.4" + firebase_remote_config_web: + dependency: transitive + description: + name: firebase_remote_config_web + sha256: c3e2be7d6e1416e4a1c7cc16d55a060a623e9e84c0ca0b07d9252584f39b7b08 + url: "https://pub.dev" + source: hosted + version: "1.4.4" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" + url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "2.0.2" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" + url: "https://pub.dev" + source: hosted + version: "2.0.7" flutter_test: dependency: "direct dev" description: flutter @@ -142,277 +250,541 @@ packages: description: flutter source: sdk version: "0.0.0" - google_fonts: + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + get_it: dependency: "direct main" description: - name: google_fonts - url: "https://pub.dartlang.org" + name: get_it + sha256: "529de303c739fca98cd7ece5fca500d8ff89649f1bb4b4e94fb20954abcd7468" + url: "https://pub.dev" source: hosted - version: "2.3.2" - http: + version: "7.6.0" + glob: dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + http: + dependency: "direct main" description: name: http - url: "https://pub.dartlang.org" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" source: hosted - version: "0.13.4" + version: "1.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.2" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + url: "https://pub.dev" + source: hosted + version: "0.18.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" source: hosted - version: "0.6.3" + version: "0.6.7" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.1.1" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + lottie: + dependency: "direct main" + description: + name: lottie + sha256: b8bdd54b488c54068c57d41ae85d02808da09e2bee8b8dd1f59f441e7efa60cd + url: "https://pub.dev" + source: hosted + version: "2.6.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + url: "https://pub.dev" source: hosted - version: "0.12.11" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" source: hosted - version: "1.7.0" - path: + version: "1.9.1" + mime: dependency: transitive description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - path_provider: - dependency: transitive - description: - name: path_provider - url: "https://pub.dartlang.org" + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" source: hosted - version: "2.0.8" - path_provider_android: - dependency: transitive + version: "1.0.4" + mocktail: + dependency: "direct dev" description: - name: path_provider_android - url: "https://pub.dartlang.org" + name: mocktail + sha256: "9503969a7c2c78c7292022c70c0289ed6241df7a9ba720010c0b215af29a5a58" + url: "https://pub.dev" source: hosted - version: "2.0.11" - path_provider_ios: + version: "1.0.0" + node_preamble: dependency: transitive description: - name: path_provider_ios - url: "https://pub.dartlang.org" + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" source: hosted - version: "2.0.7" - path_provider_linux: + version: "2.0.2" + package_config: dependency: transitive description: - name: path_provider_linux - url: "https://pub.dartlang.org" + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" source: hosted - version: "2.1.5" - path_provider_macos: + version: "2.1.0" + path: dependency: transitive description: - name: path_provider_macos - url: "https://pub.dartlang.org" + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" source: hosted - version: "2.0.5" - path_provider_platform_interface: + version: "1.8.3" + path_parsing: dependency: transitive description: - name: path_provider_platform_interface - url: "https://pub.dartlang.org" + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" source: hosted - version: "2.0.3" - path_provider_windows: + version: "1.0.1" + petitparser: dependency: transitive description: - name: path_provider_windows - url: "https://pub.dartlang.org" + name: petitparser + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "5.4.0" platform: dependency: transitive description: name: platform - url: "https://pub.dartlang.org" + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" source: hosted version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + url: "https://pub.dev" + source: hosted + version: "3.7.3" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" process: dependency: transitive description: name: process - url: "https://pub.dartlang.org" + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" source: hosted version: "4.2.4" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + scrollable_positioned_list: + dependency: "direct main" + description: + name: scrollable_positioned_list + sha256: "1b54d5f1329a1e263269abc9e2543d90806131aa14fe7c6062a8054d57249287" + url: "https://pub.dev" + source: hosted + version: "0.3.8" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" + test: + dependency: transitive + description: + name: test + sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + url: "https://pub.dev" + source: hosted + version: "1.24.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + url: "https://pub.dev" + source: hosted + version: "0.5.1" + test_core: + dependency: transitive + description: + name: test_core + sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + url: "https://pub.dev" source: hosted - version: "0.4.8" + version: "0.5.1" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.3.2" url_launcher: dependency: "direct main" description: name: url_launcher - url: "https://pub.dartlang.org" + sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" + url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.1.12" url_launcher_android: dependency: transitive description: name: url_launcher_android - url: "https://pub.dartlang.org" + sha256: eed4e6a1164aa9794409325c3b707ff424d4d1c2a785e7db67f8bbda00e36e51 + url: "https://pub.dev" source: hosted - version: "6.0.14" + version: "6.0.35" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - url: "https://pub.dartlang.org" + sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2" + url: "https://pub.dev" source: hosted - version: "6.0.14" + version: "6.1.4" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - url: "https://pub.dartlang.org" + sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5" + url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "3.0.5" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - url: "https://pub.dartlang.org" + sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e" + url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "3.0.5" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - url: "https://pub.dartlang.org" + sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - url: "https://pub.dartlang.org" + sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab" + url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.0.17" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - url: "https://pub.dartlang.org" + sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771" + url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "3.0.6" url_strategy: dependency: "direct main" description: name: url_strategy - url: "https://pub.dartlang.org" + sha256: "42b68b42a9864c4d710401add17ad06e28f1c1d5500c93b98c431f6b0ea4ab87" + url: "https://pub.dev" source: hosted version: "0.2.0" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f" + url: "https://pub.dev" + source: hosted + version: "1.1.7" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f" + url: "https://pub.dev" + source: hosted + version: "1.1.7" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e" + url: "https://pub.dev" + source: hosted + version: "1.1.7" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.1" - win32: + version: "2.1.4" + vm_service: dependency: transitive description: - name: win32 - url: "https://pub.dartlang.org" + name: vm_service + sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe + url: "https://pub.dev" source: hosted - version: "2.3.6" - xdg_directories: + version: "11.3.0" + watcher: dependency: transitive description: - name: xdg_directories - url: "https://pub.dartlang.org" + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + xml: + dependency: transitive + description: + name: xml + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" sdks: - dart: ">=2.16.1 <3.0.0" - flutter: ">=2.10.0-0" + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 707644f7..7655fa6d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,27 +1,66 @@ name: site description: Felipe Sales | Social Links. publish_to: 'none' -version: 1.0.0 +version: 2.0.0 environment: - sdk: ">=2.16.1 <3.0.0" + sdk: '>=3.0.0 <4.0.0' dependencies: - cupertino_icons: ^1.0.4 - firebase_analytics: ^9.1.7 - firebase_core: ^1.16.0 + auto_size_text: ^3.0.0 + cupertino_icons: ^1.0.5 + # dart_code_metrics: ^5.7.6 + firebase_analytics: ^10.4.4 + firebase_core: ^2.15.0 + firebase_core_platform_interface: ^4.8.0 + firebase_core_web: ^2.6.0 + firebase_remote_config: ^4.2.4 flutter: sdk: flutter - google_fonts: ^2.3.2 - url_launcher: ^6.1.0 + flutter_localizations: + sdk: flutter + flutter_svg: ^2.0.7 + get_it: ^7.6.0 + http: ^1.1.0 + intl: ^0.18.0 + lottie: ^2.6.0 + scrollable_positioned_list: ^0.3.8 + url_launcher: ^6.1.12 url_strategy: ^0.2.0 dev_dependencies: - flutter_lints: ^1.0.4 + flutter_lints: ^2.0.2 flutter_test: sdk: flutter + integration_test: + sdk: flutter + mocktail: ^1.0.0 flutter: uses-material-design: true assets: - assets/ + - assets/components/ + - assets/components/contact/ + - assets/components/experience/ + - assets/components/presentation/ + - assets/components/projects/ + - assets/components/social/ + fonts: + - family: Montserrat + fonts: + - asset: assets/fonts/Montserrat/Montserrat-Bold.ttf + weight: 700 + - asset: assets/fonts/Montserrat/Montserrat-Light.ttf + weight: 300 + - asset: assets/fonts/Montserrat/Montserrat-Medium.ttf + weight: 500 + - asset: assets/fonts/Montserrat/Montserrat-Regular.ttf + weight: 400 + - asset: assets/fonts/Montserrat/Montserrat-SemiBold.ttf + weight: 600 + - family: Poppins + fonts: + - asset: assets/fonts/Poppins/Poppins-Regular.ttf + weight: 400 + generate: true diff --git a/test/app/app_widget_test.dart b/test/app/app_widget_test.dart new file mode 100644 index 00000000..c373e63c --- /dev/null +++ b/test/app/app_widget_test.dart @@ -0,0 +1,31 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/app_widget.dart'; + +import '../flutter_test_config.dart'; +import '../utils/utils.dart'; + +void main() { + late MockFirebaseRemoteConfig mockFirebaseRemoteConfig; + late MockHttpClient mockHttpClient; + + setUp(() { + mockFirebaseRemoteConfig = MockFirebaseRemoteConfig(); + mockHttpClient = MockHttpClient(); + }); + + testWidgets('Should renders AppWidget', (tester) async { + await appWidgetTest( + tester: tester, + widget: AppWidget( + firebaseRemoteConfig: mockFirebaseRemoteConfig, + httpClient: mockHttpClient, + ), + ); + + expect( + find.byType(AppWidget), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/home_page_test.dart b/test/app/features/home/home_page_test.dart new file mode 100644 index 00000000..674c4d2b --- /dev/null +++ b/test/app/features/home/home_page_test.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import 'package:site/app/core/responsive/responsive.dart'; + +import 'package:site/app/features/home/home_page.dart'; +import 'package:site/app/widgets/drawer/drawer.dart'; + +import '../../../flutter_test_config.dart'; +import '../../../utils/utils.dart'; + +void main() { + late MockFirebaseRemoteConfig mockFirebaseRemoteConfig; + late MockHttpClient mockHttpClient; + + setUp(() { + mockFirebaseRemoteConfig = MockFirebaseRemoteConfig(); + mockHttpClient = MockHttpClient(); + }); + + testWidgets('Should renders HomePage', (tester) async { + await appWidgetTest( + tester: tester, + widget: HomePage( + firebaseRemoteConfig: mockFirebaseRemoteConfig, + httpClient: mockHttpClient, + ), + ); + + expect( + find.byType(HomePage), + findsOneWidget, + ); + + expect( + find.byWidgetPredicate( + (widget) => widget is LayoutBuilder, + ), + findsNWidgets(5), + ); + + expect( + find.byWidgetPredicate( + (widget) => + widget is Scaffold && widget.body is ScrollablePositionedList, + ), + findsOneWidget, + ); + }); + + group('HomePage Drawer', () { + final findDrawer = find.byWidgetPredicate( + (widget) => widget is Scaffold && widget.drawer is CustomDrawer, + ); + + testWidgets('Find CustomDrawer when is to show', (tester) async { + final isToShowDrawerWidth = tester.binding.window.physicalSizeTestValue = + Size(Breakpoints.appBar.toDouble(), 400); + + await appWidgetTest( + tester: tester, + widget: MediaQuery( + data: MediaQueryData(size: isToShowDrawerWidth), + child: HomePage( + firebaseRemoteConfig: mockFirebaseRemoteConfig, + httpClient: mockHttpClient, + ), + ), + ); + + expect(findDrawer, findsOneWidget); + + addTearDown(tester.binding.window.clearPhysicalSizeTestValue); + }); + + testWidgets('Not find Drawer when is not to show', (tester) async { + await appWidgetTest( + tester: tester, + widget: HomePage( + firebaseRemoteConfig: mockFirebaseRemoteConfig, + httpClient: mockHttpClient, + ), + ); + + expect(findDrawer, findsNothing); + + addTearDown(tester.binding.window.clearPhysicalSizeTestValue); + }); + }); +} diff --git a/test/app/features/home/widgets/contact/contact_mobile_test.dart b/test/app/features/home/widgets/contact/contact_mobile_test.dart new file mode 100644 index 00000000..f4775f5b --- /dev/null +++ b/test/app/features/home/widgets/contact/contact_mobile_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/contact/contact_mobile.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders ContactMobile', (tester) async { + await appWidgetTest( + tester: tester, + widget: const ContactMobile( + SizedBox(), + ), + ); + + expect( + find.byType(ContactMobile), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/contact/contact_web_test.dart b/test/app/features/home/widgets/contact/contact_web_test.dart new file mode 100644 index 00000000..cdce7f9b --- /dev/null +++ b/test/app/features/home/widgets/contact/contact_web_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/contact/contact_web.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders ContactWeb', (tester) async { + await appWidgetTest( + tester: tester, + widget: const ContactWeb( + SizedBox(), + ), + ); + + expect( + find.byType(ContactWeb), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/contact/contact_widget_test.dart b/test/app/features/home/widgets/contact/contact_widget_test.dart new file mode 100644 index 00000000..6b95821e --- /dev/null +++ b/test/app/features/home/widgets/contact/contact_widget_test.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/contact/contact_mobile.dart'; +import 'package:site/app/features/home/widgets/contact/contact_web.dart'; +import 'package:site/app/features/home/widgets/contact/contact_widget.dart'; +import 'package:site/app/features/home/widgets/contact/controller/contact_controller.dart'; + +import '../../../../../flutter_test_config.dart'; +import '../../../../../utils/utils.dart'; + +void main() { + late ContactController contactController; + + setUp(() { + contactController = MockContactController(); + }); + + testWidgets('Should renders Contact', (tester) async { + await appWidgetTest( + tester: tester, + widget: ContactWidget( + contactController: contactController, + ), + ); + + final contactWidget = find.byType(ContactWidget); + expect(contactWidget, findsOneWidget); + }); + + group('ContactWidget LayoutBuilder Should renders', () { + final contactMobile = find.byType(ContactMobile); + final contactWeb = find.byType(ContactWeb); + + testWidgets( + 'ContactMobile when constraints is less than Breakpoints.contact', + (tester) async { + final widthLargeSize = + tester.binding.window.physicalSizeTestValue = const Size(200, 400); + + await appWidgetTest( + tester: tester, + widget: MediaQuery( + data: MediaQueryData(size: widthLargeSize), + child: ContactWidget( + contactController: contactController, + ), + ), + ); + + expect(contactMobile, findsOneWidget); + expect(contactWeb, findsNothing); + + addTearDown(tester.binding.window.clearPhysicalSizeTestValue); + }, + ); + + testWidgets( + 'ContactWeb when constraints is greater than Breakpoints.contact', + (tester) async { + final widthLargeSize = + tester.binding.window.physicalSizeTestValue = const Size(2000, 400); + + await appWidgetTest( + tester: tester, + widget: MediaQuery( + data: MediaQueryData(size: widthLargeSize), + child: ContactWidget( + contactController: contactController, + ), + ), + ); + + expect(contactMobile, findsNothing); + expect(contactWeb, findsOneWidget); + + addTearDown(tester.binding.window.clearPhysicalSizeTestValue); + }, + ); + }); +} diff --git a/test/app/features/home/widgets/contact/controller/contact_controller_test.dart b/test/app/features/home/widgets/contact/controller/contact_controller_test.dart new file mode 100644 index 00000000..9d50e997 --- /dev/null +++ b/test/app/features/home/widgets/contact/controller/contact_controller_test.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; + +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; +import 'package:mocktail/mocktail.dart'; + +import 'package:site/app/features/home/widgets/contact/controller/contact_controller.dart'; +import 'package:site/data/repositories/contact/contact.dart'; + +import '../../../../../../utils/utils.dart'; + +void main() { + setupFirebaseAuthMocks(); + + late MockFirebaseRemoteConfig mockFirebaseRemoteConfig; + late MockHttpClient mockHttpClient; + late ContactRepositoryImpl contactRepository; + late ContactController contactController; + + setUp(() { + mockFirebaseRemoteConfig = MockFirebaseRemoteConfig(); + mockHttpClient = MockHttpClient(); + contactRepository = ContactRepositoryImpl( + firebaseRemoteConfig: mockFirebaseRemoteConfig, + httpClient: mockHttpClient, + ); + contactController = ContactController( + contactRepository: contactRepository, + ); + + when( + () => mockFirebaseRemoteConfig.getString('service_id'), + ).thenReturn('service_id'); + + when( + () => mockFirebaseRemoteConfig.getString('template_id'), + ).thenReturn('template_id'); + + when( + () => mockFirebaseRemoteConfig.getString('user_id'), + ).thenReturn('user_id'); + + when( + () => mockFirebaseRemoteConfig.getString('to_email'), + ).thenReturn('to_email'); + + when( + () => mockHttpClient.post( + any(), + headers: any(named: 'headers'), + body: any(named: 'body'), + ), + ).thenAnswer( + (_) async => http.Response('', 200), + ); + }); + + setUpAll(() async { + if (Firebase.apps.isEmpty) await Firebase.initializeApp(); + registerFallbackValue(UriFake()); + }); + + test('ContactController', () { + expect( + contactController, + isInstanceOf(), + ); + + expect( + contactController, + isInstanceOf(), + ); + + expect( + contactController.sendMail, + isInstanceOf(), + ); + + var sendMail = contactController.sendMail( + contact: AppFixtures().tContact, + ); + + expect( + () => sendMail, + isInstanceOf(), + ); + }); +} diff --git a/test/app/features/home/widgets/contact/widgets/custom_form_test.dart b/test/app/features/home/widgets/contact/widgets/custom_form_test.dart new file mode 100644 index 00000000..7d7cfc4b --- /dev/null +++ b/test/app/features/home/widgets/contact/widgets/custom_form_test.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:site/app/features/home/widgets/contact/widgets/widgets.dart'; + +import '../../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders CustomForm', (tester) async { + await appWidgetTest( + tester: tester, + widget: CustomForm( + formKey: GlobalKey(), + nameController: TextEditingController(), + emailController: TextEditingController(), + subjectController: TextEditingController(), + messageController: TextEditingController(), + onPressed: () { + debugPrint('onPressed'); + }, + ), + ); + + expect( + find.byType(CustomForm), + findsOneWidget, + ); + + expect( + find.byType(CustomTextFormField), + findsNWidgets(4), + ); + + expect( + find.byWidgetPredicate( + (widget) => + widget is CustomTextFormField && + widget.prefixIcon == Icons.account_circle, + ), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/contact/widgets/custom_text_form_field_test.dart b/test/app/features/home/widgets/contact/widgets/custom_text_form_field_test.dart new file mode 100644 index 00000000..7c91d0b5 --- /dev/null +++ b/test/app/features/home/widgets/contact/widgets/custom_text_form_field_test.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/contact/widgets/widgets.dart'; +import 'package:site/app/utils/utils.dart'; + +import '../../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders CustomTextFormField', (tester) async { + await appWidgetTest( + tester: tester, + widget: CustomTextFormField( + controller: TextEditingController(), + hintText: 'hintText', + prefixIcon: Icons.account_circle, + validator: (value) => ContactValidators.name(value), + onChanged: (value) { + debugPrint('onChanged'); + }, + keyboardType: TextInputType.text, + maxLines: 1, + ), + ); + + expect( + find.byType(CustomTextFormField), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/experience/experience_mobile_test.dart b/test/app/features/home/widgets/experience/experience_mobile_test.dart new file mode 100644 index 00000000..7f6a62d6 --- /dev/null +++ b/test/app/features/home/widgets/experience/experience_mobile_test.dart @@ -0,0 +1,18 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:site/app/features/home/widgets/experience/experience_mobile.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders ExperienceMobile', (tester) async { + await appWidgetTest( + tester: tester, + widget: const ExperienceMobile(), + ); + + expect( + find.byType(ExperienceMobile), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/experience/experience_test.dart b/test/app/features/home/widgets/experience/experience_test.dart new file mode 100644 index 00000000..5455a7a0 --- /dev/null +++ b/test/app/features/home/widgets/experience/experience_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/experience/experience.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders Experience', (tester) async { + await appWidgetTest( + tester: tester, + widget: const Experience(), + ); + + expect( + find.byType(Experience), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/experience/experience_web_test.dart b/test/app/features/home/widgets/experience/experience_web_test.dart new file mode 100644 index 00000000..b5b3ca83 --- /dev/null +++ b/test/app/features/home/widgets/experience/experience_web_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/experience/experience_web.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders ExperienceWeb', (tester) async { + await appWidgetTest( + tester: tester, + widget: const ExperienceWeb(), + ); + + expect( + find.byType(ExperienceWeb), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/experience/widgets/section_experience_texts_test.dart b/test/app/features/home/widgets/experience/widgets/section_experience_texts_test.dart new file mode 100644 index 00000000..37796c41 --- /dev/null +++ b/test/app/features/home/widgets/experience/widgets/section_experience_texts_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/experience/widgets/widgets.dart'; + +import '../../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders SectionExperienceTexts', (tester) async { + await appWidgetTest( + tester: tester, + widget: const SectionExperienceTexts(), + ); + + expect( + find.byType(SectionExperienceTexts), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/experience/widgets/title_description_short_test.dart b/test/app/features/home/widgets/experience/widgets/title_description_short_test.dart new file mode 100644 index 00000000..14ae19a0 --- /dev/null +++ b/test/app/features/home/widgets/experience/widgets/title_description_short_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/experience/widgets/widgets.dart'; + +import '../../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders TitleDescriptionShort', (tester) async { + await appWidgetTest( + tester: tester, + widget: const TitleDescriptionShort( + title: 'β€’ title', + description: ' β€” description', + ), + ); + + expect( + find.byType(TitleDescriptionShort), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/footer/custom_footer_test.dart b/test/app/features/home/widgets/footer/custom_footer_test.dart new file mode 100644 index 00000000..eb8b48e4 --- /dev/null +++ b/test/app/features/home/widgets/footer/custom_footer_test.dart @@ -0,0 +1,18 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:site/app/features/home/widgets/footer/footer.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders CustomFooter', (tester) async { + await appWidgetTest( + tester: tester, + widget: const CustomFooter(), + ); + + expect( + find.byType(CustomFooter), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/footer/footer_mobile_test.dart b/test/app/features/home/widgets/footer/footer_mobile_test.dart new file mode 100644 index 00000000..97979722 --- /dev/null +++ b/test/app/features/home/widgets/footer/footer_mobile_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/footer/footer.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders FooterMobile', (tester) async { + await appWidgetTest( + tester: tester, + widget: const FooterMobile(), + ); + + expect( + find.byType(FooterMobile), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/footer/footer_web_test.dart b/test/app/features/home/widgets/footer/footer_web_test.dart new file mode 100644 index 00000000..b631f39c --- /dev/null +++ b/test/app/features/home/widgets/footer/footer_web_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/footer/footer.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders FooterWeb', (tester) async { + await appWidgetTest( + tester: tester, + widget: const FooterWeb(), + ); + + expect( + find.byType(FooterWeb), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/footer/widgets/text_with_link_test.dart b/test/app/features/home/widgets/footer/widgets/text_with_link_test.dart new file mode 100644 index 00000000..22fdae75 --- /dev/null +++ b/test/app/features/home/widgets/footer/widgets/text_with_link_test.dart @@ -0,0 +1,25 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/features/home/widgets/footer/widgets/widgets.dart'; + +import '../../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders TextWithLink', (tester) async { + await appWidgetTest( + tester: tester, + widget: const TextWithLink( + text: AppUrls.gitHubProject, + link: AppUrls.gitHubProject, + ), + ); + + final textWithLinkFinder = find.byType(TextWithLink); + + await tester.tap(textWithLinkFinder); + await tester.pumpAndSettle(); + + expect(textWithLinkFinder, findsOneWidget); + }); +} diff --git a/test/app/features/home/widgets/presentation/presentation_mobile_test.dart b/test/app/features/home/widgets/presentation/presentation_mobile_test.dart new file mode 100644 index 00000000..cbc339d9 --- /dev/null +++ b/test/app/features/home/widgets/presentation/presentation_mobile_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/features/home/widgets/presentation/presentation_mobile.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should Renders PresentationMobile', (tester) async { + await appWidgetTest( + tester: tester, + widget: PresentationMobile( + ItemScrollController(), + ), + ); + + expect( + find.byType(PresentationMobile), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/presentation/presentation_test.dart b/test/app/features/home/widgets/presentation/presentation_test.dart new file mode 100644 index 00000000..6e553b8c --- /dev/null +++ b/test/app/features/home/widgets/presentation/presentation_test.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/features/home/widgets/presentation/presentation.dart'; +import 'package:site/app/features/home/widgets/presentation/presentation_mobile.dart'; +import 'package:site/app/features/home/widgets/presentation/presentation_web.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + group('Presentation Widget Renders', () { + testWidgets('Find Presentation', (tester) async { + await appWidgetTest( + tester: tester, + widget: Presentation(ItemScrollController()), + ); + + final presentation = find.byType(Presentation); + expect(presentation, findsOneWidget); + }); + + group('LayoutBuilder widget Should renders', () { + final presentationMobile = find.byType(PresentationMobile); + final presentationWeb = find.byType(PresentationWeb); + + testWidgets( + 'PresentationMobile when constraints is less than Breakpoints.presentation', + (tester) async { + final widthSmallSize = tester.binding.window.physicalSizeTestValue = + const Size(400, 400); + + await appWidgetTest( + tester: tester, + widget: MediaQuery( + data: MediaQueryData(size: widthSmallSize), + child: Presentation(ItemScrollController()), + ), + ); + + expect(presentationMobile, findsOneWidget); + expect(presentationWeb, findsNothing); + + addTearDown(tester.binding.window.clearPhysicalSizeTestValue); + }, + ); + + testWidgets( + 'PresentationWeb when constraints is greater than Breakpoints.presentation', + (tester) async { + final widthLargeSize = tester.binding.window.physicalSizeTestValue = + const Size(2000, 1000); + + await appWidgetTest( + tester: tester, + widget: MediaQuery( + data: MediaQueryData(size: widthLargeSize), + child: Presentation(ItemScrollController()), + ), + ); + + expect(presentationMobile, findsNothing); + expect(presentationWeb, findsOneWidget); + + addTearDown(tester.binding.window.clearPhysicalSizeTestValue); + }, + ); + }); + }); +} diff --git a/test/app/features/home/widgets/projects/projects_mobile_test.dart b/test/app/features/home/widgets/projects/projects_mobile_test.dart new file mode 100644 index 00000000..d9e19f2b --- /dev/null +++ b/test/app/features/home/widgets/projects/projects_mobile_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/projects/projects_mobile.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should Renders ProjectsMobile', (tester) async { + await appWidgetTest( + tester: tester, + widget: const ProjectsMobile(), + ); + + expect( + find.byType(ProjectsMobile), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/projects/widgets/app_text_button_widget_test.dart b/test/app/features/home/widgets/projects/widgets/app_text_button_widget_test.dart new file mode 100644 index 00000000..929b48aa --- /dev/null +++ b/test/app/features/home/widgets/projects/widgets/app_text_button_widget_test.dart @@ -0,0 +1,21 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/projects/widgets/widgets.dart'; + +import '../../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders AppTextButtonWidget', (tester) async { + await appWidgetTest( + tester: tester, + widget: const AppTextButtonWidget(), + ); + + final appTextButtonWidgetFinder = find.byType(AppTextButtonWidget); + + await tester.tap(appTextButtonWidgetFinder); + await tester.pumpAndSettle(); + + expect(appTextButtonWidgetFinder, findsOneWidget); + }); +} diff --git a/test/app/features/home/widgets/social/social_mobile_test.dart b/test/app/features/home/widgets/social/social_mobile_test.dart new file mode 100644 index 00000000..089248b5 --- /dev/null +++ b/test/app/features/home/widgets/social/social_mobile_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/social/social_mobile.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders SocialMobile', (tester) async { + await appWidgetTest( + tester: tester, + widget: const SocialMobile(), + ); + + expect( + find.byType(SocialMobile), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/social/social_test.dart b/test/app/features/home/widgets/social/social_test.dart new file mode 100644 index 00000000..0a0a158f --- /dev/null +++ b/test/app/features/home/widgets/social/social_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/social/social.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders Social', (tester) async { + await appWidgetTest( + tester: tester, + widget: const Social(), + ); + + expect( + find.byType(Social), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/social/social_web_test.dart b/test/app/features/home/widgets/social/social_web_test.dart new file mode 100644 index 00000000..bf2f9a7a --- /dev/null +++ b/test/app/features/home/widgets/social/social_web_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/social/social_web.dart'; + +import '../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders SocialWeb', (tester) async { + await appWidgetTest( + tester: tester, + widget: const SocialWeb(), + ); + + expect( + find.byType(SocialWeb), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/social/widgets/card_glassmorphism_test.dart b/test/app/features/home/widgets/social/widgets/card_glassmorphism_test.dart new file mode 100644 index 00000000..2e17ca2a --- /dev/null +++ b/test/app/features/home/widgets/social/widgets/card_glassmorphism_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/features/home/widgets/social/widgets/widgets.dart'; + +import '../../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders CardGlassmorphism', (tester) async { + await appWidgetTest( + tester: tester, + widget: const CardGlassmorphism( + child: SizedBox(), + ), + ); + + expect( + find.byType(CardGlassmorphism), + findsOneWidget, + ); + }); +} diff --git a/test/app/features/home/widgets/social/widgets/social_list_test.dart b/test/app/features/home/widgets/social/widgets/social_list_test.dart new file mode 100644 index 00000000..3ff690e1 --- /dev/null +++ b/test/app/features/home/widgets/social/widgets/social_list_test.dart @@ -0,0 +1,30 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:site/app/core/shared/shared.dart'; + +import 'package:site/app/features/home/widgets/social/widgets/widgets.dart'; + +import '../../../../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders SocialList', (tester) async { + await appWidgetTest( + tester: tester, + widget: const SocialList(), + ); + + await tester.tap(find.byKey(AppKeys.socialItemGitHub)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(AppKeys.socialItemLinkedIn)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(AppKeys.socialItemStackOverFlow)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(AppKeys.socialItemDiscord)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(AppKeys.socialItemUdemy)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(AppKeys.socialItemInstagram)); + await tester.pumpAndSettle(); + + expect(find.byType(SocialList), findsOneWidget); + }); +} diff --git a/test/app/utils/contact_validators_test.dart b/test/app/utils/contact_validators_test.dart new file mode 100644 index 00000000..e04d5d60 --- /dev/null +++ b/test/app/utils/contact_validators_test.dart @@ -0,0 +1,18 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:site/app/utils/utils.dart'; + +void main() { + test('ContactValidators methods', () { + var name = ContactValidators.name('Felipe'); + expect(() => name, isNotNull); + + var email = ContactValidators.email('fakeemail@gmail.com'); + expect(() => email, isNotNull); + + var message = ContactValidators.message('Hello, World!'); + expect(() => message, isNotNull); + + var subject = ContactValidators.subject('Hello, World!'); + expect(() => subject, isNotNull); + }); +} diff --git a/test/app/utils/date_time_utils_test.dart b/test/app/utils/date_time_utils_test.dart new file mode 100644 index 00000000..9902994b --- /dev/null +++ b/test/app/utils/date_time_utils_test.dart @@ -0,0 +1,9 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:site/app/utils/utils.dart'; + +void main() { + test('DateTimeUtils get year', () { + var date = DateTimeUtils.getYear(DateTime(2003)); + expect(date, isNotNull); + }); +} diff --git a/test/app/utils/launch_urls_test.dart b/test/app/utils/launch_urls_test.dart new file mode 100644 index 00000000..3bb57d20 --- /dev/null +++ b/test/app/utils/launch_urls_test.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:site/app/core/shared/app_urls.dart'; +import 'package:site/app/utils/utils.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + test('LaunchUrls launch Url', () { + var url = LaunchUrls().launchURL(AppUrls.gitHub); + expect( + () => url, + isNotNull, + ); + }); +} diff --git a/test/app/widgets/app_bar/mobile_app_bar_test.dart b/test/app/widgets/app_bar/mobile_app_bar_test.dart new file mode 100644 index 00000000..92a4afa1 --- /dev/null +++ b/test/app/widgets/app_bar/mobile_app_bar_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/widgets/app_bar/app_bar.dart'; + +import '../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders MobileAppBar', (tester) async { + await appWidgetTest( + tester: tester, + widget: const MobileAppBar(), + ); + + expect( + find.byType(MobileAppBar), + findsOneWidget, + ); + }); +} diff --git a/test/app/widgets/body/mobile_body_test.dart b/test/app/widgets/body/mobile_body_test.dart new file mode 100644 index 00000000..6f2566ab --- /dev/null +++ b/test/app/widgets/body/mobile_body_test.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/widgets/body/body.dart'; + +import '../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders MobileBody', (tester) async { + await appWidgetTest( + tester: tester, + widget: const MobileBody( + children: [ + SizedBox(), + ], + ), + ); + + expect( + find.byType(MobileBody), + findsOneWidget, + ); + + expect( + find.byWidgetPredicate( + (widget) => + widget is Padding && + widget.padding == const EdgeInsets.symmetric(horizontal: 16) && + widget.child is Column, + ), + findsOneWidget, + ); + + expect( + find.byWidgetPredicate( + (widget) => + widget is Column && + widget.mainAxisAlignment == MainAxisAlignment.start && + widget.crossAxisAlignment == CrossAxisAlignment.start && + widget.children.isNotEmpty, + ), + findsOneWidget, + ); + }); +} diff --git a/test/app/widgets/buttons/app_text_button_test.dart b/test/app/widgets/buttons/app_text_button_test.dart new file mode 100644 index 00000000..a40f70ce --- /dev/null +++ b/test/app/widgets/buttons/app_text_button_test.dart @@ -0,0 +1,24 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/core/shared/shared.dart'; +import 'package:site/app/utils/utils.dart'; +import 'package:site/app/widgets/buttons/buttons.dart'; + +import '../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders AppTextButton', (tester) async { + await appWidgetTest( + tester: tester, + widget: AppTextButton( + text: 'VER PROJETOS', + onPressed: () => LaunchUrls().launchURL(AppUrls.gitHub), + ), + ); + + expect( + find.byType(AppTextButton), + findsOneWidget, + ); + }); +} diff --git a/test/app/widgets/dividers/contact_divider_test.dart b/test/app/widgets/dividers/contact_divider_test.dart new file mode 100644 index 00000000..76303b48 --- /dev/null +++ b/test/app/widgets/dividers/contact_divider_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/widgets/dividers/dividers.dart'; + +import '../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders ContactDivider', (tester) async { + await appWidgetTest( + tester: tester, + widget: const ContactDivider(), + ); + + expect( + find.byType(ContactDivider), + findsOneWidget, + ); + }); +} diff --git a/test/app/widgets/dividers/custom_vertical_divider_test.dart b/test/app/widgets/dividers/custom_vertical_divider_test.dart new file mode 100644 index 00000000..53a9ce3d --- /dev/null +++ b/test/app/widgets/dividers/custom_vertical_divider_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/widgets/dividers/dividers.dart'; + +import '../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders CustomVerticalDivider', (tester) async { + await appWidgetTest( + tester: tester, + widget: const CustomVerticalDivider(width: 1), + ); + + expect( + find.byType(CustomVerticalDivider), + findsOneWidget, + ); + }); +} diff --git a/test/app/widgets/drawer/custom_drawer_test.dart b/test/app/widgets/drawer/custom_drawer_test.dart new file mode 100644 index 00000000..a25bbf02 --- /dev/null +++ b/test/app/widgets/drawer/custom_drawer_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/widgets/drawer/drawer.dart'; + +import '../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders CustomDrawer', (tester) async { + await appWidgetTest( + tester: tester, + widget: CustomDrawer( + ItemScrollController(), + ), + ); + + expect( + find.byType(CustomDrawer), + findsOneWidget, + ); + }); +} diff --git a/test/app/widgets/drawer/drawer_items_test.dart b/test/app/widgets/drawer/drawer_items_test.dart new file mode 100644 index 00000000..e49fe962 --- /dev/null +++ b/test/app/widgets/drawer/drawer_items_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/widgets/drawer/drawer.dart'; + +import '../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders DrawerItems', (tester) async { + await appWidgetTest( + tester: tester, + widget: DrawerItems( + ItemScrollController(), + ), + ); + + expect( + find.byType(DrawerItems), + findsOneWidget, + ); + }); +} diff --git a/test/app/widgets/drawer/drawer_tile_test.dart b/test/app/widgets/drawer/drawer_tile_test.dart new file mode 100644 index 00000000..bd0641b1 --- /dev/null +++ b/test/app/widgets/drawer/drawer_tile_test.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import 'package:site/app/widgets/drawer/drawer.dart'; + +import '../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders DrawerTile', (tester) async { + await appWidgetTest( + tester: tester, + widget: DrawerTile( + title: 'title', + leading: Icons.local_fire_department, + index: 0, + itemScrollController: ItemScrollController(), + ), + ); + + final drawerTile = find.byType(DrawerTile); + expect(drawerTile, findsOneWidget); + }); +} diff --git a/test/app/widgets/drawer/footer_test.dart b/test/app/widgets/drawer/footer_test.dart new file mode 100644 index 00000000..f2e09961 --- /dev/null +++ b/test/app/widgets/drawer/footer_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:site/app/widgets/drawer/drawer.dart'; + +import '../../../flutter_test_config.dart'; + +void main() { + testWidgets('Should renders Footer', (tester) async { + await appWidgetTest( + tester: tester, + widget: const Footer(), + ); + + expect( + find.byType(Footer), + findsOneWidget, + ); + }); +} diff --git a/test/app/widgets/snack_bars/app_show_snack_bar_test.dart b/test/app/widgets/snack_bars/app_show_snack_bar_test.dart new file mode 100644 index 00000000..bacfc0b4 --- /dev/null +++ b/test/app/widgets/snack_bars/app_show_snack_bar_test.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:site/app/core/shared/shared.dart'; + +import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/widgets/snack_bars/snack_bars.dart'; + +import '../../../flutter_test_config.dart'; + +const String snackBarText = 'E-mail enviado com sucesso!'; + +void main() { + testWidgets('Should Renders AppShowSnackBar', (tester) async { + await appWidgetTest( + tester: tester, + widget: Scaffold( + body: SizedBox( + height: 800, + width: 450, + child: Builder( + builder: (BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + appShowSnackBar( + context, + text: snackBarText, + icon: Icons.check, + color: AppColors.primaryDark, + width: 300, + ); + }, + child: const SizedBox( + height: 800, + width: 450, + key: AppKeys.snackBarKey, + ), + ); + }, + ), + ), + ), + ); + + expect( + find.text(snackBarText), + findsNothing, + ); + + await tester.tap( + find.byKey(AppKeys.snackBarKey), + warnIfMissed: false, + ); + + expect( + find.text(snackBarText), + findsNothing, + ); + + await tester.pump(); + + expect( + find.text(snackBarText), + findsOneWidget, + ); + }); +} diff --git a/test/data/repositories/contact/contact_repository_impl_test.dart b/test/data/repositories/contact/contact_repository_impl_test.dart new file mode 100644 index 00000000..775b7458 --- /dev/null +++ b/test/data/repositories/contact/contact_repository_impl_test.dart @@ -0,0 +1,67 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; +import 'package:mocktail/mocktail.dart'; + +import 'package:site/data/repositories/contact/contact.dart'; + +import '../../../utils/utils.dart'; + +void main() { + late MockFirebaseRemoteConfig mockFirebaseRemoteConfig; + late MockHttpClient mockHttpClient; + late ContactRepositoryImpl contactRepository; + + setUp(() { + mockFirebaseRemoteConfig = MockFirebaseRemoteConfig(); + mockHttpClient = MockHttpClient(); + + contactRepository = ContactRepositoryImpl( + firebaseRemoteConfig: mockFirebaseRemoteConfig, + httpClient: mockHttpClient, + ); + + when( + () => mockFirebaseRemoteConfig.getString('service_id'), + ).thenReturn('service_id'); + + when( + () => mockFirebaseRemoteConfig.getString('template_id'), + ).thenReturn('template_id'); + + when( + () => mockFirebaseRemoteConfig.getString('user_id'), + ).thenReturn('user_id'); + + when( + () => mockFirebaseRemoteConfig.getString('to_email'), + ).thenReturn('to_email'); + + when( + () => mockHttpClient.post( + any(), + headers: any(named: 'headers'), + body: any(named: 'body'), + ), + ).thenAnswer( + (_) async => http.Response( + '', + 200, + ), + ); + }); + + setUpAll(() { + registerFallbackValue(UriFake()); + }); + + test('ContactRepositoryImpl', () async { + expect( + contactRepository, + isNotNull, + ); + + await contactRepository.sendMail( + contact: AppFixtures().tContact, + ); + }); +} diff --git a/test/flutter_test_config.dart b/test/flutter_test_config.dart new file mode 100644 index 00000000..30b8ac05 --- /dev/null +++ b/test/flutter_test_config.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:site/app/core/l10n/l10n.dart'; + +Future testExecutable(FutureOr Function() main) async { + TestWidgetsFlutterBinding.ensureInitialized(); + await main(); +} + +Future appWidgetTest({ + required WidgetTester tester, + required Widget widget, +}) async { + await tester.pumpWidget( + MaterialApp( + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: widget, + ), + ); +} diff --git a/test/utils/fakes/fakes.dart b/test/utils/fakes/fakes.dart new file mode 100644 index 00000000..36a9288a --- /dev/null +++ b/test/utils/fakes/fakes.dart @@ -0,0 +1 @@ +export 'uri_fake.dart'; diff --git a/test/utils/fakes/uri_fake.dart b/test/utils/fakes/uri_fake.dart new file mode 100644 index 00000000..abb8c09b --- /dev/null +++ b/test/utils/fakes/uri_fake.dart @@ -0,0 +1,6 @@ +import 'package:mocktail/mocktail.dart'; + +class UriFake extends Fake implements Uri { + @override + String get path => 'path'; +} diff --git a/test/utils/fixtures/app_fixtures.dart b/test/utils/fixtures/app_fixtures.dart new file mode 100644 index 00000000..51093165 --- /dev/null +++ b/test/utils/fixtures/app_fixtures.dart @@ -0,0 +1,10 @@ +import 'package:site/data/models/models.dart'; + +class AppFixtures { + final tContact = Contact( + name: 'felipecastrosales', + email: 'fakeemail@gmail.com', + message: 'Hello, World!', + subject: 'Hello, World!', + ); +} diff --git a/test/utils/fixtures/fixtures.dart b/test/utils/fixtures/fixtures.dart new file mode 100644 index 00000000..4e7c3732 --- /dev/null +++ b/test/utils/fixtures/fixtures.dart @@ -0,0 +1 @@ +export 'app_fixtures.dart'; diff --git a/test/utils/mocks/mock_build_context.dart b/test/utils/mocks/mock_build_context.dart new file mode 100644 index 00000000..4ee3ccae --- /dev/null +++ b/test/utils/mocks/mock_build_context.dart @@ -0,0 +1,5 @@ +import 'package:flutter/material.dart'; + +import 'package:mocktail/mocktail.dart'; + +class MockBuildContext extends Mock implements BuildContext {} diff --git a/test/utils/mocks/mock_contact_controller.dart b/test/utils/mocks/mock_contact_controller.dart new file mode 100644 index 00000000..8d03a1b0 --- /dev/null +++ b/test/utils/mocks/mock_contact_controller.dart @@ -0,0 +1,5 @@ +import 'package:mocktail/mocktail.dart'; + +import 'package:site/app/features/home/widgets/contact/controller/contact_controller.dart'; + +class MockContactController extends Mock implements ContactController {} diff --git a/test/utils/mocks/mock_firebase_remote_config.dart b/test/utils/mocks/mock_firebase_remote_config.dart new file mode 100644 index 00000000..a9c2ffc7 --- /dev/null +++ b/test/utils/mocks/mock_firebase_remote_config.dart @@ -0,0 +1,4 @@ +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:mocktail/mocktail.dart'; + +class MockFirebaseRemoteConfig extends Mock implements FirebaseRemoteConfig {} diff --git a/test/utils/mocks/mock_http_client.dart b/test/utils/mocks/mock_http_client.dart new file mode 100644 index 00000000..254543e8 --- /dev/null +++ b/test/utils/mocks/mock_http_client.dart @@ -0,0 +1,4 @@ +import 'package:http/http.dart' as http; +import 'package:mocktail/mocktail.dart'; + +class MockHttpClient extends Mock implements http.Client {} diff --git a/test/utils/mocks/mocks.dart b/test/utils/mocks/mocks.dart new file mode 100644 index 00000000..b68f7754 --- /dev/null +++ b/test/utils/mocks/mocks.dart @@ -0,0 +1,3 @@ +export 'mock_contact_controller.dart'; +export 'mock_firebase_remote_config.dart'; +export 'mock_http_client.dart'; diff --git a/test/utils/setup/setup.dart b/test/utils/setup/setup.dart new file mode 100644 index 00000000..99d3db42 --- /dev/null +++ b/test/utils/setup/setup.dart @@ -0,0 +1 @@ +export 'setup_firebase.dart'; diff --git a/test/utils/setup/setup_firebase.dart b/test/utils/setup/setup_firebase.dart new file mode 100644 index 00000000..4619a836 --- /dev/null +++ b/test/utils/setup/setup_firebase.dart @@ -0,0 +1,20 @@ +import 'package:flutter/services.dart'; + +import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +typedef Callback = void Function(MethodCall call); + +// ignore: avoid-unused-parameters +void setupFirebaseAuthMocks([Callback? customHandlers]) { + TestWidgetsFlutterBinding.ensureInitialized(); + setupFirebaseCoreMocks(); +} + +Future neverEndingFuture() async { + while (true) { + await Future.delayed( + const Duration(minutes: 5), + ); + } +} diff --git a/test/utils/utils.dart b/test/utils/utils.dart new file mode 100644 index 00000000..c14f5e3b --- /dev/null +++ b/test/utils/utils.dart @@ -0,0 +1,4 @@ +export 'fakes/fakes.dart'; +export 'fixtures/fixtures.dart'; +export 'mocks/mocks.dart'; +export 'setup/setup.dart'; diff --git a/web/index.html b/web/index.html index be41030d..3edeb54c 100644 --- a/web/index.html +++ b/web/index.html @@ -25,40 +25,74 @@ + + Felipe Sales | Social Links + + + +
+ + + +
+ - - + + diff --git a/web/style.css b/web/style.css new file mode 100644 index 00000000..4ccf96ce --- /dev/null +++ b/web/style.css @@ -0,0 +1,64 @@ +body { + margin: 0; + padding: 0; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background: #070519; + box-shadow: inset 0 0 80px #000, inset 0 0 200px #000, inset 0 0 80px #000; +} + +.loading { + position: relative; + width: 350px; + height: 350px; + border-radius: 50%; + background: linear-gradient(#070519, #4A3AFF, #4361EE); + animation: animate 2s linear infinite; +} + +@keyframes animate { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.loading span { + position: absolute; + width: 100%; + height: 100%; + border-radius: 50%; + background: linear-gradient(#070519, #4A3AFF, #4361EE); +} + +.loading span:nth-child(1) { + filter: blur(4px); +} + +.loading span:nth-child(2) { + filter: blur(8px); +} + +.loading span:nth-child(3) { + filter: blur(16px); +} + +.loading span:nth-child(4) { + filter: blur(32px); +} + +.loading:after { + content: ''; + position: absolute; + top: 10px; + left: 10px; + right: 10px; + bottom: 10px; + background: #070519; + border: solid #070519 10px; + border-radius: 50%; +}