├── .codecov.yml
├── .env.example
├── .github
└── workflows
│ ├── release.yml
│ └── tests.yml
├── .gitignore
├── .metadata
├── .vscode
└── launch.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── cryptocurrency_app
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-hdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-mdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-night-v21
│ │ │ ├── background.png
│ │ │ └── launch_background.xml
│ │ │ ├── drawable-night
│ │ │ ├── background.png
│ │ │ └── launch_background.xml
│ │ │ ├── drawable-v21
│ │ │ ├── background.png
│ │ │ └── launch_background.xml
│ │ │ ├── drawable-xhdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-xxhdpi
│ │ │ └── splash.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ └── splash.png
│ │ │ ├── drawable
│ │ │ ├── background.png
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ ├── values-night
│ │ │ └── styles.xml
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── assets
├── icon
│ ├── icon.png
│ └── icon_ios.png
└── translations
│ ├── en.json
│ └── es.json
├── integration_test
├── data
│ └── api_data.dart
├── main_test.dart
└── search_test.dart
├── 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
│ ├── LaunchBackground.imageset
│ │ ├── Contents.json
│ │ ├── background.png
│ │ └── darkbackground.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
├── constants
│ ├── app_theme.dart
│ ├── exceptions.dart
│ ├── keys.dart
│ └── utils.dart
├── generated
│ └── locale_keys.g.dart
├── main.dart
├── models
│ ├── allowance
│ │ ├── allowance.dart
│ │ ├── allowance.freezed.dart
│ │ └── allowance.g.dart
│ ├── exchanges
│ │ ├── exchange
│ │ │ ├── exchange.dart
│ │ │ ├── exchange.freezed.dart
│ │ │ └── exchange.g.dart
│ │ └── exchanges_response
│ │ │ ├── exchanges_response.dart
│ │ │ ├── exchanges_response.freezed.dart
│ │ │ └── exchanges_response.g.dart
│ ├── graph
│ │ ├── graph
│ │ │ ├── graph.dart
│ │ │ └── graph.freezed.dart
│ │ ├── graph_response
│ │ │ ├── graph_response.dart
│ │ │ └── graph_response.freezed.dart
│ │ ├── pair_graph
│ │ │ ├── pair_graph.dart
│ │ │ └── pair_graph.freezed.dart
│ │ └── points
│ │ │ ├── points.dart
│ │ │ └── points.freezed.dart
│ ├── markets
│ │ ├── favorite_pair
│ │ │ ├── favorite_pair.dart
│ │ │ ├── favorite_pair.freezed.dart
│ │ │ └── favorite_pair.g.dart
│ │ ├── market_response
│ │ │ ├── market_response.dart
│ │ │ ├── market_response.freezed.dart
│ │ │ └── market_response.g.dart
│ │ └── pair
│ │ │ ├── pair.dart
│ │ │ ├── pair.freezed.dart
│ │ │ └── pair.g.dart
│ ├── orderbook
│ │ ├── orderbook
│ │ │ ├── orderbook.dart
│ │ │ └── orderbook.freezed.dart
│ │ ├── orderbook_response
│ │ │ ├── orderbook_response.dart
│ │ │ └── orderbook_response.freezed.dart
│ │ └── price
│ │ │ ├── price.dart
│ │ │ └── price.freezed.dart
│ ├── pair
│ │ ├── change
│ │ │ ├── change.dart
│ │ │ ├── change.freezed.dart
│ │ │ └── change.g.dart
│ │ ├── pair_response
│ │ │ ├── pair_response.dart
│ │ │ ├── pair_response.freezed.dart
│ │ │ └── pair_response.g.dart
│ │ ├── pair_summary
│ │ │ ├── pair_summary.dart
│ │ │ ├── pair_summary.freezed.dart
│ │ │ └── pair_summary.g.dart
│ │ └── price
│ │ │ ├── price.dart
│ │ │ ├── price.freezed.dart
│ │ │ └── price.g.dart
│ ├── settings
│ │ ├── settings_details
│ │ │ ├── settings_details.dart
│ │ │ └── settings_details.freezed.dart
│ │ └── settings_state
│ │ │ ├── settings_state.dart
│ │ │ └── settings_state.freezed.dart
│ └── trades
│ │ ├── trade
│ │ ├── trade.dart
│ │ └── trade.freezed.dart
│ │ └── trades_response.dart
│ │ ├── trades_response.dart
│ │ └── trades_response.freezed.dart
├── provider
│ ├── crypto_provider.dart
│ ├── navigation_provider.dart
│ ├── settings_provider.dart
│ └── time_provider.dart
├── repository
│ └── crypto_repository.dart
└── ui
│ ├── home.dart
│ ├── screens
│ ├── details.dart
│ ├── home.dart
│ ├── search.dart
│ └── settings.dart
│ └── widgets
│ ├── details
│ ├── details_widget.dart
│ ├── ohlc_section.dart
│ ├── order_book_section.dart
│ ├── summary_section.dart
│ ├── time_bar_selector.dart
│ └── trades_section.dart
│ ├── favorite_pair.dart
│ ├── line_chart.dart
│ ├── pair_tile.dart
│ └── title_price.dart
├── pubspec.yaml
├── run_all_tests.sh
├── screenshots
├── 1_dark.jpeg
├── 1_light.jpeg
├── 2_dark.jpeg
├── 2_light.jpeg
├── 3_dark.jpeg
├── 3_light.jpeg
└── cover.png
├── test
├── api_test.dart
├── data
│ └── api_data.dart
├── exception_test.dart
└── widget_test.dart
├── test_driver
└── integration_test.dart
└── web
├── favicon.png
├── icons
├── Icon-192.png
├── Icon-512.png
├── Icon-maskable-192.png
└── Icon-maskable-512.png
├── index.html
└── manifest.json
/.codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - '**/**/**/*.g.dart'
3 | - '**/**/**/*.freezed.dart'
4 | - '**/**/**/**/*.g.dart'
5 | - '**/**/**/**/*.freezed.dart'
6 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | API_KEY=
2 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | env:
9 | flutter_version: "2.10.2"
10 |
11 | jobs:
12 | build_deploy:
13 | name: Build apk and release
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Cache Flutter dependencies
18 | uses: actions/cache@v2
19 | with:
20 | path: /opt/hostedtoolcache/flutter
21 | key: ${{ runner.OS }}-flutter-install-cache-${{ env.flutter_version }}
22 | - uses: subosito/flutter-action@v2
23 | with:
24 | flutter-version: ${{ env.flutter_version }}
25 | channel: 'stable'
26 | - run: flutter pub get
27 | # build Android version
28 | - name: Create env file
29 | run: |
30 | touch .env
31 | echo API_KEY=${{ secrets.API_KEY }} >> .env
32 | cat .env
33 | - run: flutter build apk --split-per-abi
34 | # This action will create a github release and optionally upload an artifact to it.
35 | # https://github.com/ncipollo/release-action
36 | - name: Extract release notes
37 | id: extract-release-notes
38 | uses: ffurrer2/extract-release-notes@v1
39 | - name: Create a Release APK
40 | uses: ncipollo/release-action@v1
41 | with:
42 | artifacts: "build/app/outputs/apk/release/*.apk"
43 | token: ${{ secrets.GITHUB_TOKEN }}
44 | body: ${{ steps.extract-release-notes.outputs.release_notes }}
45 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | #The name of your workflow.
2 | name: test
3 | # Trigger the workflow on push or pull request
4 | on: [push,pull_request_review]
5 | env:
6 | flutter_version: "2.10.2"
7 |
8 | #A workflow run is made up of one or more jobs. Jobs run in parallel by default.
9 | jobs:
10 |
11 | unit-testing:
12 | #The type of machine to run the job on. [windows,macos, ubuntu , self-hosted]
13 | runs-on: ubuntu-latest
14 | #sequence of tasks called
15 | steps:
16 | # The branch or tag ref that triggered the workflow will be checked out.
17 | # https://github.com/actions/checkout
18 | - uses: actions/checkout@v2
19 | # Setup a flutter environment.
20 | # https://github.com/marketplace/actions/flutter-action
21 | - name: Cache Flutter dependencies
22 | uses: actions/cache@v2
23 | with:
24 | path: /opt/hostedtoolcache/flutter
25 | key: ${{ runner.OS }}-flutter-install-cache-${{ env.flutter_version }}
26 | - uses: subosito/flutter-action@v2
27 | with:
28 | flutter-version: '${{ env.flutter_version }}'
29 | channel: 'stable'
30 | - name: Create env file
31 | run: |
32 | touch .env
33 | echo API_KEY=${{ secrets.API_KEY }} >> .env
34 | cat .env
35 | - run: flutter pub get
36 | # run static analys code
37 | - run: flutter analyze
38 | # run flutter widgets tests and unit tests
39 | - run: flutter test --coverage
40 | # Upload coverage reports to Codecov
41 | # https://github.com/marketplace/actions/codecov
42 | - name: Upload coverage to Codecov
43 | uses: codecov/codecov-action@v2
44 | with:
45 | token: ${{ secrets.CODECOV_TOKEN }}
46 | file: coverage/lcov.info
47 | ios-integration:
48 | #creates a build matrix for your jobs
49 | strategy:
50 | #set of different configurations of the virtual environment.
51 | matrix:
52 | device:
53 | - "iPhone 8"
54 | - "iPhone 13 Pro"
55 | fail-fast: false
56 | runs-on: macos-latest
57 | #Identifies any jobs that must complete successfully before this job will run.
58 | needs: unit-testing
59 | steps:
60 | - name: Start Simulator
61 | uses: futureware-tech/simulator-action@v1
62 | with:
63 | model: '${{matrix.device}}'
64 | - uses: actions/checkout@v2
65 | - name: Cache Flutter dependencies
66 | uses: actions/cache@v2
67 | with:
68 | path: /opt/hostedtoolcache/flutter
69 | key: ${{ runner.OS }}-flutter-install-cache-${{ env.flutter_version }}
70 | - uses: subosito/flutter-action@v2
71 | with:
72 | flutter-version: '${{ env.flutter_version }}'
73 | channel: 'stable'
74 |
75 | - name: Create env file
76 | run: |
77 | touch .env
78 | echo API_KEY=${{ secrets.API_KEY }} >> .env
79 | cat .env
80 | - run: flutter pub get
81 | # Run flutter integrate tests
82 | - name: Run Flutter Driver tests
83 | run: flutter drive --driver=test_driver/integration_test.dart --target=integration_test/main_test.dart
84 |
85 | android-integration:
86 | runs-on: macos-latest
87 | #creates a build matrix for your jobs
88 | strategy:
89 | #set of different configurations of the virtual environment.
90 | matrix:
91 | api-level: [21, 29]
92 | target: [default]
93 | needs: unit-testing
94 | steps:
95 | - uses: actions/checkout@v2
96 | - name: Cache Flutter dependencies
97 | uses: actions/cache@v2
98 | with:
99 | path: /opt/hostedtoolcache/flutter
100 | key: ${{ runner.OS }}-flutter-install-cache-${{ env.flutter_version }}
101 | - uses: subosito/flutter-action@v2
102 | with:
103 | flutter-version: '${{ env.flutter_version }}'
104 | channel: 'stable'
105 | - name: Create env file
106 | run: |
107 | touch .env
108 | echo API_KEY=${{ secrets.API_KEY }} >> .env
109 | cat .env
110 | - name: Run Flutter Driver tests
111 | #GitHub Action for installing, configuring and running Android Emulators (work only Mac OS)
112 | #https://github.com/ReactiveCircus/android-emulator-runner
113 | uses: reactivecircus/android-emulator-runner@v2
114 | with:
115 | api-level: ${{ matrix.api-level }}
116 | target: ${{ matrix.target }}
117 | arch: x86_64
118 | profile: Nexus 6
119 | script: flutter drive --driver=test_driver/integration_test.dart --target=integration_test/main_test.dart
120 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Folder generated by flutter test --coverage
44 | coverage/
45 |
46 | .env
47 | ios/build/
48 | pubspec.lock
49 |
50 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 1aafb3a8b9b0c36241c5f5b34ee914770f015818
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "app_crypto",
9 | "request": "launch",
10 | "type": "dart"
11 | },
12 | {
13 | "name": "App Crypto",
14 | "program": "lib/main.dart",
15 | "request": "launch",
16 | "type": "dart",
17 | "args": []
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 | ## [Unreleased]
7 |
8 |
9 | ## [1.0.1] - 2022-02-21
10 | ### Update
11 |
12 | ## Changes
13 | - Flutter Upgrade
14 | - Riverpod Upgrade
15 | - Android Upgrade
16 | - GitHub Actions for Integration Testing Fixed
17 | - Settings UI Updated
18 |
19 | ## [1.0.0] - 2021-05-27
20 | ### First version
21 |
22 | ## Features
23 | - API REST (CryptoWatch)
24 | - Linear Graph View (Hour, Day, Week, etc)
25 | - OHLC Graph
26 | - Search
27 | - Light / Dark Theme
28 | - Multi Lenguage
29 | - Exchange Selection
30 | - Favorite Pair
31 |
32 |
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Salvador Valverde
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Flutter Crypto APP
3 | Complete Flutter Application with Riverpod & Freezed + Dio for API REST.
4 |
5 | [](https://github.com/salvadordeveloper/flutter-crypto-app/actions/workflows/tests.yml)
6 | [](https://github.com/salvadordeveloper/flutter-crypto-app/actions/workflows/release.yml)
7 | [](https://codecov.io/gh/salvadordeveloper/flutter-crypto-app)
8 | [](https://flutter.dev/docs/get-started/install)
9 | [](https://choosealicense.com/licenses/mit/)
10 |
11 |
12 |
13 | ## Features
14 | - API REST (CryptoWatch)
15 | - Linear Graph View (Hour, Day, Week, etc)
16 | - OHLC Graph
17 | - Search
18 | - Light / Dark Theme
19 | - Multi Language
20 | - Exchange Selection
21 | - Favorite Pair
22 |
23 | ### Stack
24 | - Flutter 2.10.2
25 | - Riverpod + Hooks
26 | - Freezed
27 | - Dio
28 |
29 | ### Testing
30 | - Unit Testing (flutter_test)
31 | - Integration Testing (integration_test)
32 | - Mock Data (http_mock_adapter)
33 | - Github Actions (iOS & Android Integration Test)
34 |
35 | ## Screenshots
36 |
37 |
38 | | Home | Details | Settings |
39 | | --- | --- | --- |
40 | |
|
|
|
41 | |
|
|
|
42 |
43 | ## Setup project
44 |
45 | Download project
46 | ```bash
47 | git clone https://github.com/salvadordeveloper/flutter-crypto-app
48 | ```
49 |
50 | Get flutter dependencies
51 | ```bash
52 | flutter pub get
53 | ```
54 |
55 | You need to create an account at https://cryptowat.ch/ to get a personal API KEY
56 |
57 | Rename the env.example file to .env and put there you API KEY
58 | ```bash
59 | API_KEY={CryptoWatch_KEY}
60 | ```
61 |
62 | Run the app
63 | ```bash
64 | flutter run
65 | ```
66 |
67 | If you have any error with generated files try to run this
68 | ```bash
69 | flutter pub run build_runner build --delete-conflicting-outputs
70 | ```
71 |
72 |
73 | ### Testing
74 |
75 | Unit Test
76 | ```bash
77 | flutter test
78 | ```
79 | Integration Test
80 | ```bash
81 | flutter drive --driver=test_driver/integration_test.dart --target=integration_test/main_test.dart
82 | ```
83 |
84 | ## Resources
85 | [Flutter Docs](https://flutter.dev/docs)
86 |
87 | [Riverpod Docs](https://riverpod.dev/docs/getting_started/)
88 |
89 | [Cryptowatch Docs](https://docs.cryptowat.ch/rest-api/)
90 |
91 |
92 | ## Licence
93 |
94 | ```
95 | MIT License
96 |
97 | Copyright (c) 2022 Salvador Valverde
98 |
99 | Permission is hereby granted, free of charge, to any person obtaining a copy
100 | of this software and associated documentation files (the "Software"), to deal
101 | in the Software without restriction, including without limitation the rights
102 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
103 | copies of the Software, and to permit persons to whom the Software is
104 | furnished to do so, subject to the following conditions:
105 |
106 | The above copyright notice and this permission notice shall be included in all
107 | copies or substantial portions of the Software.
108 |
109 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
110 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
111 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
112 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
113 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
114 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
115 | SOFTWARE.
116 | ```
117 |
118 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | #include: package:effective_dart/analysis_options.yaml
2 |
3 | analyzer:
4 | exclude:
5 | - "**/*.g.dart"
6 | - "**/*.freezed.dart"
7 |
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 31
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | applicationId "com.example.cryptocurrency_app"
41 | minSdkVersion 18
42 | targetSdkVersion 31
43 | versionCode flutterVersionCode.toInteger()
44 | versionName flutterVersionName
45 | }
46 |
47 | buildTypes {
48 | release {
49 | // TODO: Add your own signing config for the release build.
50 | // Signing with the debug keys for now, so `flutter run --release` works.
51 | signingConfig signingConfigs.debug
52 | }
53 | }
54 | }
55 |
56 | flutter {
57 | source '../..'
58 | }
59 |
60 | dependencies {
61 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
62 | }
63 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
9 |
13 |
20 |
24 |
28 |
33 |
37 |
38 |
39 |
40 |
41 |
42 |
44 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/cryptocurrency_app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.cryptocurrency_app
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/drawable-hdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/drawable-mdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-v21/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/drawable-night-v21/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/drawable-night/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/drawable-v21/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/drawable-xhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/drawable-xxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/drawable-xxxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/drawable/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-hdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-mdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
16 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
16 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.4.21'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Feb 21 12:57:55 MST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/assets/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/assets/icon/icon.png
--------------------------------------------------------------------------------
/assets/icon/icon_ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/assets/icon/icon_ios.png
--------------------------------------------------------------------------------
/assets/translations/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "homeTitle" : "Home",
3 | "openChart" : "Open Chart",
4 |
5 | "searchTitle" : "Search",
6 | "searchBar" : "Search coin...",
7 | "noResults" : "No results",
8 |
9 | "settingsTitle": "Settings",
10 |
11 | "languageSection" : "Language",
12 | "language": "Language",
13 | "dataSection" : "Data",
14 | "exchange" : "Exchange",
15 | "topPair" : "Top Pair",
16 | "designSection" : "Design",
17 | "appTheme" : "App theme",
18 |
19 | "spanish" : "Spanish",
20 | "english" : "English",
21 |
22 | "summary" : "Summary",
23 | "orderbook" : "Orderbook",
24 | "trades" : "Trades",
25 | "ohlc" : "OHLC",
26 |
27 | "price" : "Price",
28 | "last" : "Last",
29 | "high" : "High",
30 | "low" : "Low",
31 | "change" : "Change",
32 | "volume" : "Volume",
33 | "quoteVolume" : "Quote Volume",
34 | "time" : "Time",
35 | "amount" : "Amount",
36 | "bid" : "Bid",
37 | "ask" : "Ask",
38 |
39 | "errorRequestCancelled" : "Request to API server was cancelled",
40 | "errorConnectionTimeout" : "Connection timeout with API server",
41 | "errorInternetConnection" : "Connection to API server failed due to internet connection",
42 | "errorReceiveTimeout" : "Receive timeout in connection with API server",
43 | "errorSendTimeout" : "Send timout in connection iwth API server",
44 | "errorBadRequest": "Bad request",
45 | "errorRequestNotFound": "The requested resource was not found",
46 | "errorIntenalServer" : "Internal server error",
47 | "errorSomethingWentWrong" : "Something went wrong"
48 |
49 |
50 | }
--------------------------------------------------------------------------------
/assets/translations/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "homeTitle" : "Inicio",
3 | "openChart" : "Abrir grafica",
4 |
5 | "searchTitle" : "Buscar",
6 | "searchBar" : "Buscar moneda...",
7 | "noResults" : "Sin resultados",
8 |
9 | "settingsTitle": "Ajustes",
10 |
11 | "languageSection" : "Lenguage",
12 | "language": "Idioma",
13 | "dataSection" : "Datos",
14 | "exchange" : "Exchange",
15 | "topPair" : "Top Par",
16 | "designSection" : "Diseño",
17 | "appTheme" : "Tema",
18 |
19 | "spanish" : "Español",
20 | "english" : "Ingles",
21 |
22 |
23 | "summary" : "Resumen",
24 | "orderbook" : "Orderbook",
25 | "trades" : "Trades",
26 | "ohlc" : "OHLC",
27 |
28 | "price" : "Precio",
29 | "last" : "Ultimo",
30 | "high" : "Más alto",
31 | "low" : "Más bajo",
32 | "change" : "Cambio",
33 | "volume" : "Volumen",
34 | "quoteVolume" : "Quote Volume",
35 | "time" : "Tiempo",
36 | "amount" : "Cantidad",
37 | "bid" : "Bid",
38 | "ask" : "Ask",
39 |
40 | "errorRequestCancelled" : "Request to API server was cancelled",
41 | "errorConnectionTimeout" : "Connection timeout with API server",
42 | "errorInternetConnection" : "Connection to API server failed due to internet connection",
43 | "errorReceiveTimeout" : "Receive timeout in connection with API server",
44 | "errorSendTimeout" : "Send timout in connection iwth API server",
45 | "errorBadRequest": "Bad request",
46 | "errorRequestNotFound": "The requested resource was not found",
47 | "errorIntenalServer" : "Internal server error",
48 | "errorSomethingWentWrong" : "Something went wrong"
49 |
50 | }
--------------------------------------------------------------------------------
/integration_test/data/api_data.dart:
--------------------------------------------------------------------------------
1 | class ApiData {
2 | static final Map exchanges = {
3 | "result": [
4 | {
5 | "id": 17,
6 | "symbol": "mexbt",
7 | "name": "meXBT",
8 | "route": "https://api.cryptowat.ch/exchanges/mexbt",
9 | "active": false
10 | },
11 | {
12 | "id": 62,
13 | "symbol": "coinone",
14 | "name": "Coinone",
15 | "route": "https://api.cryptowat.ch/exchanges/coinone",
16 | "active": true
17 | },
18 | ],
19 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
20 | };
21 |
22 | static final Map pairs = {
23 | "result": [
24 | {
25 | "id": 579,
26 | "exchange": "binance",
27 | "pair": "btcusdt",
28 | "active": true,
29 | "route": "https://api.cryptowat.ch/markets/binance/btcusdt"
30 | },
31 | {
32 | "id": 580,
33 | "exchange": "binance",
34 | "pair": "ethbtc",
35 | "active": true,
36 | "route": "https://api.cryptowat.ch/markets/binance/ethbtc"
37 | },
38 | {
39 | "id": 581,
40 | "exchange": "binance",
41 | "pair": "ltcbtc",
42 | "active": true,
43 | "route": "https://api.cryptowat.ch/markets/binance/ltcbtc"
44 | },
45 | {
46 | "id": 582,
47 | "exchange": "binance",
48 | "pair": "neobtc",
49 | "active": true,
50 | "route": "https://api.cryptowat.ch/markets/binance/neobtc"
51 | },
52 | ],
53 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
54 | };
55 |
56 | static final Map pair_btcusdt_summary = {
57 | "result": {
58 | "price": {
59 | "last": 35503.33,
60 | "high": 43861.94,
61 | "low": 30000,
62 | "change": {"percentage": -0.18764266402587584, "absolute": -8200.75}
63 | },
64 | "volume": 257132.87322650044,
65 | "volumeQuote": 10096197214.14349
66 | },
67 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
68 | };
69 |
70 | static final Map pair_btcusdt_oderbook = {
71 | "result": {
72 | "asks": [
73 | [35922.59, 0.004088],
74 | [35925.23, 0.003071],
75 | [35925.71, 0.012824],
76 | [35927.12, 0.000556],
77 | [35927.58, 0.2178],
78 | ],
79 | "bids": [
80 | [35904.23, 0.153095],
81 | [35900.35, 0.082238],
82 | [35898, 0.12],
83 | [35897.99, 0.006152],
84 | [35897.68, 0.04332],
85 | ],
86 | "seqNum": 429614
87 | },
88 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
89 | };
90 |
91 | static final Map pair_btcusdt_trades = {
92 | "result": [
93 | [0, 1621433110, 34452.66, 0.008464],
94 | [0, 1621433110, 34454.43, 0.016662],
95 | [0, 1621433110, 34485.69, 0.00476],
96 | [0, 1621433110, 34475.97, 0.000401],
97 | [0, 1621433110, 34456.09, 0.0011],
98 | [0, 1621433110, 34456.09, 0.004997],
99 | ],
100 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
101 | };
102 |
103 | static final Map pair_btcusdt_graph = {
104 | "result": {
105 | "14400": [
106 | [
107 | 1607054400,
108 | 19422.34,
109 | 19527,
110 | 19122.74,
111 | 19162.62,
112 | 8683.588417,
113 | 167917416.81467284
114 | ],
115 | [
116 | 1607097600,
117 | 18835.47,
118 | 19146.22,
119 | 18686.38,
120 | 18943.35,
121 | 14717.586675,
122 | 278732315.17076141
123 | ],
124 | [
125 | 1607112000,
126 | 18944.06,
127 | 19078.68,
128 | 18817,
129 | 19038.73,
130 | 8799.851665,
131 | 166925728.42698553
132 | ],
133 | ]
134 | },
135 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
136 | };
137 | }
138 |
--------------------------------------------------------------------------------
/integration_test/main_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:integration_test/integration_test.dart';
2 |
3 | import 'search_test.dart' as app;
4 |
5 | void main() {
6 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
7 | app.main();
8 | }
9 |
--------------------------------------------------------------------------------
/integration_test/search_test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'package:cryptocurrency_app/constants/keys.dart';
3 | import 'package:cryptocurrency_app/main.dart' as app;
4 | import 'package:cryptocurrency_app/repository/crypto_repository.dart';
5 | import 'package:dio/dio.dart';
6 | import 'package:easy_localization/easy_localization.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_test/flutter_test.dart';
9 | import 'package:hooks_riverpod/hooks_riverpod.dart';
10 | import 'package:http_mock_adapter/http_mock_adapter.dart';
11 | import 'data/api_data.dart';
12 |
13 | void main() {
14 | final dio = Dio();
15 | final dioAdapter = DioAdapter(dio: dio);
16 | setUpAll(() {
17 | dio.httpClientAdapter = dioAdapter;
18 |
19 | //Get graph
20 | dioAdapter.onGet('/markets/binance/btcusdt/ohlc', (request) {
21 | request.reply(200, ApiData.pair_btcusdt_graph);
22 | });
23 |
24 | //Get trades
25 | dioAdapter.onGet('/markets/binance/btcusdt/trades', (request) {
26 | request.reply(200, ApiData.pair_btcusdt_trades);
27 | });
28 |
29 | //Get orderbook
30 | dioAdapter.onGet('/markets/binance/btcusdt/orderbook', (request) {
31 | request.reply(200, ApiData.pair_btcusdt_oderbook);
32 | });
33 |
34 | //Get sumary of btcusdt
35 | dioAdapter.onGet('/markets/binance/btcusdt/summary', (request) {
36 | print(request.toString());
37 | request.reply(200, ApiData.pair_btcusdt_summary);
38 | });
39 |
40 | //Get list of pairs from Binance
41 | dioAdapter.onGet('/markets/binance', (request) {
42 | print(request.toString());
43 |
44 | request.reply(200, ApiData.pairs);
45 | });
46 | });
47 |
48 | group('Search Screen Test', () {
49 | testWidgets('Search screen loading data', (tester) async {
50 | WidgetsFlutterBinding.ensureInitialized();
51 | await EasyLocalization.ensureInitialized();
52 |
53 | runApp(EasyLocalization(
54 | supportedLocales: [Locale('en'), Locale('es')],
55 | path: 'assets/translations',
56 | fallbackLocale: Locale('en'),
57 | child: ProviderScope(
58 | overrides: [clientProvider.overrideWithValue(dio)],
59 | child: app.MyApp(),
60 | )));
61 |
62 | await tester.pumpAndSettle();
63 | final searchButton = find.byKey(Keys.NAV_SEARCH);
64 |
65 | await pumpUntilFound(tester, searchButton);
66 |
67 | await tester.tap(searchButton);
68 |
69 | await tester.pumpAndSettle();
70 |
71 | final results = await find.byKey(Keys.PAIR_TILE);
72 |
73 | expect(results, findsNWidgets(4));
74 |
75 | final searchTextField = await find.byKey(Keys.SEARCH_TEXT_FIELD);
76 | await tester.showKeyboard(searchTextField);
77 | tester.testTextInput.enterText("btcusdt");
78 | await tester.testTextInput.receiveAction(TextInputAction.done);
79 |
80 | await tester.pumpAndSettle();
81 | final resultsFiltered = await find.byKey(Keys.PAIR_TILE);
82 | expect(resultsFiltered, findsOneWidget);
83 | });
84 | });
85 | }
86 |
87 | Future pumpUntilFound(
88 | WidgetTester tester,
89 | Finder finder, {
90 | Duration timeout = const Duration(seconds: 30),
91 | }) async {
92 | var timerDone = false;
93 | final timer =
94 | Timer(timeout, () => throw TimeoutException("Pump until has timed out"));
95 | while (timerDone != true) {
96 | await tester.pump();
97 |
98 | final found = tester.any(finder);
99 | if (found) {
100 | timerDone = true;
101 | }
102 | }
103 | timer.cancel();
104 | }
105 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | 9.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, '9.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 | - flutter_secure_storage (3.3.1):
4 | - Flutter
5 | - integration_test (0.0.1):
6 | - Flutter
7 | - shared_preferences (0.0.1):
8 | - Flutter
9 |
10 | DEPENDENCIES:
11 | - Flutter (from `Flutter`)
12 | - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
13 | - integration_test (from `.symlinks/plugins/integration_test/ios`)
14 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
15 |
16 | EXTERNAL SOURCES:
17 | Flutter:
18 | :path: Flutter
19 | flutter_secure_storage:
20 | :path: ".symlinks/plugins/flutter_secure_storage/ios"
21 | integration_test:
22 | :path: ".symlinks/plugins/integration_test/ios"
23 | shared_preferences:
24 | :path: ".symlinks/plugins/shared_preferences/ios"
25 |
26 | SPEC CHECKSUMS:
27 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
28 | flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
29 | integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
30 | shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
31 |
32 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
33 |
34 | COCOAPODS: 1.10.2
35 |
--------------------------------------------------------------------------------
/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.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/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 | @UIApplicationMain
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/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "background.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "appearances" : [
10 | {
11 | "appearance" : "luminosity",
12 | "value" : "dark"
13 | }
14 | ],
15 | "filename" : "darkbackground.png",
16 | "idiom" : "universal",
17 | "scale" : "1x"
18 | },
19 | {
20 | "idiom" : "universal",
21 | "scale" : "2x"
22 | },
23 | {
24 | "appearances" : [
25 | {
26 | "appearance" : "luminosity",
27 | "value" : "dark"
28 | }
29 | ],
30 | "idiom" : "universal",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "universal",
35 | "scale" : "3x"
36 | },
37 | {
38 | "appearances" : [
39 | {
40 | "appearance" : "luminosity",
41 | "value" : "dark"
42 | }
43 | ],
44 | "idiom" : "universal",
45 | "scale" : "3x"
46 | }
47 | ],
48 | "info" : {
49 | "author" : "xcode",
50 | "version" : 1
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "LaunchImage.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "LaunchImage@2x.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "filename" : "LaunchImage@3x.png",
15 | "idiom" : "universal",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "author" : "xcode",
21 | "version" : 1
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/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 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleLocalizations
14 |
15 | en
16 | es
17 |
18 | CFBundleName
19 | Crypto App
20 | CFBundlePackageType
21 | APPL
22 | CFBundleShortVersionString
23 | $(FLUTTER_BUILD_NAME)
24 | CFBundleSignature
25 | ????
26 | CFBundleVersion
27 | $(FLUTTER_BUILD_NUMBER)
28 | LSRequiresIPhoneOS
29 |
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | UIMainStoryboardFile
33 | Main
34 | UIStatusBarHidden
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 | UIViewControllerBasedStatusBarAppearance
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/lib/constants/app_theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class AppTheme {
4 | static final ThemeData light = ThemeData(
5 | iconTheme: IconThemeData(color: Colors.black87),
6 | bottomNavigationBarTheme: BottomNavigationBarThemeData(
7 | backgroundColor: Colors.blueGrey,
8 | selectedItemColor: Colors.white,
9 | unselectedItemColor: Colors.white70),
10 | appBarTheme: AppBarTheme(
11 | color: Colors.blueGrey,
12 | ),
13 | brightness: Brightness.light,
14 | scaffoldBackgroundColor: Colors.white,
15 | cardColor: Colors.grey[500],
16 | unselectedWidgetColor: Colors.black45,
17 | focusColor: Colors.black,
18 | textTheme: TextTheme(
19 | headline1: TextStyle(
20 | color: Colors.black, fontSize: 40, fontWeight: FontWeight.w500),
21 | headline2: TextStyle(
22 | color: Colors.black, fontSize: 34, fontWeight: FontWeight.w400),
23 | headline3: TextStyle(
24 | color: Colors.black, fontSize: 22, fontWeight: FontWeight.w500),
25 | headline4: TextStyle(
26 | color: Colors.black54, fontSize: 16, fontWeight: FontWeight.bold),
27 | headline5: TextStyle(
28 | color: Colors.black, fontSize: 19, fontWeight: FontWeight.w700),
29 | headline6: TextStyle(
30 | color: Colors.black, fontSize: 13, fontWeight: FontWeight.w400),
31 | subtitle1: TextStyle(
32 | color: Colors.black87, fontSize: 14, fontWeight: FontWeight.normal),
33 | subtitle2: TextStyle(
34 | color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold),
35 | ),
36 | );
37 |
38 | static final ThemeData dark = ThemeData(
39 | primaryColor: Colors.black12,
40 | bottomNavigationBarTheme: BottomNavigationBarThemeData(
41 | backgroundColor: Colors.black,
42 | selectedItemColor: Colors.white,
43 | unselectedItemColor: Colors.white70),
44 | appBarTheme: AppBarTheme(
45 | color: Colors.black,
46 | ),
47 | brightness: Brightness.dark,
48 | scaffoldBackgroundColor: Colors.black,
49 | focusColor: Colors.white,
50 | textTheme: TextTheme(
51 | headline1: TextStyle(
52 | color: Colors.white, fontSize: 40, fontWeight: FontWeight.w500),
53 | headline2: TextStyle(
54 | color: Colors.white, fontSize: 34, fontWeight: FontWeight.w400),
55 | headline3: TextStyle(
56 | color: Colors.white, fontSize: 22, fontWeight: FontWeight.w500),
57 | headline4: TextStyle(
58 | color: Colors.white70, fontSize: 16, fontWeight: FontWeight.bold),
59 | headline5: TextStyle(
60 | color: Colors.white, fontSize: 19, fontWeight: FontWeight.w700),
61 | headline6: TextStyle(
62 | color: Colors.white, fontSize: 13, fontWeight: FontWeight.w400),
63 | subtitle1: TextStyle(
64 | color: Colors.white70, fontSize: 14, fontWeight: FontWeight.normal),
65 | subtitle2: TextStyle(
66 | color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)),
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/lib/constants/exceptions.dart:
--------------------------------------------------------------------------------
1 | import 'package:dio/dio.dart';
2 | import '../../generated/locale_keys.g.dart';
3 |
4 | class DataException implements Exception {
5 | DataException({required this.message});
6 |
7 | DataException.fromDioError(DioError dioError) {
8 | switch (dioError.type) {
9 | case DioErrorType.cancel:
10 | message = LocaleKeys.errorRequestCancelled;
11 | break;
12 | case DioErrorType.connectTimeout:
13 | message = LocaleKeys.errorConnectionTimeout;
14 | break;
15 | case DioErrorType.receiveTimeout:
16 | message = LocaleKeys.errorReceiveTimeout;
17 | break;
18 | case DioErrorType.response:
19 | message = _handleError(dioError.response!.statusCode!);
20 | break;
21 | case DioErrorType.sendTimeout:
22 | message = LocaleKeys.errorSendTimeout;
23 | break;
24 | default:
25 | message = LocaleKeys.errorInternetConnection;
26 | break;
27 | }
28 | }
29 |
30 | String message = "";
31 |
32 | String _handleError(int statusCode) {
33 | switch (statusCode) {
34 | case 400:
35 | return LocaleKeys.errorBadRequest;
36 | case 404:
37 | return LocaleKeys.errorRequestNotFound;
38 | case 500:
39 | return LocaleKeys.errorIntenalServer;
40 | default:
41 | return LocaleKeys.errorSomethingWentWrong;
42 | }
43 | }
44 |
45 | @override
46 | String toString() => message;
47 | }
48 |
--------------------------------------------------------------------------------
/lib/constants/keys.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class Keys {
4 | static final NAV_BAR = Key('nav_bar');
5 | static final NAV_HOME = Key('nav_home');
6 | static final NAV_SEARCH = Key('nav_search');
7 | static final NAV_SETTINGS = Key('nav_settings');
8 |
9 | static final HOME_SCREEN = Key('home_screen');
10 | static final SEARCH_SCREEN = Key('search_screen');
11 | static final SETTINGS_SCREEN = Key('settings_screen');
12 | static final DETAILS_SCREEN = Key('details_screen');
13 |
14 | static final SEARCH_TEXT_FIELD = Key('seach_text_field');
15 |
16 | static final PAIR_TILE = Key('pair_tile');
17 | }
18 |
--------------------------------------------------------------------------------
/lib/constants/utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:intl/intl.dart';
3 |
4 | import '../models/graph/graph/graph.dart';
5 |
6 | ThemeMode getThemeMode(String type) {
7 | ThemeMode themeMode = ThemeMode.system;
8 | switch (type) {
9 | case "System":
10 | themeMode = ThemeMode.system;
11 | break;
12 | case "Dark":
13 | themeMode = ThemeMode.dark;
14 | break;
15 | case "Light":
16 | themeMode = ThemeMode.light;
17 | break;
18 | }
19 | return themeMode;
20 | }
21 |
22 | final themeModes = ["System", "Dark", "Light"];
23 |
24 | final String defaultLenguage = "English";
25 | final String defaultExchange = "binance";
26 | final String defaultPair = "btcusdt";
27 | final String defaultTheme = "System";
28 |
29 | List getPoints(Graph graph) {
30 | if (graph.pairs[0].points.length > 0)
31 | return graph.pairs[0].points.map((e) => e.closePrice).toList();
32 | else
33 | return [];
34 | }
35 |
36 | String epochToString(String epoch) {
37 | final DateTime timeStamp =
38 | DateTime.fromMillisecondsSinceEpoch(int.parse(epoch) * 1000);
39 | return DateFormat('dd/MM/yyyy').format(timeStamp);
40 | }
41 |
42 | final List demoGraphData = const [
43 | 86,
44 | 45,
45 | 59,
46 | 65,
47 | 1,
48 | 62,
49 | 26,
50 | 41,
51 | 88,
52 | 60,
53 | 17,
54 | 18,
55 | 58,
56 | 67,
57 | 55,
58 | 56,
59 | 97,
60 | 96,
61 | 22,
62 | 57,
63 | 29,
64 | 69,
65 | 19,
66 | 30,
67 | 47,
68 | 63,
69 | 33,
70 | 37,
71 | 40,
72 | 51,
73 | 53,
74 | 91,
75 | 71,
76 | 92,
77 | 28,
78 | ];
79 |
--------------------------------------------------------------------------------
/lib/generated/locale_keys.g.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:easy_localization/generate.dart
2 |
3 | abstract class LocaleKeys {
4 | static const homeTitle = 'homeTitle';
5 | static const openChart = 'openChart';
6 | static const searchTitle = 'searchTitle';
7 | static const searchBar = 'searchBar';
8 | static const noResults = 'noResults';
9 | static const settingsTitle = 'settingsTitle';
10 | static const languageSection = 'languageSection';
11 | static const language = 'language';
12 | static const dataSection = 'dataSection';
13 | static const exchange = 'exchange';
14 | static const topPair = 'topPair';
15 | static const designSection = 'designSection';
16 | static const appTheme = 'appTheme';
17 | static const spanish = 'spanish';
18 | static const english = 'english';
19 | static const summary = 'summary';
20 | static const orderbook = 'orderbook';
21 | static const trades = 'trades';
22 | static const ohlc = 'ohlc';
23 | static const price = 'price';
24 | static const last = 'last';
25 | static const high = 'high';
26 | static const low = 'low';
27 | static const change = 'change';
28 | static const volume = 'volume';
29 | static const quoteVolume = 'quoteVolume';
30 | static const time = 'time';
31 | static const amount = 'amount';
32 | static const bid = 'bid';
33 | static const ask = 'ask';
34 | static const errorRequestCancelled = 'errorRequestCancelled';
35 | static const errorConnectionTimeout = 'errorConnectionTimeout';
36 | static const errorInternetConnection = 'errorInternetConnection';
37 | static const errorReceiveTimeout = 'errorReceiveTimeout';
38 | static const errorSendTimeout = 'errorSendTimeout';
39 | static const errorBadRequest = 'errorBadRequest';
40 | static const errorRequestNotFound = 'errorRequestNotFound';
41 | static const errorIntenalServer = 'errorIntenalServer';
42 | static const errorSomethingWentWrong = 'errorSomethingWentWrong';
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/constants/app_theme.dart';
2 | import 'package:easy_localization/easy_localization.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:hooks_riverpod/hooks_riverpod.dart';
5 | import 'package:flutter_dotenv/flutter_dotenv.dart';
6 | import 'provider/settings_provider.dart';
7 | import 'ui/home.dart';
8 | import 'package:cryptocurrency_app/constants/utils.dart' as Utils;
9 |
10 | void main() async {
11 | await dotenv.load(fileName: ".env");
12 | WidgetsFlutterBinding.ensureInitialized();
13 | await EasyLocalization.ensureInitialized();
14 |
15 | runApp(EasyLocalization(
16 | supportedLocales: [Locale('en'), Locale('es')],
17 | path: 'assets/translations',
18 | fallbackLocale: Locale('en'),
19 | child: ProviderScope(child: MyApp())));
20 | }
21 |
22 | class MyApp extends HookConsumerWidget {
23 | MyApp({Key? key}) : super(key: key);
24 |
25 | @override
26 | Widget build(BuildContext context, WidgetRef ref) {
27 | final settings = ref.watch(cryptoSettings);
28 |
29 | final themeMode = settings.maybeWhen(
30 | data: (data) => Utils.getThemeMode(data.themeMode),
31 | orElse: () => ThemeMode.system);
32 |
33 | return MaterialApp(
34 | debugShowCheckedModeBanner: false,
35 | localizationsDelegates: context.localizationDelegates,
36 | supportedLocales: context.supportedLocales,
37 | locale: context.locale,
38 | home: Home(),
39 | themeMode: themeMode,
40 | theme: AppTheme.light,
41 | darkTheme: AppTheme.dark);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/models/allowance/allowance.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'allowance.freezed.dart';
4 | part 'allowance.g.dart';
5 |
6 | @freezed
7 | abstract class Allowance with _$Allowance {
8 | const factory Allowance({
9 | required double cost,
10 | required double remaining,
11 | }) = _Allowance;
12 |
13 | factory Allowance.fromJson(Map json) =>
14 | _$AllowanceFromJson(json);
15 | }
16 |
--------------------------------------------------------------------------------
/lib/models/allowance/allowance.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'allowance.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_Allowance _$_$_AllowanceFromJson(Map json) {
10 | return _$_Allowance(
11 | cost: (json['cost'] as num).toDouble(),
12 | remaining: (json['remaining'] as num).toDouble(),
13 | );
14 | }
15 |
16 | Map _$_$_AllowanceToJson(_$_Allowance instance) =>
17 | {
18 | 'cost': instance.cost,
19 | 'remaining': instance.remaining,
20 | };
21 |
--------------------------------------------------------------------------------
/lib/models/exchanges/exchange/exchange.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'exchange.freezed.dart';
4 | part 'exchange.g.dart';
5 |
6 | @freezed
7 | abstract class Exchange with _$Exchange {
8 | const factory Exchange(
9 | {required int id,
10 | required String symbol,
11 | required String name,
12 | required String route,
13 | required bool active}) = _Exchange;
14 |
15 | factory Exchange.fromJson(Map json) =>
16 | _$ExchangeFromJson(json);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/models/exchanges/exchange/exchange.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'exchange.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_Exchange _$_$_ExchangeFromJson(Map json) {
10 | return _$_Exchange(
11 | id: json['id'] as int,
12 | symbol: json['symbol'] as String,
13 | name: json['name'] as String,
14 | route: json['route'] as String,
15 | active: json['active'] as bool,
16 | );
17 | }
18 |
19 | Map _$_$_ExchangeToJson(_$_Exchange instance) =>
20 | {
21 | 'id': instance.id,
22 | 'symbol': instance.symbol,
23 | 'name': instance.name,
24 | 'route': instance.route,
25 | 'active': instance.active,
26 | };
27 |
--------------------------------------------------------------------------------
/lib/models/exchanges/exchanges_response/exchanges_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/exchanges/exchange/exchange.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 |
4 | part 'exchanges_response.freezed.dart';
5 | part 'exchanges_response.g.dart';
6 |
7 | @freezed
8 | abstract class ExchangesResponse with _$ExchangesResponse {
9 | const factory ExchangesResponse({required List result}) =
10 | _ExchangesResponse;
11 |
12 | factory ExchangesResponse.fromJson(Map json) =>
13 | _$ExchangesResponseFromJson(json);
14 | }
15 |
--------------------------------------------------------------------------------
/lib/models/exchanges/exchanges_response/exchanges_response.freezed.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
3 |
4 | part of 'exchanges_response.dart';
5 |
6 | // **************************************************************************
7 | // FreezedGenerator
8 | // **************************************************************************
9 |
10 | T _$identity(T value) => value;
11 |
12 | final _privateConstructorUsedError = UnsupportedError(
13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
14 |
15 | ExchangesResponse _$ExchangesResponseFromJson(Map json) {
16 | return _ExchangesResponse.fromJson(json);
17 | }
18 |
19 | /// @nodoc
20 | class _$ExchangesResponseTearOff {
21 | const _$ExchangesResponseTearOff();
22 |
23 | _ExchangesResponse call({required List result}) {
24 | return _ExchangesResponse(
25 | result: result,
26 | );
27 | }
28 |
29 | ExchangesResponse fromJson(Map json) {
30 | return ExchangesResponse.fromJson(json);
31 | }
32 | }
33 |
34 | /// @nodoc
35 | const $ExchangesResponse = _$ExchangesResponseTearOff();
36 |
37 | /// @nodoc
38 | mixin _$ExchangesResponse {
39 | List get result => throw _privateConstructorUsedError;
40 |
41 | Map toJson() => throw _privateConstructorUsedError;
42 | @JsonKey(ignore: true)
43 | $ExchangesResponseCopyWith get copyWith =>
44 | throw _privateConstructorUsedError;
45 | }
46 |
47 | /// @nodoc
48 | abstract class $ExchangesResponseCopyWith<$Res> {
49 | factory $ExchangesResponseCopyWith(
50 | ExchangesResponse value, $Res Function(ExchangesResponse) then) =
51 | _$ExchangesResponseCopyWithImpl<$Res>;
52 | $Res call({List result});
53 | }
54 |
55 | /// @nodoc
56 | class _$ExchangesResponseCopyWithImpl<$Res>
57 | implements $ExchangesResponseCopyWith<$Res> {
58 | _$ExchangesResponseCopyWithImpl(this._value, this._then);
59 |
60 | final ExchangesResponse _value;
61 | // ignore: unused_field
62 | final $Res Function(ExchangesResponse) _then;
63 |
64 | @override
65 | $Res call({
66 | Object? result = freezed,
67 | }) {
68 | return _then(_value.copyWith(
69 | result: result == freezed
70 | ? _value.result
71 | : result // ignore: cast_nullable_to_non_nullable
72 | as List,
73 | ));
74 | }
75 | }
76 |
77 | /// @nodoc
78 | abstract class _$ExchangesResponseCopyWith<$Res>
79 | implements $ExchangesResponseCopyWith<$Res> {
80 | factory _$ExchangesResponseCopyWith(
81 | _ExchangesResponse value, $Res Function(_ExchangesResponse) then) =
82 | __$ExchangesResponseCopyWithImpl<$Res>;
83 | @override
84 | $Res call({List result});
85 | }
86 |
87 | /// @nodoc
88 | class __$ExchangesResponseCopyWithImpl<$Res>
89 | extends _$ExchangesResponseCopyWithImpl<$Res>
90 | implements _$ExchangesResponseCopyWith<$Res> {
91 | __$ExchangesResponseCopyWithImpl(
92 | _ExchangesResponse _value, $Res Function(_ExchangesResponse) _then)
93 | : super(_value, (v) => _then(v as _ExchangesResponse));
94 |
95 | @override
96 | _ExchangesResponse get _value => super._value as _ExchangesResponse;
97 |
98 | @override
99 | $Res call({
100 | Object? result = freezed,
101 | }) {
102 | return _then(_ExchangesResponse(
103 | result: result == freezed
104 | ? _value.result
105 | : result // ignore: cast_nullable_to_non_nullable
106 | as List,
107 | ));
108 | }
109 | }
110 |
111 | /// @nodoc
112 | @JsonSerializable()
113 | class _$_ExchangesResponse implements _ExchangesResponse {
114 | const _$_ExchangesResponse({required this.result});
115 |
116 | factory _$_ExchangesResponse.fromJson(Map json) =>
117 | _$_$_ExchangesResponseFromJson(json);
118 |
119 | @override
120 | final List result;
121 |
122 | @override
123 | String toString() {
124 | return 'ExchangesResponse(result: $result)';
125 | }
126 |
127 | @override
128 | bool operator ==(dynamic other) {
129 | return identical(this, other) ||
130 | (other is _ExchangesResponse &&
131 | (identical(other.result, result) ||
132 | const DeepCollectionEquality().equals(other.result, result)));
133 | }
134 |
135 | @override
136 | int get hashCode =>
137 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(result);
138 |
139 | @JsonKey(ignore: true)
140 | @override
141 | _$ExchangesResponseCopyWith<_ExchangesResponse> get copyWith =>
142 | __$ExchangesResponseCopyWithImpl<_ExchangesResponse>(this, _$identity);
143 |
144 | @override
145 | Map toJson() {
146 | return _$_$_ExchangesResponseToJson(this);
147 | }
148 | }
149 |
150 | abstract class _ExchangesResponse implements ExchangesResponse {
151 | const factory _ExchangesResponse({required List result}) =
152 | _$_ExchangesResponse;
153 |
154 | factory _ExchangesResponse.fromJson(Map json) =
155 | _$_ExchangesResponse.fromJson;
156 |
157 | @override
158 | List get result => throw _privateConstructorUsedError;
159 | @override
160 | @JsonKey(ignore: true)
161 | _$ExchangesResponseCopyWith<_ExchangesResponse> get copyWith =>
162 | throw _privateConstructorUsedError;
163 | }
164 |
--------------------------------------------------------------------------------
/lib/models/exchanges/exchanges_response/exchanges_response.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'exchanges_response.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_ExchangesResponse _$_$_ExchangesResponseFromJson(Map json) {
10 | return _$_ExchangesResponse(
11 | result: (json['result'] as List)
12 | .map((e) => Exchange.fromJson(e as Map))
13 | .toList(),
14 | );
15 | }
16 |
17 | Map _$_$_ExchangesResponseToJson(
18 | _$_ExchangesResponse instance) =>
19 | {
20 | 'result': instance.result,
21 | };
22 |
--------------------------------------------------------------------------------
/lib/models/graph/graph/graph.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | import '../pair_graph/pair_graph.dart';
4 |
5 | part 'graph.freezed.dart';
6 |
7 | @freezed
8 | abstract class Graph with _$Graph {
9 | const factory Graph({required List pairs}) = _Graph;
10 |
11 | factory Graph.fromJson(dynamic json) {
12 | List pairs = [];
13 | json.forEach((k, v) {
14 | pairs.add(PairGraph.fromJson(v, k));
15 | });
16 | return Graph(pairs: pairs);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/models/graph/graph/graph.freezed.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
3 |
4 | part of 'graph.dart';
5 |
6 | // **************************************************************************
7 | // FreezedGenerator
8 | // **************************************************************************
9 |
10 | T _$identity(T value) => value;
11 |
12 | final _privateConstructorUsedError = UnsupportedError(
13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
14 |
15 | /// @nodoc
16 | class _$GraphTearOff {
17 | const _$GraphTearOff();
18 |
19 | _Graph call({required List pairs}) {
20 | return _Graph(
21 | pairs: pairs,
22 | );
23 | }
24 | }
25 |
26 | /// @nodoc
27 | const $Graph = _$GraphTearOff();
28 |
29 | /// @nodoc
30 | mixin _$Graph {
31 | List get pairs => throw _privateConstructorUsedError;
32 |
33 | @JsonKey(ignore: true)
34 | $GraphCopyWith get copyWith => throw _privateConstructorUsedError;
35 | }
36 |
37 | /// @nodoc
38 | abstract class $GraphCopyWith<$Res> {
39 | factory $GraphCopyWith(Graph value, $Res Function(Graph) then) =
40 | _$GraphCopyWithImpl<$Res>;
41 | $Res call({List pairs});
42 | }
43 |
44 | /// @nodoc
45 | class _$GraphCopyWithImpl<$Res> implements $GraphCopyWith<$Res> {
46 | _$GraphCopyWithImpl(this._value, this._then);
47 |
48 | final Graph _value;
49 | // ignore: unused_field
50 | final $Res Function(Graph) _then;
51 |
52 | @override
53 | $Res call({
54 | Object? pairs = freezed,
55 | }) {
56 | return _then(_value.copyWith(
57 | pairs: pairs == freezed
58 | ? _value.pairs
59 | : pairs // ignore: cast_nullable_to_non_nullable
60 | as List,
61 | ));
62 | }
63 | }
64 |
65 | /// @nodoc
66 | abstract class _$GraphCopyWith<$Res> implements $GraphCopyWith<$Res> {
67 | factory _$GraphCopyWith(_Graph value, $Res Function(_Graph) then) =
68 | __$GraphCopyWithImpl<$Res>;
69 | @override
70 | $Res call({List pairs});
71 | }
72 |
73 | /// @nodoc
74 | class __$GraphCopyWithImpl<$Res> extends _$GraphCopyWithImpl<$Res>
75 | implements _$GraphCopyWith<$Res> {
76 | __$GraphCopyWithImpl(_Graph _value, $Res Function(_Graph) _then)
77 | : super(_value, (v) => _then(v as _Graph));
78 |
79 | @override
80 | _Graph get _value => super._value as _Graph;
81 |
82 | @override
83 | $Res call({
84 | Object? pairs = freezed,
85 | }) {
86 | return _then(_Graph(
87 | pairs: pairs == freezed
88 | ? _value.pairs
89 | : pairs // ignore: cast_nullable_to_non_nullable
90 | as List,
91 | ));
92 | }
93 | }
94 |
95 | /// @nodoc
96 |
97 | class _$_Graph implements _Graph {
98 | const _$_Graph({required this.pairs});
99 |
100 | @override
101 | final List pairs;
102 |
103 | @override
104 | String toString() {
105 | return 'Graph(pairs: $pairs)';
106 | }
107 |
108 | @override
109 | bool operator ==(dynamic other) {
110 | return identical(this, other) ||
111 | (other is _Graph &&
112 | (identical(other.pairs, pairs) ||
113 | const DeepCollectionEquality().equals(other.pairs, pairs)));
114 | }
115 |
116 | @override
117 | int get hashCode =>
118 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(pairs);
119 |
120 | @JsonKey(ignore: true)
121 | @override
122 | _$GraphCopyWith<_Graph> get copyWith =>
123 | __$GraphCopyWithImpl<_Graph>(this, _$identity);
124 | }
125 |
126 | abstract class _Graph implements Graph {
127 | const factory _Graph({required List pairs}) = _$_Graph;
128 |
129 | @override
130 | List get pairs => throw _privateConstructorUsedError;
131 | @override
132 | @JsonKey(ignore: true)
133 | _$GraphCopyWith<_Graph> get copyWith => throw _privateConstructorUsedError;
134 | }
135 |
--------------------------------------------------------------------------------
/lib/models/graph/graph_response/graph_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/allowance/allowance.dart';
2 | import 'package:cryptocurrency_app/models/graph/graph/graph.dart';
3 | import 'package:freezed_annotation/freezed_annotation.dart';
4 |
5 | part 'graph_response.freezed.dart';
6 |
7 | @freezed
8 | abstract class GraphResponse with _$GraphResponse {
9 | const factory GraphResponse(
10 | {required Graph result, required Allowance allowance}) = _GraphResponse;
11 |
12 | factory GraphResponse.fromJson(Map json) {
13 | final result = Graph.fromJson(json['result']);
14 | final allowance = Allowance.fromJson(json['allowance']);
15 | return GraphResponse(result: result, allowance: allowance);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/models/graph/pair_graph/pair_graph.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | import '../points/points.dart';
4 |
5 | part 'pair_graph.freezed.dart';
6 |
7 | @freezed
8 | abstract class PairGraph with _$PairGraph {
9 | const factory PairGraph(
10 | {required String period, required List points}) = _PairGraph;
11 |
12 | factory PairGraph.fromJson(dynamic json, String period) {
13 | List points = [];
14 | json.forEach((v) {
15 | points.add(Points.fromJson(v));
16 | });
17 | return PairGraph(period: period, points: points);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/models/graph/pair_graph/pair_graph.freezed.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
3 |
4 | part of 'pair_graph.dart';
5 |
6 | // **************************************************************************
7 | // FreezedGenerator
8 | // **************************************************************************
9 |
10 | T _$identity(T value) => value;
11 |
12 | final _privateConstructorUsedError = UnsupportedError(
13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
14 |
15 | /// @nodoc
16 | class _$PairGraphTearOff {
17 | const _$PairGraphTearOff();
18 |
19 | _PairGraph call({required String period, required List points}) {
20 | return _PairGraph(
21 | period: period,
22 | points: points,
23 | );
24 | }
25 | }
26 |
27 | /// @nodoc
28 | const $PairGraph = _$PairGraphTearOff();
29 |
30 | /// @nodoc
31 | mixin _$PairGraph {
32 | String get period => throw _privateConstructorUsedError;
33 | List get points => throw _privateConstructorUsedError;
34 |
35 | @JsonKey(ignore: true)
36 | $PairGraphCopyWith get copyWith =>
37 | throw _privateConstructorUsedError;
38 | }
39 |
40 | /// @nodoc
41 | abstract class $PairGraphCopyWith<$Res> {
42 | factory $PairGraphCopyWith(PairGraph value, $Res Function(PairGraph) then) =
43 | _$PairGraphCopyWithImpl<$Res>;
44 | $Res call({String period, List points});
45 | }
46 |
47 | /// @nodoc
48 | class _$PairGraphCopyWithImpl<$Res> implements $PairGraphCopyWith<$Res> {
49 | _$PairGraphCopyWithImpl(this._value, this._then);
50 |
51 | final PairGraph _value;
52 | // ignore: unused_field
53 | final $Res Function(PairGraph) _then;
54 |
55 | @override
56 | $Res call({
57 | Object? period = freezed,
58 | Object? points = freezed,
59 | }) {
60 | return _then(_value.copyWith(
61 | period: period == freezed
62 | ? _value.period
63 | : period // ignore: cast_nullable_to_non_nullable
64 | as String,
65 | points: points == freezed
66 | ? _value.points
67 | : points // ignore: cast_nullable_to_non_nullable
68 | as List,
69 | ));
70 | }
71 | }
72 |
73 | /// @nodoc
74 | abstract class _$PairGraphCopyWith<$Res> implements $PairGraphCopyWith<$Res> {
75 | factory _$PairGraphCopyWith(
76 | _PairGraph value, $Res Function(_PairGraph) then) =
77 | __$PairGraphCopyWithImpl<$Res>;
78 | @override
79 | $Res call({String period, List points});
80 | }
81 |
82 | /// @nodoc
83 | class __$PairGraphCopyWithImpl<$Res> extends _$PairGraphCopyWithImpl<$Res>
84 | implements _$PairGraphCopyWith<$Res> {
85 | __$PairGraphCopyWithImpl(_PairGraph _value, $Res Function(_PairGraph) _then)
86 | : super(_value, (v) => _then(v as _PairGraph));
87 |
88 | @override
89 | _PairGraph get _value => super._value as _PairGraph;
90 |
91 | @override
92 | $Res call({
93 | Object? period = freezed,
94 | Object? points = freezed,
95 | }) {
96 | return _then(_PairGraph(
97 | period: period == freezed
98 | ? _value.period
99 | : period // ignore: cast_nullable_to_non_nullable
100 | as String,
101 | points: points == freezed
102 | ? _value.points
103 | : points // ignore: cast_nullable_to_non_nullable
104 | as List,
105 | ));
106 | }
107 | }
108 |
109 | /// @nodoc
110 |
111 | class _$_PairGraph implements _PairGraph {
112 | const _$_PairGraph({required this.period, required this.points});
113 |
114 | @override
115 | final String period;
116 | @override
117 | final List points;
118 |
119 | @override
120 | String toString() {
121 | return 'PairGraph(period: $period, points: $points)';
122 | }
123 |
124 | @override
125 | bool operator ==(dynamic other) {
126 | return identical(this, other) ||
127 | (other is _PairGraph &&
128 | (identical(other.period, period) ||
129 | const DeepCollectionEquality().equals(other.period, period)) &&
130 | (identical(other.points, points) ||
131 | const DeepCollectionEquality().equals(other.points, points)));
132 | }
133 |
134 | @override
135 | int get hashCode =>
136 | runtimeType.hashCode ^
137 | const DeepCollectionEquality().hash(period) ^
138 | const DeepCollectionEquality().hash(points);
139 |
140 | @JsonKey(ignore: true)
141 | @override
142 | _$PairGraphCopyWith<_PairGraph> get copyWith =>
143 | __$PairGraphCopyWithImpl<_PairGraph>(this, _$identity);
144 | }
145 |
146 | abstract class _PairGraph implements PairGraph {
147 | const factory _PairGraph(
148 | {required String period, required List points}) = _$_PairGraph;
149 |
150 | @override
151 | String get period => throw _privateConstructorUsedError;
152 | @override
153 | List get points => throw _privateConstructorUsedError;
154 | @override
155 | @JsonKey(ignore: true)
156 | _$PairGraphCopyWith<_PairGraph> get copyWith =>
157 | throw _privateConstructorUsedError;
158 | }
159 |
--------------------------------------------------------------------------------
/lib/models/graph/points/points.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'points.freezed.dart';
4 |
5 | @freezed
6 | abstract class Points with _$Points {
7 | const factory Points(
8 | {required double closeTime,
9 | required double openTime,
10 | required double highPrice,
11 | required double lowPrice,
12 | required double closePrice,
13 | required double volume,
14 | required double quoteVolume}) = _Points;
15 |
16 | factory Points.fromJson(dynamic json) {
17 | return _Points(
18 | closeTime: json[0].toDouble(),
19 | openTime: json[1].toDouble(),
20 | highPrice: json[2].toDouble(),
21 | lowPrice: json[3].toDouble(),
22 | closePrice: json[4].toDouble(),
23 | volume: json[5].toDouble(),
24 | quoteVolume: json[6].toDouble(),
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/models/markets/favorite_pair/favorite_pair.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/markets/pair/pair.dart';
2 | import 'package:cryptocurrency_app/models/pair/pair_summary/pair_summary.dart';
3 | import 'package:freezed_annotation/freezed_annotation.dart';
4 | part 'favorite_pair.freezed.dart';
5 | part 'favorite_pair.g.dart';
6 |
7 | @freezed
8 | abstract class FavoritePair with _$FavoritePair {
9 | const factory FavoritePair(
10 | {required Pair pair, required PairSummary pairSummary}) = _FavoritePair;
11 |
12 | factory FavoritePair.fromJson(Map json) =>
13 | _$FavoritePairFromJson(json);
14 | }
15 |
--------------------------------------------------------------------------------
/lib/models/markets/favorite_pair/favorite_pair.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'favorite_pair.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_FavoritePair _$_$_FavoritePairFromJson(Map json) {
10 | return _$_FavoritePair(
11 | pair: Pair.fromJson(json['pair'] as Map),
12 | pairSummary:
13 | PairSummary.fromJson(json['pairSummary'] as Map),
14 | );
15 | }
16 |
17 | Map _$_$_FavoritePairToJson(_$_FavoritePair instance) =>
18 | {
19 | 'pair': instance.pair,
20 | 'pairSummary': instance.pairSummary,
21 | };
22 |
--------------------------------------------------------------------------------
/lib/models/markets/market_response/market_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/allowance/allowance.dart';
2 | import 'package:cryptocurrency_app/models/markets/pair/pair.dart';
3 | import 'package:freezed_annotation/freezed_annotation.dart';
4 | part 'market_response.g.dart';
5 | part 'market_response.freezed.dart';
6 |
7 | @freezed
8 | abstract class MarketResponse with _$MarketResponse {
9 | const factory MarketResponse(
10 | {required List result,
11 | required Allowance allowance}) = _MarketResponse;
12 | factory MarketResponse.fromJson(Map json) =>
13 | _$MarketResponseFromJson(json);
14 | }
15 |
--------------------------------------------------------------------------------
/lib/models/markets/market_response/market_response.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'market_response.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_MarketResponse _$_$_MarketResponseFromJson(Map json) {
10 | return _$_MarketResponse(
11 | result: (json['result'] as List)
12 | .map((e) => Pair.fromJson(e as Map))
13 | .toList(),
14 | allowance: Allowance.fromJson(json['allowance'] as Map),
15 | );
16 | }
17 |
18 | Map _$_$_MarketResponseToJson(_$_MarketResponse instance) =>
19 | {
20 | 'result': instance.result,
21 | 'allowance': instance.allowance,
22 | };
23 |
--------------------------------------------------------------------------------
/lib/models/markets/pair/pair.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | part 'pair.g.dart';
3 | part 'pair.freezed.dart';
4 |
5 | @freezed
6 | abstract class Pair with _$Pair {
7 | const factory Pair({
8 | int? id,
9 | required String exchange,
10 | required String pair,
11 | bool? active,
12 | String? route,
13 | }) = _Pair;
14 |
15 | factory Pair.fromJson(Map json) => _$PairFromJson(json);
16 | }
17 |
--------------------------------------------------------------------------------
/lib/models/markets/pair/pair.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'pair.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_Pair _$_$_PairFromJson(Map json) {
10 | return _$_Pair(
11 | id: json['id'] as int?,
12 | exchange: json['exchange'] as String,
13 | pair: json['pair'] as String,
14 | active: json['active'] as bool?,
15 | route: json['route'] as String?,
16 | );
17 | }
18 |
19 | Map _$_$_PairToJson(_$_Pair instance) => {
20 | 'id': instance.id,
21 | 'exchange': instance.exchange,
22 | 'pair': instance.pair,
23 | 'active': instance.active,
24 | 'route': instance.route,
25 | };
26 |
--------------------------------------------------------------------------------
/lib/models/orderbook/orderbook/orderbook.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/orderbook/price/price.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 | part 'orderbook.freezed.dart';
4 |
5 | @freezed
6 | abstract class OrderBook with _$OrderBook {
7 | const factory OrderBook(
8 | List asks,
9 | List bids,
10 | int seqNum,
11 | ) = _OrderBook;
12 |
13 | factory OrderBook.fromJson(Map json) {
14 | List asks = [];
15 | json['asks'].forEach((v) {
16 | asks.add(Price(
17 | price: double.parse(v[0].toString()),
18 | amount: double.parse(v[1].toString())));
19 | });
20 |
21 | List bids = [];
22 | json['bids'].forEach((v) {
23 | bids.add(
24 | Price(
25 | price: double.parse(v[0].toString()),
26 | amount: double.parse(
27 | v[1].toString(),
28 | ),
29 | ),
30 | );
31 | });
32 | final seqNum = json['seqNum'];
33 | return OrderBook(asks, bids, seqNum);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/models/orderbook/orderbook_response/orderbook_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/orderbook/orderbook/orderbook.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 | part 'orderbook_response.freezed.dart';
4 |
5 | @freezed
6 | abstract class OrderBookResponse with _$OrderBookResponse {
7 | const factory OrderBookResponse(OrderBook result) = _OrderBookResponse;
8 |
9 | factory OrderBookResponse.fromJson(Map json) {
10 | final result = new OrderBook.fromJson(json['result']);
11 | return OrderBookResponse(result);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/models/orderbook/orderbook_response/orderbook_response.freezed.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
3 |
4 | part of 'orderbook_response.dart';
5 |
6 | // **************************************************************************
7 | // FreezedGenerator
8 | // **************************************************************************
9 |
10 | T _$identity(T value) => value;
11 |
12 | final _privateConstructorUsedError = UnsupportedError(
13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
14 |
15 | /// @nodoc
16 | class _$OrderBookResponseTearOff {
17 | const _$OrderBookResponseTearOff();
18 |
19 | _OrderBookResponse call(OrderBook result) {
20 | return _OrderBookResponse(
21 | result,
22 | );
23 | }
24 | }
25 |
26 | /// @nodoc
27 | const $OrderBookResponse = _$OrderBookResponseTearOff();
28 |
29 | /// @nodoc
30 | mixin _$OrderBookResponse {
31 | OrderBook get result => throw _privateConstructorUsedError;
32 |
33 | @JsonKey(ignore: true)
34 | $OrderBookResponseCopyWith get copyWith =>
35 | throw _privateConstructorUsedError;
36 | }
37 |
38 | /// @nodoc
39 | abstract class $OrderBookResponseCopyWith<$Res> {
40 | factory $OrderBookResponseCopyWith(
41 | OrderBookResponse value, $Res Function(OrderBookResponse) then) =
42 | _$OrderBookResponseCopyWithImpl<$Res>;
43 | $Res call({OrderBook result});
44 |
45 | $OrderBookCopyWith<$Res> get result;
46 | }
47 |
48 | /// @nodoc
49 | class _$OrderBookResponseCopyWithImpl<$Res>
50 | implements $OrderBookResponseCopyWith<$Res> {
51 | _$OrderBookResponseCopyWithImpl(this._value, this._then);
52 |
53 | final OrderBookResponse _value;
54 | // ignore: unused_field
55 | final $Res Function(OrderBookResponse) _then;
56 |
57 | @override
58 | $Res call({
59 | Object? result = freezed,
60 | }) {
61 | return _then(_value.copyWith(
62 | result: result == freezed
63 | ? _value.result
64 | : result // ignore: cast_nullable_to_non_nullable
65 | as OrderBook,
66 | ));
67 | }
68 |
69 | @override
70 | $OrderBookCopyWith<$Res> get result {
71 | return $OrderBookCopyWith<$Res>(_value.result, (value) {
72 | return _then(_value.copyWith(result: value));
73 | });
74 | }
75 | }
76 |
77 | /// @nodoc
78 | abstract class _$OrderBookResponseCopyWith<$Res>
79 | implements $OrderBookResponseCopyWith<$Res> {
80 | factory _$OrderBookResponseCopyWith(
81 | _OrderBookResponse value, $Res Function(_OrderBookResponse) then) =
82 | __$OrderBookResponseCopyWithImpl<$Res>;
83 | @override
84 | $Res call({OrderBook result});
85 |
86 | @override
87 | $OrderBookCopyWith<$Res> get result;
88 | }
89 |
90 | /// @nodoc
91 | class __$OrderBookResponseCopyWithImpl<$Res>
92 | extends _$OrderBookResponseCopyWithImpl<$Res>
93 | implements _$OrderBookResponseCopyWith<$Res> {
94 | __$OrderBookResponseCopyWithImpl(
95 | _OrderBookResponse _value, $Res Function(_OrderBookResponse) _then)
96 | : super(_value, (v) => _then(v as _OrderBookResponse));
97 |
98 | @override
99 | _OrderBookResponse get _value => super._value as _OrderBookResponse;
100 |
101 | @override
102 | $Res call({
103 | Object? result = freezed,
104 | }) {
105 | return _then(_OrderBookResponse(
106 | result == freezed
107 | ? _value.result
108 | : result // ignore: cast_nullable_to_non_nullable
109 | as OrderBook,
110 | ));
111 | }
112 | }
113 |
114 | /// @nodoc
115 |
116 | class _$_OrderBookResponse implements _OrderBookResponse {
117 | const _$_OrderBookResponse(this.result);
118 |
119 | @override
120 | final OrderBook result;
121 |
122 | @override
123 | String toString() {
124 | return 'OrderBookResponse(result: $result)';
125 | }
126 |
127 | @override
128 | bool operator ==(dynamic other) {
129 | return identical(this, other) ||
130 | (other is _OrderBookResponse &&
131 | (identical(other.result, result) ||
132 | const DeepCollectionEquality().equals(other.result, result)));
133 | }
134 |
135 | @override
136 | int get hashCode =>
137 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(result);
138 |
139 | @JsonKey(ignore: true)
140 | @override
141 | _$OrderBookResponseCopyWith<_OrderBookResponse> get copyWith =>
142 | __$OrderBookResponseCopyWithImpl<_OrderBookResponse>(this, _$identity);
143 | }
144 |
145 | abstract class _OrderBookResponse implements OrderBookResponse {
146 | const factory _OrderBookResponse(OrderBook result) = _$_OrderBookResponse;
147 |
148 | @override
149 | OrderBook get result => throw _privateConstructorUsedError;
150 | @override
151 | @JsonKey(ignore: true)
152 | _$OrderBookResponseCopyWith<_OrderBookResponse> get copyWith =>
153 | throw _privateConstructorUsedError;
154 | }
155 |
--------------------------------------------------------------------------------
/lib/models/orderbook/price/price.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | part 'price.freezed.dart';
3 |
4 | @freezed
5 | abstract class Price with _$Price {
6 | const factory Price({required double price, required double amount}) = _Price;
7 | }
8 |
--------------------------------------------------------------------------------
/lib/models/orderbook/price/price.freezed.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
3 |
4 | part of 'price.dart';
5 |
6 | // **************************************************************************
7 | // FreezedGenerator
8 | // **************************************************************************
9 |
10 | T _$identity(T value) => value;
11 |
12 | final _privateConstructorUsedError = UnsupportedError(
13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
14 |
15 | /// @nodoc
16 | class _$PriceTearOff {
17 | const _$PriceTearOff();
18 |
19 | _Price call({required double price, required double amount}) {
20 | return _Price(
21 | price: price,
22 | amount: amount,
23 | );
24 | }
25 | }
26 |
27 | /// @nodoc
28 | const $Price = _$PriceTearOff();
29 |
30 | /// @nodoc
31 | mixin _$Price {
32 | double get price => throw _privateConstructorUsedError;
33 | double get amount => throw _privateConstructorUsedError;
34 |
35 | @JsonKey(ignore: true)
36 | $PriceCopyWith get copyWith => throw _privateConstructorUsedError;
37 | }
38 |
39 | /// @nodoc
40 | abstract class $PriceCopyWith<$Res> {
41 | factory $PriceCopyWith(Price value, $Res Function(Price) then) =
42 | _$PriceCopyWithImpl<$Res>;
43 | $Res call({double price, double amount});
44 | }
45 |
46 | /// @nodoc
47 | class _$PriceCopyWithImpl<$Res> implements $PriceCopyWith<$Res> {
48 | _$PriceCopyWithImpl(this._value, this._then);
49 |
50 | final Price _value;
51 | // ignore: unused_field
52 | final $Res Function(Price) _then;
53 |
54 | @override
55 | $Res call({
56 | Object? price = freezed,
57 | Object? amount = freezed,
58 | }) {
59 | return _then(_value.copyWith(
60 | price: price == freezed
61 | ? _value.price
62 | : price // ignore: cast_nullable_to_non_nullable
63 | as double,
64 | amount: amount == freezed
65 | ? _value.amount
66 | : amount // ignore: cast_nullable_to_non_nullable
67 | as double,
68 | ));
69 | }
70 | }
71 |
72 | /// @nodoc
73 | abstract class _$PriceCopyWith<$Res> implements $PriceCopyWith<$Res> {
74 | factory _$PriceCopyWith(_Price value, $Res Function(_Price) then) =
75 | __$PriceCopyWithImpl<$Res>;
76 | @override
77 | $Res call({double price, double amount});
78 | }
79 |
80 | /// @nodoc
81 | class __$PriceCopyWithImpl<$Res> extends _$PriceCopyWithImpl<$Res>
82 | implements _$PriceCopyWith<$Res> {
83 | __$PriceCopyWithImpl(_Price _value, $Res Function(_Price) _then)
84 | : super(_value, (v) => _then(v as _Price));
85 |
86 | @override
87 | _Price get _value => super._value as _Price;
88 |
89 | @override
90 | $Res call({
91 | Object? price = freezed,
92 | Object? amount = freezed,
93 | }) {
94 | return _then(_Price(
95 | price: price == freezed
96 | ? _value.price
97 | : price // ignore: cast_nullable_to_non_nullable
98 | as double,
99 | amount: amount == freezed
100 | ? _value.amount
101 | : amount // ignore: cast_nullable_to_non_nullable
102 | as double,
103 | ));
104 | }
105 | }
106 |
107 | /// @nodoc
108 |
109 | class _$_Price implements _Price {
110 | const _$_Price({required this.price, required this.amount});
111 |
112 | @override
113 | final double price;
114 | @override
115 | final double amount;
116 |
117 | @override
118 | String toString() {
119 | return 'Price(price: $price, amount: $amount)';
120 | }
121 |
122 | @override
123 | bool operator ==(dynamic other) {
124 | return identical(this, other) ||
125 | (other is _Price &&
126 | (identical(other.price, price) ||
127 | const DeepCollectionEquality().equals(other.price, price)) &&
128 | (identical(other.amount, amount) ||
129 | const DeepCollectionEquality().equals(other.amount, amount)));
130 | }
131 |
132 | @override
133 | int get hashCode =>
134 | runtimeType.hashCode ^
135 | const DeepCollectionEquality().hash(price) ^
136 | const DeepCollectionEquality().hash(amount);
137 |
138 | @JsonKey(ignore: true)
139 | @override
140 | _$PriceCopyWith<_Price> get copyWith =>
141 | __$PriceCopyWithImpl<_Price>(this, _$identity);
142 | }
143 |
144 | abstract class _Price implements Price {
145 | const factory _Price({required double price, required double amount}) =
146 | _$_Price;
147 |
148 | @override
149 | double get price => throw _privateConstructorUsedError;
150 | @override
151 | double get amount => throw _privateConstructorUsedError;
152 | @override
153 | @JsonKey(ignore: true)
154 | _$PriceCopyWith<_Price> get copyWith => throw _privateConstructorUsedError;
155 | }
156 |
--------------------------------------------------------------------------------
/lib/models/pair/change/change.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | part 'change.freezed.dart';
3 | part 'change.g.dart';
4 |
5 | @freezed
6 | abstract class Change with _$Change {
7 | const factory Change({required double percentage, required double absolute}) =
8 | _Change;
9 |
10 | factory Change.fromJson(Map json) => _$ChangeFromJson(json);
11 | }
12 |
--------------------------------------------------------------------------------
/lib/models/pair/change/change.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'change.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_Change _$_$_ChangeFromJson(Map json) {
10 | return _$_Change(
11 | percentage: (json['percentage'] as num).toDouble(),
12 | absolute: (json['absolute'] as num).toDouble(),
13 | );
14 | }
15 |
16 | Map _$_$_ChangeToJson(_$_Change instance) => {
17 | 'percentage': instance.percentage,
18 | 'absolute': instance.absolute,
19 | };
20 |
--------------------------------------------------------------------------------
/lib/models/pair/pair_response/pair_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/allowance/allowance.dart';
2 | import 'package:cryptocurrency_app/models/pair/pair_summary/pair_summary.dart';
3 | import 'package:freezed_annotation/freezed_annotation.dart';
4 |
5 | part 'pair_response.freezed.dart';
6 | part 'pair_response.g.dart';
7 |
8 | @freezed
9 | abstract class PairResponse with _$PairResponse {
10 | const factory PairResponse(PairSummary result, Allowance allowance) =
11 | _PairResponse;
12 |
13 | factory PairResponse.fromJson(Map json) =>
14 | _$PairResponseFromJson(json);
15 | }
16 |
--------------------------------------------------------------------------------
/lib/models/pair/pair_response/pair_response.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'pair_response.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_PairResponse _$_$_PairResponseFromJson(Map json) {
10 | return _$_PairResponse(
11 | PairSummary.fromJson(json['result'] as Map),
12 | Allowance.fromJson(json['allowance'] as Map),
13 | );
14 | }
15 |
16 | Map _$_$_PairResponseToJson(_$_PairResponse instance) =>
17 | {
18 | 'result': instance.result,
19 | 'allowance': instance.allowance,
20 | };
21 |
--------------------------------------------------------------------------------
/lib/models/pair/pair_summary/pair_summary.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/pair/price/price.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 |
4 | part 'pair_summary.freezed.dart';
5 | part 'pair_summary.g.dart';
6 |
7 | @freezed
8 | abstract class PairSummary with _$PairSummary {
9 | const factory PairSummary({
10 | required Price price,
11 | required double volume,
12 | required double volumeQuote,
13 | }) = _PairSummary;
14 |
15 | factory PairSummary.fromJson(Map json) =>
16 | _$PairSummaryFromJson(json);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/models/pair/pair_summary/pair_summary.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'pair_summary.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_PairSummary _$_$_PairSummaryFromJson(Map json) {
10 | return _$_PairSummary(
11 | price: Price.fromJson(json['price'] as Map),
12 | volume: (json['volume'] as num).toDouble(),
13 | volumeQuote: (json['volumeQuote'] as num).toDouble(),
14 | );
15 | }
16 |
17 | Map _$_$_PairSummaryToJson(_$_PairSummary instance) =>
18 | {
19 | 'price': instance.price,
20 | 'volume': instance.volume,
21 | 'volumeQuote': instance.volumeQuote,
22 | };
23 |
--------------------------------------------------------------------------------
/lib/models/pair/price/price.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/pair/change/change.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 | part 'price.freezed.dart';
4 | part 'price.g.dart';
5 |
6 | @freezed
7 | abstract class Price with _$Price {
8 | const factory Price({
9 | required double last,
10 | required double high,
11 | required double low,
12 | required Change change,
13 | }) = _Price;
14 |
15 | factory Price.fromJson(Map json) => _$PriceFromJson(json);
16 | }
17 |
--------------------------------------------------------------------------------
/lib/models/pair/price/price.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'price.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$_Price _$_$_PriceFromJson(Map json) {
10 | return _$_Price(
11 | last: (json['last'] as num).toDouble(),
12 | high: (json['high'] as num).toDouble(),
13 | low: (json['low'] as num).toDouble(),
14 | change: Change.fromJson(json['change'] as Map),
15 | );
16 | }
17 |
18 | Map _$_$_PriceToJson(_$_Price instance) => {
19 | 'last': instance.last,
20 | 'high': instance.high,
21 | 'low': instance.low,
22 | 'change': instance.change,
23 | };
24 |
--------------------------------------------------------------------------------
/lib/models/settings/settings_details/settings_details.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'settings_details.freezed.dart';
4 |
5 | @freezed
6 | abstract class SettingsDetails with _$SettingsDetails {
7 | const factory SettingsDetails({
8 | required String currentLanguage,
9 | required String favoriteExchange,
10 | required String favoritePair,
11 | required String themeMode,
12 | }) = _SettingsDetails;
13 | }
14 |
--------------------------------------------------------------------------------
/lib/models/settings/settings_state/settings_state.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/settings/settings_details/settings_details.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 |
4 | part 'settings_state.freezed.dart';
5 |
6 | @freezed
7 | abstract class SettingsState with _$SettingsState {
8 | const factory SettingsState.initial() = _SettingsStateInitial;
9 | const factory SettingsState.loading() = _SettingsStateLoading;
10 | const factory SettingsState.data({required SettingsDetails details}) =
11 | _SetttingsStateData;
12 | const factory SettingsState.error({String? error}) = _SettingsStateError;
13 | }
14 |
--------------------------------------------------------------------------------
/lib/models/trades/trade/trade.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | part 'trade.freezed.dart';
3 |
4 | @freezed
5 | abstract class Trade with _$Trade {
6 | const factory Trade(
7 | String id, String timestamp, String price, String amount) = _Trade;
8 |
9 | factory Trade.fromJson(List json) {
10 | final id = json[0].toString();
11 | final timestamp = json[1].toString();
12 | final price = json[2].toString();
13 | final amount = json[3].toString();
14 | return Trade(id, timestamp, price, amount);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/models/trades/trades_response.dart/trades_response.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/trades/trade/trade.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 | part 'trades_response.freezed.dart';
4 |
5 | @freezed
6 | abstract class TradesResponse with _$TradesResponse {
7 | const factory TradesResponse({List? result}) = _TradesResponse;
8 | factory TradesResponse.fromJson(Map json) {
9 | final List result = [];
10 | json['result'].forEach((v) {
11 | result.add(new Trade.fromJson(v));
12 | });
13 | return TradesResponse(result: result);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/models/trades/trades_response.dart/trades_response.freezed.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
3 |
4 | part of 'trades_response.dart';
5 |
6 | // **************************************************************************
7 | // FreezedGenerator
8 | // **************************************************************************
9 |
10 | T _$identity(T value) => value;
11 |
12 | final _privateConstructorUsedError = UnsupportedError(
13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
14 |
15 | /// @nodoc
16 | class _$TradesResponseTearOff {
17 | const _$TradesResponseTearOff();
18 |
19 | _TradesResponse call({List? result}) {
20 | return _TradesResponse(
21 | result: result,
22 | );
23 | }
24 | }
25 |
26 | /// @nodoc
27 | const $TradesResponse = _$TradesResponseTearOff();
28 |
29 | /// @nodoc
30 | mixin _$TradesResponse {
31 | List? get result => throw _privateConstructorUsedError;
32 |
33 | @JsonKey(ignore: true)
34 | $TradesResponseCopyWith get copyWith =>
35 | throw _privateConstructorUsedError;
36 | }
37 |
38 | /// @nodoc
39 | abstract class $TradesResponseCopyWith<$Res> {
40 | factory $TradesResponseCopyWith(
41 | TradesResponse value, $Res Function(TradesResponse) then) =
42 | _$TradesResponseCopyWithImpl<$Res>;
43 | $Res call({List? result});
44 | }
45 |
46 | /// @nodoc
47 | class _$TradesResponseCopyWithImpl<$Res>
48 | implements $TradesResponseCopyWith<$Res> {
49 | _$TradesResponseCopyWithImpl(this._value, this._then);
50 |
51 | final TradesResponse _value;
52 | // ignore: unused_field
53 | final $Res Function(TradesResponse) _then;
54 |
55 | @override
56 | $Res call({
57 | Object? result = freezed,
58 | }) {
59 | return _then(_value.copyWith(
60 | result: result == freezed
61 | ? _value.result
62 | : result // ignore: cast_nullable_to_non_nullable
63 | as List?,
64 | ));
65 | }
66 | }
67 |
68 | /// @nodoc
69 | abstract class _$TradesResponseCopyWith<$Res>
70 | implements $TradesResponseCopyWith<$Res> {
71 | factory _$TradesResponseCopyWith(
72 | _TradesResponse value, $Res Function(_TradesResponse) then) =
73 | __$TradesResponseCopyWithImpl<$Res>;
74 | @override
75 | $Res call({List? result});
76 | }
77 |
78 | /// @nodoc
79 | class __$TradesResponseCopyWithImpl<$Res>
80 | extends _$TradesResponseCopyWithImpl<$Res>
81 | implements _$TradesResponseCopyWith<$Res> {
82 | __$TradesResponseCopyWithImpl(
83 | _TradesResponse _value, $Res Function(_TradesResponse) _then)
84 | : super(_value, (v) => _then(v as _TradesResponse));
85 |
86 | @override
87 | _TradesResponse get _value => super._value as _TradesResponse;
88 |
89 | @override
90 | $Res call({
91 | Object? result = freezed,
92 | }) {
93 | return _then(_TradesResponse(
94 | result: result == freezed
95 | ? _value.result
96 | : result // ignore: cast_nullable_to_non_nullable
97 | as List?,
98 | ));
99 | }
100 | }
101 |
102 | /// @nodoc
103 |
104 | class _$_TradesResponse implements _TradesResponse {
105 | const _$_TradesResponse({this.result});
106 |
107 | @override
108 | final List? result;
109 |
110 | @override
111 | String toString() {
112 | return 'TradesResponse(result: $result)';
113 | }
114 |
115 | @override
116 | bool operator ==(dynamic other) {
117 | return identical(this, other) ||
118 | (other is _TradesResponse &&
119 | (identical(other.result, result) ||
120 | const DeepCollectionEquality().equals(other.result, result)));
121 | }
122 |
123 | @override
124 | int get hashCode =>
125 | runtimeType.hashCode ^ const DeepCollectionEquality().hash(result);
126 |
127 | @JsonKey(ignore: true)
128 | @override
129 | _$TradesResponseCopyWith<_TradesResponse> get copyWith =>
130 | __$TradesResponseCopyWithImpl<_TradesResponse>(this, _$identity);
131 | }
132 |
133 | abstract class _TradesResponse implements TradesResponse {
134 | const factory _TradesResponse({List? result}) = _$_TradesResponse;
135 |
136 | @override
137 | List? get result => throw _privateConstructorUsedError;
138 | @override
139 | @JsonKey(ignore: true)
140 | _$TradesResponseCopyWith<_TradesResponse> get copyWith =>
141 | throw _privateConstructorUsedError;
142 | }
143 |
--------------------------------------------------------------------------------
/lib/provider/crypto_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/constants/exceptions.dart';
2 | import 'package:cryptocurrency_app/provider/time_provider.dart';
3 | import 'package:cryptocurrency_app/models/exchanges/exchange/exchange.dart';
4 | import 'package:cryptocurrency_app/models/graph/graph/graph.dart';
5 | import 'package:cryptocurrency_app/models/markets/favorite_pair/favorite_pair.dart';
6 | import 'package:cryptocurrency_app/models/markets/pair/pair.dart';
7 | import 'package:cryptocurrency_app/models/orderbook/orderbook/orderbook.dart';
8 | import 'package:cryptocurrency_app/models/pair/pair_summary/pair_summary.dart';
9 | import 'package:cryptocurrency_app/models/trades/trade/trade.dart';
10 | import 'package:cryptocurrency_app/provider/settings_provider.dart';
11 | import 'package:cryptocurrency_app/repository/crypto_repository.dart';
12 | import 'package:dio/dio.dart';
13 | import 'package:hooks_riverpod/hooks_riverpod.dart';
14 | import '../../generated/locale_keys.g.dart';
15 |
16 | final pairsProvider = FutureProvider>((ref) async {
17 | final settings = ref.watch(cryptoSettings);
18 | String exchangeName = settings.maybeWhen(
19 | data: (details) => details.favoriteExchange,
20 | orElse: () =>
21 | throw DataException(message: LocaleKeys.errorSomethingWentWrong));
22 | List pairs = await ref.read(cryptoRepository).getPairs(exchangeName);
23 | return pairs;
24 | });
25 |
26 | final searchTextProvider = StateProvider((ref) => "");
27 |
28 | final pairsSearchProvider = FutureProvider>((ref) async {
29 | final pairs = ref.watch(pairsProvider);
30 | final search = ref.watch(searchTextProvider);
31 |
32 | List list = [];
33 | pairs.maybeWhen(
34 | data: (data) {
35 | if (search.isNotEmpty)
36 | list =
37 | data.where((element) => element.pair.contains(search)).toList();
38 | else
39 | list = data;
40 | },
41 | orElse: () => {});
42 | return list;
43 | });
44 |
45 | final exchangesProvider = FutureProvider>((ref) async {
46 | final cancelToken = CancelToken();
47 | ref.onDispose(() => cancelToken.cancel());
48 |
49 | List exchanges =
50 | await ref.read(cryptoRepository).getExchanges(cancelToken: cancelToken);
51 | return exchanges;
52 | });
53 |
54 | final favoritePairProvider = FutureProvider((ref) async {
55 | final cancelToken = CancelToken();
56 | ref.onDispose(() => cancelToken.cancel());
57 |
58 | final settings = ref.watch(cryptoSettings);
59 | String exchangeName = settings.maybeWhen(
60 | data: (details) => details.favoriteExchange, orElse: () => "");
61 | String pairName = settings.maybeWhen(
62 | data: (details) => details.favoritePair, orElse: () => "");
63 |
64 | if (exchangeName.isEmpty || pairName.isEmpty) {
65 | throw DataException(message: LocaleKeys.errorSomethingWentWrong);
66 | }
67 |
68 | Pair pair = Pair(pair: pairName, exchange: exchangeName);
69 | try {
70 | PairSummary pairSummary = await ref
71 | .read(cryptoRepository)
72 | .getPairSummary(exchangeName, pairName, cancelToken: cancelToken);
73 | return FavoritePair(pair: pair, pairSummary: pairSummary);
74 | } on DataException catch (error) {
75 | if (error.message == LocaleKeys.errorRequestNotFound) {
76 | ref.read(cryptoSettings.notifier).verifyFavoritePair();
77 | }
78 | throw error;
79 | }
80 | });
81 |
82 | final pairSummaryProvider =
83 | FutureProvider.family((ref, pair) async {
84 | final cancelToken = CancelToken();
85 | ref.onDispose(() => cancelToken.cancel());
86 |
87 | final pairSummary = await ref
88 | .read(cryptoRepository)
89 | .getPairSummary(pair.exchange, pair.pair, cancelToken: cancelToken);
90 | return pairSummary;
91 | });
92 |
93 | final pairOrderBookProvider =
94 | FutureProvider.family((ref, pair) async {
95 | final cancelToken = CancelToken();
96 | ref.onDispose(() => cancelToken.cancel());
97 |
98 | final orderBook = await ref
99 | .read(cryptoRepository)
100 | .getOrderBook(pair.exchange, pair.pair, cancelToken: cancelToken);
101 |
102 | return orderBook;
103 | });
104 |
105 | final tradesProvider =
106 | FutureProvider.family, Pair>((ref, pair) async {
107 | final cancelToken = CancelToken();
108 | ref.onDispose(() => cancelToken.cancel());
109 |
110 | final trades = await ref
111 | .read(cryptoRepository)
112 | .getTrades(pair.exchange, pair.pair, cancelToken: cancelToken);
113 | return trades;
114 | });
115 |
116 | final graphDataProvider = FutureProvider.family((ref, pair) async {
117 | String interval = ref.watch(timeDataProvider).periods;
118 | String fromHours = ref.watch(timeDataProvider).before;
119 | String before = "";
120 | if (fromHours.isNotEmpty) {
121 | before = (DateTime.now()
122 | .subtract(Duration(hours: int.parse(fromHours)))
123 | .toUtc()
124 | .millisecondsSinceEpoch ~/
125 | 1000)
126 | .toString();
127 | }
128 |
129 | final graph = await ref.read(cryptoRepository).getPairGraph(
130 | pair.exchange, pair.pair,
131 | periods: interval, before: before);
132 | return graph;
133 | });
134 |
--------------------------------------------------------------------------------
/lib/provider/navigation_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:hooks_riverpod/hooks_riverpod.dart';
2 |
3 | final navigationProvider = StateNotifierProvider(
4 | (ref) => NavigationNotifier());
5 |
6 | enum NavigationBarEvent { HOME, SEARCH, SETTINGS }
7 |
8 | class NavigationNotifier extends StateNotifier {
9 | NavigationNotifier() : super(defaultPage);
10 |
11 | static const defaultPage = PageModel(NavigationBarEvent.HOME, 0);
12 |
13 | void selectPage(int i) {
14 | switch (i) {
15 | case 0:
16 | state = PageModel(NavigationBarEvent.HOME, i);
17 | break;
18 | case 1:
19 | state = PageModel(NavigationBarEvent.SEARCH, i);
20 | break;
21 | case 2:
22 | state = PageModel(NavigationBarEvent.SETTINGS, i);
23 | break;
24 | }
25 | }
26 | }
27 |
28 | class PageModel {
29 | const PageModel(this.page, this.index);
30 | final NavigationBarEvent page;
31 | final index;
32 | }
33 |
--------------------------------------------------------------------------------
/lib/provider/settings_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/constants/exceptions.dart';
2 | import 'package:cryptocurrency_app/constants/utils.dart' as Utils;
3 | import 'package:cryptocurrency_app/models/markets/pair/pair.dart';
4 | import 'package:cryptocurrency_app/models/settings/settings_details/settings_details.dart';
5 | import 'package:cryptocurrency_app/models/settings/settings_state/settings_state.dart';
6 | import 'package:cryptocurrency_app/repository/crypto_repository.dart';
7 | import 'package:flutter_secure_storage/flutter_secure_storage.dart';
8 | import 'package:hooks_riverpod/hooks_riverpod.dart';
9 | import '../../generated/locale_keys.g.dart';
10 |
11 | final flutterDatabase =
12 | Provider((ref) => FlutterSecureStorage());
13 |
14 | final cryptoSettings = StateNotifierProvider(
15 | (ref) => SettingsNotifier(ref.read));
16 |
17 | class SettingsNotifier extends StateNotifier {
18 | final Reader read;
19 |
20 | late SettingsDetails details;
21 |
22 | SettingsNotifier(this.read) : super(SettingsState.initial()) {
23 | loadData();
24 | }
25 |
26 | void loadData() async {
27 | state = SettingsState.loading();
28 | final language = (await read(flutterDatabase).read(key: "language")) ??
29 | Utils.defaultLenguage;
30 | final exchange = (await read(flutterDatabase).read(key: "exchange")) ??
31 | Utils.defaultExchange;
32 | final pair =
33 | (await read(flutterDatabase).read(key: "pair")) ?? Utils.defaultPair;
34 | final theme =
35 | (await read(flutterDatabase).read(key: "theme")) ?? Utils.defaultTheme;
36 | details = SettingsDetails(
37 | currentLanguage: language,
38 | favoriteExchange: exchange,
39 | favoritePair: pair,
40 | themeMode: theme);
41 | state = SettingsState.data(details: details);
42 | }
43 |
44 | void setLenguage(String language) async {
45 | state = SettingsState.loading();
46 | await read(flutterDatabase).write(key: "language", value: language);
47 | details = details.copyWith(currentLanguage: language);
48 | state = SettingsState.data(details: details);
49 | }
50 |
51 | Future setFavoriteExchange(String exchange) async {
52 | state = SettingsState.loading();
53 | await read(flutterDatabase).write(key: "exchange", value: exchange);
54 | details = details.copyWith(favoriteExchange: exchange);
55 | state = SettingsState.data(details: details);
56 | verifyFavoritePair();
57 | }
58 |
59 | Future verifyFavoritePair() async {
60 | try {
61 | await read(cryptoRepository)
62 | .getPairSummary(details.favoriteExchange, details.favoritePair);
63 | } on DataException catch (error) {
64 | if (error.message == LocaleKeys.errorRequestNotFound) {
65 | List pairs =
66 | await read(cryptoRepository).getPairs(details.favoriteExchange);
67 | setFavoritePair(pairs.first.pair);
68 | }
69 | }
70 | }
71 |
72 | Future setFavoritePair(String pair) async {
73 | state = SettingsState.loading();
74 | await read(flutterDatabase).write(key: "pair", value: pair);
75 | details = details.copyWith(favoritePair: pair);
76 | state = SettingsState.data(details: details);
77 | }
78 |
79 | void setTheme(String theme) async {
80 | state = SettingsState.loading();
81 | await read(flutterDatabase).write(key: "theme", value: theme);
82 | details = details.copyWith(themeMode: theme);
83 | state = SettingsState.data(details: details);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/lib/provider/time_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:hooks_riverpod/hooks_riverpod.dart';
2 |
3 | class TimeGraphData {
4 | String name;
5 | String periods;
6 | String before;
7 | TimeGraphData(this.name, this.periods, this.before);
8 | }
9 |
10 | final timeList = [
11 | TimeGraphData("1H", "60", "1"),
12 | TimeGraphData("1D", "300", "24"),
13 | TimeGraphData("1W", "1800", "168"),
14 | TimeGraphData("1M", "3600", "730"),
15 | TimeGraphData("1Y", "86400", "8760"),
16 | TimeGraphData("ALL", "", "")
17 | ];
18 |
19 | final timeDataProvider =
20 | StateProvider((ref) => TimeGraphData("1M", "60", "12"));
21 |
--------------------------------------------------------------------------------
/lib/repository/crypto_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/constants/exceptions.dart';
2 | import 'package:cryptocurrency_app/models/exchanges/exchange/exchange.dart';
3 | import 'package:cryptocurrency_app/models/exchanges/exchanges_response/exchanges_response.dart';
4 | import 'package:cryptocurrency_app/models/graph/graph/graph.dart';
5 | import 'package:cryptocurrency_app/models/graph/graph_response/graph_response.dart';
6 | import 'package:cryptocurrency_app/models/markets/market_response/market_response.dart';
7 | import 'package:cryptocurrency_app/models/markets/pair/pair.dart';
8 | import 'package:cryptocurrency_app/models/orderbook/orderbook/orderbook.dart';
9 | import 'package:cryptocurrency_app/models/orderbook/orderbook_response/orderbook_response.dart';
10 | import 'package:cryptocurrency_app/models/pair/pair_response/pair_response.dart';
11 | import 'package:cryptocurrency_app/models/pair/pair_summary/pair_summary.dart';
12 | import 'package:cryptocurrency_app/models/trades/trade/trade.dart';
13 | import 'package:cryptocurrency_app/models/trades/trades_response.dart/trades_response.dart';
14 | import 'package:dio/dio.dart';
15 | import 'package:hooks_riverpod/hooks_riverpod.dart';
16 |
17 | import 'package:flutter_dotenv/flutter_dotenv.dart';
18 |
19 | final clientProvider = Provider((ref) => Dio(BaseOptions(headers: {
20 | "X-CW-API-Key": dotenv.env['API_KEY'],
21 | }, baseUrl: 'https://api.cryptowat.ch')));
22 |
23 | final cryptoRepository =
24 | Provider((ref) => CryptoRepositoryAPI(ref.read));
25 |
26 | abstract class CryptoRepository {
27 | Future> getPairs(String market);
28 | Future getPairSummary(String makeret, String pair);
29 | Future getPairGraph(String market, String pair,
30 | {String periods, String after, String before});
31 | Future> getExchanges();
32 | Future getOrderBook(String market, String pair);
33 | Future> getTrades(String market, String pair);
34 | }
35 |
36 | class CryptoRepositoryAPI implements CryptoRepository {
37 | final Reader read;
38 | CryptoRepositoryAPI(this.read);
39 |
40 | @override
41 | Future> getPairs(String market, {CancelToken? cancelToken}) async {
42 | try {
43 | final response = await read(clientProvider)
44 | .get('/markets/$market', cancelToken: cancelToken);
45 | return MarketResponse.fromJson(response.data).result;
46 | } on DioError catch (error) {
47 | throw DataException.fromDioError(error);
48 | }
49 | }
50 |
51 | @override
52 | Future getPairSummary(String market, String pair,
53 | {CancelToken? cancelToken}) async {
54 | try {
55 | final response = await read(clientProvider)
56 | .get('/markets/$market/$pair/summary', cancelToken: cancelToken);
57 | return PairResponse.fromJson(response.data).result;
58 | } on DioError catch (error) {
59 | throw DataException.fromDioError(error);
60 | }
61 | }
62 |
63 | @override
64 | Future getOrderBook(String market, String pair,
65 | {CancelToken? cancelToken}) async {
66 | try {
67 | final response = await read(clientProvider)
68 | .get('/markets/$market/$pair/orderbook', cancelToken: cancelToken);
69 | return OrderBookResponse.fromJson(response.data).result;
70 | } on DioError catch (error) {
71 | throw DataException.fromDioError(error);
72 | }
73 | }
74 |
75 | @override
76 | Future> getTrades(String market, String pair,
77 | {CancelToken? cancelToken}) async {
78 | try {
79 | final response = await read(clientProvider)
80 | .get('/markets/$market/$pair/trades', cancelToken: cancelToken);
81 | return TradesResponse.fromJson(response.data).result!;
82 | } on DioError catch (error) {
83 | throw DataException.fromDioError(error);
84 | }
85 | }
86 |
87 | @override
88 | Future getPairGraph(String market, String pair,
89 | {String periods = "",
90 | String after = "",
91 | String before = "",
92 | CancelToken? cancelToken}) async {
93 | try {
94 | final response = await read(clientProvider).get(
95 | '/markets/$market/$pair/ohlc',
96 | queryParameters: {
97 | "periods": periods,
98 | "after": after,
99 | "before": before
100 | },
101 | cancelToken: cancelToken);
102 | return GraphResponse.fromJson(response.data).result;
103 | } on DioError catch (error) {
104 | throw DataException.fromDioError(error);
105 | }
106 | }
107 |
108 | @override
109 | Future> getExchanges({CancelToken? cancelToken}) async {
110 | try {
111 | final response = await read(clientProvider)
112 | .get('/exchanges', cancelToken: cancelToken);
113 | return ExchangesResponse.fromJson(response.data).result;
114 | } on DioError catch (error) {
115 | throw DataException.fromDioError(error);
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/lib/ui/home.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/constants/keys.dart';
2 | import 'package:cryptocurrency_app/provider/navigation_provider.dart';
3 | import 'package:cryptocurrency_app/ui/screens/home.dart';
4 | import 'package:cryptocurrency_app/ui/screens/search.dart';
5 | import 'package:cryptocurrency_app/ui/screens/settings.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:hooks_riverpod/hooks_riverpod.dart';
8 | import 'package:easy_localization/easy_localization.dart';
9 |
10 | import '../generated/locale_keys.g.dart';
11 |
12 | class Home extends HookConsumerWidget {
13 | const Home({Key? key}) : super(key: key);
14 | @override
15 | Widget build(BuildContext context, WidgetRef ref) {
16 | final PageModel navigation = ref.watch(navigationProvider);
17 |
18 | return Scaffold(
19 | body: currentScreen(navigation.index),
20 | bottomNavigationBar: BottomNavigationBar(
21 | key: Keys.NAV_BAR,
22 | currentIndex: navigation.index,
23 | onTap: (index) {
24 | ref.read(navigationProvider.notifier).selectPage(index);
25 | },
26 | items: [
27 | BottomNavigationBarItem(
28 | label: LocaleKeys.homeTitle.tr(),
29 | icon: Icon(
30 | Icons.home,
31 | key: Keys.NAV_HOME,
32 | ),
33 | ),
34 | BottomNavigationBarItem(
35 | label: LocaleKeys.searchTitle.tr(),
36 | icon: Icon(
37 | Icons.search,
38 | key: Keys.NAV_SEARCH,
39 | ),
40 | ),
41 | BottomNavigationBarItem(
42 | label: LocaleKeys.settingsTitle.tr(),
43 | icon: Icon(
44 | Icons.settings,
45 | key: Keys.NAV_SETTINGS,
46 | ),
47 | ),
48 | ]),
49 | );
50 | }
51 |
52 | Widget currentScreen(int index) {
53 | switch (index) {
54 | case 0:
55 | return HomeScreen();
56 | case 1:
57 | return SearchScreen();
58 | case 2:
59 | return SettingScreen();
60 | default:
61 | return HomeScreen();
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/ui/screens/details.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/constants/keys.dart';
2 | import 'package:cryptocurrency_app/constants/utils.dart' as Utils;
3 | import 'package:cryptocurrency_app/models/markets/pair/pair.dart';
4 | import 'package:cryptocurrency_app/provider/crypto_provider.dart';
5 | import 'package:cryptocurrency_app/ui/widgets/details/details_widget.dart';
6 | import 'package:cryptocurrency_app/ui/widgets/details/time_bar_selector.dart';
7 | import 'package:cryptocurrency_app/ui/widgets/line_chart.dart';
8 | import 'package:cryptocurrency_app/ui/widgets/title_price.dart';
9 | import 'package:flutter/material.dart';
10 | import 'package:hooks_riverpod/hooks_riverpod.dart';
11 |
12 | class DetailsScreen extends HookConsumerWidget {
13 | final Pair pair;
14 | DetailsScreen({required this.pair});
15 |
16 | @override
17 | Widget build(BuildContext context, WidgetRef ref) {
18 | final graph = ref.watch(graphDataProvider(pair));
19 |
20 | return Scaffold(
21 | key: Keys.DETAILS_SCREEN,
22 | appBar: AppBar(
23 | actions: [
24 | Container(
25 | width: 120,
26 | margin: EdgeInsets.symmetric(vertical: 6, horizontal: 5),
27 | )
28 | ],
29 | ),
30 | body: Container(
31 | child: SingleChildScrollView(
32 | child: Column(
33 | crossAxisAlignment: CrossAxisAlignment.start,
34 | children: [
35 | SizedBox(
36 | height: 5,
37 | ),
38 | Container(
39 | padding: EdgeInsets.symmetric(horizontal: 15),
40 | child: TitlePrice(pair: pair)),
41 | SizedBox(
42 | height: 20,
43 | ),
44 | Container(
45 | height: 250,
46 | child: graph.when(
47 | data: (data) =>
48 | LineChartWidget(data: Utils.getPoints(data)),
49 | loading: () => LineChartWidget(loading: true),
50 | error: (e, ex) => LineChartWidget(error: true)),
51 | ),
52 | SizedBox(
53 | height: 20,
54 | ),
55 | TimeBarSelector(),
56 | SizedBox(
57 | height: 15,
58 | ),
59 | DetailsWidget(pair: pair),
60 | SizedBox(
61 | height: 30,
62 | ),
63 | ],
64 | ),
65 | ),
66 | ),
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/lib/ui/screens/home.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/constants/keys.dart';
2 | import 'package:cryptocurrency_app/provider/crypto_provider.dart';
3 | import 'package:cryptocurrency_app/ui/widgets/favorite_pair.dart';
4 | import 'package:cryptocurrency_app/ui/widgets/pair_tile.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:hooks_riverpod/hooks_riverpod.dart';
7 | import 'package:easy_localization/easy_localization.dart';
8 | import '../../generated/locale_keys.g.dart';
9 |
10 | class HomeScreen extends HookConsumerWidget {
11 | @override
12 | Widget build(BuildContext context, WidgetRef ref) {
13 | final pairs = ref.watch(pairsProvider);
14 | final favoritePair = ref.watch(favoritePairProvider);
15 | return Container(
16 | key: Keys.HOME_SCREEN,
17 | child: Column(
18 | children: [
19 | AppBar(
20 | toolbarHeight: 65,
21 | centerTitle: false,
22 | title: Text(
23 | LocaleKeys.homeTitle.tr(),
24 | style: TextStyle(color: Colors.white, fontSize: 25),
25 | ),
26 | actions: [
27 | Container(
28 | margin: EdgeInsets.all(10),
29 | decoration: BoxDecoration(
30 | color: Colors.white,
31 | borderRadius: BorderRadius.circular(50)),
32 | width: 45,
33 | child: Icon(
34 | Icons.person_outline,
35 | size: 30,
36 | color: Colors.black,
37 | ),
38 | ),
39 | ],
40 | ),
41 | Expanded(
42 | child: Column(
43 | children: [
44 | Container(
45 | height: 190,
46 | child: favoritePair.when(
47 | data: (data) {
48 | return FavoritePairWidget(data);
49 | },
50 | loading: () => Center(
51 | child: CircularProgressIndicator(),
52 | ),
53 | error: (error, e) => Center(
54 | child: Text(error.toString().tr()),
55 | ),
56 | ),
57 | ),
58 | Expanded(
59 | child: pairs.when(
60 | data: (data) {
61 | return Container(
62 | child: ListView.builder(
63 | padding: EdgeInsets.only(top: 0.0),
64 | itemCount: data.length,
65 | itemBuilder: (ctx, int idx) => ProviderScope(
66 | overrides: [
67 | currentPair.overrideWithValue(data[idx]),
68 | ],
69 | child: const PairTile(),
70 | ),
71 | ),
72 | );
73 | },
74 | loading: () => Center(
75 | child: CircularProgressIndicator(),
76 | ),
77 | error: (error, e) => Center(
78 | child: Text(error.toString().tr()),
79 | ),
80 | ),
81 | )
82 | ],
83 | ),
84 | ),
85 | ],
86 | ),
87 | );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib/ui/screens/search.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/constants/keys.dart';
2 | import 'package:cryptocurrency_app/provider/crypto_provider.dart';
3 | import 'package:cryptocurrency_app/ui/widgets/pair_tile.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:hooks_riverpod/hooks_riverpod.dart';
6 | import 'package:easy_localization/easy_localization.dart';
7 | import '../../generated/locale_keys.g.dart';
8 |
9 | class SearchScreen extends HookConsumerWidget {
10 | @override
11 | Widget build(BuildContext context, WidgetRef ref) {
12 | final pairs = ref.watch(pairsSearchProvider);
13 |
14 | return Container(
15 | key: Keys.SEARCH_SCREEN,
16 | child: Column(
17 | children: [
18 | AppBar(
19 | title: Text(LocaleKeys.searchTitle.tr()),
20 | ),
21 | Expanded(
22 | child: Column(
23 | children: [
24 | Container(
25 | height: 50,
26 | padding: EdgeInsets.symmetric(horizontal: 15),
27 | decoration: BoxDecoration(
28 | borderRadius: BorderRadius.all(Radius.circular(5)),
29 | color: Theme.of(context).cardColor,
30 | ),
31 | child: TextFormField(
32 | key: Keys.SEARCH_TEXT_FIELD,
33 | initialValue: ref.read(searchTextProvider),
34 | style: TextStyle(color: Colors.white, fontSize: 21),
35 | decoration: new InputDecoration(
36 | prefixIcon: new Icon(Icons.search,
37 | color: Colors.white, size: 32),
38 | hintText: LocaleKeys.searchBar.tr(),
39 | hintStyle: new TextStyle(color: Colors.white),
40 | border: InputBorder.none),
41 | onChanged: (value) =>
42 | {ref.read(searchTextProvider.notifier).state = value},
43 | ),
44 | ),
45 | Expanded(
46 | child: pairs.maybeWhen(
47 | data: (data) {
48 | return Stack(
49 | children: [
50 | ListView.builder(
51 | padding: EdgeInsets.zero,
52 | itemCount: data.length,
53 | itemBuilder: (ctx, int id) => ProviderScope(
54 | overrides: [
55 | currentPair.overrideWithValue(data[id]),
56 | ],
57 | child: const PairTile(),
58 | ),
59 | ),
60 | if (data.length == 0)
61 | Center(child: Text(LocaleKeys.noResults.tr()))
62 | ],
63 | );
64 | },
65 | orElse: () => Center(
66 | child: CircularProgressIndicator(),
67 | )),
68 | )
69 | ],
70 | ),
71 | ),
72 | ],
73 | ),
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/ui/widgets/details/details_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/markets/pair/pair.dart';
2 | import 'package:cryptocurrency_app/provider/crypto_provider.dart';
3 | import 'package:cryptocurrency_app/ui/widgets/details/summary_section.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_hooks/flutter_hooks.dart';
6 | import 'package:hooks_riverpod/hooks_riverpod.dart';
7 | import 'package:easy_localization/easy_localization.dart';
8 | import '../../../generated/locale_keys.g.dart';
9 |
10 | import 'ohlc_section.dart';
11 | import 'order_book_section.dart';
12 | import 'trades_section.dart';
13 |
14 | class DetailsWidget extends HookConsumerWidget {
15 | final Pair pair;
16 | const DetailsWidget({Key? key, required this.pair}) : super(key: key);
17 |
18 | @override
19 | Widget build(BuildContext context, WidgetRef ref) {
20 | final _controller = useTabController(initialLength: 4);
21 | final graph = ref.watch(graphDataProvider(pair));
22 | final summary = ref.watch(pairSummaryProvider(pair));
23 | final orderBook = ref.watch(pairOrderBookProvider(pair));
24 | final trades = ref.watch(tradesProvider(pair));
25 |
26 | return Container(
27 | child: Column(
28 | children: [
29 | TabBar(
30 | labelColor: Theme.of(context).focusColor,
31 | unselectedLabelColor: Theme.of(context).unselectedWidgetColor,
32 | unselectedLabelStyle: Theme.of(context).textTheme.headline4,
33 | labelStyle: Theme.of(context).textTheme.headline4,
34 | indicatorWeight: 4,
35 | indicatorSize: TabBarIndicatorSize.label,
36 | indicatorColor: Theme.of(context).focusColor,
37 | isScrollable: true,
38 | controller: _controller,
39 | tabs: [
40 | Container(
41 | width: 100,
42 | child: Tab(
43 | text: LocaleKeys.summary.tr(),
44 | )),
45 | Tab(
46 | text: LocaleKeys.orderbook.tr(),
47 | ),
48 | Tab(text: LocaleKeys.trades.tr()),
49 | Tab(
50 | text: LocaleKeys.ohlc.tr(),
51 | ),
52 | ],
53 | ),
54 | Container(
55 | height: 300,
56 | child: TabBarView(
57 | controller: _controller,
58 | children: [
59 | summary.when(
60 | data: (data) => SummarySection(data: data),
61 | loading: () => Center(
62 | child: CircularProgressIndicator(),
63 | ),
64 | error: (error, e) => Center(
65 | child: Text(error.toString().tr()),
66 | )),
67 | orderBook.when(
68 | data: (data) => OrderBookSection(data: data),
69 | loading: () => Center(
70 | child: CircularProgressIndicator(),
71 | ),
72 | error: (error, e) => Center(
73 | child: Text(error.toString().tr()),
74 | )),
75 | trades.when(
76 | data: (data) => TradesSection(data: data),
77 | loading: () => Center(
78 | child: CircularProgressIndicator(),
79 | ),
80 | error: (error, e) => Center(
81 | child: Text(error.toString().tr()),
82 | )),
83 | graph.when(
84 | data: (data) => OHLCSection(
85 | data: data,
86 | ),
87 | loading: () => Center(
88 | child: CircularProgressIndicator(),
89 | ),
90 | error: (error, e) => Center(
91 | child: Text(error.toString().tr()),
92 | )),
93 | ],
94 | ),
95 | ),
96 | ],
97 | ),
98 | );
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lib/ui/widgets/details/ohlc_section.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/graph/graph/graph.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_candlesticks/flutter_candlesticks.dart';
4 |
5 | class OHLCSection extends StatelessWidget {
6 | final Graph data;
7 | const OHLCSection({Key? key, required this.data}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Container(
12 | margin: EdgeInsets.symmetric(horizontal: 30, vertical: 40),
13 | child: new OHLCVGraph(
14 | data: data.pairs[0].points
15 | .map((e) => {
16 | "open": e.openTime,
17 | "high": e.highPrice,
18 | "low": e.lowPrice,
19 | "close": e.closePrice,
20 | "volumeto": e.volume
21 | })
22 | .toList(),
23 | enableGridLines: true,
24 | volumeProp: 0.2,
25 | gridLineAmount: 5,
26 | gridLineColor: Colors.grey[300]!,
27 | gridLineLabelColor: Colors.grey));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/ui/widgets/details/order_book_section.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/orderbook/orderbook/orderbook.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:easy_localization/easy_localization.dart';
4 | import '../../../generated/locale_keys.g.dart';
5 |
6 | class OrderBookSection extends StatelessWidget {
7 | final OrderBook data;
8 | OrderBookSection({Key? key, required this.data}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Container(
13 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
14 | child: Column(
15 | mainAxisSize: MainAxisSize.max,
16 | children: [
17 | Row(
18 | mainAxisAlignment: MainAxisAlignment.spaceAround,
19 | children: [
20 | Text(
21 | LocaleKeys.bid.tr(),
22 | style: Theme.of(context).textTheme.subtitle2,
23 | ),
24 | Text(
25 | LocaleKeys.ask.tr(),
26 | style: Theme.of(context).textTheme.subtitle2,
27 | )
28 | ],
29 | ),
30 | SizedBox(
31 | height: 10,
32 | ),
33 | Container(
34 | height: 220,
35 | child: Row(
36 | children: [
37 | Flexible(
38 | flex: 1,
39 | child: ListView.builder(
40 | itemCount: data.bids.length,
41 | itemBuilder: (context, index) {
42 | return Container(
43 | margin: EdgeInsets.symmetric(vertical: 2),
44 | child: Row(
45 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
46 | children: [
47 | Text(
48 | data.bids[index].amount.toString(),
49 | style: Theme.of(context).textTheme.subtitle1,
50 | ),
51 | Text(
52 | data.bids[index].price.toString(),
53 | style: Theme.of(context).textTheme.subtitle1,
54 | )
55 | ],
56 | ),
57 | );
58 | },
59 | ),
60 | ),
61 | SizedBox(
62 | width: 30,
63 | ),
64 | Flexible(
65 | flex: 1,
66 | child: ListView.builder(
67 | itemCount: data.asks.length,
68 | itemBuilder: (context, index) {
69 | return Container(
70 | margin: EdgeInsets.symmetric(vertical: 2),
71 | child: Row(
72 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
73 | children: [
74 | Text(
75 | data.asks[index].amount.toString(),
76 | style: Theme.of(context).textTheme.subtitle1,
77 | ),
78 | Text(
79 | data.asks[index].price.toString(),
80 | style: Theme.of(context).textTheme.subtitle1,
81 | )
82 | ],
83 | ),
84 | );
85 | },
86 | ),
87 | )
88 | ],
89 | ),
90 | )
91 | ],
92 | ),
93 | );
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/lib/ui/widgets/details/summary_section.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/pair/pair_summary/pair_summary.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:easy_localization/easy_localization.dart';
4 | import '../../../generated/locale_keys.g.dart';
5 |
6 | class SummarySection extends StatelessWidget {
7 | final PairSummary data;
8 | const SummarySection({Key? key, required this.data}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Container(
13 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
14 | child: Column(
15 | children: [
16 | Row(
17 | children: [
18 | Text(
19 | LocaleKeys.price.tr(),
20 | style: Theme.of(context).textTheme.subtitle2,
21 | )
22 | ],
23 | ),
24 | SizedBox(height: 5),
25 | Row(
26 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
27 | children: [
28 | Text(
29 | LocaleKeys.last.tr(),
30 | style: Theme.of(context).textTheme.subtitle1,
31 | ),
32 | Text(
33 | data.price.last.toString(),
34 | style: Theme.of(context).textTheme.subtitle1,
35 | )
36 | ],
37 | ),
38 | SizedBox(height: 5),
39 | Row(
40 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
41 | children: [
42 | Text(
43 | LocaleKeys.high.tr(),
44 | style: Theme.of(context).textTheme.subtitle1,
45 | ),
46 | Text(
47 | data.price.high.toString(),
48 | style: Theme.of(context).textTheme.subtitle1,
49 | )
50 | ],
51 | ),
52 | SizedBox(height: 5),
53 | Row(
54 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
55 | children: [
56 | Text(
57 | LocaleKeys.low.tr(),
58 | style: Theme.of(context).textTheme.subtitle1,
59 | ),
60 | Text(
61 | data.price.low.toString(),
62 | style: Theme.of(context).textTheme.subtitle1,
63 | )
64 | ],
65 | ),
66 | SizedBox(height: 5),
67 | Row(
68 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
69 | children: [
70 | Text(
71 | LocaleKeys.change.tr(),
72 | style: Theme.of(context).textTheme.subtitle1,
73 | ),
74 | Text(
75 | data.price.change.absolute.toString(),
76 | style: Theme.of(context).textTheme.subtitle1,
77 | )
78 | ],
79 | ),
80 | SizedBox(height: 10),
81 | Row(
82 | children: [
83 | Text(
84 | LocaleKeys.volume.tr(),
85 | style: Theme.of(context).textTheme.subtitle2,
86 | )
87 | ],
88 | ),
89 | SizedBox(height: 5),
90 | Row(
91 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
92 | children: [
93 | Text(
94 | LocaleKeys.volume.tr(),
95 | style: Theme.of(context).textTheme.subtitle1,
96 | ),
97 | Text(
98 | data.volume.toString(),
99 | style: Theme.of(context).textTheme.subtitle1,
100 | )
101 | ],
102 | ),
103 | SizedBox(height: 5),
104 | Row(
105 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
106 | children: [
107 | Text(
108 | LocaleKeys.quoteVolume.tr(),
109 | style: Theme.of(context).textTheme.subtitle1,
110 | ),
111 | Text(
112 | data.volumeQuote.toString(),
113 | style: Theme.of(context).textTheme.subtitle1,
114 | )
115 | ],
116 | ),
117 | ],
118 | ),
119 | );
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/lib/ui/widgets/details/time_bar_selector.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/provider/time_provider.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 |
5 | class TimeBarSelector extends HookConsumerWidget {
6 | @override
7 | Widget build(BuildContext context, WidgetRef ref) {
8 | return Container(
9 | padding: EdgeInsets.symmetric(horizontal: 15),
10 | child: Row(
11 | mainAxisAlignment: MainAxisAlignment.spaceAround,
12 | crossAxisAlignment: CrossAxisAlignment.center,
13 | children: timeList
14 | .mapIndexed(
15 | (e, i) => InkWell(
16 | onTap: () {
17 | ref.read(timeDataProvider.notifier).state = e;
18 | },
19 | child: Container(
20 | padding: EdgeInsets.symmetric(vertical: 3, horizontal: 8),
21 | decoration: BoxDecoration(
22 | color: ref.read(timeDataProvider.notifier).state.name ==
23 | e.name
24 | ? Theme.of(context).cardColor
25 | : Colors.transparent,
26 | borderRadius: BorderRadius.all(Radius.circular(5))),
27 | child: Center(
28 | child: Text(
29 | e.name,
30 | style: ref.read(timeDataProvider.notifier).state.name ==
31 | e.name
32 | ? Theme.of(context)
33 | .textTheme
34 | .headline3!
35 | .apply(color: Colors.white)
36 | : Theme.of(context).textTheme.headline4,
37 | ),
38 | ),
39 | ),
40 | ),
41 | )
42 | .toList()),
43 | );
44 | }
45 | }
46 |
47 | extension IndexedIterable on Iterable {
48 | Iterable mapIndexed(T Function(E e, int i) f) {
49 | var i = 0;
50 | return map((e) => f(e, i++));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/ui/widgets/details/trades_section.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/trades/trade/trade.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:easy_localization/easy_localization.dart';
4 | import '../../../generated/locale_keys.g.dart';
5 | import '../../../constants/utils.dart' as Utils;
6 |
7 | class TradesSection extends StatelessWidget {
8 | final List data;
9 | const TradesSection({Key? key, required this.data}) : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Container(
14 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
15 | child: Column(
16 | children: [
17 | Row(
18 | children: [
19 | Expanded(
20 | child: Text(
21 | LocaleKeys.time.tr(),
22 | style: Theme.of(context).textTheme.subtitle2,
23 | ),
24 | ),
25 | Expanded(
26 | child: Text(
27 | LocaleKeys.price.tr(),
28 | textAlign: TextAlign.center,
29 | style: Theme.of(context).textTheme.subtitle2,
30 | ),
31 | ),
32 | Expanded(
33 | child: Text(
34 | LocaleKeys.amount.tr(),
35 | textAlign: TextAlign.right,
36 | style: Theme.of(context).textTheme.subtitle2,
37 | ),
38 | )
39 | ],
40 | ),
41 | SizedBox(
42 | height: 4,
43 | ),
44 | Container(
45 | height: 250,
46 | child: ListView.builder(
47 | itemCount: 4,
48 | itemBuilder: (context, index) {
49 | return Container(
50 | padding: EdgeInsets.symmetric(vertical: 2),
51 | child: Row(
52 | children: [
53 | Expanded(
54 | flex: 1,
55 | child: Text(
56 | Utils.epochToString(data[index].timestamp),
57 | style: Theme.of(context).textTheme.subtitle1,
58 | ),
59 | ),
60 | Expanded(
61 | flex: 1,
62 | child: Text(
63 | data[index].price,
64 | textAlign: TextAlign.center,
65 | style: Theme.of(context).textTheme.subtitle1,
66 | ),
67 | ),
68 | Expanded(
69 | flex: 1,
70 | child: Text(
71 | data[index].amount,
72 | textAlign: TextAlign.right,
73 | style: Theme.of(context).textTheme.subtitle1,
74 | ),
75 | )
76 | ],
77 | ),
78 | );
79 | }),
80 | ),
81 | ],
82 | ),
83 | );
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/lib/ui/widgets/favorite_pair.dart:
--------------------------------------------------------------------------------
1 | import 'package:cryptocurrency_app/models/markets/favorite_pair/favorite_pair.dart';
2 | import 'package:cryptocurrency_app/ui/screens/details.dart';
3 | import 'package:cryptocurrency_app/ui/widgets/title_price.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_hooks/flutter_hooks.dart';
6 | import 'package:easy_localization/easy_localization.dart';
7 | import '../../generated/locale_keys.g.dart';
8 |
9 | class FavoritePairWidget extends HookWidget {
10 | final FavoritePair data;
11 | FavoritePairWidget(this.data);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Container(
16 | padding: EdgeInsets.symmetric(horizontal: 15),
17 | child: Column(
18 | crossAxisAlignment: CrossAxisAlignment.start,
19 | children: [
20 | SizedBox(
21 | height: 5,
22 | ),
23 | TitlePrice(pair: data.pair),
24 | Container(
25 | margin: EdgeInsets.only(top: 10),
26 | color: Theme.of(context).dividerColor,
27 | height: 1,
28 | width: double.infinity,
29 | ),
30 | InkWell(
31 | onTap: () {
32 | Navigator.push(
33 | context,
34 | MaterialPageRoute(
35 | builder: (context) => DetailsScreen(
36 | pair: data.pair,
37 | ),
38 | ),
39 | );
40 | },
41 | child: Container(
42 | padding: EdgeInsets.symmetric(vertical: 10),
43 | child: Row(
44 | children: [
45 | Icon(
46 | Icons.add_chart,
47 | size: 30,
48 | color: Theme.of(context).iconTheme.color,
49 | ),
50 | SizedBox(width: 10),
51 | Text(LocaleKeys.openChart.tr(),
52 | style: Theme.of(context).textTheme.headline3),
53 | ],
54 | ),
55 | ),
56 | ),
57 | Container(
58 | padding: EdgeInsets.symmetric(vertical: 5),
59 | color: Theme.of(context).dividerColor,
60 | height: 1,
61 | width: double.infinity,
62 | ),
63 | SizedBox(
64 | height: 10,
65 | ),
66 | ],
67 | ),
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/ui/widgets/line_chart.dart:
--------------------------------------------------------------------------------
1 | import 'package:fl_chart/fl_chart.dart';
2 | import 'package:flutter/material.dart';
3 | import 'dart:math';
4 | import '../../generated/locale_keys.g.dart';
5 | import 'package:easy_localization/easy_localization.dart';
6 | import 'package:cryptocurrency_app/constants/utils.dart' as Utils;
7 |
8 | class LineChartWidget extends StatelessWidget {
9 | final List data;
10 | final Color color;
11 | final bool loading;
12 | final bool error;
13 |
14 | const LineChartWidget(
15 | {Key? key,
16 | this.data = const [],
17 | this.color = const Color(0xff02d39a),
18 | this.loading = false,
19 | this.error = false})
20 | : super(key: key);
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 | return Stack(alignment: AlignmentDirectional.center, children: [
25 | Opacity(
26 | opacity: data.length > 0 && !loading & !error ? 1 : 0.3,
27 | child: Container(
28 | width: double.infinity,
29 | child: LineChart(
30 | mainData(data.length > 0 && !loading & !error
31 | ? data
32 | : Utils.demoGraphData),
33 | swapAnimationDuration: Duration(seconds: 0),
34 | ),
35 | ),
36 | ),
37 | if (loading)
38 | Center(
39 | child: CircularProgressIndicator(),
40 | )
41 | else if (error || data.length == 0)
42 | Center(
43 | child: Text(LocaleKeys.noResults.tr(),
44 | style: Theme.of(context).textTheme.headline3),
45 | )
46 | ]);
47 | }
48 |
49 | LineChartData mainData(List data) {
50 | return LineChartData(
51 | gridData: FlGridData(
52 | show: true,
53 | drawVerticalLine: false,
54 | drawHorizontalLine: false,
55 | horizontalInterval: 4,
56 | getDrawingHorizontalLine: (value) {
57 | return FlLine(
58 | color: const Color(0xff37434d),
59 | strokeWidth: 1,
60 | );
61 | },
62 | getDrawingVerticalLine: (value) {
63 | return FlLine(
64 | color: const Color(0xff37434d),
65 | strokeWidth: 1,
66 | );
67 | },
68 | ),
69 | titlesData: FlTitlesData(
70 | show: false,
71 | ),
72 | borderData: FlBorderData(
73 | show: false,
74 | ),
75 | minX: 0,
76 | maxX: data.length.toDouble() - 1,
77 | minY: data.reduce(min).toDouble(),
78 | maxY: data.reduce(max).toDouble(),
79 | lineBarsData: [
80 | LineChartBarData(
81 | spots: listData(data),
82 | colors: [color],
83 | barWidth: 3,
84 | isStrokeCapRound: true,
85 | dotData: FlDotData(
86 | show: false,
87 | ),
88 | belowBarData: BarAreaData(
89 | show: true,
90 | gradientFrom: Offset(0, .9),
91 | gradientTo: Offset(0, 0.5),
92 | colors: [color.withOpacity(.01), color.withOpacity(.3)],
93 | ),
94 | ),
95 | ],
96 | );
97 | }
98 |
99 | List listData(List data) {
100 | return data
101 | .mapIndexed((e, i) => FlSpot(i.toDouble(), e.toDouble()))
102 | .toList();
103 | }
104 | }
105 |
106 | extension IndexedIterable on Iterable {
107 | Iterable mapIndexed(T Function(E e, int i) f) {
108 | var i = 0;
109 | return map((e) => f(e, i++));
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/lib/ui/widgets/title_price.dart:
--------------------------------------------------------------------------------
1 | import 'package:auto_size_text/auto_size_text.dart';
2 | import 'package:cryptocurrency_app/models/markets/pair/pair.dart';
3 | import 'package:cryptocurrency_app/provider/crypto_provider.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:hooks_riverpod/hooks_riverpod.dart';
6 | import 'package:easy_localization/easy_localization.dart';
7 |
8 | class TitlePrice extends HookConsumerWidget {
9 | final Pair pair;
10 |
11 | TitlePrice({required this.pair});
12 |
13 | @override
14 | Widget build(BuildContext context, WidgetRef ref) {
15 | final data = ref.watch(pairSummaryProvider(pair));
16 |
17 | return data.when(
18 | data: (data) {
19 | return Container(
20 | child: Column(
21 | crossAxisAlignment: CrossAxisAlignment.start,
22 | children: [
23 | AutoSizeText(pair.pair,
24 | maxLines: 1, style: Theme.of(context).textTheme.headline2),
25 | AutoSizeText(data.price.last.toString(),
26 | maxLines: 1, style: Theme.of(context).textTheme.headline1),
27 | if (true)
28 | Row(children: [
29 | AutoSizeText(data.price.change.absolute.toStringAsFixed(5),
30 | textAlign: TextAlign.start,
31 | minFontSize: 0,
32 | stepGranularity: 0.1,
33 | maxLines: 1,
34 | style: TextStyle(
35 | color: data.price.change.absolute >= 0
36 | ? Colors.green
37 | : Colors.red,
38 | fontSize:
39 | Theme.of(context).textTheme.headline5?.fontSize,
40 | fontWeight: FontWeight.w800)),
41 | AutoSizeText(
42 | ' (${data.price.change.percentage.toStringAsFixed(2)}%)',
43 | textAlign: TextAlign.start,
44 | minFontSize: 0,
45 | stepGranularity: 0.1,
46 | maxLines: 1,
47 | style: Theme.of(context).textTheme.headline4),
48 | ]),
49 | ],
50 | ),
51 | );
52 | },
53 | loading: () => Center(
54 | child: CircularProgressIndicator(),
55 | ),
56 | error: (error, e) => Center(
57 | child: Text(error.toString().tr()),
58 | ),
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: cryptocurrency_app
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.0.0+1
19 |
20 | environment:
21 | sdk: '>=2.12.0 <3.0.0'
22 |
23 | dependencies:
24 | flutter:
25 | sdk: flutter
26 | cupertino_icons: ^1.0.0
27 | hooks_riverpod: 2.0.0-dev.3
28 | flutter_riverpod: 2.0.0-dev.3
29 | dio: ^4.0.0
30 | flutter_dotenv: ^5.0.0
31 | fl_chart: ^0.36.1
32 | settings_ui: ^2.0.2
33 | auto_size_text: ^3.0.0-nullsafety.0
34 | flutter_secure_storage: ^5.0.2
35 | easy_localization: ^3.0.0
36 | freezed_annotation:
37 | flutter_candlesticks:
38 | git: https://github.com/salvadordeveloper/flutter-candlesticks.git
39 | mockito: ^5.0.9
40 |
41 | dev_dependencies:
42 | integration_test:
43 | sdk: flutter
44 | flutter_test:
45 | sdk: flutter
46 | http_mock_adapter: ^0.3.2
47 | build_runner:
48 | freezed:
49 | json_serializable:
50 | flutter_launcher_icons: ^0.9.0
51 | flutter_native_splash: ^1.1.8+4
52 |
53 | flutter_icons:
54 | android: "launcher_icon"
55 | ios: true
56 | image_path: "assets/icon/icon.png"
57 | image_path_ios: "assets/icon/icon_ios.png"
58 |
59 | flutter_native_splash:
60 | color: "#3D3D3D"
61 | image: assets/icon/icon.png
62 | color_dark: "#131313"
63 |
64 | # For information on the generic Dart part of this file, see the
65 | # following page: https://dart.dev/tools/pub/pubspec
66 | # The following section is specific to Flutter.
67 | flutter:
68 | # The following line ensures that the Material Icons font is
69 | # included with your application, so that you can use the icons in
70 | # the material Icons class.
71 | uses-material-design: true
72 |
73 | assets:
74 | - assets/translations/
75 | - assets/icon/icon.png
76 | - .env
77 | # To add assets to your application, add an assets section, like this:
78 | # assets:
79 | # - images/a_dot_burr.jpeg
80 | # - images/a_dot_ham.jpeg
81 | # An image asset can refer to one or more resolution-specific "variants", see
82 | # https://flutter.dev/assets-and-images/#resolution-aware.
83 | # For details regarding adding assets from package dependencies, see
84 | # https://flutter.dev/assets-and-images/#from-packages
85 | # To add custom fonts to your application, add a fonts section here,
86 | # in this "flutter" section. Each entry in this list should have a
87 | # "family" key with the font family name, and a "fonts" key with a
88 | # list giving the asset and other descriptors for the font. For
89 | # example:
90 | # fonts:
91 | # - family: Schyler
92 | # fonts:
93 | # - asset: fonts/Schyler-Regular.ttf
94 | # - asset: fonts/Schyler-Italic.ttf
95 | # style: italic
96 | # - family: Trajan Pro
97 | # fonts:
98 | # - asset: fonts/TrajanPro.ttf
99 | # - asset: fonts/TrajanPro_Bold.ttf
100 | # weight: 700
101 | #
102 | # For details regarding fonts from package dependencies,
103 | # see https://flutter.dev/custom-fonts/#from-packages
104 |
--------------------------------------------------------------------------------
/run_all_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # start the app and write the uri to a file
4 | #flutter run --vmservice-out-file="test_driver/uri.txt"
5 |
6 |
7 | flutter drive --driver=test_driver/integration_test.dart --target=integration_test/main_test.dart
--------------------------------------------------------------------------------
/screenshots/1_dark.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/screenshots/1_dark.jpeg
--------------------------------------------------------------------------------
/screenshots/1_light.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/screenshots/1_light.jpeg
--------------------------------------------------------------------------------
/screenshots/2_dark.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/screenshots/2_dark.jpeg
--------------------------------------------------------------------------------
/screenshots/2_light.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/screenshots/2_light.jpeg
--------------------------------------------------------------------------------
/screenshots/3_dark.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/screenshots/3_dark.jpeg
--------------------------------------------------------------------------------
/screenshots/3_light.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/screenshots/3_light.jpeg
--------------------------------------------------------------------------------
/screenshots/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/screenshots/cover.png
--------------------------------------------------------------------------------
/test/data/api_data.dart:
--------------------------------------------------------------------------------
1 | class ApiData {
2 | static final Map exchanges = {
3 | "result": [
4 | {
5 | "id": 17,
6 | "symbol": "mexbt",
7 | "name": "meXBT",
8 | "route": "https://api.cryptowat.ch/exchanges/mexbt",
9 | "active": false
10 | },
11 | {
12 | "id": 62,
13 | "symbol": "coinone",
14 | "name": "Coinone",
15 | "route": "https://api.cryptowat.ch/exchanges/coinone",
16 | "active": true
17 | },
18 | ],
19 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
20 | };
21 |
22 | static final Map pairs = {
23 | "result": [
24 | {
25 | "id": 579,
26 | "exchange": "binance",
27 | "pair": "btcusdt",
28 | "active": true,
29 | "route": "https://api.cryptowat.ch/markets/binance/btcusdt"
30 | },
31 | {
32 | "id": 580,
33 | "exchange": "binance",
34 | "pair": "ethbtc",
35 | "active": true,
36 | "route": "https://api.cryptowat.ch/markets/binance/ethbtc"
37 | },
38 | {
39 | "id": 581,
40 | "exchange": "binance",
41 | "pair": "ltcbtc",
42 | "active": true,
43 | "route": "https://api.cryptowat.ch/markets/binance/ltcbtc"
44 | },
45 | {
46 | "id": 582,
47 | "exchange": "binance",
48 | "pair": "neobtc",
49 | "active": true,
50 | "route": "https://api.cryptowat.ch/markets/binance/neobtc"
51 | },
52 | ],
53 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
54 | };
55 |
56 | static final Map pair_btcusdt_summary = {
57 | "result": {
58 | "price": {
59 | "last": 35503.33,
60 | "high": 43861.94,
61 | "low": 30000,
62 | "change": {"percentage": -0.18764266402587584, "absolute": -8200.75}
63 | },
64 | "volume": 257132.87322650044,
65 | "volumeQuote": 10096197214.14349
66 | },
67 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
68 | };
69 |
70 | static final Map pair_btcusdt_oderbook = {
71 | "result": {
72 | "asks": [
73 | [35922.59, 0.004088],
74 | [35925.23, 0.003071],
75 | [35925.71, 0.012824],
76 | [35927.12, 0.000556],
77 | [35927.58, 0.2178],
78 | ],
79 | "bids": [
80 | [35904.23, 0.153095],
81 | [35900.35, 0.082238],
82 | [35898, 0.12],
83 | [35897.99, 0.006152],
84 | [35897.68, 0.04332],
85 | ],
86 | "seqNum": 429614
87 | },
88 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
89 | };
90 |
91 | static final Map pair_btcusdt_trades = {
92 | "result": [
93 | [0, 1621433110, 34452.66, 0.008464],
94 | [0, 1621433110, 34454.43, 0.016662],
95 | [0, 1621433110, 34485.69, 0.00476],
96 | [0, 1621433110, 34475.97, 0.000401],
97 | [0, 1621433110, 34456.09, 0.0011],
98 | [0, 1621433110, 34456.09, 0.004997],
99 | ],
100 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
101 | };
102 |
103 | static final Map pair_btcusdt_graph = {
104 | "result": {
105 | "14400": [
106 | [
107 | 1607054400,
108 | 19422.34,
109 | 19527,
110 | 19122.74,
111 | 19162.62,
112 | 8683.588417,
113 | 167917416.81467284
114 | ],
115 | [
116 | 1607097600,
117 | 18835.47,
118 | 19146.22,
119 | 18686.38,
120 | 18943.35,
121 | 14717.586675,
122 | 278732315.17076141
123 | ],
124 | [
125 | 1607112000,
126 | 18944.06,
127 | 19078.68,
128 | 18817,
129 | 19038.73,
130 | 8799.851665,
131 | 166925728.42698553
132 | ],
133 | ]
134 | },
135 | "allowance": {"cost": 0, "remaining": 100, "upgrade": ""}
136 | };
137 | }
138 |
--------------------------------------------------------------------------------
/test/exception_test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:io';
3 |
4 | import 'package:cryptocurrency_app/constants/exceptions.dart';
5 | import 'package:dio/dio.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_test/flutter_test.dart';
8 | import 'package:easy_localization/src/localization.dart';
9 | import 'package:easy_localization/src/translations.dart';
10 | import '../lib/generated/locale_keys.g.dart';
11 |
12 | void main() {
13 | setUpAll(() {
14 | File('assets/translations/en.json').readAsString().then((String contents) {
15 | Map data = jsonDecode(contents);
16 | Localization.load(Locale('en'), translations: Translations(data));
17 | });
18 | });
19 |
20 | test('Create DataException', () {
21 | final exception =
22 | DataException(message: LocaleKeys.errorSomethingWentWrong);
23 | expect(exception.toString(), LocaleKeys.errorSomethingWentWrong);
24 | });
25 |
26 | test('Exception Dio Cancel', () {
27 | final error = DioError(
28 | requestOptions: RequestOptions(path: ""), type: DioErrorType.cancel);
29 | final exception = DataException.fromDioError(error);
30 | expect(exception.toString(), LocaleKeys.errorRequestCancelled);
31 | });
32 |
33 | test('Exception Connection Timeout', () {
34 | final error = DioError(
35 | requestOptions: RequestOptions(path: ""),
36 | type: DioErrorType.connectTimeout);
37 | final exception = DataException.fromDioError(error);
38 | expect(exception.toString(), LocaleKeys.errorConnectionTimeout);
39 | });
40 |
41 | test('Exception other', () {
42 | final error = DioError(
43 | requestOptions: RequestOptions(path: ""), type: DioErrorType.other);
44 | final exception = DataException.fromDioError(error);
45 | expect(exception.toString(), LocaleKeys.errorInternetConnection);
46 | });
47 |
48 | test('Exception Receive Timeout', () {
49 | final error = DioError(
50 | requestOptions: RequestOptions(path: ""),
51 | type: DioErrorType.receiveTimeout);
52 | final exception = DataException.fromDioError(error);
53 | expect(exception.toString(), LocaleKeys.errorReceiveTimeout);
54 | });
55 |
56 | test('Exception Response 400', () {
57 | final error = DioError(
58 | requestOptions: RequestOptions(path: ""),
59 | type: DioErrorType.response,
60 | response: Response(
61 | requestOptions: RequestOptions(
62 | path: "",
63 | ),
64 | statusCode: 400));
65 | final exception = DataException.fromDioError(error);
66 | expect(exception.toString(), LocaleKeys.errorBadRequest);
67 | });
68 |
69 | test('Exception Response 404', () {
70 | final error = DioError(
71 | requestOptions: RequestOptions(path: ""),
72 | type: DioErrorType.response,
73 | response: Response(
74 | requestOptions: RequestOptions(
75 | path: "",
76 | ),
77 | statusCode: 404));
78 | final exception = DataException.fromDioError(error);
79 | expect(exception.toString(), LocaleKeys.errorRequestNotFound);
80 | });
81 |
82 | test('Exception Response 500', () {
83 | final error = DioError(
84 | requestOptions: RequestOptions(path: ""),
85 | type: DioErrorType.response,
86 | response: Response(
87 | requestOptions: RequestOptions(
88 | path: "",
89 | ),
90 | statusCode: 500));
91 | final exception = DataException.fromDioError(error);
92 | expect(exception.toString(), LocaleKeys.errorIntenalServer);
93 | });
94 |
95 | test('Exception Send Timeout', () {
96 | final error = DioError(
97 | requestOptions: RequestOptions(
98 | path: "",
99 | ),
100 | type: DioErrorType.sendTimeout);
101 | final exception = DataException.fromDioError(error);
102 | expect(exception.toString(), LocaleKeys.errorSendTimeout);
103 | });
104 | }
105 |
--------------------------------------------------------------------------------
/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 | void main() {}
9 |
--------------------------------------------------------------------------------
/test_driver/integration_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:integration_test/integration_test_driver.dart';
2 |
3 | Future main() => integrationDriver();
4 |
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/web/favicon.png
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salvadordeveloper/flutter-crypto-app/ff3e872d6d7dbb690781a263ee3ef42a332d17cc/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 | flutter_crypto_app
30 |
31 |
32 |
33 |
36 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flutter_crypto_app",
3 | "short_name": "flutter_crypto_app",
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 |
--------------------------------------------------------------------------------