├── .fvmrc ├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── .metadata ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── hungps │ │ │ │ ├── flutter_pokedex │ │ │ │ └── MainActivity.kt │ │ │ │ └── flutterpokedex │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── files │ ├── abilites.json │ ├── items.json │ ├── moves.json │ └── pokemons.json ├── fonts │ ├── CircularStd-Black.ttf │ ├── CircularStd-Bold.ttf │ ├── CircularStd-Book.ttf │ └── CircularStd-Medium.ttf └── images │ ├── bulbasaur.png │ ├── charmander.png │ ├── chespin.png │ ├── chikorita.png │ ├── chimchar.png │ ├── cyndaquil.png │ ├── dotted.png │ ├── female.png │ ├── fennekin.png │ ├── froakie.png │ ├── grookey.png │ ├── litten.png │ ├── male.png │ ├── mudkip.png │ ├── oshawott.png │ ├── pika_loader.gif │ ├── piplup.png │ ├── pokeball.png │ ├── popplio.png │ ├── rowlet.png │ ├── scorbunny.png │ ├── snivy.png │ ├── sobble.png │ ├── squirtle.png │ ├── tepig.png │ ├── thumbnail.png │ ├── torchic.png │ ├── totodile.png │ ├── treecko.png │ └── turtwig.png ├── build.yaml ├── flutter_01.png ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── core │ ├── exceptions.dart │ └── usecase.dart ├── data │ ├── entities │ │ ├── item.dart │ │ ├── item.freezed.dart │ │ ├── item.g.dart │ │ ├── pokemon.dart │ │ ├── pokemon.freezed.dart │ │ ├── pokemon.g.dart │ │ ├── pokemon_generation.dart │ │ ├── pokemon_generation.freezed.dart │ │ ├── pokemon_generation.g.dart │ │ ├── pokemon_properties.dart │ │ ├── pokemon_properties.freezed.dart │ │ ├── pokemon_properties.g.dart │ │ └── pokemon_types.dart │ ├── generations.dart │ ├── repositories │ │ ├── item_repository.dart │ │ ├── item_repository.default.dart │ │ ├── pokemon_repository.dart │ │ └── pokemon_repository.default.dart │ ├── source │ │ ├── github │ │ │ ├── github_datasource.dart │ │ │ ├── models │ │ │ │ ├── item.dart │ │ │ │ ├── item.g.dart │ │ │ │ ├── pokemon.dart │ │ │ │ └── pokemon.g.dart │ │ │ └── network.dart │ │ ├── local │ │ │ ├── local_datasource.dart │ │ │ └── models │ │ │ │ ├── item.dart │ │ │ │ ├── item.g.dart │ │ │ │ ├── pokemon.dart │ │ │ │ ├── pokemon.g.dart │ │ │ │ ├── pokemon_gender.dart │ │ │ │ ├── pokemon_gender.g.dart │ │ │ │ ├── pokemon_stats.dart │ │ │ │ └── pokemon_stats.g.dart │ │ └── mappers │ │ │ ├── github_to_local_mapper.dart │ │ │ └── local_to_entity_mapper.dart │ ├── states │ │ ├── item │ │ │ ├── item_bloc.dart │ │ │ ├── item_event.dart │ │ │ ├── item_event.freezed.dart │ │ │ ├── item_selector.dart │ │ │ ├── item_state.dart │ │ │ └── item_state.freezed.dart │ │ ├── pokemon │ │ │ ├── pokemon_bloc.dart │ │ │ ├── pokemon_event.dart │ │ │ ├── pokemon_event.freezed.dart │ │ │ ├── pokemon_selector.dart │ │ │ ├── pokemon_state.dart │ │ │ └── pokemon_state.freezed.dart │ │ └── settings │ │ │ ├── settings_bloc.dart │ │ │ ├── settings_event.dart │ │ │ ├── settings_event.freezed.dart │ │ │ ├── settings_selector.dart │ │ │ ├── settings_state.dart │ │ │ └── settings_state.freezed.dart │ ├── types.dart │ └── usecases │ │ ├── item_usecases.dart │ │ └── pokemon_usecases.dart ├── di.config.dart ├── di.dart ├── main.dart ├── presenter │ ├── app.dart │ ├── assets.gen.dart │ ├── fonts.gen.dart │ ├── modals │ │ ├── generation_modal.dart │ │ └── search_modal.dart │ ├── navigation │ │ ├── navigation.dart │ │ └── navigation.gr.dart │ ├── pages │ │ ├── home │ │ │ ├── home.dart │ │ │ ├── sections │ │ │ │ ├── header.dart │ │ │ │ └── news.dart │ │ │ └── widgets │ │ │ │ ├── category_card.dart │ │ │ │ └── news_card.dart │ │ ├── items │ │ │ ├── items.dart │ │ │ ├── sections │ │ │ │ ├── fab_menu.dart │ │ │ │ └── items_grid.dart │ │ │ └── widgets │ │ │ │ ├── item_card.dart │ │ │ │ └── item_category.dart │ │ ├── pokedex │ │ │ ├── pokedex.dart │ │ │ ├── sections │ │ │ │ ├── fab_menu.dart │ │ │ │ └── pokemon_grid.dart │ │ │ └── widgets │ │ │ │ └── generation_card.dart │ │ ├── pokemon_info │ │ │ ├── pokemon_info.dart │ │ │ ├── sections │ │ │ │ ├── background_decoration.dart │ │ │ │ ├── pokemon_info_card.dart │ │ │ │ ├── pokemon_info_card_about.dart │ │ │ │ ├── pokemon_info_card_basestats.dart │ │ │ │ ├── pokemon_info_card_evolutions.dart │ │ │ │ └── pokemon_overall_info.dart │ │ │ └── state_provider.dart │ │ ├── splash │ │ │ └── splash.dart │ │ └── types │ │ │ ├── bold_texts.dart │ │ │ ├── colored_pokeball.dart │ │ │ ├── modal_contents.dart │ │ │ ├── modal_draggable.dart │ │ │ ├── modal_sheet.dart │ │ │ ├── redirector.dart │ │ │ ├── type_container.dart │ │ │ ├── type_entities │ │ │ ├── type_constants.dart │ │ │ ├── type_funcs.dart │ │ │ └── widget_list.dart │ │ │ ├── type_grid.dart │ │ │ └── types.dart │ ├── themes │ │ ├── colors.dart │ │ ├── extensions.dart │ │ ├── styles.dart │ │ ├── themes.dart │ │ ├── themes │ │ │ ├── themes.dark.dart │ │ │ └── themes.light.dart │ │ └── typography.dart │ └── widgets │ │ ├── animated_fade.dart │ │ ├── animated_overlay.dart │ │ ├── animated_slide.dart │ │ ├── app_bar.dart │ │ ├── auto_slideup_panel.dart │ │ ├── button.dart │ │ ├── decoration.dart │ │ ├── fab.dart │ │ ├── hero.dart │ │ ├── input.dart │ │ ├── keyboard.dart │ │ ├── loading.dart │ │ ├── main_tab_view.dart │ │ ├── modal.dart │ │ ├── pokemon_card.dart │ │ ├── pokemon_image.dart │ │ ├── pokemon_refresh_control.dart │ │ ├── pokemon_type.dart │ │ ├── progress.dart │ │ └── scaffold.dart └── utils │ ├── extensions │ ├── animation.dart │ └── string.dart │ ├── size.dart │ └── string.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Podfile ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── app_icon_1024.png │ │ ├── app_icon_128.png │ │ ├── app_icon_16.png │ │ ├── app_icon_256.png │ │ ├── app_icon_32.png │ │ ├── app_icon_512.png │ │ └── app_icon_64.png │ ├── Base.lproj │ └── MainMenu.xib │ ├── Configs │ ├── AppInfo.xcconfig │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements ├── pubspec.lock ├── pubspec.yaml ├── screenshots ├── home-news.png ├── home.png ├── pokedex-fab-generation.png ├── pokedex-fab.png ├── pokedex.png ├── pokemon-info-about.png ├── pokemon-info-base-stats.png ├── pokemon-info-evolution.png ├── pokemon-info-expanded.png └── thumbnail.png ├── test └── widget_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.fvmrc: -------------------------------------------------------------------------------- 1 | { 2 | "flutter": "3.29.3", 3 | "flavors": {} 4 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /**/*.g.dart linguist-generated=true 2 | /**/*.gr.dart linguist-generated=true 3 | /**/*.gen.dart linguist-generated=true 4 | /**/*.freezed.dart linguist-generated=true 5 | /**/*.config.dart linguist-generated=true 6 | /**/*.lock linguist-generated=true 7 | 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['https://www.buymeacoffee.com/hungps', 'https://paypal.me/hungps'] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/Flutter/flutter_export_environment.sh 65 | **/ios/ServiceDefinitions.json 66 | **/ios/Runner/GeneratedPluginRegistrant.* 67 | **/ios/build/ 68 | 69 | # Exceptions to above rules. 70 | !**/ios/**/default.mode1v3 71 | !**/ios/**/default.mode2v3 72 | !**/ios/**/default.pbxuser 73 | !**/ios/**/default.perspectivev3 74 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 75 | 76 | .flutter-plugins-dependencies 77 | 78 | generate.sh 79 | 80 | # FVM Version Cache 81 | .fvm/ 82 | 83 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 9944297138845a94256f1cf37beb88ff9a8e811a 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 17 | base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 18 | - platform: android 19 | create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 20 | base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 21 | - platform: ios 22 | create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 23 | base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 24 | - platform: linux 25 | create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 26 | base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 27 | - platform: macos 28 | create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 29 | base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 30 | - platform: web 31 | create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 32 | base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 33 | - platform: windows 34 | create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 35 | base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "dart-code.dart-code", 8 | "dart-code.flutter", 9 | "Gruntfuggly.triggertaskonsave" 10 | ], 11 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 12 | "unwantedRecommendations": [] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Run: Development", 9 | "request": "launch", 10 | "type": "dart", 11 | "program": "lib/main.dart" 12 | }, 13 | { 14 | "name": "Run in Profile mode: Development", 15 | "request": "launch", 16 | "type": "dart", 17 | "program": "lib/main.dart", 18 | "flutterMode": "profile" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "dart.previewFlutterUiGuides": true, 4 | "dart.previewFlutterUiGuidesCustomTracking": true, 5 | "dart.flutterSdkPath": ".fvm/flutter_sdk", 6 | "[dart]": { 7 | "editor.selectionHighlight": false, 8 | "editor.suggest.snippetsPreventQuickSuggestions": false, 9 | "editor.suggestSelection": "first", 10 | "editor.tabCompletion": "onlySnippets", 11 | "editor.wordBasedSuggestions": "off", 12 | "files.insertFinalNewline": true 13 | }, 14 | "triggerTaskOnSave.on": true, 15 | "triggerTaskOnSave.showStatusBarToggle": true, 16 | "triggerTaskOnSave.tasks": { 17 | "easy_localization: generate keys": ["**/translations/*"] 18 | }, 19 | "search.exclude": { 20 | "**/.fvm": true, 21 | "**/build": true 22 | }, 23 | "files.watcherExclude": { 24 | "**/.fvm": true, 25 | "**/build": true 26 | }, 27 | "files.exclude": { 28 | "**/.gitkeep": true, 29 | "**/*.g.dart": true, 30 | "**/*.gr.dart": true, 31 | "**/*.gen.dart": true, 32 | "**/*.freezed.dart": true, 33 | "**/*.config.dart": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build_runner: watch", 8 | "type": "flutter", 9 | "command": "flutter", 10 | "args": [ 11 | "pub", 12 | "run", 13 | "build_runner", 14 | "watch", 15 | "--delete-conflicting-outputs" 16 | ], 17 | "problemMatcher": ["$dart-build_runner"], 18 | "group": "build", 19 | "isBackground": true, 20 | "runOptions": { 21 | "runOn": "folderOpen", 22 | "instanceLimit": 1, 23 | "reevaluateOnRerun": true 24 | } 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Pham Sy Hung 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | Copyright (c) 2019 Pham Sy Hung 24 | 25 | Licensed under the Apache License, Version 2.0 (the "License"); 26 | you may not use this file except in compliance with the License. 27 | You may obtain a copy of the License at 28 | 29 | http://www.apache.org/licenses/LICENSE-2.0 30 | 31 | Unless required by applicable law or agreed to in writing, software 32 | distributed under the License is distributed on an "AS IS" BASIS, 33 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 34 | See the License for the specific language governing permissions and 35 | limitations under the License. 36 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | 30 | analyzer: 31 | errors: 32 | invalid_annotation_target: ignore 33 | 34 | formatter: 35 | page_width: 100 36 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | 15 | .cxx/ 16 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace 'com.hungps.flutterpokedex' 27 | 28 | compileSdkVersion flutter.compileSdkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.hungps.flutterpokedex" 46 | minSdkVersion flutter.minSdkVersion 47 | targetSdkVersion flutter.targetSdkVersion 48 | versionCode flutterVersionCode.toInteger() 49 | versionName flutterVersionName 50 | } 51 | 52 | buildTypes { 53 | release { 54 | // TODO: Add your own signing config for the release build. 55 | // Signing with the debug keys for now, so `flutter run --release` works. 56 | signingConfig signingConfigs.debug 57 | 58 | minifyEnabled true 59 | // useProguard true 60 | 61 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 62 | } 63 | } 64 | } 65 | 66 | flutter { 67 | source '../..' 68 | } 69 | 70 | dependencies { 71 | } 72 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 16 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/hungps/flutter_pokedex/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.hungps.flutter_pokedex 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/hungps/flutterpokedex/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.hungps.flutterpokedex 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = '../build' 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(':app') 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.layout.buildDirectory 18 | } 19 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat May 03 22:04:52 ICT 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" // apply true 21 | id "com.android.application" version "8.8.2" apply false 22 | id "org.jetbrains.kotlin.android" version "2.1.20" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /assets/fonts/CircularStd-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/fonts/CircularStd-Black.ttf -------------------------------------------------------------------------------- /assets/fonts/CircularStd-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/fonts/CircularStd-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/CircularStd-Book.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/fonts/CircularStd-Book.ttf -------------------------------------------------------------------------------- /assets/fonts/CircularStd-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/fonts/CircularStd-Medium.ttf -------------------------------------------------------------------------------- /assets/images/bulbasaur.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/bulbasaur.png -------------------------------------------------------------------------------- /assets/images/charmander.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/charmander.png -------------------------------------------------------------------------------- /assets/images/chespin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/chespin.png -------------------------------------------------------------------------------- /assets/images/chikorita.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/chikorita.png -------------------------------------------------------------------------------- /assets/images/chimchar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/chimchar.png -------------------------------------------------------------------------------- /assets/images/cyndaquil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/cyndaquil.png -------------------------------------------------------------------------------- /assets/images/dotted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/dotted.png -------------------------------------------------------------------------------- /assets/images/female.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/female.png -------------------------------------------------------------------------------- /assets/images/fennekin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/fennekin.png -------------------------------------------------------------------------------- /assets/images/froakie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/froakie.png -------------------------------------------------------------------------------- /assets/images/grookey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/grookey.png -------------------------------------------------------------------------------- /assets/images/litten.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/litten.png -------------------------------------------------------------------------------- /assets/images/male.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/male.png -------------------------------------------------------------------------------- /assets/images/mudkip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/mudkip.png -------------------------------------------------------------------------------- /assets/images/oshawott.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/oshawott.png -------------------------------------------------------------------------------- /assets/images/pika_loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/pika_loader.gif -------------------------------------------------------------------------------- /assets/images/piplup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/piplup.png -------------------------------------------------------------------------------- /assets/images/pokeball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/pokeball.png -------------------------------------------------------------------------------- /assets/images/popplio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/popplio.png -------------------------------------------------------------------------------- /assets/images/rowlet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/rowlet.png -------------------------------------------------------------------------------- /assets/images/scorbunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/scorbunny.png -------------------------------------------------------------------------------- /assets/images/snivy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/snivy.png -------------------------------------------------------------------------------- /assets/images/sobble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/sobble.png -------------------------------------------------------------------------------- /assets/images/squirtle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/squirtle.png -------------------------------------------------------------------------------- /assets/images/tepig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/tepig.png -------------------------------------------------------------------------------- /assets/images/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/thumbnail.png -------------------------------------------------------------------------------- /assets/images/torchic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/torchic.png -------------------------------------------------------------------------------- /assets/images/totodile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/totodile.png -------------------------------------------------------------------------------- /assets/images/treecko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/treecko.png -------------------------------------------------------------------------------- /assets/images/turtwig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/assets/images/turtwig.png -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | auto_route_generator:auto_route_generator: 5 | options: 6 | enable_cached_builds: true 7 | generate_for: 8 | - lib/presenter/pages/**/*.dart 9 | auto_route_generator:auto_router_generator: 10 | options: 11 | enable_cached_builds: true 12 | generate_for: 13 | - lib/presenter/navigation/navigation.dart 14 | -------------------------------------------------------------------------------- /flutter_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/flutter_01.png -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - path_provider_foundation (0.0.1): 4 | - Flutter 5 | - FlutterMacOS 6 | - sqflite_darwin (0.0.4): 7 | - Flutter 8 | - FlutterMacOS 9 | 10 | DEPENDENCIES: 11 | - Flutter (from `Flutter`) 12 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 13 | - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) 14 | 15 | EXTERNAL SOURCES: 16 | Flutter: 17 | :path: Flutter 18 | path_provider_foundation: 19 | :path: ".symlinks/plugins/path_provider_foundation/darwin" 20 | sqflite_darwin: 21 | :path: ".symlinks/plugins/sqflite_darwin/darwin" 22 | 23 | SPEC CHECKSUMS: 24 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 25 | path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 26 | sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 27 | 28 | PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011 29 | 30 | COCOAPODS: 1.15.2 31 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Pokedex 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_pokedex 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/core/exceptions.dart: -------------------------------------------------------------------------------- 1 | class ServerException implements Exception {} 2 | 3 | class CacheException implements Exception {} 4 | -------------------------------------------------------------------------------- /lib/core/usecase.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | abstract class UseCase { 4 | const UseCase(); 5 | 6 | FutureOr call(Params params); 7 | } 8 | 9 | class NoParams {} 10 | -------------------------------------------------------------------------------- /lib/data/entities/item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'item.freezed.dart'; 5 | part 'item.g.dart'; 6 | 7 | @freezed 8 | abstract class Item with _$Item { 9 | const factory Item({ 10 | required String name, 11 | required String image, 12 | required String category, 13 | required String effect, 14 | }) = _Item; 15 | 16 | factory Item.fromJson(Map json) => _$ItemFromJson(json); 17 | } 18 | -------------------------------------------------------------------------------- /lib/data/entities/item.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'item.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _Item _$ItemFromJson(Map json) => _Item( 10 | name: json['name'] as String, 11 | image: json['image'] as String, 12 | category: json['category'] as String, 13 | effect: json['effect'] as String, 14 | ); 15 | 16 | Map _$ItemToJson(_Item instance) => { 17 | 'name': instance.name, 18 | 'image': instance.image, 19 | 'category': instance.category, 20 | 'effect': instance.effect, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/data/entities/pokemon.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | import 'package:pokedex/data/entities/pokemon_properties.dart'; 6 | import 'package:pokedex/data/entities/pokemon_types.dart'; 7 | 8 | part 'pokemon.freezed.dart'; 9 | part 'pokemon.g.dart'; 10 | 11 | @freezed 12 | abstract class Pokemon with _$Pokemon { 13 | const factory Pokemon({ 14 | required String number, 15 | required String name, 16 | required String description, 17 | required List types, 18 | required String image, 19 | required String height, 20 | required String weight, 21 | required String genera, 22 | required List eggGroups, 23 | required PokemonGender gender, 24 | required PokemonStats stats, 25 | required double baseExp, 26 | required List evolutions, 27 | required String evolutionReason, 28 | }) = _Pokemon; 29 | 30 | const Pokemon._(); 31 | 32 | factory Pokemon.fromJson(Map json) => _$PokemonFromJson(json); 33 | 34 | Color get color => types.first.color; 35 | 36 | Map get typeEffectiveness { 37 | final effectiveness = PokemonTypes.values.where((type) => type != PokemonTypes.unknown).map( 38 | (type) => MapEntry( 39 | type, 40 | types 41 | .map((pokemonType) => pokemonType.effectiveness[type] ?? 1.0) 42 | .reduce((total, effectiveness) => total * effectiveness), 43 | ), 44 | ); 45 | 46 | return Map.fromEntries(effectiveness); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/data/entities/pokemon_generation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'pokemon_generation.freezed.dart'; 5 | part 'pokemon_generation.g.dart'; 6 | 7 | @freezed 8 | abstract class PokemonGeneration with _$PokemonGeneration { 9 | const factory PokemonGeneration({ 10 | required String title, 11 | required List pokemonImages, 12 | }) = _PokemonGeneration; 13 | 14 | factory PokemonGeneration.fromJson(Map json) => 15 | _$PokemonGenerationFromJson(json); 16 | } 17 | -------------------------------------------------------------------------------- /lib/data/entities/pokemon_generation.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'pokemon_generation.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _PokemonGeneration _$PokemonGenerationFromJson(Map json) => _PokemonGeneration( 10 | title: json['title'] as String, 11 | pokemonImages: (json['pokemonImages'] as List).map((e) => e as String).toList(), 12 | ); 13 | 14 | Map _$PokemonGenerationToJson(_PokemonGeneration instance) => { 15 | 'title': instance.title, 16 | 'pokemonImages': instance.pokemonImages, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/data/entities/pokemon_properties.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'pokemon_properties.freezed.dart'; 5 | part 'pokemon_properties.g.dart'; 6 | 7 | @freezed 8 | abstract class PokemonGender with _$PokemonGender { 9 | const factory PokemonGender({ 10 | required bool genderless, 11 | required double maleRate, 12 | required double femaleRate, 13 | }) = _PokemonGender; 14 | 15 | factory PokemonGender.fromJson(Map json) => _$PokemonGenderFromJson(json); 16 | } 17 | 18 | @freezed 19 | abstract class PokemonStats with _$PokemonStats { 20 | const factory PokemonStats({ 21 | required int attack, 22 | required int specialAttack, 23 | required int defense, 24 | required int specialDefense, 25 | required int hp, 26 | required int speed, 27 | }) = _PokemonStats; 28 | 29 | const PokemonStats._(); 30 | 31 | int get total => attack + specialAttack + defense + specialDefense + hp + speed; 32 | 33 | factory PokemonStats.fromJson(Map json) => _$PokemonStatsFromJson(json); 34 | } 35 | -------------------------------------------------------------------------------- /lib/data/entities/pokemon_properties.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'pokemon_properties.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _PokemonGender _$PokemonGenderFromJson(Map json) => _PokemonGender( 10 | genderless: json['genderless'] as bool, 11 | maleRate: (json['maleRate'] as num).toDouble(), 12 | femaleRate: (json['femaleRate'] as num).toDouble(), 13 | ); 14 | 15 | Map _$PokemonGenderToJson(_PokemonGender instance) => { 16 | 'genderless': instance.genderless, 17 | 'maleRate': instance.maleRate, 18 | 'femaleRate': instance.femaleRate, 19 | }; 20 | 21 | _PokemonStats _$PokemonStatsFromJson(Map json) => _PokemonStats( 22 | attack: (json['attack'] as num).toInt(), 23 | specialAttack: (json['specialAttack'] as num).toInt(), 24 | defense: (json['defense'] as num).toInt(), 25 | specialDefense: (json['specialDefense'] as num).toInt(), 26 | hp: (json['hp'] as num).toInt(), 27 | speed: (json['speed'] as num).toInt(), 28 | ); 29 | 30 | Map _$PokemonStatsToJson(_PokemonStats instance) => { 31 | 'attack': instance.attack, 32 | 'specialAttack': instance.specialAttack, 33 | 'defense': instance.defense, 34 | 'specialDefense': instance.specialDefense, 35 | 'hp': instance.hp, 36 | 'speed': instance.speed, 37 | }; 38 | -------------------------------------------------------------------------------- /lib/data/generations.dart: -------------------------------------------------------------------------------- 1 | import 'package:pokedex/data/entities/pokemon_generation.dart'; 2 | 3 | const List pokemonGenerations = [ 4 | PokemonGeneration( 5 | title: 'Generation I', 6 | pokemonImages: [ 7 | 'assets/images/bulbasaur.png', 8 | 'assets/images/charmander.png', 9 | 'assets/images/squirtle.png' 10 | ], 11 | ), 12 | PokemonGeneration( 13 | title: 'Generation II', 14 | pokemonImages: [ 15 | 'assets/images/chikorita.png', 16 | 'assets/images/cyndaquil.png', 17 | 'assets/images/totodile.png' 18 | ], 19 | ), 20 | PokemonGeneration( 21 | title: 'Generation III', 22 | pokemonImages: [ 23 | 'assets/images/treecko.png', 24 | 'assets/images/torchic.png', 25 | 'assets/images/mudkip.png' 26 | ], 27 | ), 28 | PokemonGeneration( 29 | title: 'Generation IV', 30 | pokemonImages: [ 31 | 'assets/images/turtwig.png', 32 | 'assets/images/chimchar.png', 33 | 'assets/images/piplup.png' 34 | ], 35 | ), 36 | PokemonGeneration( 37 | title: 'Generation V', 38 | pokemonImages: [ 39 | 'assets/images/snivy.png', 40 | 'assets/images/tepig.png', 41 | 'assets/images/oshawott.png' 42 | ], 43 | ), 44 | PokemonGeneration( 45 | title: 'Generation VI', 46 | pokemonImages: [ 47 | 'assets/images/chespin.png', 48 | 'assets/images/fennekin.png', 49 | 'assets/images/froakie.png' 50 | ], 51 | ), 52 | PokemonGeneration( 53 | title: 'Generation VII', 54 | pokemonImages: [ 55 | 'assets/images/rowlet.png', 56 | 'assets/images/litten.png', 57 | 'assets/images/popplio.png' 58 | ], 59 | ), 60 | PokemonGeneration( 61 | title: 'Generation VIII', 62 | pokemonImages: [ 63 | 'assets/images/grookey.png', 64 | 'assets/images/scorbunny.png', 65 | 'assets/images/sobble.png' 66 | ], 67 | ), 68 | ]; 69 | -------------------------------------------------------------------------------- /lib/data/repositories/item_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:pokedex/data/entities/item.dart'; 2 | 3 | abstract class ItemRepository { 4 | const ItemRepository(); 5 | 6 | Future> getAllItems(); 7 | 8 | Future> getItems({required int limit, required int page}); 9 | } 10 | -------------------------------------------------------------------------------- /lib/data/repositories/item_repository.default.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:pokedex/data/repositories/item_repository.dart'; 3 | import 'package:pokedex/data/source/github/github_datasource.dart'; 4 | import 'package:pokedex/data/source/local/local_datasource.dart'; 5 | import 'package:pokedex/data/source/mappers/github_to_local_mapper.dart'; 6 | import 'package:pokedex/data/source/mappers/local_to_entity_mapper.dart'; 7 | import 'package:pokedex/data/entities/item.dart'; 8 | 9 | @Singleton(as: ItemRepository) 10 | class DefaultItemRepository extends ItemRepository { 11 | final GithubDataSource _githubDataSource; 12 | final LocalDataSource _localDataSource; 13 | 14 | const DefaultItemRepository({ 15 | required GithubDataSource githubDataSource, 16 | required LocalDataSource localDataSource, 17 | }) : _githubDataSource = githubDataSource, 18 | _localDataSource = localDataSource; 19 | 20 | @override 21 | Future> getAllItems() async { 22 | final hasCachedData = await _localDataSource.hasData(); 23 | 24 | if (!hasCachedData) { 25 | final itemGithubModel = await _githubDataSource.getItems(); 26 | final itemHiveModels = itemGithubModel.map((e) => e.toHiveModel()); 27 | 28 | await _localDataSource.saveItems(itemHiveModels); 29 | } 30 | 31 | final itemHiveModels = await _localDataSource.getAllItems(); 32 | final itemEntities = itemHiveModels.map((e) => e.toEntity()).toList(); 33 | 34 | return itemEntities; 35 | } 36 | 37 | @override 38 | Future> getItems({required int limit, required int page}) async { 39 | final hasCachedData = await _localDataSource.hasItemData(); 40 | 41 | if (!hasCachedData) { 42 | final itemGithubModels = await _githubDataSource.getItems(); 43 | final itemHiveModels = itemGithubModels.map((e) => e.toHiveModel()); 44 | 45 | await _localDataSource.saveItems(itemHiveModels); 46 | } 47 | 48 | final itemHiveModels = await _localDataSource.getItems( 49 | page: page, 50 | limit: limit, 51 | ); 52 | final itemEntities = itemHiveModels.map((e) => e.toEntity()).toList(); 53 | 54 | return itemEntities; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/data/repositories/pokemon_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:pokedex/data/entities/pokemon.dart'; 2 | 3 | abstract class PokemonRepository { 4 | const PokemonRepository(); 5 | 6 | Future> getAllPokemons(); 7 | 8 | Future> getPokemons({required int limit, required int page}); 9 | 10 | Future getPokemon(String number); 11 | } 12 | -------------------------------------------------------------------------------- /lib/data/repositories/pokemon_repository.default.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:pokedex/data/repositories/pokemon_repository.dart'; 3 | import 'package:pokedex/data/source/github/github_datasource.dart'; 4 | import 'package:pokedex/data/source/local/local_datasource.dart'; 5 | import 'package:pokedex/data/source/mappers/github_to_local_mapper.dart'; 6 | import 'package:pokedex/data/source/mappers/local_to_entity_mapper.dart'; 7 | import 'package:pokedex/data/entities/pokemon.dart'; 8 | 9 | @Singleton(as: PokemonRepository) 10 | class PokemonDefaultRepository extends PokemonRepository { 11 | final GithubDataSource _githubDataSource; 12 | final LocalDataSource _localDataSource; 13 | 14 | const PokemonDefaultRepository({ 15 | required GithubDataSource githubDataSource, 16 | required LocalDataSource localDataSource, 17 | }) : _githubDataSource = githubDataSource, 18 | _localDataSource = localDataSource; 19 | 20 | @override 21 | Future> getAllPokemons() async { 22 | final hasCachedData = await _localDataSource.hasData(); 23 | 24 | if (!hasCachedData) { 25 | final pokemonGithubModels = await _githubDataSource.getPokemons(); 26 | final pokemonHiveModels = pokemonGithubModels.map((e) => e.toHiveModel()); 27 | 28 | await _localDataSource.savePokemons(pokemonHiveModels); 29 | } 30 | 31 | final pokemonHiveModels = await _localDataSource.getAllPokemons(); 32 | 33 | final pokemonEntities = pokemonHiveModels.map((e) => e.toEntity()).toList(); 34 | 35 | return pokemonEntities; 36 | } 37 | 38 | @override 39 | Future> getPokemons({required int limit, required int page}) async { 40 | final hasCachedData = await _localDataSource.hasData(); 41 | 42 | if (!hasCachedData) { 43 | final pokemonGithubModels = await _githubDataSource.getPokemons(); 44 | final pokemonHiveModels = pokemonGithubModels.map((e) => e.toHiveModel()); 45 | 46 | await _localDataSource.savePokemons(pokemonHiveModels); 47 | } 48 | 49 | final pokemonHiveModels = await _localDataSource.getPokemons( 50 | page: page, 51 | limit: limit, 52 | ); 53 | final pokemonEntities = pokemonHiveModels.map((e) => e.toEntity()).toList(); 54 | 55 | return pokemonEntities; 56 | } 57 | 58 | @override 59 | Future getPokemon(String number) async { 60 | final pokemonModel = await _localDataSource.getPokemon(number); 61 | 62 | if (pokemonModel == null) return null; 63 | 64 | // get all evolutions 65 | final evolutions = await _localDataSource.getEvolutions(pokemonModel); 66 | 67 | final pokemon = pokemonModel.toEntity(evolutions: evolutions); 68 | 69 | return pokemon; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/data/source/github/github_datasource.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:pokedex/data/source/github/network.dart'; 5 | import 'package:pokedex/data/source/github/models/item.dart'; 6 | import 'package:pokedex/data/source/github/models/pokemon.dart'; 7 | 8 | @singleton 9 | class GithubDataSource { 10 | static const String itemsURL = 11 | 'https://gist.githubusercontent.com/hungps/48f4355fb1a89ddaf47f56961dc8a245/raw/pokemon-items.json'; 12 | 13 | static const String url = 14 | 'https://gist.githubusercontent.com/hungps/0bfdd96d3ab9ee20c2e572e47c6834c7/raw/pokemons.json'; 15 | 16 | final NetworkManager _networkManager; 17 | 18 | const GithubDataSource({ 19 | required NetworkManager networkManager, 20 | }) : _networkManager = networkManager; 21 | 22 | Future> getPokemons() async { 23 | final response = await _networkManager.request(RequestMethod.get, url); 24 | 25 | final data = (json.decode(response.data) as List) 26 | .map((item) => GithubPokemonModel.fromJson(item)) 27 | .toList(); 28 | 29 | return data; 30 | } 31 | 32 | Future> getItems() async { 33 | final response = await _networkManager.request(RequestMethod.get, itemsURL); 34 | 35 | final data = 36 | (json.decode(response.data) as List).map((item) => GithubItemModel.fromJson(item)).toList(); 37 | 38 | return data; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/data/source/github/models/item.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'item.g.dart'; 4 | 5 | @JsonSerializable() 6 | class GithubItemModel { 7 | GithubItemModel( 8 | this.name, 9 | this.category, 10 | this.imageurl, 11 | this.effect, 12 | ); 13 | 14 | factory GithubItemModel.fromJson(Map json) => _$GithubItemModelFromJson(json); 15 | 16 | Map toJson() => _$GithubItemModelToJson(this); 17 | 18 | @JsonKey(disallowNullValue: true) 19 | final String name; 20 | 21 | @JsonKey(defaultValue: '') 22 | final String category; 23 | 24 | @JsonKey(disallowNullValue: true) 25 | final String imageurl; 26 | 27 | @JsonKey(disallowNullValue: true) 28 | final String effect; 29 | } 30 | -------------------------------------------------------------------------------- /lib/data/source/github/models/item.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'item.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | GithubItemModel _$GithubItemModelFromJson(Map json) { 10 | $checkKeys( 11 | json, 12 | disallowNullValues: const ['name', 'imageurl', 'effect'], 13 | ); 14 | return GithubItemModel( 15 | json['name'] as String, 16 | json['category'] as String? ?? '', 17 | json['imageurl'] as String, 18 | json['effect'] as String, 19 | ); 20 | } 21 | 22 | Map _$GithubItemModelToJson(GithubItemModel instance) => { 23 | 'name': instance.name, 24 | 'category': instance.category, 25 | 'imageurl': instance.imageurl, 26 | 'effect': instance.effect, 27 | }; 28 | -------------------------------------------------------------------------------- /lib/data/source/github/network.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:injectable/injectable.dart'; 3 | 4 | @singleton 5 | class NetworkManager { 6 | final Dio _dio; 7 | 8 | const NetworkManager({ 9 | required Dio dio, 10 | }) : _dio = dio; 11 | 12 | Future> request( 13 | RequestMethod method, 14 | String url, { 15 | data, 16 | Map? headers, 17 | Map? queryParameters, 18 | }) { 19 | return _dio.request( 20 | url, 21 | data: data, 22 | queryParameters: queryParameters, 23 | options: Options( 24 | method: method.value, 25 | headers: headers, 26 | ), 27 | ); 28 | } 29 | } 30 | 31 | enum RequestMethod { 32 | get, 33 | head, 34 | post, 35 | put, 36 | delete, 37 | connect, 38 | options, 39 | trace, 40 | patch, 41 | } 42 | 43 | extension RequestMethodX on RequestMethod { 44 | String get value => name.toUpperCase(); 45 | } 46 | -------------------------------------------------------------------------------- /lib/data/source/local/models/item.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'item.g.dart'; 4 | 5 | @HiveType(typeId: 4) 6 | class ItemHiveModel extends HiveObject { 7 | static const String boxKey = 'item'; 8 | 9 | @HiveField(0) 10 | String? name; 11 | 12 | @HiveField(1) 13 | String? category; 14 | 15 | @HiveField(2) 16 | String? imageurl; 17 | 18 | @HiveField(3) 19 | String? effect; 20 | } 21 | -------------------------------------------------------------------------------- /lib/data/source/local/models/item.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'item.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class ItemHiveModelAdapter extends TypeAdapter { 10 | @override 11 | final int typeId = 4; 12 | 13 | @override 14 | ItemHiveModel read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return ItemHiveModel() 20 | ..name = fields[0] as String? 21 | ..category = fields[1] as String? 22 | ..imageurl = fields[2] as String? 23 | ..effect = fields[3] as String?; 24 | } 25 | 26 | @override 27 | void write(BinaryWriter writer, ItemHiveModel obj) { 28 | writer 29 | ..writeByte(4) 30 | ..writeByte(0) 31 | ..write(obj.name) 32 | ..writeByte(1) 33 | ..write(obj.category) 34 | ..writeByte(2) 35 | ..write(obj.imageurl) 36 | ..writeByte(3) 37 | ..write(obj.effect); 38 | } 39 | 40 | @override 41 | int get hashCode => typeId.hashCode; 42 | 43 | @override 44 | bool operator ==(Object other) => 45 | identical(this, other) || 46 | other is ItemHiveModelAdapter && runtimeType == other.runtimeType && typeId == other.typeId; 47 | } 48 | -------------------------------------------------------------------------------- /lib/data/source/local/models/pokemon.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | import 'pokemon_gender.dart'; 4 | import 'pokemon_stats.dart'; 5 | 6 | part 'pokemon.g.dart'; 7 | 8 | @HiveType(typeId: 1) 9 | class PokemonHiveModel extends HiveObject { 10 | static const String boxKey = 'pokemon'; 11 | 12 | @HiveField(0) 13 | late String number; 14 | 15 | @HiveField(1) 16 | late String name; 17 | 18 | @HiveField(2) 19 | late String description; 20 | 21 | @HiveField(3) 22 | late List types; 23 | 24 | @HiveField(4) 25 | late String image; 26 | 27 | @HiveField(5) 28 | late String height; 29 | 30 | @HiveField(6) 31 | late String weight; 32 | 33 | @HiveField(7) 34 | late String genera; 35 | 36 | @HiveField(8) 37 | late List eggGroups; 38 | 39 | @HiveField(9) 40 | late PokemonGenderHiveModel gender; 41 | 42 | @HiveField(10) 43 | late PokemonStatsHiveModel stats; 44 | 45 | @HiveField(11) 46 | late double baseExp; 47 | 48 | @HiveField(12) 49 | late List evolutions; 50 | 51 | @HiveField(13) 52 | late String evolutionReason; 53 | } 54 | -------------------------------------------------------------------------------- /lib/data/source/local/models/pokemon.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'pokemon.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class PokemonHiveModelAdapter extends TypeAdapter { 10 | @override 11 | final int typeId = 1; 12 | 13 | @override 14 | PokemonHiveModel read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return PokemonHiveModel() 20 | ..number = fields[0] as String 21 | ..name = fields[1] as String 22 | ..description = fields[2] as String 23 | ..types = (fields[3] as List).cast() 24 | ..image = fields[4] as String 25 | ..height = fields[5] as String 26 | ..weight = fields[6] as String 27 | ..genera = fields[7] as String 28 | ..eggGroups = (fields[8] as List).cast() 29 | ..gender = fields[9] as PokemonGenderHiveModel 30 | ..stats = fields[10] as PokemonStatsHiveModel 31 | ..baseExp = fields[11] as double 32 | ..evolutions = (fields[12] as List).cast() 33 | ..evolutionReason = fields[13] as String; 34 | } 35 | 36 | @override 37 | void write(BinaryWriter writer, PokemonHiveModel obj) { 38 | writer 39 | ..writeByte(14) 40 | ..writeByte(0) 41 | ..write(obj.number) 42 | ..writeByte(1) 43 | ..write(obj.name) 44 | ..writeByte(2) 45 | ..write(obj.description) 46 | ..writeByte(3) 47 | ..write(obj.types) 48 | ..writeByte(4) 49 | ..write(obj.image) 50 | ..writeByte(5) 51 | ..write(obj.height) 52 | ..writeByte(6) 53 | ..write(obj.weight) 54 | ..writeByte(7) 55 | ..write(obj.genera) 56 | ..writeByte(8) 57 | ..write(obj.eggGroups) 58 | ..writeByte(9) 59 | ..write(obj.gender) 60 | ..writeByte(10) 61 | ..write(obj.stats) 62 | ..writeByte(11) 63 | ..write(obj.baseExp) 64 | ..writeByte(12) 65 | ..write(obj.evolutions) 66 | ..writeByte(13) 67 | ..write(obj.evolutionReason); 68 | } 69 | 70 | @override 71 | int get hashCode => typeId.hashCode; 72 | 73 | @override 74 | bool operator ==(Object other) => 75 | identical(this, other) || 76 | other is PokemonHiveModelAdapter && 77 | runtimeType == other.runtimeType && 78 | typeId == other.typeId; 79 | } 80 | -------------------------------------------------------------------------------- /lib/data/source/local/models/pokemon_gender.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'pokemon_gender.g.dart'; 4 | 5 | @HiveType(typeId: 2) 6 | class PokemonGenderHiveModel extends HiveObject { 7 | static const String boxKey = 'pokemonGender'; 8 | 9 | @HiveField(0) 10 | late bool genderless; 11 | 12 | @HiveField(1) 13 | late double male; 14 | 15 | @HiveField(2) 16 | late double female; 17 | } 18 | -------------------------------------------------------------------------------- /lib/data/source/local/models/pokemon_gender.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'pokemon_gender.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class PokemonGenderHiveModelAdapter extends TypeAdapter { 10 | @override 11 | final int typeId = 2; 12 | 13 | @override 14 | PokemonGenderHiveModel read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return PokemonGenderHiveModel() 20 | ..genderless = fields[0] as bool 21 | ..male = fields[1] as double 22 | ..female = fields[2] as double; 23 | } 24 | 25 | @override 26 | void write(BinaryWriter writer, PokemonGenderHiveModel obj) { 27 | writer 28 | ..writeByte(3) 29 | ..writeByte(0) 30 | ..write(obj.genderless) 31 | ..writeByte(1) 32 | ..write(obj.male) 33 | ..writeByte(2) 34 | ..write(obj.female); 35 | } 36 | 37 | @override 38 | int get hashCode => typeId.hashCode; 39 | 40 | @override 41 | bool operator ==(Object other) => 42 | identical(this, other) || 43 | other is PokemonGenderHiveModelAdapter && 44 | runtimeType == other.runtimeType && 45 | typeId == other.typeId; 46 | } 47 | -------------------------------------------------------------------------------- /lib/data/source/local/models/pokemon_stats.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'pokemon_stats.g.dart'; 4 | 5 | @HiveType(typeId: 3) 6 | class PokemonStatsHiveModel extends HiveObject { 7 | static const String boxKey = 'pokemonStats'; 8 | 9 | @HiveField(0) 10 | late int attack; 11 | 12 | @HiveField(1) 13 | late int specialAttack; 14 | 15 | @HiveField(2) 16 | late int defense; 17 | 18 | @HiveField(3) 19 | late int specialDefense; 20 | 21 | @HiveField(4) 22 | late int hp; 23 | 24 | @HiveField(5) 25 | late int speed; 26 | } 27 | -------------------------------------------------------------------------------- /lib/data/source/local/models/pokemon_stats.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'pokemon_stats.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class PokemonStatsHiveModelAdapter extends TypeAdapter { 10 | @override 11 | final int typeId = 3; 12 | 13 | @override 14 | PokemonStatsHiveModel read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return PokemonStatsHiveModel() 20 | ..attack = fields[0] as int 21 | ..specialAttack = fields[1] as int 22 | ..defense = fields[2] as int 23 | ..specialDefense = fields[3] as int 24 | ..hp = fields[4] as int 25 | ..speed = fields[5] as int; 26 | } 27 | 28 | @override 29 | void write(BinaryWriter writer, PokemonStatsHiveModel obj) { 30 | writer 31 | ..writeByte(6) 32 | ..writeByte(0) 33 | ..write(obj.attack) 34 | ..writeByte(1) 35 | ..write(obj.specialAttack) 36 | ..writeByte(2) 37 | ..write(obj.defense) 38 | ..writeByte(3) 39 | ..write(obj.specialDefense) 40 | ..writeByte(4) 41 | ..write(obj.hp) 42 | ..writeByte(5) 43 | ..write(obj.speed); 44 | } 45 | 46 | @override 47 | int get hashCode => typeId.hashCode; 48 | 49 | @override 50 | bool operator ==(Object other) => 51 | identical(this, other) || 52 | other is PokemonStatsHiveModelAdapter && 53 | runtimeType == other.runtimeType && 54 | typeId == other.typeId; 55 | } 56 | -------------------------------------------------------------------------------- /lib/data/source/mappers/github_to_local_mapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:pokedex/utils/extensions/string.dart'; 2 | import 'package:pokedex/data/source/github/models/item.dart'; 3 | 4 | import 'package:pokedex/data/source/github/models/pokemon.dart'; 5 | import 'package:pokedex/data/source/local/models/item.dart'; 6 | 7 | import 'package:pokedex/data/source/local/models/pokemon.dart'; 8 | import 'package:pokedex/data/source/local/models/pokemon_gender.dart'; 9 | import 'package:pokedex/data/source/local/models/pokemon_stats.dart'; 10 | 11 | extension GithubPokemonModelToLocalX on GithubPokemonModel { 12 | PokemonHiveModel toHiveModel() => PokemonHiveModel() 13 | ..number = id.trim() 14 | ..name = name.trim() 15 | ..description = xDescription.trim() 16 | ..types = types.toList(growable: false) 17 | ..image = imageUrl.trim() 18 | ..height = height.trim() 19 | ..weight = weight.trim() 20 | ..genera = category.trim() 21 | ..eggGroups = eggGroups?.split(RegExp(r',\s*?')).map((e) => e.trim()).toList() ?? [] 22 | ..gender = (PokemonGenderHiveModel() 23 | ..male = genderMalePercentage?.parseDouble() ?? 0 24 | ..female = genderFemalePercentage?.parseDouble() ?? 0 25 | ..genderless = genderless == 1) 26 | ..stats = (PokemonStatsHiveModel() 27 | ..hp = hp.toInt() 28 | ..speed = speed.toInt() 29 | ..attack = attack.toInt() 30 | ..defense = defense.toInt() 31 | ..specialAttack = specialAttack.toInt() 32 | ..specialDefense = specialDefense.toInt()) 33 | ..baseExp = baseExp.parseDouble() 34 | ..evolutions = evolutions 35 | ..evolutionReason = reason ?? ''; 36 | } 37 | 38 | extension GithubItemModelToLocalX on GithubItemModel { 39 | ItemHiveModel toHiveModel() => ItemHiveModel() 40 | ..name = name.trim() 41 | ..category = category.trim() 42 | ..imageurl = imageurl.trim() 43 | ..effect = effect.trim(); 44 | } 45 | -------------------------------------------------------------------------------- /lib/data/source/mappers/local_to_entity_mapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:pokedex/data/source/local/models/item.dart'; 2 | import 'package:pokedex/data/source/local/models/pokemon.dart'; 3 | import 'package:pokedex/data/source/local/models/pokemon_gender.dart'; 4 | import 'package:pokedex/data/source/local/models/pokemon_stats.dart'; 5 | import 'package:pokedex/data/entities/item.dart'; 6 | import 'package:pokedex/data/entities/pokemon.dart'; 7 | import 'package:pokedex/data/entities/pokemon_properties.dart'; 8 | import 'package:pokedex/data/entities/pokemon_types.dart'; 9 | 10 | extension PokemonHiveModelX on PokemonHiveModel { 11 | Pokemon toEntity({List evolutions = const []}) => Pokemon( 12 | number: number.trim(), 13 | name: name.trim(), 14 | description: description.trim(), 15 | types: types.map((e) => PokemonTypes.parse(e)).toList(), 16 | image: image.trim(), 17 | height: height.trim(), 18 | weight: weight.trim(), 19 | genera: genera.trim(), 20 | eggGroups: eggGroups.map((e) => e.trim()).where((e) => e.isNotEmpty).toList(), 21 | gender: gender.toEntity(), 22 | stats: stats.toEntity(), 23 | baseExp: baseExp, 24 | evolutions: evolutions.map((e) => e.toEntity()).toList(), 25 | evolutionReason: evolutionReason, 26 | ); 27 | } 28 | 29 | extension PokemonGenderHiveModelX on PokemonGenderHiveModel { 30 | PokemonGender toEntity() => PokemonGender( 31 | maleRate: male, 32 | femaleRate: female, 33 | genderless: genderless, 34 | ); 35 | } 36 | 37 | extension PokemonStatsHiveModelX on PokemonStatsHiveModel { 38 | PokemonStats toEntity() => PokemonStats( 39 | attack: attack, 40 | specialAttack: specialAttack, 41 | defense: defense, 42 | specialDefense: specialDefense, 43 | hp: hp, 44 | speed: speed, 45 | ); 46 | } 47 | 48 | extension ItemHiveModelX on ItemHiveModel { 49 | Item toEntity({List evolutions = const []}) => Item( 50 | name: name?.trim() ?? '', 51 | category: name?.trim() ?? '', 52 | image: imageurl?.trim() ?? '', 53 | effect: effect?.trim() ?? '', 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /lib/data/states/item/item_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'package:injectable/injectable.dart'; 3 | import 'package:pokedex/data/repositories/item_repository.dart'; 4 | import 'package:pokedex/data/states/item/item_event.dart'; 5 | import 'package:pokedex/data/states/item/item_state.dart'; 6 | import 'package:stream_transform/stream_transform.dart'; 7 | 8 | @singleton 9 | class ItemBloc extends Bloc { 10 | static const int itemsPerPage = 20; 11 | 12 | final ItemRepository _itemRepository; 13 | 14 | ItemBloc({ 15 | required ItemRepository itemRepository, 16 | }) : _itemRepository = itemRepository, 17 | super(const ItemState()) { 18 | on( 19 | _onLoadStarted, 20 | transformer: (events, mapper) => events.switchMap(mapper), 21 | ); 22 | on( 23 | _onLoadMoreStarted, 24 | transformer: (events, mapper) => events.switchMap(mapper), 25 | ); 26 | } 27 | 28 | void _onLoadStarted(ItemLoadStarted event, Emitter emit) async { 29 | try { 30 | emit(state.copyWith( 31 | status: ItemStateStatus.loading, 32 | )); 33 | 34 | final items = event.loadAll 35 | ? await _itemRepository.getAllItems() 36 | : await _itemRepository.getItems(page: 1, limit: itemsPerPage); 37 | 38 | final canLoadMore = items.length >= itemsPerPage; 39 | 40 | emit(state.copyWith( 41 | status: ItemStateStatus.success, 42 | items: items, 43 | page: 1, 44 | canLoadMore: canLoadMore, 45 | )); 46 | } on Exception catch (e) { 47 | emit(state.copyWith( 48 | status: ItemStateStatus.failure, 49 | error: e, 50 | )); 51 | } 52 | } 53 | 54 | void _onLoadMoreStarted(ItemLoadMoreStarted event, Emitter emit) async { 55 | try { 56 | if (!state.canLoadMore) return; 57 | 58 | emit(state.copyWith( 59 | status: ItemStateStatus.loadingMore, 60 | )); 61 | 62 | final items = await _itemRepository.getItems( 63 | page: state.page + 1, 64 | limit: itemsPerPage, 65 | ); 66 | 67 | final canLoadMore = items.length >= itemsPerPage; 68 | 69 | emit(state.copyWith( 70 | status: ItemStateStatus.success, 71 | items: [...state.items, ...items], 72 | page: canLoadMore ? state.page + 1 : state.page, 73 | canLoadMore: canLoadMore, 74 | )); 75 | } on Exception catch (e) { 76 | emit(state.copyWith( 77 | status: ItemStateStatus.failure, 78 | error: e, 79 | )); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/data/states/item/item_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'item_event.freezed.dart'; 5 | 6 | @freezed 7 | sealed class ItemEvent with _$ItemEvent { 8 | const factory ItemEvent.loadStarted({@Default(false) bool loadAll}) = ItemLoadStarted; 9 | 10 | const factory ItemEvent.loadMoreStarted() = ItemLoadMoreStarted; 11 | 12 | const factory ItemEvent.selectChanged({required String itemId}) = ItemSelectChanged; 13 | } 14 | -------------------------------------------------------------------------------- /lib/data/states/item/item_selector.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:pokedex/data/entities/item.dart'; 4 | import 'package:pokedex/data/states/item/item_bloc.dart'; 5 | import 'package:pokedex/data/states/item/item_state.dart'; 6 | 7 | class ItemStateSelector extends BlocSelector { 8 | ItemStateSelector({ 9 | super.key, 10 | required super.selector, 11 | required Widget Function(T) builder, 12 | }) : super( 13 | builder: (_, value) => builder(value), 14 | ); 15 | } 16 | 17 | class ItemStateStatusSelector extends ItemStateSelector { 18 | ItemStateStatusSelector(Widget Function(ItemStateStatus) builder, {super.key}) 19 | : super( 20 | selector: (state) => state.status, 21 | builder: builder, 22 | ); 23 | } 24 | 25 | class ItemCanLoadMoreSelector extends ItemStateSelector { 26 | ItemCanLoadMoreSelector(Widget Function(bool) builder, {super.key}) 27 | : super( 28 | selector: (state) => state.canLoadMore, 29 | builder: builder, 30 | ); 31 | } 32 | 33 | class NumberOfItemsSelector extends ItemStateSelector { 34 | NumberOfItemsSelector(Widget Function(int) builder, {super.key}) 35 | : super( 36 | selector: (state) => state.items.length, 37 | builder: builder, 38 | ); 39 | } 40 | 41 | class CurrentItemSelector extends ItemStateSelector { 42 | CurrentItemSelector(Widget Function(Item) builder, {super.key}) 43 | : super( 44 | selector: (state) => state.selectedItem, 45 | builder: builder, 46 | ); 47 | } 48 | 49 | class ItemSelector extends ItemStateSelector { 50 | ItemSelector(int index, Widget Function(Item, bool) builder, {super.key}) 51 | : super( 52 | selector: (state) => ItemSelectorState( 53 | state.items[index], 54 | state.selectedItemIndex == index, 55 | ), 56 | builder: (value) => builder(value.item, value.selected), 57 | ); 58 | } 59 | 60 | class ItemSelectorState { 61 | final Item item; 62 | final bool selected; 63 | 64 | const ItemSelectorState(this.item, this.selected); 65 | 66 | @override 67 | bool operator ==(Object other) => 68 | other is ItemSelectorState && item == other.item && selected == other.selected; 69 | 70 | @override 71 | int get hashCode => item.hashCode ^ selected.hashCode; 72 | } 73 | -------------------------------------------------------------------------------- /lib/data/states/item/item_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:pokedex/data/entities/item.dart'; 4 | 5 | part 'item_state.freezed.dart'; 6 | 7 | enum ItemStateStatus { 8 | initial, 9 | loading, 10 | loadingMore, 11 | success, 12 | failure, 13 | } 14 | 15 | @freezed 16 | abstract class ItemState with _$ItemState { 17 | const factory ItemState({ 18 | @Default(ItemStateStatus.initial) ItemStateStatus status, 19 | @Default([]) List items, 20 | @Default(0) int selectedItemIndex, 21 | @Default(1) int page, 22 | @Default(true) bool canLoadMore, 23 | Exception? error, 24 | }) = _ItemState; 25 | 26 | const ItemState._(); 27 | 28 | Item get selectedItem => items[selectedItemIndex]; 29 | } 30 | -------------------------------------------------------------------------------- /lib/data/states/pokemon/pokemon_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'pokemon_event.freezed.dart'; 5 | 6 | @freezed 7 | sealed class PokemonEvent with _$PokemonEvent { 8 | const factory PokemonEvent.loadStarted({@Default(false) bool loadAll}) = PokemonLoadStarted; 9 | 10 | const factory PokemonEvent.loadMoreStarted() = PokemonLoadMoreStarted; 11 | 12 | const factory PokemonEvent.selectChanged({required String pokemonId}) = PokemonSelectChanged; 13 | } 14 | -------------------------------------------------------------------------------- /lib/data/states/pokemon/pokemon_selector.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:pokedex/data/entities/pokemon.dart'; 4 | import 'package:pokedex/data/states/pokemon/pokemon_bloc.dart'; 5 | import 'package:pokedex/data/states/pokemon/pokemon_state.dart'; 6 | 7 | class PokemonStateSelector extends BlocSelector { 8 | PokemonStateSelector({ 9 | super.key, 10 | required super.selector, 11 | required Widget Function(T) builder, 12 | }) : super( 13 | builder: (_, value) => builder(value), 14 | ); 15 | } 16 | 17 | class PokemonStateStatusSelector extends PokemonStateSelector { 18 | PokemonStateStatusSelector(Widget Function(PokemonStateStatus) builder, {super.key}) 19 | : super( 20 | selector: (state) => state.status, 21 | builder: builder, 22 | ); 23 | } 24 | 25 | class PokemonCanLoadMoreSelector extends PokemonStateSelector { 26 | PokemonCanLoadMoreSelector(Widget Function(bool) builder, {super.key}) 27 | : super( 28 | selector: (state) => state.canLoadMore, 29 | builder: builder, 30 | ); 31 | } 32 | 33 | class NumberOfPokemonsSelector extends PokemonStateSelector { 34 | NumberOfPokemonsSelector(Widget Function(int) builder, {super.key}) 35 | : super( 36 | selector: (state) => state.pokemons.length, 37 | builder: builder, 38 | ); 39 | } 40 | 41 | class CurrentPokemonSelector extends PokemonStateSelector { 42 | CurrentPokemonSelector(Widget Function(Pokemon) builder, {super.key}) 43 | : super( 44 | selector: (state) => state.selectedPokemon, 45 | builder: builder, 46 | ); 47 | } 48 | 49 | class PokemonSelector extends PokemonStateSelector { 50 | PokemonSelector(int index, Widget Function(Pokemon, bool) builder, {super.key}) 51 | : super( 52 | selector: (state) => PokemonSelectorState( 53 | state.pokemons[index], 54 | state.selectedPokemonIndex == index, 55 | ), 56 | builder: (value) => builder(value.pokemon, value.selected), 57 | ); 58 | } 59 | 60 | class PokemonSelectorState { 61 | final Pokemon pokemon; 62 | final bool selected; 63 | 64 | const PokemonSelectorState(this.pokemon, this.selected); 65 | 66 | @override 67 | bool operator ==(Object other) => 68 | other is PokemonSelectorState && pokemon == other.pokemon && selected == other.selected; 69 | 70 | @override 71 | int get hashCode => pokemon.hashCode ^ selected.hashCode; 72 | } 73 | -------------------------------------------------------------------------------- /lib/data/states/pokemon/pokemon_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:pokedex/data/entities/pokemon.dart'; 4 | 5 | part 'pokemon_state.freezed.dart'; 6 | 7 | enum PokemonStateStatus { 8 | initial, 9 | loading, 10 | loadingMore, 11 | success, 12 | failure, 13 | } 14 | 15 | @freezed 16 | abstract class PokemonState with _$PokemonState { 17 | const factory PokemonState({ 18 | @Default(PokemonStateStatus.initial) PokemonStateStatus status, 19 | @Default([]) List pokemons, 20 | @Default(0) int selectedPokemonIndex, 21 | @Default(1) int page, 22 | @Default(true) bool canLoadMore, 23 | Exception? error, 24 | }) = _PokemonState; 25 | 26 | const PokemonState._(); 27 | 28 | Pokemon get selectedPokemon => pokemons[selectedPokemonIndex]; 29 | } 30 | -------------------------------------------------------------------------------- /lib/data/states/settings/settings_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'package:injectable/injectable.dart'; 3 | import 'package:pokedex/data/states/settings/settings_event.dart'; 4 | import 'package:pokedex/data/states/settings/settings_state.dart'; 5 | 6 | @singleton 7 | class SettingsBloc extends Bloc { 8 | SettingsBloc() : super(const SettingsState()) { 9 | on(_onThemeChanged); 10 | } 11 | 12 | void _onThemeChanged(SettingsThemeChanged event, Emitter emit) async { 13 | emit(state.copyWith(theme: event.theme)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/data/states/settings/settings_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:pokedex/presenter/themes/themes.dart'; 4 | 5 | part 'settings_event.freezed.dart'; 6 | 7 | @freezed 8 | sealed class SettingsEvent with _$SettingsEvent { 9 | const factory SettingsEvent.themeChanged(AppTheme theme) = SettingsThemeChanged; 10 | } 11 | -------------------------------------------------------------------------------- /lib/data/states/settings/settings_selector.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:pokedex/data/states/settings/settings_bloc.dart'; 4 | import 'package:pokedex/data/states/settings/settings_state.dart'; 5 | import 'package:pokedex/presenter/themes/themes.dart'; 6 | 7 | class SettingsSelector extends BlocSelector { 8 | SettingsSelector({ 9 | super.key, 10 | required super.selector, 11 | required Widget Function(T) builder, 12 | }) : super( 13 | builder: (_, value) => builder(value), 14 | ); 15 | } 16 | 17 | class SettingsThemeSelector extends SettingsSelector { 18 | SettingsThemeSelector({ 19 | super.key, 20 | required super.builder, 21 | }) : super( 22 | selector: (state) => state.theme, 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /lib/data/states/settings/settings_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:pokedex/presenter/themes/themes.dart'; 2 | import 'package:pokedex/presenter/themes/themes/themes.light.dart'; 3 | 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:freezed_annotation/freezed_annotation.dart'; 6 | 7 | part 'settings_state.freezed.dart'; 8 | 9 | @freezed 10 | abstract class SettingsState with _$SettingsState { 11 | const factory SettingsState({ 12 | @Default(LightAppTheme()) AppTheme theme, 13 | }) = _SettingsState; 14 | } 15 | -------------------------------------------------------------------------------- /lib/data/usecases/item_usecases.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:pokedex/core/usecase.dart'; 3 | import 'package:pokedex/data/repositories/item_repository.dart'; 4 | import 'package:pokedex/data/entities/item.dart'; 5 | 6 | @singleton 7 | class GetItemUseCase extends UseCase, NoParams?> { 8 | final ItemRepository _itemRepository; 9 | 10 | const GetItemUseCase({ 11 | required ItemRepository itemRepository, 12 | }) : _itemRepository = itemRepository; 13 | 14 | @override 15 | Future> call([NoParams? params]) { 16 | return _itemRepository.getAllItems(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/data/usecases/pokemon_usecases.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:pokedex/core/usecase.dart'; 3 | import 'package:pokedex/data/repositories/pokemon_repository.dart'; 4 | import 'package:pokedex/data/entities/pokemon.dart'; 5 | 6 | @singleton 7 | class GetAllPokemonsUseCase extends UseCase, NoParams?> { 8 | final PokemonRepository _pokemonRepository; 9 | 10 | const GetAllPokemonsUseCase({ 11 | required PokemonRepository pokemonRepository, 12 | }) : _pokemonRepository = pokemonRepository; 13 | 14 | @override 15 | Future> call([NoParams? params]) { 16 | return _pokemonRepository.getAllPokemons(); 17 | } 18 | } 19 | 20 | class GetPokemonsParams { 21 | const GetPokemonsParams({ 22 | required this.page, 23 | required this.limit, 24 | }); 25 | 26 | final int page; 27 | final int limit; 28 | } 29 | 30 | @singleton 31 | class GetPokemonsUseCase extends UseCase, GetPokemonsParams> { 32 | final PokemonRepository _pokemonRepository; 33 | 34 | const GetPokemonsUseCase({ 35 | required PokemonRepository pokemonRepository, 36 | }) : _pokemonRepository = pokemonRepository; 37 | 38 | @override 39 | Future> call(GetPokemonsParams params) { 40 | return _pokemonRepository.getPokemons(page: params.page, limit: params.limit); 41 | } 42 | } 43 | 44 | class GetPokemonParam { 45 | final String number; 46 | 47 | const GetPokemonParam(this.number); 48 | } 49 | 50 | @singleton 51 | class GetPokemonUseCase extends UseCase { 52 | final PokemonRepository _pokemonRepository; 53 | 54 | const GetPokemonUseCase({ 55 | required PokemonRepository pokemonRepository, 56 | }) : _pokemonRepository = pokemonRepository; 57 | 58 | @override 59 | Future call(GetPokemonParam params) { 60 | return _pokemonRepository.getPokemon(params.number); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/di.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:get_it/get_it.dart'; 7 | import 'package:injectable/injectable.dart'; 8 | import 'package:pokedex/data/states/item/item_bloc.dart'; 9 | import 'package:pokedex/data/states/pokemon/pokemon_bloc.dart'; 10 | import 'package:pokedex/data/states/settings/settings_bloc.dart'; 11 | 12 | import 'di.config.dart'; 13 | 14 | final getIt = GetIt.instance; 15 | 16 | @InjectableInit() 17 | FutureOr configureDependencies() => getIt.init(); 18 | 19 | @module 20 | abstract class RegisterModule { 21 | @singleton 22 | Dio get dio => Dio(); 23 | } 24 | 25 | class GlobalBlocProviders extends StatelessWidget { 26 | final Widget child; 27 | 28 | const GlobalBlocProviders({ 29 | super.key, 30 | required this.child, 31 | }); 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return MultiBlocProvider( 36 | providers: [ 37 | BlocProvider( 38 | create: (context) => getIt.get(), 39 | ), 40 | BlocProvider( 41 | create: (context) => getIt.get(), 42 | ), 43 | BlocProvider( 44 | create: (context) => getIt.get(), 45 | ) 46 | ], 47 | child: child, 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/app.dart'; 3 | import 'package:pokedex/di.dart'; 4 | 5 | void main() async { 6 | WidgetsFlutterBinding.ensureInitialized(); 7 | 8 | await configureDependencies(); 9 | 10 | runApp( 11 | GlobalBlocProviders( 12 | child: PokedexApp(), 13 | ), 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /lib/presenter/app.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_web_frame/flutter_web_frame.dart'; 5 | import 'package:pokedex/presenter/navigation/navigation.dart'; 6 | import 'package:pokedex/data/states/settings/settings_selector.dart'; 7 | 8 | class PokedexApp extends StatelessWidget { 9 | final AppRouter _router = AppRouter(); 10 | 11 | PokedexApp({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return FlutterWebFrame( 16 | maximumSize: const Size(400, 800), 17 | backgroundColor: Colors.black12, 18 | enabled: MediaQuery.sizeOf(context).shortestSide > 600, 19 | builder: (_) => SettingsThemeSelector( 20 | builder: (theme) => MaterialApp.router( 21 | title: 'Flutter Pokedex', 22 | theme: theme.themeData, 23 | routerConfig: _router.config(), 24 | scrollBehavior: AppScrollBehavior(), 25 | ), 26 | ), 27 | ); 28 | } 29 | } 30 | 31 | class AppScrollBehavior extends MaterialScrollBehavior { 32 | @override 33 | Set get dragDevices => { 34 | PointerDeviceKind.touch, 35 | PointerDeviceKind.mouse, 36 | PointerDeviceKind.trackpad, 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /lib/presenter/fonts.gen.dart: -------------------------------------------------------------------------------- 1 | /// GENERATED CODE - DO NOT MODIFY BY HAND 2 | /// ***************************************************** 3 | /// FlutterGen 4 | /// ***************************************************** 5 | 6 | // coverage:ignore-file 7 | // ignore_for_file: type=lint 8 | // ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use 9 | 10 | class FontFamily { 11 | FontFamily._(); 12 | 13 | /// Font family: CircularStd 14 | static const String circularStd = 'CircularStd'; 15 | } 16 | -------------------------------------------------------------------------------- /lib/presenter/modals/generation_modal.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/data/generations.dart'; 3 | import 'package:pokedex/data/entities/pokemon_generation.dart'; 4 | import 'package:pokedex/presenter/pages/pokedex/widgets/generation_card.dart'; 5 | import 'package:pokedex/presenter/widgets/modal.dart'; 6 | 7 | class GenerationModal extends StatelessWidget { 8 | const GenerationModal({super.key}); 9 | 10 | Widget _buildGenerationCard(PokemonGeneration generation) { 11 | return GenerationCard(generation); 12 | } 13 | 14 | Widget _buildGenerations(BuildContext context, ScrollController scrollController) { 15 | final safeAreaBottom = MediaQuery.paddingOf(context).bottom; 16 | 17 | return Expanded( 18 | child: GridView.builder( 19 | padding: EdgeInsets.fromLTRB(26, 26, 26, 26 + safeAreaBottom), 20 | controller: scrollController, 21 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 22 | crossAxisCount: 2, 23 | childAspectRatio: 1.55, 24 | crossAxisSpacing: 10, 25 | mainAxisSpacing: 10, 26 | ), 27 | itemCount: pokemonGenerations.length, 28 | itemBuilder: (_, index) => _buildGenerationCard(pokemonGenerations[index]), 29 | ), 30 | ); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return DraggableScrollableSheet( 36 | expand: true, 37 | initialChildSize: 0.7, 38 | maxChildSize: 1, 39 | minChildSize: 0.5, 40 | builder: (_, scrollController) { 41 | return Modal( 42 | title: 'Generation', 43 | child: _buildGenerations(context, scrollController), 44 | ); 45 | }, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/presenter/modals/search_modal.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/widgets/modal.dart'; 3 | import 'package:pokedex/presenter/widgets/input.dart'; 4 | 5 | class SearchBottomModal extends StatelessWidget { 6 | const SearchBottomModal({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | final viewInsets = MediaQuery.viewInsetsOf(context).bottom; 11 | final safeAreaBottom = MediaQuery.paddingOf(context).bottom; 12 | 13 | return Modal( 14 | child: Flexible( 15 | child: Padding( 16 | padding: EdgeInsets.fromLTRB(26, 14, 26, 14 + viewInsets + safeAreaBottom), 17 | child: AppSearchBar( 18 | hintText: 'Search Pokemon, Move, Ability etc', 19 | ), 20 | ), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/presenter/navigation/navigation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:auto_route/auto_route.dart'; 3 | import 'package:pokedex/presenter/pages/home/home.dart'; 4 | import 'package:pokedex/presenter/pages/items/items.dart'; 5 | import 'package:pokedex/presenter/pages/pokedex/pokedex.dart'; 6 | import 'package:pokedex/presenter/pages/pokemon_info/pokemon_info.dart'; 7 | import 'package:pokedex/presenter/pages/splash/splash.dart'; 8 | import 'package:pokedex/presenter/pages/types/types.dart'; 9 | 10 | part 'navigation.gr.dart'; 11 | 12 | @AutoRouterConfig() 13 | class AppRouter extends RootStackRouter { 14 | @override 15 | List get routes => [ 16 | AutoRoute(path: '/', page: SplashRoute.page), 17 | AutoRoute(path: '/home', page: HomeRoute.page), 18 | AutoRoute(path: '/pokemons', page: PokedexRoute.page), 19 | AutoRoute(path: '/pokemons/:id', page: PokemonInfoRoute.page), 20 | AutoRoute(path: '/types', page: TypeEffectRoute.page), 21 | AutoRoute(path: '/items', page: ItemsRoute.page), 22 | ]; 23 | 24 | @override 25 | RouteType get defaultRouteType => const RouteType.custom( 26 | transitionsBuilder: TransitionsBuilders.fadeIn, 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /lib/presenter/pages/home/home.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:auto_route/auto_route.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:pokedex/presenter/navigation/navigation.dart'; 7 | import 'package:pokedex/data/states/settings/settings_bloc.dart'; 8 | import 'package:pokedex/data/states/settings/settings_event.dart'; 9 | import 'package:pokedex/data/states/settings/settings_selector.dart'; 10 | import 'package:pokedex/presenter/themes/colors.dart'; 11 | import 'package:pokedex/presenter/themes/extensions.dart'; 12 | import 'package:pokedex/presenter/themes/themes/themes.dark.dart'; 13 | import 'package:pokedex/presenter/themes/themes/themes.light.dart'; 14 | import 'package:pokedex/presenter/widgets/app_bar.dart'; 15 | import 'package:pokedex/presenter/widgets/button.dart'; 16 | import 'package:pokedex/presenter/widgets/input.dart'; 17 | import 'package:pokedex/presenter/widgets/keyboard.dart'; 18 | import 'package:pokedex/presenter/widgets/scaffold.dart'; 19 | 20 | part 'widgets/category_card.dart'; 21 | part 'widgets/news_card.dart'; 22 | part 'sections/header.dart'; 23 | part 'sections/news.dart'; 24 | 25 | @RoutePage() 26 | class HomePage extends StatefulWidget { 27 | const HomePage({super.key}); 28 | 29 | @override 30 | State createState() => _HomePageState(); 31 | } 32 | 33 | class _HomePageState extends State { 34 | @override 35 | Widget build(BuildContext context) { 36 | return KeyboardDismisser( 37 | child: Scaffold( 38 | backgroundColor: context.colors.backgroundDark, 39 | body: NestedScrollView( 40 | headerSliverBuilder: (_, innerBoxIsScrolled) => [ 41 | AppExpandableSliverAppBar( 42 | backgroundColor: context.colors.primary, 43 | title: Visibility( 44 | visible: innerBoxIsScrolled, 45 | child: const Text('Pokedex'), 46 | ), 47 | background: _HeaderSection( 48 | height: min(MediaQuery.sizeOf(context).height * 0.82, 582), 49 | ), 50 | ), 51 | ], 52 | body: const _NewsSection(), 53 | ), 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/presenter/pages/home/sections/news.dart: -------------------------------------------------------------------------------- 1 | part of '../home.dart'; 2 | 3 | class _NewsSection extends StatelessWidget { 4 | const _NewsSection(); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return ListView( 9 | physics: const ClampingScrollPhysics(), 10 | padding: const EdgeInsets.all(24), 11 | children: [ 12 | Row( 13 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 14 | children: [ 15 | Text( 16 | 'Pokémon News', 17 | style: context.typographies.headingSmall, 18 | ), 19 | TextButton( 20 | onPressed: () {}, 21 | style: TextButton.styleFrom(foregroundColor: context.colors.secondary), 22 | child: const Text('View All'), 23 | ), 24 | ], 25 | ), 26 | ListView.separated( 27 | shrinkWrap: true, 28 | physics: const NeverScrollableScrollPhysics(), 29 | itemCount: 9, 30 | separatorBuilder: (_, __) => const Divider(height: 24), 31 | itemBuilder: (_, __) { 32 | return const _NewsListTile( 33 | title: 'Pokémon Rumble Rush Arrives Soon', 34 | time: '15 May 2019', 35 | thumbnail: AssetImage('assets/images/thumbnail.png'), 36 | ); 37 | }, 38 | ), 39 | ], 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/presenter/pages/home/widgets/news_card.dart: -------------------------------------------------------------------------------- 1 | part of '../home.dart'; 2 | 3 | class _NewsListTile extends StatelessWidget { 4 | final ImageProvider thumbnail; 5 | final String title; 6 | final String time; 7 | 8 | const _NewsListTile({ 9 | required this.title, 10 | required this.time, 11 | required this.thumbnail, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Row( 17 | mainAxisSize: MainAxisSize.max, 18 | children: [ 19 | Expanded( 20 | child: Column( 21 | crossAxisAlignment: CrossAxisAlignment.start, 22 | children: [ 23 | Text( 24 | title, 25 | style: context.typographies.body.withWeight(FontWeight.bold), 26 | ), 27 | const SizedBox(height: 6), 28 | Text( 29 | time, 30 | style: context.typographies.caption.withColor(context.colors.hint), 31 | ), 32 | ], 33 | ), 34 | ), 35 | const SizedBox(width: 36), 36 | ClipRRect( 37 | borderRadius: BorderRadius.circular(12), 38 | child: Image( 39 | image: thumbnail, 40 | width: 110, 41 | height: 66, 42 | fit: BoxFit.cover, 43 | ), 44 | ), 45 | ], 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/presenter/pages/items/items.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:auto_route/auto_route.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:pokedex/presenter/widgets/loading.dart'; 7 | import 'package:pokedex/utils/extensions/animation.dart'; 8 | import 'package:pokedex/data/states/item/item_bloc.dart'; 9 | import 'package:pokedex/data/states/item/item_event.dart'; 10 | import 'package:pokedex/data/states/item/item_selector.dart'; 11 | import 'package:pokedex/data/states/item/item_state.dart'; 12 | import 'package:pokedex/presenter/modals/generation_modal.dart'; 13 | import 'package:pokedex/presenter/modals/search_modal.dart'; 14 | import 'package:pokedex/presenter/widgets/animated_overlay.dart'; 15 | import 'package:pokedex/presenter/widgets/app_bar.dart'; 16 | import 'package:pokedex/presenter/widgets/fab.dart'; 17 | import 'package:pokedex/presenter/widgets/scaffold.dart'; 18 | import 'package:pokedex/presenter/widgets/pokemon_refresh_control.dart'; 19 | 20 | import 'widgets/item_card.dart'; 21 | 22 | part 'sections/fab_menu.dart'; 23 | part 'sections/items_grid.dart'; 24 | 25 | @RoutePage() 26 | class ItemsPage extends StatefulWidget { 27 | const ItemsPage({super.key}); 28 | 29 | @override 30 | ItemsPageState createState() => ItemsPageState(); 31 | } 32 | 33 | class ItemsPageState extends State { 34 | @override 35 | Widget build(BuildContext context) { 36 | return PokeballScaffold( 37 | body: const Stack( 38 | children: [ 39 | _ItemGrid(), 40 | ], 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/presenter/pages/items/widgets/item_category.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ItemCategory extends StatelessWidget { 4 | const ItemCategory(this.name, {super.key}); 5 | 6 | final String name; 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container( 11 | margin: const EdgeInsets.only(top: 3), 12 | child: Material( 13 | color: Colors.transparent, 14 | child: Container( 15 | padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), 16 | decoration: ShapeDecoration( 17 | shape: const StadiumBorder(), 18 | color: Colors.white.withValues(alpha: 0.2), 19 | ), 20 | child: Row( 21 | mainAxisSize: MainAxisSize.min, 22 | children: [ 23 | Text( 24 | name, 25 | textScaler: TextScaler.noScaling, 26 | style: const TextStyle( 27 | fontSize: 10, 28 | height: 0.8, 29 | fontWeight: FontWeight.normal, 30 | color: Colors.white, 31 | ), 32 | textAlign: TextAlign.center, 33 | ), 34 | const SizedBox(width: 5), 35 | ], 36 | ), 37 | ), 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/presenter/pages/pokedex/pokedex.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:auto_route/auto_route.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:pokedex/presenter/navigation/navigation.dart'; 7 | import 'package:pokedex/presenter/widgets/loading.dart'; 8 | import 'package:pokedex/utils/extensions/animation.dart'; 9 | import 'package:pokedex/data/entities/pokemon.dart'; 10 | import 'package:pokedex/data/states/pokemon/pokemon_bloc.dart'; 11 | import 'package:pokedex/data/states/pokemon/pokemon_event.dart'; 12 | import 'package:pokedex/data/states/pokemon/pokemon_selector.dart'; 13 | import 'package:pokedex/data/states/pokemon/pokemon_state.dart'; 14 | import 'package:pokedex/presenter/modals/generation_modal.dart'; 15 | import 'package:pokedex/presenter/modals/search_modal.dart'; 16 | import 'package:pokedex/presenter/widgets/app_bar.dart'; 17 | import 'package:pokedex/presenter/widgets/pokemon_card.dart'; 18 | import 'package:pokedex/presenter/widgets/animated_overlay.dart'; 19 | import 'package:pokedex/presenter/widgets/fab.dart'; 20 | import 'package:pokedex/presenter/widgets/scaffold.dart'; 21 | import 'package:pokedex/presenter/widgets/pokemon_refresh_control.dart'; 22 | 23 | part 'sections/fab_menu.dart'; 24 | part 'sections/pokemon_grid.dart'; 25 | 26 | @RoutePage() 27 | class PokedexPage extends StatefulWidget { 28 | const PokedexPage({super.key}); 29 | 30 | @override 31 | State createState() => _PokedexPageState(); 32 | } 33 | 34 | class _PokedexPageState extends State { 35 | @override 36 | Widget build(BuildContext context) { 37 | return PokeballScaffold( 38 | body: const Stack( 39 | children: [ 40 | _PokemonGrid(), 41 | _FabMenu(), 42 | ], 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/presenter/pages/pokedex/widgets/generation_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/data/entities/pokemon_generation.dart'; 3 | import 'package:pokedex/presenter/assets.gen.dart'; 4 | import 'package:pokedex/presenter/themes/colors.dart'; 5 | import 'package:pokedex/presenter/themes/extensions.dart'; 6 | 7 | class GenerationCard extends StatelessWidget { 8 | const GenerationCard(this.generation, {super.key}); 9 | 10 | final PokemonGeneration generation; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return LayoutBuilder(builder: (context, constraints) { 15 | final height = constraints.maxHeight; 16 | 17 | return Container( 18 | decoration: BoxDecoration( 19 | color: context.colors.background, 20 | borderRadius: BorderRadius.circular(15), 21 | boxShadow: context.styles.cardShadow, 22 | ), 23 | child: Stack( 24 | children: [ 25 | Container( 26 | constraints: const BoxConstraints.expand(), 27 | padding: const EdgeInsets.all(16), 28 | child: Column( 29 | mainAxisSize: MainAxisSize.max, 30 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 31 | crossAxisAlignment: CrossAxisAlignment.center, 32 | children: [ 33 | Text( 34 | generation.title, 35 | style: const TextStyle(fontWeight: FontWeight.bold), 36 | ), 37 | Row( 38 | mainAxisSize: MainAxisSize.max, 39 | mainAxisAlignment: MainAxisAlignment.start, 40 | children: generation.pokemonImages 41 | .map( 42 | (pokemon) => Expanded( 43 | child: Image.asset( 44 | pokemon, 45 | fit: BoxFit.contain, 46 | width: height * 0.41, 47 | height: height * 0.41, 48 | ), 49 | ), 50 | ) 51 | .toList(), 52 | ), 53 | ], 54 | ), 55 | ), 56 | Positioned( 57 | bottom: -height * 0.136, 58 | right: -height * 0.03, 59 | child: Image( 60 | image: Assets.images.pokeball.provider(), 61 | width: height * 0.754, 62 | height: height * 0.754, 63 | color: AppColors.black.withValues(alpha: 0.05), 64 | ), 65 | ), 66 | ], 67 | ), 68 | ); 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/presenter/pages/pokemon_info/sections/pokemon_info_card.dart: -------------------------------------------------------------------------------- 1 | part of '../pokemon_info.dart'; 2 | 3 | class _PokemonInfoCard extends StatefulWidget { 4 | static const double minCardHeightFraction = 0.54; 5 | 6 | const _PokemonInfoCard(); 7 | 8 | @override 9 | State<_PokemonInfoCard> createState() => _PokemonInfoCardState(); 10 | } 11 | 12 | class _PokemonInfoCardState extends State<_PokemonInfoCard> { 13 | AnimationController get slideController => PokemonInfoStateProvider.of(context).slideController; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | final screenHeight = MediaQuery.sizeOf(context).height; 18 | final safeArea = MediaQuery.paddingOf(context); 19 | final appBarHeight = AppBar().preferredSize.height; 20 | 21 | final cardMinHeight = screenHeight * _PokemonInfoCard.minCardHeightFraction; 22 | final cardMaxHeight = screenHeight - appBarHeight - safeArea.top; 23 | 24 | return AutoSlideUpPanel( 25 | minHeight: cardMinHeight, 26 | maxHeight: cardMaxHeight, 27 | onPanelSlide: (position) => slideController.value = position, 28 | child: CurrentPokemonSelector((pokemon) { 29 | return MainTabView( 30 | paddingAnimation: slideController, 31 | tabs: [ 32 | MainTabData( 33 | label: 'About', 34 | child: _PokemonAbout(pokemon), 35 | ), 36 | MainTabData( 37 | label: 'Base Stats', 38 | child: _PokemonBaseStats(pokemon), 39 | ), 40 | MainTabData( 41 | label: 'Evolution', 42 | child: _PokemonEvolution(pokemon), 43 | ), 44 | const MainTabData( 45 | label: 'Moves', 46 | child: Align( 47 | alignment: Alignment.topCenter, 48 | child: Text('Under development'), 49 | ), 50 | ), 51 | ], 52 | ); 53 | }), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/presenter/pages/pokemon_info/state_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PokemonInfoStateProvider extends InheritedWidget { 4 | final AnimationController slideController; 5 | final AnimationController rotateController; 6 | 7 | const PokemonInfoStateProvider({ 8 | super.key, 9 | required this.slideController, 10 | required this.rotateController, 11 | required super.child, 12 | }); 13 | 14 | static PokemonInfoStateProvider of(BuildContext context) { 15 | final result = context.dependOnInheritedWidgetOfExactType(); 16 | 17 | return result!; 18 | } 19 | 20 | @override 21 | bool updateShouldNotify(covariant PokemonInfoStateProvider oldWidget) => false; 22 | } 23 | -------------------------------------------------------------------------------- /lib/presenter/pages/splash/splash.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:auto_route/auto_route.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:pokedex/presenter/navigation/navigation.dart'; 6 | import 'package:pokedex/presenter/widgets/loading.dart'; 7 | 8 | @RoutePage() 9 | class SplashPage extends StatefulWidget { 10 | const SplashPage({super.key}); 11 | 12 | @override 13 | State createState() => _SplashPageState(); 14 | } 15 | 16 | class _SplashPageState extends State { 17 | @override 18 | void initState() { 19 | scheduleMicrotask(() async { 20 | await Future.delayed(const Duration(milliseconds: 400)); 21 | if (!mounted) return; 22 | await context.router.replaceAll([const HomeRoute()]); 23 | }); 24 | 25 | super.initState(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return const Scaffold( 31 | body: Center( 32 | child: Column( 33 | mainAxisAlignment: MainAxisAlignment.center, 34 | children: [ 35 | PikaLoadingIndicator(), 36 | Text( 37 | 'Pokedex', 38 | textAlign: TextAlign.center, 39 | style: TextStyle( 40 | fontSize: 24, 41 | fontWeight: FontWeight.w600, 42 | ), 43 | ) 44 | ], 45 | ), 46 | ), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/bold_texts.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/themes/colors.dart'; 3 | 4 | // A class that generates Bold Texts required for the individual types icon 5 | class BoldText extends StatelessWidget { 6 | const BoldText({ 7 | super.key, 8 | required this.text, 9 | }); 10 | 11 | final String text; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Text( 16 | text, 17 | style: const TextStyle(fontWeight: FontWeight.bold, color: AppColors.whiteGrey, shadows: [ 18 | Shadow( 19 | // bottomLeft 20 | offset: Offset(-1, -1), 21 | color: Colors.black), 22 | Shadow( 23 | // bottomRight 24 | offset: Offset(1, -1), 25 | color: Colors.black), 26 | Shadow( 27 | // topRight 28 | offset: Offset(1, 1), 29 | color: Colors.black), 30 | Shadow( 31 | // topLeft 32 | offset: Offset(-1, 1), 33 | color: Colors.black), 34 | ]), 35 | textAlign: TextAlign.center, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/colored_pokeball.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/pages/types/type_container.dart'; 3 | // A class that is responsible for the pokeball kind of grids that contain types 4 | 5 | class CircularContainer extends StatelessWidget { 6 | const CircularContainer({ 7 | super.key, 8 | required this.width, 9 | required this.index, 10 | }); 11 | 12 | final double width; 13 | final int index; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Stack( 18 | children: [ 19 | Align( 20 | child: Container( 21 | decoration: BoxDecoration( 22 | shape: BoxShape.circle, border: Border.all(color: Colors.black87, width: 2.5)), 23 | ), 24 | ), 25 | Align( 26 | child: Container( 27 | height: 5, 28 | color: Colors.white, 29 | )), 30 | Align( 31 | alignment: Alignment.center, 32 | child: Container( 33 | width: width / 7, 34 | decoration: BoxDecoration( 35 | color: Colors.white70, 36 | shape: BoxShape.circle, 37 | border: Border.all(color: Colors.black87, width: 2.5)), 38 | ), 39 | ), 40 | Align( 41 | alignment: Alignment.center, 42 | child: TypeDisplayContainer( 43 | index: index, 44 | path: "name", 45 | value: null, 46 | width: null, 47 | j: null, 48 | height: 30, 49 | typeList: const [], 50 | ), 51 | ), 52 | ], 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/modal_draggable.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/assets.gen.dart'; 3 | import 'package:pokedex/presenter/pages/types/modal_contents.dart'; 4 | import 'package:pokedex/presenter/themes/colors.dart'; 5 | 6 | // Class responsible for generating the modal page when clicked on a type 7 | class ModalDraggable extends StatelessWidget { 8 | const ModalDraggable({ 9 | super.key, 10 | required this.width, 11 | required this.index, 12 | }); 13 | 14 | final double width; 15 | final int index; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return DraggableScrollableSheet( 20 | initialChildSize: 0.25, 21 | maxChildSize: 0.92, 22 | expand: false, 23 | builder: (b, s) { 24 | return Container( 25 | decoration: BoxDecoration( 26 | color: Theme.of(context).colorScheme.surface, 27 | borderRadius: const BorderRadius.only( 28 | topLeft: Radius.circular(20), topRight: Radius.circular(20))), 29 | child: Stack( 30 | children: [ 31 | Align( 32 | child: Image( 33 | image: Assets.images.pokeball.provider(), 34 | width: width / 2, 35 | height: width / 2, 36 | color: AppColors.black.withValues(alpha: 0.1), 37 | ), 38 | ), 39 | ModalContents(index: index, width: width, scroller: s), //type_listview 40 | ], 41 | ), 42 | ); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/modal_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/pages/types/modal_draggable.dart'; 3 | import 'package:pokedex/data/types.dart'; 4 | import 'colored_pokeball.dart'; 5 | 6 | // Class that is responsible for making the modal sheet appear when a type is clicked on 7 | 8 | class ModalSheet extends StatelessWidget { 9 | const ModalSheet({ 10 | super.key, 11 | required this.width, 12 | required this.index, 13 | }); 14 | 15 | final double width; 16 | final int index; 17 | @override 18 | Widget build(BuildContext context) { 19 | return Card( 20 | color: types[index].color, 21 | shape: const CircleBorder(), 22 | child: InkWell( 23 | customBorder: const CircleBorder(), 24 | onTap: () { 25 | showModalBottomSheet( 26 | isScrollControlled: true, 27 | elevation: 10, 28 | shape: const RoundedRectangleBorder( 29 | borderRadius: BorderRadius.only( 30 | topLeft: Radius.circular(20), topRight: Radius.circular(20))), 31 | context: context, 32 | builder: (b) { 33 | return ModalDraggable(width: width, index: index); 34 | }); 35 | }, 36 | child: CircularContainer(width: width, index: index), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/redirector.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/data/types.dart'; 3 | import 'package:pokedex/presenter/assets.gen.dart'; 4 | import 'package:pokedex/presenter/themes/colors.dart'; 5 | import 'package:pokedex/utils/extensions/string.dart'; 6 | 7 | // Class responsible for creating the cards that redirects to other pages in the list view 8 | 9 | class Redirection extends StatelessWidget { 10 | const Redirection({ 11 | super.key, 12 | required this.index, 13 | required this.term, 14 | required this.func, 15 | }); 16 | 17 | final int index; 18 | final String term; 19 | final void Function() func; 20 | @override 21 | Widget build(BuildContext context) { 22 | return Card( 23 | child: InkWell( 24 | onTap: func, 25 | child: SizedBox( 26 | height: 50, 27 | child: Row( 28 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 29 | children: [ 30 | Row( 31 | children: [ 32 | Padding( 33 | padding: const EdgeInsets.only(left: 8.0), 34 | child: Image( 35 | image: Assets.images.pokeball.provider(), 36 | width: 30, 37 | height: 30, 38 | color: types[index].color.withValues(alpha: 0.5), 39 | ), 40 | ), 41 | Padding( 42 | padding: const EdgeInsets.only(left: 8.0), 43 | child: Text("${types[index].type.displayName.capitalize()} Type $term"), 44 | ) 45 | ], 46 | ), 47 | Padding( 48 | padding: const EdgeInsets.only(right: 8.0), 49 | child: Icon( 50 | Icons.arrow_forward_ios, 51 | size: 20, 52 | color: AppColors.black.withValues(alpha: 0.5), 53 | ), 54 | ) 55 | ], 56 | ), 57 | ), 58 | ), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/type_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/data/types.dart'; 3 | import 'package:pokedex/presenter/pages/types/type_entities/type_constants.dart'; 4 | import 'package:pokedex/presenter/themes/colors.dart'; 5 | 6 | import 'bold_texts.dart'; 7 | 8 | // The Class that is responsible for the type images 9 | class TypeDisplayContainer extends StatelessWidget { 10 | const TypeDisplayContainer({ 11 | super.key, 12 | required this.index, 13 | required this.path, 14 | required this.value, 15 | required this.width, 16 | required this.height, 17 | required this.typeList, 18 | required this.j, 19 | }); 20 | 21 | final int index; 22 | final int? j; 23 | final String path; 24 | final List typeList; 25 | final dynamic value; 26 | final double? width; 27 | final double height; 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | var col = Colors.black; 32 | var text = ''; 33 | void assigner() { 34 | if (path == "name") { 35 | col = types[index].color; 36 | text = types[index].type.displayName.toUpperCase(); 37 | } else if (j != null) { 38 | col = types[typeIndices[typeList[j!].toLowerCase()]!].color; 39 | text = types[typeIndices[typeList[j!].toLowerCase()]!].type.displayName.toUpperCase(); 40 | } 41 | } 42 | 43 | assigner(); 44 | return Container( 45 | alignment: Alignment.center, 46 | margin: const EdgeInsets.only(left: 5, right: 5), 47 | width: width, 48 | height: height, 49 | decoration: BoxDecoration( 50 | borderRadius: BorderRadius.circular(200), 51 | border: Border.all(color: AppColors.black.withAlpha(100)), 52 | boxShadow: [ 53 | (width != 75) 54 | ? const BoxShadow( 55 | color: AppColors.grey, 56 | blurRadius: 25.0, // soften the shadow 57 | spreadRadius: 7.0, //extend the shadow 58 | offset: Offset( 59 | 15.0, // Move to right 10 horizontally 60 | 5.0, // Move to bottom 5 Vertically 61 | ), 62 | ) 63 | : const BoxShadow() 64 | ], 65 | color: col, 66 | ), 67 | child: BoldText(text: text)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/type_entities/type_constants.dart: -------------------------------------------------------------------------------- 1 | // A list of types in the world of Pokemon 2 | const List typeNames = [ 3 | "normal", 4 | "fire", 5 | "water", 6 | "electric", 7 | "grass", 8 | "ice", 9 | "fighting", 10 | "poison", 11 | "ground", 12 | "flying", 13 | "psychic", 14 | "bug", 15 | "rock", 16 | "ghost", 17 | "dragon", 18 | "dark", 19 | "steel", 20 | "fairy" 21 | ]; 22 | // A map that consists of types and it's indices 23 | const Map typeIndices = { 24 | "normal": 0, 25 | "fire": 1, 26 | "water": 2, 27 | "electric": 3, 28 | "grass": 4, 29 | "ice": 5, 30 | "fighting": 6, 31 | "poison": 7, 32 | "ground": 8, 33 | "flying": 9, 34 | "psychic": 10, 35 | "bug": 11, 36 | "rock": 12, 37 | "ghost": 13, 38 | "dragon": 14, 39 | "dark": 15, 40 | "steel": 16, 41 | "fairy": 17 42 | }; 43 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/type_entities/type_funcs.dart: -------------------------------------------------------------------------------- 1 | import 'package:pokedex/data/types.dart'; 2 | import 'package:pokedex/presenter/pages/types/type_entities/type_constants.dart'; 3 | 4 | // A function that returns a list of types of pokemon that when attacked to, hit normally 5 | List normalTypeReturner(List combined) { 6 | var normalSet = typeNames.toSet(); // Create a set from the constant list of types 7 | for (var i = 0; i < combined.length; i++) { 8 | if (normalSet.contains(combined[i].toLowerCase())) { 9 | normalSet.remove(combined[i].toLowerCase()); 10 | } 11 | } // Removes the types that are already present as super/weak/nil effective from the set 12 | return normalSet.toList(); 13 | } 14 | 15 | // A function that return the required list depending on the number given 16 | List effectreturner(int i, dynamic n) { 17 | if (n == 0) { 18 | return types[i].nilEffective; 19 | } else if (n == 1) { 20 | return normalTypeReturner( 21 | types[i].nilEffective + types[i].superEffective + types[i].notEffective); 22 | } else if (n == 0.5) { 23 | return types[i].notEffective; 24 | } else { 25 | return types[i].superEffective; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/type_entities/widget_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/pages/types/type_container.dart'; 3 | import 'package:pokedex/presenter/pages/types/type_entities/type_funcs.dart'; 4 | 5 | // A function that return a list of widgets that are used in displaying the effectiveness 6 | List lister(int index, dynamic value, double width, String term) { 7 | var arr = effectreturner(index, value); 8 | return [ 9 | Container( 10 | alignment: Alignment.centerLeft, 11 | child: Padding( 12 | padding: const EdgeInsets.all(8), 13 | child: Text( 14 | term, 15 | textAlign: TextAlign.left, 16 | style: const TextStyle( 17 | fontSize: 16, 18 | // color: Colors.black54, 19 | ), 20 | ), 21 | ), 22 | ), 23 | Container( 24 | alignment: Alignment.topCenter, 25 | padding: const EdgeInsets.only(left: 8.0, top: 8), 26 | width: width * 0.75, 27 | child: Wrap( 28 | crossAxisAlignment: WrapCrossAlignment.center, 29 | alignment: WrapAlignment.center, 30 | children: [ 31 | for (int j = 0; j < arr.length; j++) 32 | Padding( 33 | padding: const EdgeInsets.only(top: 8.0), 34 | child: TypeDisplayContainer( 35 | index: index, 36 | path: "effects", 37 | value: value, 38 | width: 75.0, 39 | j: j, 40 | height: 25, 41 | typeList: arr, 42 | ), 43 | ) 44 | ], 45 | )), 46 | Container( 47 | margin: const EdgeInsets.only(top: 20), 48 | height: 1, 49 | width: width / 1.7, 50 | color: Colors.black12, 51 | ), 52 | ]; 53 | } 54 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/type_grid.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/data/types.dart'; 3 | 4 | import 'modal_sheet.dart'; 5 | 6 | // Class responsible for creating the grid of pokeballs that consists of the respective types 7 | class TypeEffectGrid extends StatelessWidget { 8 | const TypeEffectGrid({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final w = MediaQuery.sizeOf(context).width; 13 | 14 | return Builder(builder: (context) { 15 | return CustomScrollView( 16 | slivers: [ 17 | SliverPadding( 18 | padding: const EdgeInsets.all(10), 19 | sliver: SliverGrid( 20 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 21 | crossAxisCount: 3, 22 | crossAxisSpacing: 10, 23 | mainAxisSpacing: 10, 24 | ), 25 | delegate: SliverChildBuilderDelegate( 26 | (_, index) => ModalSheet( 27 | width: w, 28 | index: index, 29 | ), 30 | childCount: types.length, 31 | ), 32 | ), 33 | ), 34 | ], 35 | ); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/presenter/pages/types/types.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:pokedex/presenter/pages/types/type_grid.dart'; 5 | import 'package:pokedex/presenter/widgets/app_bar.dart'; 6 | import 'package:pokedex/presenter/widgets/scaffold.dart'; 7 | 8 | // Class that is related to creating the whole type-effects page 9 | @RoutePage() 10 | class TypeEffectPage extends StatelessWidget { 11 | const TypeEffectPage({super.key}); 12 | 13 | List _buildHeader(BuildContext context, bool innerBoxIsScrolled) { 14 | return [ 15 | AppMovingTitleSliverAppBar( 16 | title: 'Type Effects', 17 | ), 18 | ]; 19 | } 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return PokeballScaffold( 24 | body: NestedScrollView( 25 | headerSliverBuilder: _buildHeader, 26 | body: const TypeEffectGrid(), 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/presenter/themes/extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/themes/colors.dart'; 3 | import 'package:pokedex/presenter/themes/styles.dart'; 4 | import 'package:pokedex/presenter/themes/themes.dart'; 5 | import 'package:pokedex/presenter/themes/themes/themes.light.dart'; 6 | import 'package:pokedex/presenter/themes/typography.dart'; 7 | 8 | extension AppThemeExtension on BuildContext { 9 | AppTheme get appTheme => Theme.of(this).extension() ?? const LightAppTheme(); 10 | 11 | AppThemeTypography get typographies => appTheme.typographies; 12 | 13 | AppThemeColors get colors => appTheme.colors; 14 | 15 | AppThemeStyles get styles => appTheme.styles; 16 | } 17 | 18 | extension TextStyleExtension on TextStyle { 19 | TextStyle withHeight(double? height) => copyWith(height: height); 20 | 21 | TextStyle withColor(Color? color) => copyWith(color: color); 22 | 23 | TextStyle withSize(double? size) => copyWith(fontSize: size); 24 | 25 | // Use [merge] instead of [copyWith] as a workaround for this issue: 26 | // https://github.com/material-foundation/flutter-packages/issues/141 27 | TextStyle withWeight(FontWeight? weight) => merge(TextStyle(fontWeight: weight)); 28 | } 29 | -------------------------------------------------------------------------------- /lib/presenter/themes/styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppThemeStyles { 4 | final List cardShadow; 5 | 6 | final ButtonStyle buttonSmall; 7 | final ButtonStyle buttonMedium; 8 | final ButtonStyle buttonLarge; 9 | final ButtonStyle buttonText; 10 | 11 | const AppThemeStyles({ 12 | this.cardShadow = const [ 13 | BoxShadow( 14 | color: Color(0x1F000000), 15 | offset: Offset(0, 8), 16 | blurRadius: 23, 17 | ), 18 | ], 19 | this.buttonSmall = const ButtonStyle( 20 | minimumSize: WidgetStatePropertyAll(Size.zero), 21 | padding: WidgetStatePropertyAll(EdgeInsets.symmetric( 22 | vertical: 4, 23 | horizontal: 12, 24 | )), 25 | shape: WidgetStatePropertyAll(RoundedRectangleBorder( 26 | borderRadius: BorderRadius.all(Radius.circular(6)), 27 | )), 28 | textStyle: WidgetStatePropertyAll(TextStyle( 29 | fontSize: 12, 30 | fontWeight: FontWeight.w500, 31 | height: 1.3, 32 | )), 33 | ), 34 | this.buttonMedium = const ButtonStyle( 35 | minimumSize: WidgetStatePropertyAll(Size.zero), 36 | padding: WidgetStatePropertyAll(EdgeInsets.symmetric( 37 | vertical: 8, 38 | horizontal: 24, 39 | )), 40 | shape: WidgetStatePropertyAll(RoundedRectangleBorder( 41 | borderRadius: BorderRadius.all(Radius.circular(12)), 42 | )), 43 | textStyle: WidgetStatePropertyAll(TextStyle( 44 | fontSize: 16, 45 | fontWeight: FontWeight.w500, 46 | height: 1.5, 47 | )), 48 | ), 49 | this.buttonLarge = const ButtonStyle( 50 | minimumSize: WidgetStatePropertyAll(Size.zero), 51 | padding: WidgetStatePropertyAll(EdgeInsets.symmetric( 52 | vertical: 12, 53 | horizontal: 24, 54 | )), 55 | shape: WidgetStatePropertyAll(RoundedRectangleBorder( 56 | borderRadius: BorderRadius.all(Radius.circular(12)), 57 | )), 58 | textStyle: WidgetStatePropertyAll(TextStyle( 59 | fontSize: 16, 60 | fontWeight: FontWeight.w500, 61 | height: 1.5, 62 | )), 63 | ), 64 | this.buttonText = const ButtonStyle( 65 | minimumSize: WidgetStatePropertyAll(Size.zero), 66 | backgroundColor: WidgetStatePropertyAll(Colors.transparent), 67 | padding: WidgetStatePropertyAll(EdgeInsets.zero), 68 | splashFactory: NoSplash.splashFactory, 69 | textStyle: WidgetStatePropertyAll(TextStyle( 70 | fontSize: 16, 71 | fontWeight: FontWeight.w500, 72 | height: 1, 73 | )), 74 | ), 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /lib/presenter/themes/themes/themes.dark.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/themes/colors.dart'; 3 | import 'package:pokedex/presenter/themes/styles.dart'; 4 | import 'package:pokedex/presenter/themes/themes.dart'; 5 | 6 | class DarkAppTheme extends AppTheme { 7 | const DarkAppTheme() 8 | : super( 9 | name: 'dark', 10 | brightness: Brightness.dark, 11 | colors: const AppThemeColors( 12 | primarySwatch: Colors.red, 13 | primary: Color(0xFFFA6555), 14 | secondary: Color(0xFF6C79DB), 15 | accent: Color(0xFF27C754), 16 | background: Color(0xFF25272A), 17 | backgroundDark: Color(0xFF191A1D), 18 | disabled: Color(0x64303943), 19 | information: Color(0xFF6C79DB), 20 | success: Color(0xFF78C850), 21 | alert: Color(0xFFF6C747), 22 | warning: Color(0xFFFF9D5C), 23 | error: Color(0xFFFA6555), 24 | text: Color(0xFFFFFFFF), 25 | textOnPrimary: Color(0xFFFFFFFF), 26 | border: Color(0x33FFFFFF), 27 | hint: Color(0x99FFFFFF), 28 | ), 29 | styles: const AppThemeStyles( 30 | cardShadow: [ 31 | BoxShadow( 32 | color: Color(0x4D000000), 33 | offset: Offset(0, 8), 34 | blurRadius: 23, 35 | ), 36 | ], 37 | ), 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /lib/presenter/themes/themes/themes.light.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/themes/colors.dart'; 3 | import 'package:pokedex/presenter/themes/themes.dart'; 4 | 5 | class LightAppTheme extends AppTheme { 6 | const LightAppTheme() 7 | : super( 8 | name: 'light', 9 | brightness: Brightness.light, 10 | colors: const AppThemeColors( 11 | primarySwatch: Colors.red, 12 | primary: Color(0xFFFA6555), 13 | secondary: Color(0xFF6C79DB), 14 | accent: Color(0xFF27C754), 15 | background: Color(0xFFFFFFFF), 16 | backgroundDark: Color(0xFFF5F5F5), 17 | disabled: Color(0x64303943), 18 | information: Color(0xFF6C79DB), 19 | success: Color(0xFF78C850), 20 | alert: Color(0xFFF6C747), 21 | warning: Color(0xFFFF9D5C), 22 | error: Color(0xFFFA6555), 23 | text: Color(0xFF303943), 24 | textOnPrimary: Color(0xFFFFFFFF), 25 | border: Color(0xFFEBEBEB), 26 | hint: Color(0x99303943), 27 | ), 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /lib/presenter/widgets/animated_fade.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AnimatedFade extends AnimatedWidget { 4 | const AnimatedFade({ 5 | super.key, 6 | required this.child, 7 | required this.animation, 8 | }) : super(listenable: animation); 9 | 10 | final Animation animation; 11 | final Widget child; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final opacity = animation.value; 16 | 17 | return IgnorePointer( 18 | ignoring: opacity < 1, 19 | child: Opacity( 20 | opacity: opacity, 21 | child: child, 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/presenter/widgets/animated_overlay.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AnimatedOverlay extends AnimatedWidget { 4 | final Color color; 5 | final Widget? child; 6 | final void Function()? onPress; 7 | 8 | const AnimatedOverlay({ 9 | super.key, 10 | required Animation animation, 11 | required this.color, 12 | this.child, 13 | this.onPress, 14 | }) : super(listenable: animation); 15 | 16 | Animation get animation => listenable as Animation; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Stack( 21 | fit: StackFit.expand, 22 | children: [ 23 | IgnorePointer( 24 | ignoring: animation.value == 0, 25 | child: InkWell( 26 | onTap: onPress, 27 | child: Container( 28 | color: color.withValues(alpha: animation.value * 0.5), 29 | ), 30 | ), 31 | ), 32 | if (child != null) child!, 33 | ], 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/presenter/widgets/animated_slide.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AnimatedSlide extends AnimatedWidget { 4 | const AnimatedSlide({ 5 | super.key, 6 | required this.child, 7 | required this.animation, 8 | }) : super(listenable: animation); 9 | 10 | final Animation animation; 11 | final Widget child; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final slideWidth = MediaQuery.sizeOf(context).width * 0.3; 16 | 17 | return Transform.translate( 18 | offset: Offset(slideWidth * (1 - animation.value), 0), 19 | child: child, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/presenter/widgets/auto_slideup_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:sliding_up_panel/sliding_up_panel.dart'; 3 | 4 | class AutoSlideUpPanel extends StatefulWidget { 5 | final double minHeight; 6 | final double maxHeight; 7 | final Widget child; 8 | final void Function(double)? onPanelSlide; 9 | 10 | const AutoSlideUpPanel({ 11 | super.key, 12 | required this.minHeight, 13 | required this.maxHeight, 14 | required this.child, 15 | this.onPanelSlide, 16 | }); 17 | 18 | @override 19 | State createState() => _PokemonInfoCardState(); 20 | } 21 | 22 | class _PokemonInfoCardState extends State with SingleTickerProviderStateMixin { 23 | late AnimationController _autoSlideUpController; 24 | 25 | @override 26 | void initState() { 27 | _autoSlideUpController = AnimationController( 28 | vsync: this, 29 | duration: const Duration(milliseconds: 300), 30 | )..forward(); 31 | 32 | super.initState(); 33 | } 34 | 35 | @override 36 | void dispose() { 37 | _autoSlideUpController.dispose(); 38 | 39 | super.dispose(); 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return AnimatedBuilder( 45 | animation: _autoSlideUpController, 46 | child: widget.child, 47 | builder: (_, child) { 48 | return SlidingUpPanel( 49 | minHeight: widget.minHeight * _autoSlideUpController.value, 50 | maxHeight: widget.maxHeight, 51 | boxShadow: null, 52 | color: Colors.transparent, 53 | panel: child, 54 | onPanelSlide: widget.onPanelSlide, 55 | ); 56 | }, 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/presenter/widgets/button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ThemeSwitcherButton extends StatelessWidget { 4 | static const IconData _lightThemeIcon = Icons.wb_sunny_outlined; 5 | static const IconData _darkThemeIcon = Icons.dark_mode_outlined; 6 | 7 | final bool isDarkTheme; 8 | final VoidCallback onPressed; 9 | 10 | const ThemeSwitcherButton({ 11 | super.key, 12 | required this.isDarkTheme, 13 | required this.onPressed, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return IconButton( 19 | onPressed: onPressed, 20 | icon: Icon(isDarkTheme ? _darkThemeIcon : _lightThemeIcon), 21 | iconSize: 25, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/presenter/widgets/decoration.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/assets.gen.dart'; 3 | import 'package:pokedex/presenter/themes/extensions.dart'; 4 | import 'package:pokedex/presenter/widgets/app_bar.dart'; 5 | 6 | class PositionedPokeball extends StatelessWidget { 7 | final double widthFraction; 8 | 9 | const PositionedPokeball({ 10 | super.key, 11 | this.widthFraction = 0.664, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | final safeAreaTop = MediaQuery.paddingOf(context).top; 17 | final pokeballSize = MediaQuery.sizeOf(context).width * widthFraction; 18 | final appBarHeight = AppBar().preferredSize.height; 19 | final iconButtonPadding = AppAppBar.padding.right; 20 | final iconSize = IconTheme.of(context).size ?? 0; 21 | 22 | final pokeballTopMargin = -(pokeballSize / 2 - safeAreaTop - appBarHeight / 2); 23 | final pokeballRightMargin = -(pokeballSize / 2 - iconButtonPadding - iconSize / 2); 24 | 25 | return Positioned( 26 | top: pokeballTopMargin, 27 | right: pokeballRightMargin, 28 | child: Image( 29 | image: Assets.images.pokeball.provider(), 30 | width: pokeballSize, 31 | height: pokeballSize, 32 | color: context.colors.text.withValues(alpha: 0.05), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/presenter/widgets/hero.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HeroText extends Hero { 4 | HeroText(String text, {super.key, TextStyle? style, String? tag, Key? textKey}) 5 | : super( 6 | tag: tag ?? text, 7 | child: Material( 8 | color: Colors.transparent, 9 | child: Text(text, key: textKey, style: style), 10 | ), 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /lib/presenter/widgets/input.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppSearchBar extends TextFormField { 4 | AppSearchBar({ 5 | super.key, 6 | super.controller, 7 | super.initialValue, 8 | super.focusNode, 9 | super.keyboardType, 10 | super.textCapitalization, 11 | super.textInputAction, 12 | super.style, 13 | super.strutStyle, 14 | super.textDirection, 15 | super.textAlign, 16 | super.textAlignVertical, 17 | super.autofocus, 18 | super.readOnly, 19 | super.showCursor, 20 | super.obscuringCharacter, 21 | super.obscureText, 22 | super.autocorrect, 23 | super.smartDashesType, 24 | super.smartQuotesType, 25 | super.enableSuggestions, 26 | super.maxLengthEnforcement, 27 | super.maxLines, 28 | super.minLines, 29 | super.expands, 30 | super.maxLength, 31 | super.onChanged, 32 | super.onTap, 33 | super.onTapOutside, 34 | super.onEditingComplete, 35 | super.onFieldSubmitted, 36 | super.onSaved, 37 | super.validator, 38 | super.inputFormatters, 39 | super.enabled, 40 | super.cursorWidth, 41 | super.cursorHeight, 42 | super.cursorRadius, 43 | super.cursorColor, 44 | super.keyboardAppearance, 45 | super.scrollPadding, 46 | super.enableInteractiveSelection, 47 | super.selectionControls, 48 | super.buildCounter, 49 | super.scrollPhysics, 50 | super.autofillHints, 51 | super.autovalidateMode, 52 | super.scrollController, 53 | super.restorationId, 54 | super.enableIMEPersonalizedLearning, 55 | super.mouseCursor, 56 | super.contextMenuBuilder, 57 | super.spellCheckConfiguration, 58 | super.magnifierConfiguration, 59 | super.undoController, 60 | super.onAppPrivateCommand, 61 | super.cursorOpacityAnimates, 62 | super.selectionHeightStyle, 63 | super.selectionWidthStyle, 64 | super.dragStartBehavior, 65 | super.contentInsertionConfiguration, 66 | super.clipBehavior, 67 | super.scribbleEnabled, 68 | super.canRequestFocus, 69 | InputDecoration decoration = const InputDecoration(), 70 | String? hintText, 71 | }) : super( 72 | decoration: decoration.copyWith( 73 | hintText: hintText, 74 | prefixIcon: const Icon(Icons.search, size: 26), 75 | ), 76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /lib/presenter/widgets/keyboard.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class KeyboardDismisser extends StatelessWidget { 4 | final Widget child; 5 | 6 | const KeyboardDismisser({ 7 | super.key, 8 | required this.child, 9 | }); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Listener( 14 | onPointerDown: (event) => FocusManager.instance.primaryFocus?.unfocus(), 15 | child: child, 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/presenter/widgets/loading.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/assets.gen.dart'; 3 | 4 | class PikaLoadingIndicator extends StatelessWidget { 5 | const PikaLoadingIndicator({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Center( 10 | child: Image( 11 | image: Assets.images.pikaLoader.provider(), 12 | fit: BoxFit.contain, 13 | ), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/presenter/widgets/main_tab_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/themes/colors.dart'; 3 | import 'package:pokedex/presenter/themes/extensions.dart'; 4 | 5 | class MainTabData { 6 | final Widget child; 7 | final String label; 8 | 9 | const MainTabData({ 10 | required this.label, 11 | required this.child, 12 | }); 13 | } 14 | 15 | class MainTabView extends StatelessWidget { 16 | final List tabs; 17 | final Animation? paddingAnimation; 18 | 19 | const MainTabView({ 20 | super.key, 21 | required this.tabs, 22 | this.paddingAnimation, 23 | }); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return DefaultTabController( 28 | length: tabs.length, 29 | initialIndex: 0, 30 | child: Container( 31 | width: MediaQuery.sizeOf(context).width, 32 | decoration: BoxDecoration( 33 | color: context.colors.background, 34 | borderRadius: const BorderRadius.vertical(top: Radius.circular(30)), 35 | ), 36 | child: Column( 37 | mainAxisSize: MainAxisSize.max, 38 | crossAxisAlignment: CrossAxisAlignment.stretch, 39 | children: [ 40 | _buildTopAnimatedPadding(), 41 | _buildTabBar(), 42 | _buildTabContent(), 43 | ], 44 | ), 45 | ), 46 | ); 47 | } 48 | 49 | Widget _buildTopAnimatedPadding() { 50 | if (paddingAnimation == null) { 51 | return const SizedBox(height: 6); 52 | } 53 | 54 | return AnimatedBuilder( 55 | animation: paddingAnimation!, 56 | builder: (context, _) => SizedBox( 57 | height: (1 - paddingAnimation!.value) * 16 + 6, 58 | ), 59 | ); 60 | } 61 | 62 | Widget _buildTabBar() { 63 | return TabBar( 64 | labelPadding: const EdgeInsets.symmetric(vertical: 16), 65 | indicatorSize: TabBarIndicatorSize.label, 66 | indicatorWeight: 2, 67 | indicatorColor: AppColors.indigo, 68 | tabs: tabs.map((tab) => Text(tab.label)).toList(), 69 | ); 70 | } 71 | 72 | Widget _buildTabContent() { 73 | return Expanded( 74 | child: TabBarView( 75 | children: tabs.map((tab) => tab.child).toList(), 76 | ), 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/presenter/widgets/modal.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/themes/extensions.dart'; 3 | 4 | class Modal extends StatelessWidget { 5 | static const Radius _borderRadius = Radius.circular(30.0); 6 | 7 | const Modal({ 8 | super.key, 9 | this.title, 10 | required this.child, 11 | }); 12 | 13 | final String? title; 14 | final Widget child; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Container( 19 | padding: const EdgeInsets.only(top: 14), 20 | decoration: BoxDecoration( 21 | color: context.colors.background, 22 | borderRadius: const BorderRadius.only( 23 | topLeft: _borderRadius, 24 | topRight: _borderRadius, 25 | ), 26 | ), 27 | child: Column( 28 | mainAxisSize: MainAxisSize.min, 29 | children: [ 30 | _DragLine(), 31 | _Title(title), 32 | child, 33 | ], 34 | ), 35 | ); 36 | } 37 | } 38 | 39 | class _DragLine extends StatelessWidget { 40 | @override 41 | Widget build(BuildContext context) { 42 | final width = MediaQuery.sizeOf(context).width * 0.2; 43 | 44 | return Container( 45 | width: width, 46 | height: 3, 47 | decoration: ShapeDecoration( 48 | shape: const StadiumBorder(), 49 | color: context.colors.border, 50 | ), 51 | ); 52 | } 53 | } 54 | 55 | class _Title extends StatelessWidget { 56 | const _Title(this.text); 57 | 58 | final String? text; 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | if (text == null) { 63 | return const SizedBox(); 64 | } 65 | 66 | return Padding( 67 | padding: const EdgeInsets.only( 68 | top: 18, 69 | bottom: 8, 70 | ), 71 | child: Text( 72 | text ?? '', 73 | style: const TextStyle( 74 | fontSize: 20, 75 | fontWeight: FontWeight.w900, 76 | ), 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/presenter/widgets/pokemon_refresh_control.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:pokedex/presenter/widgets/loading.dart'; 3 | 4 | class PokemonRefreshControl extends StatelessWidget { 5 | final Future Function() onRefresh; 6 | 7 | const PokemonRefreshControl({ 8 | super.key, 9 | required this.onRefresh, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return CupertinoSliverRefreshControl( 15 | onRefresh: onRefresh, 16 | builder: (_, __, ___, ____, _____) => const PikaLoadingIndicator(), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/presenter/widgets/pokemon_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/data/entities/pokemon_types.dart'; 3 | import 'package:pokedex/presenter/themes/extensions.dart'; 4 | 5 | class PokemonType extends StatelessWidget { 6 | const PokemonType( 7 | this.type, { 8 | super.key, 9 | this.large = false, 10 | this.colored = false, 11 | this.extra = '', 12 | }); 13 | 14 | final PokemonTypes type; 15 | final String extra; 16 | final bool large; 17 | final bool colored; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Material( 22 | color: Colors.transparent, 23 | child: Container( 24 | padding: EdgeInsets.symmetric( 25 | horizontal: large ? 19 : 12, 26 | vertical: large ? 6 : 4, 27 | ), 28 | decoration: ShapeDecoration( 29 | shape: const StadiumBorder(), 30 | color: (colored ? type.color : context.colors.background).withValues(alpha: 0.3), 31 | ), 32 | child: Row( 33 | mainAxisSize: MainAxisSize.min, 34 | children: [ 35 | Text( 36 | type.displayName, 37 | textScaler: TextScaler.noScaling, 38 | style: TextStyle( 39 | fontSize: large ? 12 : 8, 40 | height: 0.8, 41 | fontWeight: large ? FontWeight.bold : FontWeight.normal, 42 | color: colored ? type.color : context.colors.textOnPrimary, 43 | ), 44 | textAlign: TextAlign.center, 45 | ), 46 | const SizedBox(width: 5), 47 | Text( 48 | extra, 49 | textScaler: TextScaler.noScaling, 50 | style: TextStyle( 51 | fontSize: large ? 12 : 8, 52 | height: 0.8, 53 | fontWeight: large ? FontWeight.bold : FontWeight.normal, 54 | color: colored ? type.color : context.colors.textOnPrimary, 55 | ), 56 | ), 57 | ], 58 | ), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/presenter/widgets/progress.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pokedex/presenter/themes/extensions.dart'; 3 | 4 | class ProgressBar extends StatelessWidget { 5 | const ProgressBar({ 6 | super.key, 7 | this.color, 8 | this.backgroundColor, 9 | this.enableAnimation = true, 10 | required this.progress, 11 | }); 12 | 13 | final Color? backgroundColor; 14 | final Color? color; 15 | final double progress; 16 | final bool enableAnimation; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | final child = Container( 21 | decoration: ShapeDecoration( 22 | shape: const StadiumBorder(), 23 | color: color ?? context.colors.primary, 24 | ), 25 | ); 26 | 27 | return Container( 28 | clipBehavior: Clip.hardEdge, 29 | height: 3, 30 | alignment: Alignment.centerLeft, 31 | decoration: ShapeDecoration( 32 | shape: const StadiumBorder(), 33 | color: backgroundColor ?? context.colors.border, 34 | ), 35 | child: enableAnimation 36 | ? AnimatedAlign( 37 | duration: const Duration(milliseconds: 260), 38 | alignment: const Alignment(1, 0), 39 | widthFactor: progress, 40 | child: child, 41 | ) 42 | : FractionallySizedBox( 43 | widthFactor: progress, 44 | child: child, 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/presenter/widgets/scaffold.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:pokedex/presenter/assets.gen.dart'; 5 | import 'package:pokedex/presenter/themes/extensions.dart'; 6 | import 'package:pokedex/presenter/widgets/app_bar.dart'; 7 | 8 | class PokeballScaffold extends Scaffold { 9 | PokeballScaffold({ 10 | super.key, 11 | super.appBar, 12 | super.floatingActionButton, 13 | super.floatingActionButtonLocation, 14 | super.floatingActionButtonAnimator, 15 | super.persistentFooterButtons, 16 | super.drawer, 17 | super.onDrawerChanged, 18 | super.endDrawer, 19 | super.onEndDrawerChanged, 20 | super.bottomNavigationBar, 21 | super.bottomSheet, 22 | super.backgroundColor, 23 | super.resizeToAvoidBottomInset, 24 | super.primary, 25 | super.drawerDragStartBehavior, 26 | super.extendBody, 27 | super.extendBodyBehindAppBar, 28 | super.drawerScrimColor, 29 | super.drawerEdgeDragWidth, 30 | super.drawerEnableOpenDragGesture, 31 | super.endDrawerEnableOpenDragGesture, 32 | super.restorationId, 33 | super.persistentFooterAlignment, 34 | Widget? body, 35 | }) : super( 36 | body: Stack( 37 | fit: StackFit.expand, 38 | children: [ 39 | const PositionedPokeball(), 40 | if (body != null) body, 41 | ], 42 | ), 43 | ); 44 | } 45 | 46 | class PositionedPokeball extends StatelessWidget { 47 | final double widthFraction; 48 | final double maxSize; 49 | 50 | const PositionedPokeball({ 51 | super.key, 52 | this.widthFraction = 0.664, 53 | this.maxSize = 250, 54 | }); 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | final safeAreaTop = MediaQuery.paddingOf(context).top; 59 | final pokeballSize = min(MediaQuery.sizeOf(context).width * widthFraction, maxSize); 60 | final iconButtonPadding = AppAppBar.padding.right; 61 | final iconSize = IconTheme.of(context).size ?? 0; 62 | 63 | final pokeballTopMargin = -(pokeballSize / 2 - safeAreaTop - kToolbarHeight / 2); 64 | final pokeballRightMargin = -(pokeballSize / 2 - iconButtonPadding - iconSize / 2); 65 | 66 | return Positioned( 67 | top: pokeballTopMargin, 68 | right: pokeballRightMargin, 69 | child: Image( 70 | image: Assets.images.pokeball.provider(), 71 | width: pokeballSize, 72 | height: pokeballSize, 73 | color: context.colors.text.withValues(alpha: 0.05), 74 | ), 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/utils/extensions/animation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/animation.dart'; 2 | 3 | extension AnimationControllerX on AnimationController { 4 | Animation curvedTweenAnimation({required T begin, required T end}) { 5 | return Tween(begin: begin, end: end).animate(CurvedAnimation( 6 | curve: Curves.easeInOut, 7 | parent: this, 8 | )); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/utils/extensions/string.dart: -------------------------------------------------------------------------------- 1 | extension StringX on String { 2 | String capitalize() { 3 | if (length > 0) { 4 | return '${this[0].toUpperCase()}${substring(1)}'; 5 | } 6 | 7 | return this; 8 | } 9 | 10 | double parseDouble([double defaultValue = 0.0]) { 11 | return double.tryParse(replaceAll(RegExp(r'[^0-9\.]'), '')) ?? defaultValue; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/utils/size.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | Size getTextSize(BuildContext context, String text, TextStyle style) { 5 | final textPainter = TextPainter( 6 | text: TextSpan( 7 | text: text, 8 | style: style, 9 | ), 10 | maxLines: 1, 11 | textDirection: TextDirection.ltr, 12 | )..layout(); 13 | return textPainter.size; 14 | } 15 | -------------------------------------------------------------------------------- /lib/utils/string.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | String removeTrailingZero(double n) { 4 | final formatter = NumberFormat() 5 | ..minimumFractionDigits = 0 6 | ..maximumFractionDigits = 2; 7 | 8 | return formatter.format(n); 9 | } 10 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void fl_register_plugins(FlPluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import path_provider_foundation 9 | import sqflite_darwin 10 | 11 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 12 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 13 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 14 | } 15 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = flutter_pokedex 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.hungps.flutterPokedex 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.hungps. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /screenshots/home-news.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/home-news.png -------------------------------------------------------------------------------- /screenshots/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/home.png -------------------------------------------------------------------------------- /screenshots/pokedex-fab-generation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/pokedex-fab-generation.png -------------------------------------------------------------------------------- /screenshots/pokedex-fab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/pokedex-fab.png -------------------------------------------------------------------------------- /screenshots/pokedex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/pokedex.png -------------------------------------------------------------------------------- /screenshots/pokemon-info-about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/pokemon-info-about.png -------------------------------------------------------------------------------- /screenshots/pokemon-info-base-stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/pokemon-info-base-stats.png -------------------------------------------------------------------------------- /screenshots/pokemon-info-evolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/pokemon-info-evolution.png -------------------------------------------------------------------------------- /screenshots/pokemon-info-expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/pokemon-info-expanded.png -------------------------------------------------------------------------------- /screenshots/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/screenshots/thumbnail.png -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | void main() { 11 | testWidgets('Some testcase', (WidgetTester tester) async { 12 | expect(true, isTrue); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | flutter_pokedex 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_pokedex", 3 | "short_name": "flutter_pokedex", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void RegisterPlugins(flutter::PluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | return true; 35 | } 36 | 37 | void FlutterWindow::OnDestroy() { 38 | if (flutter_controller_) { 39 | flutter_controller_ = nullptr; 40 | } 41 | 42 | Win32Window::OnDestroy(); 43 | } 44 | 45 | LRESULT 46 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 47 | WPARAM const wparam, 48 | LPARAM const lparam) noexcept { 49 | // Give Flutter, including plugins, an opportunity to handle window messages. 50 | if (flutter_controller_) { 51 | std::optional result = 52 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 53 | lparam); 54 | if (result) { 55 | return *result; 56 | } 57 | } 58 | 59 | switch (message) { 60 | case WM_FONTCHANGE: 61 | flutter_controller_->engine()->ReloadSystemFonts(); 62 | break; 63 | } 64 | 65 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 66 | } 67 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"flutter_pokedex", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hungps/flutter_pokedex/7e2b6d491bc3db5730398a84beff2bf64c0d7bcd/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | std::string utf8_string; 52 | if (target_length == 0 || target_length > utf8_string.max_size()) { 53 | return utf8_string; 54 | } 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------