├── .gitignore
├── .metadata
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── se
│ │ │ │ └── bocker
│ │ │ │ └── codestatsflutter
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── assets
├── icon
│ └── ic_launcher.png
└── web_hi_res_512.png
├── fonts
└── OCRAEXT.TTF
├── ios
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── flutter_export_environment.sh
├── Podfile
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── Runner
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-App-1024x1024@1x.png
│ │ ├── Icon-App-20x20@1x.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@1x.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ ├── Icon-App-76x76@1x.png
│ │ ├── Icon-App-76x76@2x.png
│ │ └── Icon-App-83.5x83.5@2x.png
│ └── LaunchImage.imageset
│ │ ├── Contents.json
│ │ ├── LaunchImage.png
│ │ ├── LaunchImage@2x.png
│ │ ├── LaunchImage@3x.png
│ │ └── README.md
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ ├── Runner-Bridging-Header.h
│ └── main.m
├── lib
├── bloc
│ ├── bloc_provider.dart
│ ├── codestats_bloc.dart
│ ├── state.dart
│ └── state.g.dart
├── hydrated.dart
├── main.dart
├── models
│ ├── pulse
│ │ ├── pulse.dart
│ │ ├── pulse.g.dart
│ │ ├── xp.dart
│ │ └── xp.g.dart
│ └── user
│ │ ├── day_language_xps.dart
│ │ ├── day_language_xps.g.dart
│ │ ├── user.dart
│ │ ├── user.g.dart
│ │ ├── xp.dart
│ │ └── xp.g.dart
├── passthrough_simulation.dart
├── queries.dart
├── schema.graphql
├── sequence_animation.dart
├── utils.dart
└── widgets
│ ├── Snappable.dart
│ ├── add_user_page.dart
│ ├── backdrop.dart
│ ├── bouncable.dart
│ ├── breathing_widget.dart
│ ├── choose_user_menu.dart
│ ├── dash_board_body.dart
│ ├── day_language_xps.dart
│ ├── day_of_year_xps.dart
│ ├── dots_indicator.dart
│ ├── expandable_user.dart
│ ├── expandable_user_list.dart
│ ├── explosion.dart
│ ├── fluid_slider.dart
│ ├── glass_crack
│ └── glass_crack.dart
│ ├── language_levels.dart
│ ├── level_percent_indicator.dart
│ ├── level_progress_circle.dart
│ ├── linear_percent_indicator.dart
│ ├── no_user.dart
│ ├── profile_page.dart
│ ├── pulse_notification.dart
│ ├── random_loading_animation.dart
│ ├── recent_period_selector.dart
│ ├── reload_data.dart
│ ├── settings.dart
│ ├── shimmer.dart
│ ├── spotlight.dart
│ ├── subheader.dart
│ ├── tab_navigator.dart
│ ├── tiltable_stack.dart
│ ├── total_xp_header.dart
│ └── wave_progress.dart
├── midi
└── zelda.sf2
├── pubspec.yaml
└── screenshots
├── adduser.png
├── demo.webp
├── languages.png
├── profile.png
├── recent.png
├── settings.png
└── year.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.lock
4 | *.log
5 | *.pyc
6 | *.swp
7 | .DS_Store
8 | .atom/
9 | .buildlog/
10 | .history
11 | .svn/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # Visual Studio Code related
20 | .vscode/
21 |
22 | # Flutter/Dart/Pub related
23 | **/doc/api/
24 | .dart_tool/
25 | .flutter-plugins
26 | .packages
27 | .pub-cache/
28 | .pub/
29 | build/
30 |
31 | # Android related
32 | **/android/**/gradle-wrapper.jar
33 | **/android/.gradle
34 | **/android/captures/
35 | **/android/gradlew
36 | **/android/gradlew.bat
37 | **/android/local.properties
38 | **/android/**/GeneratedPluginRegistrant.java
39 |
40 | # iOS/XCode related
41 | **/ios/**/*.mode1v3
42 | **/ios/**/*.mode2v3
43 | **/ios/**/*.moved-aside
44 | **/ios/**/*.pbxuser
45 | **/ios/**/*.perspectivev3
46 | **/ios/**/*sync/
47 | **/ios/**/.sconsign.dblite
48 | **/ios/**/.tags*
49 | **/ios/**/.vagrant/
50 | **/ios/**/DerivedData/
51 | **/ios/**/Icon?
52 | **/ios/**/Pods/
53 | **/ios/**/.symlinks/
54 | **/ios/**/profile
55 | **/ios/**/xcuserdata
56 | **/ios/.generated/
57 | **/ios/Flutter/App.framework
58 | **/ios/Flutter/Flutter.framework
59 | **/ios/Flutter/Generated.xcconfig
60 | **/ios/Flutter/app.flx
61 | **/ios/Flutter/app.zip
62 | **/ios/Flutter/flutter_assets/
63 | **/ios/ServiceDefinitions.json
64 | **/ios/Runner/GeneratedPluginRegistrant.*
65 |
66 | # Exceptions to above rules.
67 | !**/ios/**/default.mode1v3
68 | !**/ios/**/default.mode2v3
69 | !**/ios/**/default.pbxuser
70 | !**/ios/**/default.perspectivev3
71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
72 |
--------------------------------------------------------------------------------
/.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: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
8 | channel: beta
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Jonathan Böcker
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 | # Code::Stats Viewer
2 |
3 |
4 |
5 |
6 | ## Stargazers over time
7 |
8 | [](https://starchart.cc/Schwusch/codestats_flutter)
9 |
10 |  |  | 
11 | | ------------------------- | ------------------------- | -----------
12 |  |  | 
13 |
14 |
15 | Old demo:
16 |
17 | 
18 |
19 | ## Running the app
20 |
21 | To run this project:
22 | - Follow the [Flutter installation instructions](https://flutter.io/setup/)
23 | - Clone this project and run `flutter doctor` in the project root directory
24 | - Run `flutter run`
25 |
26 | ## The code
27 |
28 | Application-specific code is in [/lib](/lib).
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | analyzer:
2 | strong-mode:
3 | implicit-casts: true
4 | errors:
5 | todo: ignore
6 | missing_return: error
7 |
8 | linter:
9 | rules:
10 | - avoid_empty_else
11 | - cancel_subscriptions
12 | - close_sinks
13 | - unnecessary_const
14 | - unnecessary_new
--------------------------------------------------------------------------------
/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 28
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "se.bocker.codestatsflutter"
42 | minSdkVersion 17
43 | targetSdkVersion 28
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | }
47 |
48 | buildTypes {
49 | release {
50 | // TODO: Add your own signing config for the release build.
51 | // Signing with the debug keys for now, so `flutter run --release` works.
52 | signingConfig signingConfigs.debug
53 | }
54 | }
55 | }
56 |
57 | flutter {
58 | source '../..'
59 | }
60 |
61 | dependencies {
62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
63 | }
64 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
11 |
15 |
22 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/se/bocker/codestatsflutter/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package se.bocker.codestatsflutter
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 |
6 | import io.flutter.app.FlutterActivity
7 | import io.flutter.plugin.common.MethodChannel
8 | import io.flutter.plugins.GeneratedPluginRegistrant
9 |
10 | class MainActivity : FlutterActivity() {
11 | private var intentData: String? = null
12 |
13 | override fun onCreate(savedInstanceState: Bundle?) {
14 | super.onCreate(savedInstanceState)
15 | GeneratedPluginRegistrant.registerWith(this)
16 |
17 | if (Intent.ACTION_VIEW == intent.action) {
18 | intentData = intent.data?.lastPathSegment
19 | }
20 |
21 | MethodChannel(flutterView, "app.channel.shared.data").setMethodCallHandler { methodCall, result ->
22 | when (methodCall.method) {
23 | "getIntentLastPathSegment" -> {
24 | result.success(intentData)
25 | intentData = null
26 | }
27 | }
28 | }
29 | }
30 |
31 | override fun onNewIntent(intent: Intent?) {
32 | super.onNewIntent(intent)
33 | if (Intent.ACTION_VIEW == intent?.action) {
34 | intentData = intent.data?.lastPathSegment
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.41'
3 | repositories {
4 | google()
5 | jcenter()
6 | maven { url "https://jitpack.io" }
7 | }
8 |
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.5.0'
11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | rootProject.buildDir = '../build'
23 | subprojects {
24 | project.buildDir = "${rootProject.buildDir}/${project.name}"
25 | }
26 | subprojects {
27 | project.evaluationDependsOn(':app')
28 | }
29 |
30 | task clean(type: Delete) {
31 | delete rootProject.buildDir
32 | }
33 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
3 | org.gradle.jvmargs=-Xmx1536M
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 02 18:53:44 CEST 2019
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-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/assets/icon/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/assets/icon/ic_launcher.png
--------------------------------------------------------------------------------
/assets/web_hi_res_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/assets/web_hi_res_512.png
--------------------------------------------------------------------------------
/fonts/OCRAEXT.TTF:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/fonts/OCRAEXT.TTF
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.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/Flutter/flutter_export_environment.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # This is a generated file; do not edit or check into version control.
3 | export "FLUTTER_ROOT=/home/schwusch/flutter"
4 | export "FLUTTER_APPLICATION_PATH=/home/schwusch/codestats_flutter"
5 | export "FLUTTER_TARGET=lib/main.dart"
6 | export "FLUTTER_BUILD_DIR=build"
7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios"
8 | export "FLUTTER_FRAMEWORK_DIR=/home/schwusch/flutter/bin/cache/artifacts/engine/ios"
9 | export "FLUTTER_BUILD_NAME=1.0.21"
10 | export "FLUTTER_BUILD_NUMBER=22"
11 |
--------------------------------------------------------------------------------
/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 parse_KV_file(file, separator='=')
14 | file_abs_path = File.expand_path(file)
15 | if !File.exists? file_abs_path
16 | return [];
17 | end
18 | pods_ary = []
19 | skip_line_start_symbols = ["#", "/"]
20 | File.foreach(file_abs_path) { |line|
21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
22 | plugin = line.split(pattern=separator)
23 | if plugin.length == 2
24 | podname = plugin[0].strip()
25 | path = plugin[1].strip()
26 | podpath = File.expand_path("#{path}", file_abs_path)
27 | pods_ary.push({:name => podname, :path => podpath});
28 | else
29 | puts "Invalid plugin specification: #{line}"
30 | end
31 | }
32 | return pods_ary
33 | end
34 |
35 | target 'Runner' do
36 | use_frameworks!
37 |
38 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
39 | # referring to absolute paths on developers' machines.
40 | system('rm -rf .symlinks')
41 | system('mkdir -p .symlinks/plugins')
42 |
43 | # Flutter Pods
44 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
45 | if generated_xcode_build_settings.empty?
46 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
47 | end
48 | generated_xcode_build_settings.map { |p|
49 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
50 | symlink = File.join('.symlinks', 'flutter')
51 | File.symlink(File.dirname(p[:path]), symlink)
52 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
53 | end
54 | }
55 |
56 | # Plugin Pods
57 | plugin_pods = parse_KV_file('../.flutter-plugins')
58 | plugin_pods.map { |p|
59 | symlink = File.join('.symlinks', 'plugins', p[:name])
60 | File.symlink(p[:path], symlink)
61 | pod p[:name], :path => File.join(symlink, 'ios')
62 | }
63 | end
64 |
65 | post_install do |installer|
66 | installer.pods_project.targets.each do |target|
67 | target.build_configurations.each do |config|
68 | config.build_settings['ENABLE_BITCODE'] = 'NO'
69 | end
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/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 | BuildSystemType
6 | Original
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/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: [UIApplicationLaunchOptionsKey: 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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/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/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Schwusch/codestats_flutter/11b1fdd0ca5ac7a7660ef6b4ea059946ef7b8b13/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | codestats_flutter
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/lib/bloc/bloc_provider.dart:
--------------------------------------------------------------------------------
1 |
2 | // Generic Interface for all BLoCs
3 | import 'package:flutter/material.dart';
4 |
5 | abstract class BlocBase {
6 | void dispose();
7 | }
8 |
9 | // Generic BLoC provider
10 | class BlocProvider extends StatefulWidget {
11 | BlocProvider({
12 | Key key,
13 | @required this.child,
14 | @required this.bloc,
15 | }): super(key: key);
16 |
17 | final T bloc;
18 | final Widget child;
19 |
20 | @override
21 | _BlocProviderState createState() => _BlocProviderState();
22 |
23 | static T of(BuildContext context){
24 | final type = _typeOf>();
25 | BlocProvider provider = context.ancestorWidgetOfExactType(type);
26 | return provider.bloc;
27 | }
28 |
29 | static Type _typeOf() => T;
30 | }
31 |
32 | class _BlocProviderState extends State> {
33 | @override
34 | void dispose() {
35 | widget.bloc.dispose();
36 | super.dispose();
37 | }
38 |
39 | @override
40 | Widget build(BuildContext context) {
41 | return widget.child;
42 | }
43 | }
--------------------------------------------------------------------------------
/lib/bloc/state.dart:
--------------------------------------------------------------------------------
1 | import 'package:codestats_flutter/models/user/user.dart';
2 | import 'package:json_annotation/json_annotation.dart';
3 |
4 | part 'state.g.dart';
5 |
6 | @JsonSerializable(nullable: true, useWrappers: true)
7 | class UserState {
8 | Map allUsers;
9 |
10 | UserState({
11 | this.allUsers
12 | });
13 |
14 | factory UserState.empty() => UserState(allUsers: {});
15 |
16 | factory UserState.fromJson(Map json) => _$UserStateFromJson(json);
17 | Map toJson() => _$UserStateToJson(this);
18 | }
--------------------------------------------------------------------------------
/lib/bloc/state.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'state.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | UserState _$UserStateFromJson(Map json) {
10 | return UserState(
11 | allUsers: (json['allUsers'] as Map)?.map((k, e) =>
12 | MapEntry(
13 | k, e == null ? null : User.fromJson(e as Map))) ?? {});
14 | }
15 |
16 | Map _$UserStateToJson(UserState instance) =>
17 | _$UserStateJsonMapWrapper(instance);
18 |
19 | class _$UserStateJsonMapWrapper extends $JsonMapWrapper {
20 | final UserState _v;
21 | _$UserStateJsonMapWrapper(this._v);
22 |
23 | @override
24 | Iterable get keys => const ['allUsers'];
25 |
26 | @override
27 | dynamic operator [](Object key) {
28 | if (key is String) {
29 | switch (key) {
30 | case 'allUsers':
31 | return _v.allUsers;
32 | }
33 | }
34 | return null;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/hydrated.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io' show File;
3 | import 'package:flutter/foundation.dart';
4 | import 'package:rxdart/rxdart.dart';
5 | import 'package:path_provider/path_provider.dart';
6 |
7 | /// A [BehaviorSubject] that automatically persists its values and hydrates on creation.
8 | ///
9 | /// HydratedSubject supports serialized classes and [shared_preferences] types such as: `int`, `double`, `bool`, `String`, and `List`
10 | ///
11 | /// Serialized classes are supported by using the `hydrate: (String)=>Class` and `persist: (Class)=>String` constructor arguments.
12 | ///
13 | /// Example:
14 | ///
15 | /// ```
16 | /// final count$ = HydratedSubject("count", seedValue: 0);
17 | /// ```
18 | ///
19 | /// Serialized class example:
20 | ///
21 | /// ```
22 | /// final user$ = HydratedSubject(
23 | /// "user",
24 | /// hydrate: (String s) => User.fromJSON(s),
25 | /// persist: (User user) => user.toJSON(),
26 | /// seedValue: User.empty(),
27 | /// );
28 | /// ```
29 | ///
30 | /// Hydration is performed automatically and is asynchronous. The `onHydrate` callback is called when hydration is complete.
31 | ///
32 | /// ```
33 | /// final user$ = HydratedSubject(
34 | /// "count",
35 | /// onHydrate: () => loading$.add(false),
36 | /// );
37 | /// ```
38 |
39 | class HydratedSubject extends Subject implements ValueObservable {
40 | String _key;
41 | T _seedValue;
42 | _Wrapper _wrapper;
43 |
44 | T Function(String value) _hydrate;
45 | String Function(T value) _persist;
46 | void Function() onHydrate;
47 | bool isHydrated = false;
48 |
49 | Future get _localPath async {
50 | final directory = await getApplicationDocumentsDirectory();
51 |
52 | return directory.path;
53 | }
54 |
55 | Future get _localFile async {
56 | final path = await _localPath;
57 | return File('$path/$_key.txt');
58 | }
59 |
60 |
61 |
62 | HydratedSubject._(
63 | this._key,
64 | this._seedValue,
65 | this._hydrate,
66 | this._persist,
67 | this.onHydrate,
68 | StreamController controller,
69 | Observable observable,
70 | this._wrapper,
71 | ) : super(controller, observable) {
72 | hydrateSubject();
73 | }
74 |
75 | factory HydratedSubject(
76 | String key, {
77 | T seedValue,
78 | T Function(String value) hydrate,
79 | String Function(T value) persist,
80 | void onHydrate(),
81 | void onListen(),
82 | void onCancel(),
83 | bool sync: false,
84 | }) {
85 | // assert that T is a type compatible with shared_preferences,
86 | // or that we have hydrate and persist mapping functions
87 | assert(T == int ||
88 | T == double ||
89 | T == bool ||
90 | T == String ||
91 | [""] is T ||
92 | (hydrate != null && persist != null));
93 |
94 | // ignore: close_sinks
95 | final controller = StreamController.broadcast(
96 | onListen: onListen,
97 | onCancel: onCancel,
98 | sync: sync,
99 | );
100 |
101 | final wrapper = _Wrapper(seedValue);
102 |
103 | return HydratedSubject._(
104 | key,
105 | seedValue,
106 | hydrate,
107 | persist,
108 | onHydrate,
109 | controller,
110 | Observable.defer(
111 | () => wrapper.latestValue == null
112 | ? controller.stream
113 | : Observable(controller.stream)
114 | .startWith(wrapper.latestValue),
115 | reusable: true),
116 | wrapper);
117 | }
118 |
119 | @override
120 | void onAdd(T event) {
121 | _wrapper.latestValue = event;
122 | _persistValue(event);
123 | }
124 |
125 | @override
126 | ValueObservable get stream => this;
127 |
128 | /// Get the latest value emitted by the Subject
129 | @override
130 | T get value => _wrapper.latestValue;
131 |
132 | /// Set and emit the new value
133 | set value(T newValue) => add(newValue);
134 |
135 | /// Hydrates the HydratedSubject with a value stored on the user's device.
136 | ///
137 | /// Must be called to retreive values stored on the device.
138 | Future hydrateSubject() async {
139 | final file = await _localFile;
140 |
141 | var val;
142 |
143 | if (T == int)
144 | val = int.parse( await file.readAsString());
145 | else if (T == double)
146 | val = double.parse(await file.readAsString());
147 | else if (T == bool)
148 | val = (await file.readAsString()) == 'true';
149 | else if (T == String)
150 | val = await file.readAsString();
151 | else if (this._hydrate != null)
152 | val = await compute(this._hydrate, await file.readAsString());
153 | else
154 | Exception(
155 | "HydratedSubject – shared_preferences returned an invalid type",
156 | );
157 |
158 | // do not hydrate if the store is empty or matches the seed value
159 | // TODO: allow writing of seedValue if it is intentional
160 | if (val != null && val != _seedValue) {
161 | add(val);
162 | }
163 | isHydrated = true;
164 | if (onHydrate != null) {
165 | this.onHydrate();
166 | }
167 | }
168 |
169 | _persistValue(T val) async {
170 | final file = await _localFile;
171 |
172 | if (val is int || val is double || val is bool || val is String)
173 | await file.writeAsString('$val');
174 | else if (this._persist != null)
175 | await file.writeAsString(await compute(this._persist, val));
176 | else
177 | Exception(
178 | "HydratedSubject – value must be int, double, bool, String, or List",
179 | );
180 | }
181 |
182 | /// A unique key that references a storage container for a value persisted on the device.
183 | String get key => this._key;
184 | }
185 |
186 | class _Wrapper {
187 | T latestValue;
188 |
189 | _Wrapper(this.latestValue);
190 | }
191 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:codestats_flutter/bloc/bloc_provider.dart';
2 | import 'package:codestats_flutter/bloc/codestats_bloc.dart';
3 | import 'package:codestats_flutter/widgets/add_user_page.dart';
4 | import 'package:codestats_flutter/widgets/tab_navigator.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter/services.dart';
7 | import 'package:flutter_midi/flutter_midi.dart';
8 |
9 | void main() {
10 | runApp(CodeStatsApp());
11 | SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
12 | }
13 |
14 | class CodeStatsApp extends StatefulWidget {
15 | static const platform = MethodChannel('app.channel.shared.data');
16 |
17 | @override
18 | CodeStatsAppState createState() => CodeStatsAppState();
19 | }
20 |
21 | class CodeStatsAppState extends State
22 | with WidgetsBindingObserver {
23 | final UserBloc _bloc = UserBloc();
24 |
25 | getIntentLastPathSegment({bool fetchAll = false}) async {
26 | String user;
27 | try {
28 | user =
29 | await CodeStatsApp.platform.invokeMethod("getIntentLastPathSegment");
30 |
31 | } catch (e) {}
32 | print("getIntentLastPathSegment: $user");
33 |
34 | var addUser = () {
35 | _bloc.addUser(user);
36 | };
37 |
38 | if (user != null && user != "users") {
39 | if (_bloc.currentUserController.isHydrated) {
40 | addUser();
41 | } else {
42 | _bloc.currentUserController.onHydrate = addUser;
43 | }
44 | } else if(fetchAll) {
45 | if (_bloc.currentUserController.isHydrated) {
46 | _bloc.fetchAllUsers();
47 | } else {
48 | _bloc.currentUserController.onHydrate = () {
49 | _bloc.fetchAllUsers();
50 | };
51 | }
52 | }
53 | }
54 |
55 | void loadMidi(String asset) async {
56 | print("Loading File...");
57 | FlutterMidi.unmute();
58 | ByteData _byte = await rootBundle.load(asset);
59 | FlutterMidi.prepare(sf2: _byte, name: asset.replaceAll("midi/", ""));
60 | }
61 |
62 | @override
63 | void initState() {
64 | super.initState();
65 | loadMidi("midi/zelda.sf2");
66 | getIntentLastPathSegment(fetchAll: true);
67 | WidgetsBinding.instance.addObserver(this);
68 | }
69 |
70 | @override
71 | void dispose() {
72 | WidgetsBinding.instance.removeObserver(this);
73 | super.dispose();
74 | }
75 |
76 | @override
77 | void didChangeAppLifecycleState(AppLifecycleState state) {
78 | super.didChangeAppLifecycleState(state);
79 | if (state == AppLifecycleState.resumed) {
80 | getIntentLastPathSegment(fetchAll: true);
81 | }
82 | }
83 |
84 | @override
85 | Widget build(BuildContext context) {
86 | return BlocProvider(
87 | bloc: _bloc,
88 | child: MaterialApp(
89 | debugShowCheckedModeBanner: false,
90 | title: 'Code::Stats',
91 | theme: ThemeData(
92 | textTheme: Typography(platform: TargetPlatform.android).white.apply(
93 | bodyColor: Colors.blueGrey[600],
94 | displayColor: Colors.blueGrey[600]),
95 | primarySwatch: Colors.blueGrey,
96 | ),
97 | initialRoute: "home",
98 | routes: {
99 | "home": (_) => TabNavigator(bloc: _bloc,),
100 | "addUser": (_) => AddUserPage(),
101 | },
102 | ),
103 | );
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/lib/models/pulse/pulse.dart:
--------------------------------------------------------------------------------
1 | import 'package:codestats_flutter/models/pulse/xp.dart';
2 | import 'package:json_annotation/json_annotation.dart';
3 |
4 | part 'pulse.g.dart';
5 |
6 | @JsonSerializable(nullable: true, useWrappers: true)
7 | class Pulse {
8 | final String machine;
9 | final String sent_at;
10 | final String sent_at_local;
11 | final List xps;
12 |
13 | Pulse(this.machine, this.sent_at, this.sent_at_local, this.xps);
14 |
15 | factory Pulse.fromJson(Map json) => _$PulseFromJson(json);
16 |
17 | Map toJson() => _$PulseToJson(this);
18 | }
19 |
--------------------------------------------------------------------------------
/lib/models/pulse/pulse.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'pulse.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | Pulse _$PulseFromJson(Map json) {
10 | return Pulse(
11 | json['machine'] as String,
12 | json['sent_at'] as String,
13 | json['sent_at_local'] as String,
14 | (json['xps'] as List)
15 | ?.map((e) =>
16 | e == null ? null : PulseXp.fromJson(e as Map))
17 | ?.toList());
18 | }
19 |
20 | Map _$PulseToJson(Pulse instance) =>
21 | _$PulseJsonMapWrapper(instance);
22 |
23 | class _$PulseJsonMapWrapper extends $JsonMapWrapper {
24 | final Pulse _v;
25 | _$PulseJsonMapWrapper(this._v);
26 |
27 | @override
28 | Iterable get keys =>
29 | const ['machine', 'sent_at', 'sent_at_local', 'xps'];
30 |
31 | @override
32 | dynamic operator [](Object key) {
33 | if (key is String) {
34 | switch (key) {
35 | case 'machine':
36 | return _v.machine;
37 | case 'sent_at':
38 | return _v.sent_at;
39 | case 'sent_at_local':
40 | return _v.sent_at_local;
41 | case 'xps':
42 | return _v.xps;
43 | }
44 | }
45 | return null;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/models/pulse/xp.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'xp.g.dart';
4 |
5 | @JsonSerializable(nullable: true, useWrappers: true)
6 | class PulseXp {
7 | final int amount;
8 | final String language;
9 |
10 | PulseXp(this.amount, this.language);
11 |
12 | factory PulseXp.fromJson(Map json) => _$PulseXpFromJson(json);
13 |
14 | Map toJson() => _$PulseXpToJson(this);
15 |
16 | @override
17 | String toString() => "${language}: ${amount} XP";
18 | }
19 |
--------------------------------------------------------------------------------
/lib/models/pulse/xp.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'xp.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | PulseXp _$PulseXpFromJson(Map json) {
10 | return PulseXp(json['amount'] as int, json['language'] as String);
11 | }
12 |
13 | Map _$PulseXpToJson(PulseXp instance) =>
14 | _$PulseXpJsonMapWrapper(instance);
15 |
16 | class _$PulseXpJsonMapWrapper extends $JsonMapWrapper {
17 | final PulseXp _v;
18 | _$PulseXpJsonMapWrapper(this._v);
19 |
20 | @override
21 | Iterable get keys => const ['amount', 'language'];
22 |
23 | @override
24 | dynamic operator [](Object key) {
25 | if (key is String) {
26 | switch (key) {
27 | case 'amount':
28 | return _v.amount;
29 | case 'language':
30 | return _v.language;
31 | }
32 | }
33 | return null;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/models/user/day_language_xps.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'day_language_xps.g.dart';
4 |
5 | @JsonSerializable(nullable: true, useWrappers: true)
6 | class DayLanguageXps {
7 | final int xp;
8 | final String language;
9 | final String date;
10 |
11 | DayLanguageXps(this.xp, this.language, this.date);
12 |
13 | factory DayLanguageXps.fromJson(Map json) => _$DayLanguageXpsFromJson(json);
14 |
15 | Map toJson() => _$DayLanguageXpsToJson(this);
16 | }
--------------------------------------------------------------------------------
/lib/models/user/day_language_xps.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'day_language_xps.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | DayLanguageXps _$DayLanguageXpsFromJson(Map json) {
10 | return DayLanguageXps(
11 | json['xp'] as int, json['language'] as String, json['date'] as String);
12 | }
13 |
14 | Map _$DayLanguageXpsToJson(DayLanguageXps instance) =>
15 | _$DayLanguageXpsJsonMapWrapper(instance);
16 |
17 | class _$DayLanguageXpsJsonMapWrapper extends $JsonMapWrapper {
18 | final DayLanguageXps _v;
19 | _$DayLanguageXpsJsonMapWrapper(this._v);
20 |
21 | @override
22 | Iterable get keys => const ['xp', 'language', 'date'];
23 |
24 | @override
25 | dynamic operator [](Object key) {
26 | if (key is String) {
27 | switch (key) {
28 | case 'xp':
29 | return _v.xp;
30 | case 'language':
31 | return _v.language;
32 | case 'date':
33 | return _v.date;
34 | }
35 | }
36 | return null;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/models/user/user.dart:
--------------------------------------------------------------------------------
1 | import 'package:codestats_flutter/models/user/day_language_xps.dart';
2 | import 'package:codestats_flutter/models/user/xp.dart';
3 | import 'package:json_annotation/json_annotation.dart';
4 |
5 | part 'user.g.dart';
6 |
7 | @JsonSerializable(nullable: true, useWrappers: true)
8 | class User {
9 | final List totalMachines;
10 | final List totalLangs;
11 | final List recentMachines;
12 | final List recentLangs;
13 | final Map hourOfDayXps;
14 | final Map dayOfYearXps;
15 | final List dayLanguageXps;
16 | final String registered;
17 | int totalXp;
18 |
19 | User(
20 | this.totalMachines,
21 | this.totalLangs,
22 | this.recentMachines,
23 | this.recentLangs,
24 | this.hourOfDayXps,
25 | this.dayOfYearXps,
26 | this.dayLanguageXps,
27 | this.totalXp,
28 | this.registered,
29 | );
30 |
31 | factory User.fromJson(Map json) => _$UserFromJson(json);
32 |
33 | Map toJson() => _$UserToJson(this);
34 | }
35 |
--------------------------------------------------------------------------------
/lib/models/user/user.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'user.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | User _$UserFromJson(Map json) {
10 | return User(
11 | (json['totalMachines'] as List)
12 | ?.map(
13 | (e) => e == null ? null : Xp.fromJson(e as Map))
14 | ?.toList(),
15 | (json['totalLangs'] as List)
16 | ?.map(
17 | (e) => e == null ? null : Xp.fromJson(e as Map))
18 | ?.toList(),
19 | (json['recentMachines'] as List)
20 | ?.map(
21 | (e) => e == null ? null : Xp.fromJson(e as Map))
22 | ?.toList(),
23 | (json['recentLangs'] as List)
24 | ?.map(
25 | (e) => e == null ? null : Xp.fromJson(e as Map))
26 | ?.toList(),
27 | (json['hourOfDayXps'] as Map)
28 | ?.map((k, e) => MapEntry(k, e as int)),
29 | (json['dayOfYearXps'] as Map)
30 | ?.map((k, e) => MapEntry(k, e as int)),
31 | (json['dayLanguageXps'] as List)
32 | ?.map((e) => e == null
33 | ? null
34 | : DayLanguageXps.fromJson(e as Map))
35 | ?.toList(),
36 | json['totalXp'] as int,
37 | json['registered'] as String);
38 | }
39 |
40 | Map _$UserToJson(User instance) =>
41 | _$UserJsonMapWrapper(instance);
42 |
43 | class _$UserJsonMapWrapper extends $JsonMapWrapper {
44 | final User _v;
45 | _$UserJsonMapWrapper(this._v);
46 |
47 | @override
48 | Iterable get keys => const [
49 | 'totalMachines',
50 | 'totalLangs',
51 | 'recentMachines',
52 | 'recentLangs',
53 | 'hourOfDayXps',
54 | 'dayOfYearXps',
55 | 'dayLanguageXps',
56 | 'registered',
57 | 'totalXp'
58 | ];
59 |
60 | @override
61 | dynamic operator [](Object key) {
62 | if (key is String) {
63 | switch (key) {
64 | case 'totalMachines':
65 | return _v.totalMachines;
66 | case 'totalLangs':
67 | return _v.totalLangs;
68 | case 'recentMachines':
69 | return _v.recentMachines;
70 | case 'recentLangs':
71 | return _v.recentLangs;
72 | case 'hourOfDayXps':
73 | return _v.hourOfDayXps;
74 | case 'dayOfYearXps':
75 | return _v.dayOfYearXps;
76 | case 'dayLanguageXps':
77 | return _v.dayLanguageXps;
78 | case 'registered':
79 | return _v.registered;
80 | case 'totalXp':
81 | return _v.totalXp;
82 | }
83 | }
84 | return null;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/lib/models/user/xp.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'xp.g.dart';
4 |
5 | @JsonSerializable(nullable: true, useWrappers: true)
6 | class Xp {
7 | int xp;
8 | final String name;
9 |
10 | Xp(this.xp, this.name);
11 |
12 | factory Xp.fromJson(Map json) => _$XpFromJson(json);
13 |
14 | Map toJson() => _$XpToJson(this);
15 | }
16 |
--------------------------------------------------------------------------------
/lib/models/user/xp.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'xp.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | Xp _$XpFromJson(Map json) {
10 | return Xp(json['xp'] as int, json['name'] as String);
11 | }
12 |
13 | Map _$XpToJson(Xp instance) => _$XpJsonMapWrapper(instance);
14 |
15 | class _$XpJsonMapWrapper extends $JsonMapWrapper {
16 | final Xp _v;
17 | _$XpJsonMapWrapper(this._v);
18 |
19 | @override
20 | Iterable get keys => const ['xp', 'name'];
21 |
22 | @override
23 | dynamic operator [](Object key) {
24 | if (key is String) {
25 | switch (key) {
26 | case 'xp':
27 | return _v.xp;
28 | case 'name':
29 | return _v.name;
30 | }
31 | }
32 | return null;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/passthrough_simulation.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/physics.dart';
2 |
3 | class PassThroughSimulation extends Simulation {
4 |
5 | final double reverse;
6 |
7 | PassThroughSimulation({this.reverse = 0});
8 |
9 | @override
10 | double dx(double time) => (reverse - time).abs();
11 |
12 | @override
13 | bool isDone(double time) {
14 | if (reverse == 0) {
15 | return false;
16 | }
17 | return reverse - time < 0;
18 | }
19 |
20 | @override
21 | double x(double time) => (reverse - time).abs();
22 | }
--------------------------------------------------------------------------------
/lib/queries.dart:
--------------------------------------------------------------------------------
1 | import 'package:date_format/date_format.dart';
2 |
3 | String formatDateUtc(DateTime date) {
4 | return formatDate(
5 | date, [yyyy, "-", mm, "-", dd, "T", HH, ":", mm, ":", ss, ".", SSS, z]);
6 | }
7 |
8 | String profiles(List users, DateTime since, int recentDays) {
9 | var buffer = StringBuffer();
10 | buffer.write("{\n");
11 |
12 | users.forEach((user) => buffer.write("""
13 | $user: profile(username: "$user") {
14 | ...ProfileInfo
15 | }
16 | """));
17 |
18 | buffer.write("""}
19 | fragment ProfileInfo on Profile {
20 | totalXp: totalXp
21 | totalLangs: languages {
22 | name
23 | xp
24 | }
25 | recentLangs: languages(since: "${formatDateUtc(since.subtract(Duration(hours: 12)).toUtc())}") {
26 | name
27 | xp
28 | }
29 | totalMachines: machines {
30 | name
31 | xp
32 | }
33 | recentMachines: machines(since: "${formatDateUtc(since.subtract(Duration(hours: 12)).toUtc())}") {
34 | name
35 | xp
36 | }
37 | dayLanguageXps: dayLanguageXps(since: "${formatDate(since.subtract(Duration(days: recentDays)), [
38 | yyyy,
39 | '-',
40 | mm,
41 | '-',
42 | dd
43 | ])}") {
44 | date
45 | language
46 | xp
47 | }
48 | dayOfYearXps: dayOfYearXps
49 | hourOfDayXps: hourOfDayXps
50 | registered: registered
51 | }
52 | """);
53 |
54 | return buffer.toString();
55 | }
56 |
--------------------------------------------------------------------------------
/lib/schema.graphql:
--------------------------------------------------------------------------------
1 | schema {
2 | query: RootQueryType
3 | }
4 |
5 | """ISO 8601 date"""
6 | scalar Date
7 |
8 | """RFC3339 time with timezone"""
9 | scalar Datetime
10 |
11 | """User profile public data"""
12 | type Profile {
13 | """User's dates when they have been active and their XP"""
14 | dates(since: Date): [ProfileDate]
15 |
16 | """
17 | User's dates since given date with their summed XP per language per day
18 | """
19 | dayLanguageXps(since: Date!): [ProfileDaylanguage]
20 |
21 | """User's XP by day of week"""
22 | dayOfWeekXps(since: Date): [ProfileDowXp]
23 |
24 | """User's XP by day of year"""
25 | dayOfYearXps: ProfileDayOfYearXp
26 |
27 | """User's XP by hour of day (in 24 hour format)"""
28 | hourOfDayXps: ProfileHourOfDayXp
29 |
30 | """User's languages and their XP"""
31 | languages(since: Datetime): [ProfileLanguage]
32 |
33 | """User's machines and their XP"""
34 | machines(since: Datetime): [ProfileMachine]
35 |
36 | """Timestamp when user registered into service"""
37 | registered: Datetime
38 |
39 | """Total amount of XP of user"""
40 | totalXp: Int
41 | }
42 |
43 | """Date when user was active and its total XP for a profile"""
44 | type ProfileDate {
45 | date: String
46 | xp: Int
47 | }
48 |
49 | """Date when profile has an amount of XP of the language"""
50 | type ProfileDaylanguage {
51 | date: Date
52 | language: String
53 | xp: Int
54 | }
55 |
56 | """
57 | Map where key is number of day of year (including leap day, like in the year 2000) and value is amount of XP
58 | """
59 | scalar ProfileDayOfYearXp
60 |
61 | """Day of week and its combined XP"""
62 | type ProfileDowXp {
63 | day: Int
64 | xp: Int
65 | }
66 |
67 | """
68 | Map where key is hour of day (in 24 hour format) and value is amount of XP
69 | """
70 | scalar ProfileHourOfDayXp
71 |
72 | """Language and its total XP for a profile"""
73 | type ProfileLanguage {
74 | name: String
75 | xp: Int
76 | }
77 |
78 | """Machine and its total XP for a profile"""
79 | type ProfileMachine {
80 | name: String
81 | xp: Int
82 | }
83 |
84 | type RootQueryType {
85 | """Get profile by username"""
86 | profile(
87 | """Username of profile"""
88 | username: String
89 | ): Profile
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/lib/sequence_animation.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:meta/meta.dart';
3 |
4 | class _AnimationInformation {
5 | _AnimationInformation({
6 | this.animatable,
7 | this.from,
8 | this.to,
9 | this.curve,
10 | this.tag,
11 | });
12 |
13 | final Animatable animatable;
14 | final Duration from;
15 | final Duration to;
16 | final Curve curve;
17 | final Object tag;
18 | }
19 |
20 | class SequenceAnimationBuilder {
21 | List<_AnimationInformation> _animations = [];
22 |
23 | /// Adds an [Animatable] to the sequence, in the most cases this would be a [Tween].
24 | /// The from and to [Duration] specify points in time where the animation takes place.
25 | /// You can also specify a [Curve] for the [Animatable].
26 | ///
27 | /// [Animatable]s which animate on the same tag are not allowed to overlap and they also need to be add in the same order they are played.
28 | /// These restrictions only apply to [Animatable]s operating on the same tag.
29 | ///
30 | ///
31 | /// ## Sample code
32 | ///
33 | /// ```dart
34 | /// SequenceAnimation sequenceAnimation = new SequenceAnimationBuilder()
35 | /// .addAnimatable(
36 | /// animatable: new ColorTween(begin: Colors.red, end: Colors.yellow),
37 | /// from: const Duration(seconds: 0),
38 | /// to: const Duration(seconds: 2),
39 | /// tag: "color",
40 | /// )
41 | /// .animate(controller);
42 | /// ```
43 | ///
44 | SequenceAnimationBuilder addAnimatable({
45 | @required Animatable animatable,
46 | @required Duration from,
47 | @required Duration to,
48 | Curve curve: Curves.linear,
49 | @required Object tag,
50 | }) {
51 | assert(to >= from);
52 | _animations.add(_AnimationInformation(
53 | animatable: animatable, from: from, to: to, curve: curve, tag: tag));
54 | return this;
55 | }
56 |
57 | /// The controllers duration is going to be overwritten by this class, you should not specify it on your own
58 | SequenceAnimation animate(AnimationController controller) {
59 | int longestTimeMicro = 0;
60 | _animations.forEach((info) {
61 | int micro = info.to.inMicroseconds;
62 | if (micro > longestTimeMicro) {
63 | longestTimeMicro = micro;
64 | }
65 | });
66 | // Sets the duration of the controller
67 | controller.duration = Duration(microseconds: longestTimeMicro);
68 |
69 | Map