├── ios
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── 100.png
│ │ │ ├── 1024.png
│ │ │ ├── 114.png
│ │ │ ├── 120.png
│ │ │ ├── 144.png
│ │ │ ├── 152.png
│ │ │ ├── 167.png
│ │ │ ├── 180.png
│ │ │ ├── 20.png
│ │ │ ├── 29.png
│ │ │ ├── 40.png
│ │ │ ├── 50.png
│ │ │ ├── 57.png
│ │ │ ├── 58.png
│ │ │ ├── 60.png
│ │ │ ├── 72.png
│ │ │ ├── 76.png
│ │ │ ├── 80.png
│ │ │ ├── 87.png
│ │ │ └── Contents.json
│ │ └── LaunchImage.imageset
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ └── project.pbxproj
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── RunnerTests
│ └── RunnerTests.swift
├── Podfile.lock
├── .gitignore
└── Podfile
├── store
├── icon.png
├── phone_1.png
├── phone_2.png
├── feature_graphic.png
└── README.md
├── lib
├── clocks
│ ├── generative_clock
│ │ ├── ATTRIBUTION.md
│ │ ├── globals.dart
│ │ ├── custom_randomizer.dart
│ │ ├── LICENSE
│ │ ├── digit_checker.dart
│ │ ├── node.dart
│ │ └── generative_clock.dart
│ ├── particle_clock
│ │ ├── ATTRIBUTION.md
│ │ ├── palette.dart
│ │ ├── clock_bg_particle_painter.dart
│ │ ├── LICENSE
│ │ ├── bg_fx.dart
│ │ ├── clock_face_painter.dart
│ │ ├── particle.dart
│ │ ├── clock_seconds_painter.dart
│ │ ├── particle_clock.dart
│ │ ├── clock_fx_painter.dart
│ │ ├── utils
│ │ │ └── rnd.dart
│ │ ├── clock_fx.dart
│ │ ├── scene.dart
│ │ └── particle_clock_fx.dart
│ └── clock_face.dart
├── main.dart
└── update_button.dart
├── android
├── gradle.properties
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── 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
│ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── dev
│ │ │ │ │ └── shorebird
│ │ │ │ │ └── u_shorebird_clock
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
└── build.gradle
├── shorebird.yaml
├── pubspec.yaml
├── .gitignore
├── LICENSE
├── .github
└── workflows
│ ├── push_to_deploy_android.yaml
│ └── patch.yaml
├── .metadata
├── analysis_options.yaml
├── README.md
├── assets
└── palettes.json
└── pubspec.lock
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/store/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/store/icon.png
--------------------------------------------------------------------------------
/store/phone_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/store/phone_1.png
--------------------------------------------------------------------------------
/store/phone_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/store/phone_2.png
--------------------------------------------------------------------------------
/lib/clocks/generative_clock/ATTRIBUTION.md:
--------------------------------------------------------------------------------
1 | This was adapted from https://github.com/Fabian-Stein/flutterClock.
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/ATTRIBUTION.md:
--------------------------------------------------------------------------------
1 | This was adapted from https://github.com/miickel/flutter_particle_clock.
--------------------------------------------------------------------------------
/store/feature_graphic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/store/feature_graphic.png
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/store/README.md:
--------------------------------------------------------------------------------
1 | feature_graphic is based on:
2 | https://unsplash.com/photos/KMn4VEeEPR8
3 |
4 | icon is based on:
5 | https://unsplash.com/photos/Xd_H7iOwKN0
6 |
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shorebirdtech/time_shift/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/dev/shorebird/u_shorebird_clock/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package dev.shorebird.u_shorebird_clock
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
6 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.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/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/shorebird.yaml:
--------------------------------------------------------------------------------
1 | # This file is used to configure the Shorebird updater used by your application.
2 | # Learn more at https://shorebird.dev
3 | # This file should be checked into version control.
4 |
5 | # This is the unique identifier assigned to your app.
6 | # It is used by your app to request the correct patches from Shorebird servers.
7 | app_id: b3be02c7-97a8-4135-be71-814cc43d096e
8 |
--------------------------------------------------------------------------------
/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.
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/palette.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | /// Holds a list of colors.
4 | class Palette {
5 | /// The palette's color members. All unique.
6 | List? components;
7 |
8 | Palette({this.components});
9 |
10 | /// Creates a new palette from JSON.
11 | factory Palette.fromJson(List json) {
12 | var components = json.map((c) => Color(int.tryParse(c)!)).toList();
13 | return Palette(components: components);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
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 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: time_shift
2 | description: Demo app showing Shorebird updates.
3 | publish_to: "none"
4 |
5 | version: 1.0.5+1
6 |
7 | environment:
8 | sdk: ">=2.19.4 <3.0.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 | restart_app: ^1.2.1
14 | shorebird_code_push: ^1.1.3
15 |
16 | dev_dependencies:
17 | flutter_test:
18 | sdk: flutter
19 |
20 | flutter_lints: ^2.0.0
21 |
22 | flutter:
23 | uses-material-design: true
24 | assets:
25 | - assets/palettes.json
26 | - shorebird.yaml
27 |
--------------------------------------------------------------------------------
/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/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - restart_app (0.0.1):
4 | - Flutter
5 |
6 | DEPENDENCIES:
7 | - Flutter (from `Flutter`)
8 | - restart_app (from `.symlinks/plugins/restart_app/ios`)
9 |
10 | EXTERNAL SOURCES:
11 | Flutter:
12 | :path: Flutter
13 | restart_app:
14 | :path: ".symlinks/plugins/restart_app/ios"
15 |
16 | SPEC CHECKSUMS:
17 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
18 | restart_app: 806659942bf932f6ce51c5372f91ce5e81c8c14a
19 |
20 | PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189
21 |
22 | COCOAPODS: 1.11.0
23 |
--------------------------------------------------------------------------------
/lib/clocks/generative_clock/globals.dart:
--------------------------------------------------------------------------------
1 | import 'custom_randomizer.dart';
2 | import 'digit_checker.dart';
3 |
4 | double aspectRatio = 5 / 3;
5 |
6 | CustomRandomizer randomizer = CustomRandomizer();
7 | DigitChecker digitChecker = DigitChecker();
8 |
9 | double maxDistanceBetweenNodes = 0.05;
10 | // the number of columns and rows, with which the screen is partitioned
11 | // we do this as an optimization, the reason is explained in the paint method in digital clock
12 | int columns = (aspectRatio / maxDistanceBetweenNodes).ceil();
13 | int rows = (1.0 / maxDistanceBetweenNodes).ceil();
14 |
15 | int quadrants() {
16 | return rows * columns;
17 | }
18 |
--------------------------------------------------------------------------------
/lib/clocks/clock_face.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:time_shift/clocks/generative_clock/generative_clock.dart';
3 | import 'package:time_shift/clocks/particle_clock/particle_clock.dart';
4 |
5 | /// All available clock faces.
6 | enum ClockFace {
7 | generative,
8 | particle,
9 | }
10 |
11 | extension ClockFaceWidget on ClockFace {
12 | /// Builds the clock face widget associated with this [ClockFace].
13 | Widget get widget {
14 | switch (this) {
15 | case ClockFace.generative:
16 | return const GenerativeClock();
17 | case ClockFace.particle:
18 | return const ParticleClock();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.2.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
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 | tasks.register("clean", Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/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 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/clocks/generative_clock/custom_randomizer.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | /// a custom randomizer, which offers convenience functions
4 | class CustomRandomizer {
5 | Random rng = Random();
6 |
7 | /// returns a random floating point value between [low] and [high].
8 | double doubleInRange(double low, high) {
9 | assert(high > low);
10 | return rng.nextDouble() * (high - low) + low;
11 | }
12 |
13 | /// returns a random floating point value between 0.0 and [high].
14 | double doubleUpTo(double high) {
15 | return rng.nextDouble() * high;
16 | }
17 |
18 | /// returns a random [Point] with a x value between [xLow] and [xHigh]
19 | /// and a y value between [yLow] and [yHigh].
20 | Point point(double xLow, xHigh, yLow, yHigh) {
21 | return Point(
22 | doubleInRange(xLow, xHigh),
23 | doubleInRange(yLow, yHigh),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 |
46 | # Shorebird related
47 | .shorebird/
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/clock_bg_particle_painter.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'clock_fx.dart';
4 |
5 | class ClockBgParticlePainter extends CustomPainter {
6 | ClockFx? fx;
7 |
8 | // ChangeNotifier used as repaint notifier.
9 | ClockBgParticlePainter({required this.fx}) : super(repaint: fx);
10 |
11 | @override
12 | void paint(Canvas canvas, Size size) {
13 | for (var p in fx!.particles) {
14 | var pos = Offset(p!.x, p.y);
15 |
16 | var paint = Paint()
17 | ..color = p.color.withAlpha((255 * p.a).floor())
18 | ..strokeWidth = p.size * .2
19 | ..style = p.isFilled ? PaintingStyle.fill : PaintingStyle.stroke;
20 |
21 | if (p.isFilled) {
22 | var rect = Rect.fromCenter(
23 | center: pos,
24 | width: p.size,
25 | height: p.size,
26 | );
27 |
28 | canvas.drawRect(rect, paint);
29 | } else {
30 | canvas.drawCircle(pos, p.size / 1.2, paint);
31 | }
32 | }
33 | }
34 |
35 | @override
36 | bool shouldRepaint(oldDelegate) => false;
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Mickel Andersson mickel@ultimatemachine.se (ultimatemachine.se & tayl.app)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/.github/workflows/push_to_deploy_android.yaml:
--------------------------------------------------------------------------------
1 | name: Shorebird Push to Deploy Android
2 |
3 | on:
4 | push:
5 | branches:
6 | - releases/*
7 |
8 | env:
9 | SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}
10 |
11 | jobs:
12 | push_to_deploy_android:
13 | defaults:
14 | run:
15 | shell: bash
16 |
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - name: 📚 Git Checkout
21 | uses: actions/checkout@v3
22 |
23 | - name: 🔐 Setup Keystore
24 | run: |
25 | echo "${{ secrets.KEYSTORE }}" | base64 --decode > android/keystore.jks
26 | echo "${{ secrets.KEY_PROPERTIES }}" > android/key.properties
27 |
28 | - name: 🐦 Setup Shorebird
29 | uses: shorebirdtech/setup-shorebird@v1
30 |
31 | - name: 🚀 Shorebird Release
32 | if: ${{ github.event.created }}
33 | uses: shorebirdtech/shorebird-release@v0
34 | with:
35 | platform: android
36 |
37 | - name: 🚀 Shorebird Patch
38 | if: ${{ !github.event.created }}
39 | uses: shorebirdtech/shorebird-patch@v0
40 | with:
41 | platform: android
42 |
43 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Mickel Andersson ([ultimatemachine.se](https://ultimatemachine.se) & [tayl.app](https://tayl.app))
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
8 | channel: stable
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
17 | base_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
18 | - platform: android
19 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
20 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
21 | - platform: ios
22 | create_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
23 | base_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
24 |
25 | # User provided section
26 |
27 | # List of Local paths (relative to this file) that should be
28 | # ignored by the migrate tool.
29 | #
30 | # Files that are not part of the templates will be ignored by default.
31 | unmanaged_files:
32 | - 'lib/main.dart'
33 | - 'ios/Runner.xcodeproj/project.pbxproj'
34 |
--------------------------------------------------------------------------------
/.github/workflows/patch.yaml:
--------------------------------------------------------------------------------
1 | name: Shorebird Patch
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | clock_face:
7 | description: "Clock face to use"
8 | required: true
9 | type: choice
10 | options:
11 | - particle
12 | - generative
13 | default: "particle"
14 |
15 | jobs:
16 | create_patch:
17 | defaults:
18 | run:
19 | shell: bash
20 |
21 | runs-on: macos-latest
22 |
23 | steps:
24 | - name: 📚 Git Checkout
25 | uses: actions/checkout@v3
26 |
27 | - name: ☕️ Setup Java
28 | uses: actions/setup-java@v3
29 | with:
30 | distribution: "temurin"
31 | java-version: "11"
32 |
33 | - name: 🐦 Setup Shorebird
34 | uses: shorebirdtech/setup-shorebird@v0
35 |
36 | - name: 🔐 Setup Keystore
37 | run: |
38 | echo "${{ secrets.KEYSTORE }}" | base64 --decode > android/keystore.jks
39 | echo "${{ secrets.KEY_PROPERTIES }}" > android/key.properties
40 |
41 | - name: ✨ Create Patch
42 | run: shorebird patch android --force -- --dart-define clock_face=${{ inputs.clock_face }}
43 | env:
44 | SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}
45 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 |
4 | import 'clocks/clock_face.dart';
5 |
6 | /// Use this argument to choose a clock face. Defaults to particle if value is
7 | /// provided or if the value provided is unrecognized.
8 | ///
9 | /// Example:
10 | /// `shorebird run -- --dart-define=clock_face=generative`
11 | const clockFaceArgName = 'clock_face';
12 |
13 | /// Use this argument to show the performance overlay.
14 | ///
15 | /// Example:
16 | /// `shorebird run -- --dart-define=show_perf_overlay=true`
17 | const showPerfOverlayArgName = 'show_perf_overlay';
18 |
19 | void main() {
20 | WidgetsFlutterBinding.ensureInitialized();
21 | SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
22 |
23 | const clockName = String.fromEnvironment(clockFaceArgName);
24 | final clock = ClockFace.values.firstWhere(
25 | (clock) => clock.name == clockName,
26 | orElse: () => ClockFace.generative,
27 | );
28 |
29 | runApp(
30 | MaterialApp(
31 | showPerformanceOverlay:
32 | const bool.fromEnvironment(showPerfOverlayArgName),
33 | debugShowCheckedModeBanner: false,
34 | home: Scaffold(
35 | body: clock.widget,
36 | ),
37 | ),
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/bg_fx.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/widgets.dart';
4 |
5 | import 'clock_fx.dart';
6 | import 'particle.dart';
7 | import 'utils/rnd.dart';
8 |
9 | const easingDelayDuration = Duration(seconds: 15);
10 |
11 | class BgFx extends ClockFx {
12 | BgFx({required Size size, required DateTime? time})
13 | : super(
14 | size: size,
15 | time: time,
16 | numParticles: 120,
17 | );
18 |
19 | @override
20 | void tick(Duration duration) {
21 | var secFrac = 1 - (DateTime.now().millisecond / 1000);
22 |
23 | var vecSpeed = duration.compareTo(easingDelayDuration) > 0
24 | ? max(.2, Curves.easeInOutSine.transform(secFrac))
25 | : 1;
26 |
27 | for (var p in particles) {
28 | p!.y -= p.vy * vecSpeed;
29 |
30 | if (p.y > height || p.y < 0 || p.life == 0) {
31 | _activateParticle(p);
32 | }
33 | }
34 |
35 | super.tick(duration);
36 | }
37 |
38 | void _activateParticle(Particle p) {
39 | var xRnd = Rnd.getDouble(0, width / 5);
40 | p.x = Rnd.getBool() ? width - xRnd : 0 + xRnd;
41 | p.y = Rnd.getDouble(0, height);
42 | p.a = Rnd.ratio > .95 ? Rnd.getDouble(.6, .8) : Rnd.getDouble(.08, .4);
43 | p.isFilled = Rnd.getBool();
44 | p.size = Rnd.getDouble(height / 20, height / 5);
45 |
46 | p.life = 1;
47 |
48 | p.vx = 0;
49 | p.vy = Rnd.getDouble(-3, 3);
50 |
51 | double v = Rnd.getDouble(.1, .5);
52 |
53 | p.vx = 0;
54 | p.vy *= v;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '11.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | target 'RunnerTests' do
36 | inherit! :search_paths
37 | end
38 | end
39 |
40 | post_install do |installer|
41 | installer.pods_project.targets.each do |target|
42 | flutter_additional_ios_build_settings(target)
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/lib/clocks/generative_clock/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 The Chromium Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without modification,
4 | are permitted provided that the following conditions are met:
5 |
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above
9 | copyright notice, this list of conditions and the following
10 | disclaimer in the documentation and/or other materials provided
11 | with the distribution.
12 | * Neither the name of Google Inc. nor the names of its
13 | contributors may be used to endorse or promote products derived
14 | from this software without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/clock_face_painter.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 | import 'package:flutter/material.dart';
3 |
4 | /// The number of sections to divide the clock face.
5 | const int arms = 60;
6 |
7 | /// Which parts of the sections to indicate quarters.
8 | final int quarters = (arms / 12).floor();
9 |
10 | class ClockFacePainter extends CustomPainter {
11 | final Color? accentColor;
12 |
13 | ClockFacePainter({required this.accentColor});
14 |
15 | @override
16 | void paint(Canvas canvas, Size size) {
17 | var cx = size.width / 2;
18 | var cy = size.height / 2;
19 | var radius = min(size.width / 2, size.height / 2);
20 | var circleRaidus = radius * .012;
21 |
22 | var paint = Paint()
23 | ..style = PaintingStyle.fill
24 | ..strokeWidth = circleRaidus * .5
25 | ..color = accentColor!;
26 |
27 | for (var i = 1; i <= arms; i++) {
28 | var angle = i * 360 / arms;
29 | var a = -angle * pi / 180;
30 | var point = Offset(cx + radius * cos(a), cy + radius * sin(a));
31 |
32 | if (i % quarters == 0) {
33 | canvas.drawCircle(
34 | point,
35 | circleRaidus * 2,
36 | paint
37 | ..color = accentColor!
38 | ..style = PaintingStyle.fill,
39 | );
40 | } else {
41 | canvas.drawCircle(
42 | point,
43 | circleRaidus,
44 | paint
45 | ..color = accentColor!.withAlpha(100)
46 | ..style = PaintingStyle.stroke,
47 | );
48 | }
49 | }
50 | }
51 |
52 | @override
53 | bool shouldRepaint(oldDelegate) => false;
54 | }
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Time Shift
2 |
3 | This repository contains a sample Flutter application using Shorebird's CodePush solution.
4 |
5 | This example is built using the [Flutter Particle Clock](https://github.com/miickel/flutter_particle_clock).
6 |
7 | App icons generated from icon.png using [App Icon Generator](https://www.appicon.co/).
8 |
9 | ## Goal
10 |
11 | The purpose of this app is to be a test-balloon for store approvals. Currently it's only
12 | been submitted to Google Play App Store, but we will eventually submit it to other stores.
13 |
14 | * [Google Play App Store](https://play.google.com/store/apps/details?id=dev.shorebird.u_shorebird_clock)
15 |
16 | ## Design
17 |
18 | Purpose is to have something worth code-pushing to. In this case, we figured it might
19 | be fun to have an app to which we could code-push a different clockface every hour.
20 |
21 | Flutter ran a clock contest a few years back and there are numerous gorgeous examples
22 | of clocks built with Flutter:
23 | https://docs.flutter.dev/clock
24 |
25 | The current build contains two clocks. It is currently hard coded to show the Particle
26 | Clock, but a straightforward expansion would be to control which clock face is shown
27 | with a compile-time flag and then write a script which builds N-different versions of
28 | the app for the N clock faces and pushes a new one to Shorebird every hour.
29 |
30 | Another feature I'd like to add is more prevalent display of the author's name
31 | and link. e.g. if you tap the clock it could say "@miickel" in the corner
32 | and when you click on it it could open the original source in a browser window.
33 |
34 | Patches welcome.
35 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/particle.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class Particle {
4 | /// The particle's X-position.
5 | double x;
6 |
7 | /// The particle's Y-position.
8 | double y;
9 |
10 | /// The particle's emit angle in radians.
11 | double a;
12 |
13 | /// Speed vector (horizontally).
14 | double vx;
15 |
16 | /// Speed vector (vertically).
17 | double vy;
18 |
19 | /// The distance from canvas center.
20 | double dist;
21 |
22 | /// The distance from canvas center in percentage (0-1).
23 | double distFrac;
24 |
25 | /// The particle's size.
26 | double size;
27 |
28 | /// The life length of the particle (0-1).
29 | double life;
30 |
31 | /// The life remaining of the particle (0-1).
32 | double lifeLeft;
33 |
34 | /// If the particle is filled or only stroked.
35 | bool isFilled;
36 |
37 | /// If the particle should have "speed marks".
38 | bool? isFlowing;
39 |
40 | /// The color of the particle.
41 | Color color;
42 |
43 | /// The particle's distribution value.
44 | int distribution;
45 |
46 | /// The particle's type.
47 | ParticleType type;
48 |
49 | Particle({
50 | this.x = 0,
51 | this.y = 0,
52 | this.a = 0,
53 | this.vx = 0,
54 | this.vy = 0,
55 | this.dist = 0,
56 | this.distFrac = 0,
57 | this.size = 0,
58 | this.life = 0,
59 | this.lifeLeft = 0,
60 | this.isFilled = false,
61 | this.isFlowing = false,
62 | this.color = Colors.black,
63 | this.distribution = 0,
64 | this.type = ParticleType.noise,
65 | });
66 | }
67 |
68 | enum ParticleType {
69 | hour,
70 | minute,
71 | noise,
72 | }
73 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleDisplayName
10 | Shorebird Clock
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | time_shift
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | $(FLUTTER_BUILD_NAME)
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | $(FLUTTER_BUILD_NUMBER)
27 | LSRequiresIPhoneOS
28 |
29 | UIApplicationSupportsIndirectInputEvents
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UIViewControllerBasedStatusBarAppearance
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/clock_seconds_painter.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class ClockSecondsPainter extends CustomPainter {
6 | final Color? accentColor;
7 |
8 | ClockSecondsPainter({required this.accentColor});
9 |
10 | @override
11 | void paint(Canvas canvas, Size size) {
12 | var center = Offset(size.width / 2, size.height / 2);
13 |
14 | var now = DateTime.now();
15 | var secSize = pi / 50;
16 |
17 | // Animate smoothly with easing.
18 | var msecs = Curves.easeOutCirc.transform(now.millisecond / 1000);
19 | msecs = msecs * 2 * pi / 60;
20 |
21 | var timeAngle = (now.second - 1) * (2 * pi / 60) + msecs - secSize / 2;
22 | timeAngle = timeAngle - pi / 2;
23 |
24 | var radiusInner = size.height * .13;
25 | var radiusOuter = size.height * .9;
26 |
27 | var paint = Paint()
28 | ..style = PaintingStyle.stroke
29 | ..strokeWidth = center.dy * .003
30 | ..strokeCap = StrokeCap.round
31 | ..color = accentColor!.withAlpha(40);
32 |
33 | var outerRect = Rect.fromCenter(
34 | center: center,
35 | width: radiusOuter,
36 | height: radiusOuter,
37 | );
38 |
39 | var innerRect = Rect.fromCenter(
40 | center: center,
41 | width: radiusInner,
42 | height: radiusInner,
43 | );
44 |
45 | // Outer track.
46 | canvas.drawArc(outerRect, 0, pi * 2, false, paint);
47 |
48 | // Inner track.
49 | canvas.drawArc(innerRect, 0, pi * 2, false, paint);
50 |
51 | paint
52 | ..strokeWidth = center.dy * .015
53 | ..color = accentColor!;
54 |
55 | // Outer indicator.
56 | canvas.drawArc(outerRect, timeAngle, secSize, false, paint);
57 |
58 | // Inner indicator.
59 | canvas.drawArc(
60 | innerRect, timeAngle - (secSize * 6), secSize * 12, false, paint);
61 | }
62 |
63 | @override
64 | bool shouldRepaint(oldDelegate) => false;
65 | }
66 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
16 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/lib/clocks/generative_clock/digit_checker.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | /// The [DigitChecker] stores the information of the 10 digits and can determine if a point intersects with a given digit
4 | class DigitChecker {
5 | // ten digits with 15 "pixels" each
6 | // we "paint" the digits by defining the 1 as filled and 0 as unfilled
7 | List digits = [
8 | 1, 1, 1, // 0
9 | 1, 0, 1,
10 | 1, 0, 1,
11 | 1, 0, 1,
12 | 1, 1, 1,
13 |
14 | 0, 1, 1, // 1
15 | 0, 0, 1,
16 | 0, 0, 1,
17 | 0, 0, 1,
18 | 0, 0, 1,
19 |
20 | 1, 1, 1, // 2
21 | 0, 0, 1,
22 | 1, 1, 1,
23 | 1, 0, 0,
24 | 1, 1, 1,
25 |
26 | 1, 1, 1, // 3
27 | 0, 0, 1,
28 | 0, 1, 1,
29 | 0, 0, 1,
30 | 1, 1, 1,
31 |
32 | 1, 0, 1, // 4
33 | 1, 0, 1,
34 | 1, 1, 1,
35 | 0, 0, 1,
36 | 0, 0, 1,
37 |
38 | 1, 1, 1, // 5
39 | 1, 0, 0,
40 | 1, 1, 1,
41 | 0, 0, 1,
42 | 1, 1, 1,
43 |
44 | 1, 1, 1, // 6
45 | 1, 0, 0,
46 | 1, 1, 1,
47 | 1, 0, 1,
48 | 1, 1, 1,
49 |
50 | 1, 1, 1, // 7
51 | 0, 0, 1,
52 | 0, 0, 1,
53 | 0, 0, 1,
54 | 0, 0, 1,
55 |
56 | 1, 1, 1, // 8
57 | 1, 0, 1,
58 | 1, 1, 1,
59 | 1, 0, 1,
60 | 1, 1, 1,
61 |
62 | 1, 1, 1, // 9
63 | 1, 0, 1,
64 | 1, 1, 1,
65 | 0, 0, 1,
66 | 1, 1, 1
67 | ];
68 |
69 | /// is the [point] inside the [digit] stretched over the [rect]
70 | bool isIn(int digit, Point point, Rectangle rect) {
71 | if (rect.containsPoint(point)) {
72 | double x = point.x - rect.topLeft.x;
73 | double y = point.y - rect.topLeft.y;
74 |
75 | int column = (3 * x / rect.width).floor();
76 | int row = (5 * y / rect.height).floor();
77 |
78 | int offset = digit * 15;
79 |
80 | int index = offset + column + row * 3;
81 |
82 | if (digits[index] == 1) {
83 | return true;
84 | }
85 | }
86 | return false;
87 | }
88 |
89 | /// is the [point] outside the [digit] stretched over the [rect]
90 | bool isOut(int digit, Point point, Rectangle rect) {
91 | return !isIn(digit, point, rect);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/particle_clock.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:convert';
3 |
4 | import 'package:flutter/material.dart';
5 |
6 | import 'palette.dart';
7 | import 'scene.dart';
8 |
9 | class ParticleClock extends StatefulWidget {
10 | const ParticleClock({super.key});
11 |
12 | @override
13 | State createState() => _ParticleClockState();
14 | }
15 |
16 | class _ParticleClockState extends State
17 | with SingleTickerProviderStateMixin {
18 | DateTime _dateTime = DateTime.now();
19 | Timer? _timer;
20 | double seek = 0.0;
21 | double seekIncrement = 1 / 3600;
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 | _updateTime();
27 | _updateModel();
28 | }
29 |
30 | @override
31 | void dispose() {
32 | _timer?.cancel();
33 | super.dispose();
34 | }
35 |
36 | Future> _loadPalettes() async {
37 | String data =
38 | await DefaultAssetBundle.of(context).loadString("assets/palettes.json");
39 | var palettes = json.decode(data) as List;
40 | return palettes.map((p) => Palette.fromJson(p)).toList();
41 | }
42 |
43 | void _updateModel() {
44 | // Cause the clock to rebuild when the model changes.
45 | setState(() {});
46 | }
47 |
48 | void _updateTime() {
49 | setState(() {
50 | _dateTime = DateTime.now();
51 | _timer = Timer(
52 | const Duration(seconds: 1) -
53 | Duration(milliseconds: _dateTime.millisecond),
54 | _updateTime,
55 | );
56 | });
57 | }
58 |
59 | @override
60 | Widget build(BuildContext context) {
61 | return Directionality(
62 | textDirection: TextDirection.ltr,
63 | child: FutureBuilder(
64 | future: _loadPalettes(),
65 | builder: (context, snapshot) {
66 | if (!snapshot.hasData) {
67 | return const SizedBox(
68 | child: Center(
69 | child: Text("Could not load palettes."),
70 | ),
71 | );
72 | }
73 |
74 | List? palettes = snapshot.data;
75 |
76 | return LayoutBuilder(
77 | builder: (context, constraints) {
78 | return Scene(
79 | size: constraints.biggest,
80 | palettes: palettes,
81 | time: _dateTime,
82 | brightness: Theme.of(context).brightness,
83 | );
84 | },
85 | );
86 | },
87 | ),
88 | );
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/clock_fx_painter.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | import 'clock_fx.dart';
6 | import 'particle.dart';
7 |
8 | /// Where handles should start, denoted in percentage of full radius.
9 | const double handlesStart = .1;
10 |
11 | /// Where noise should start, denoted in percentage of full radius.
12 | const double noiseStart = .15;
13 |
14 | /// Alpha value for noise particles.
15 | const double noiseAlpha = 160;
16 |
17 | class ClockFxPainter extends CustomPainter {
18 | ClockFx? fx;
19 |
20 | // ChangeNotifier used as repaint notifier.
21 | ClockFxPainter({required this.fx}) : super(repaint: fx);
22 |
23 | @override
24 | void paint(Canvas canvas, Size size) {
25 | for (var p in fx!.particles) {
26 | double a;
27 |
28 | // Fade in particles by calculating alpha based on distance.
29 | if (p!.type == ParticleType.noise) {
30 | a = max(0.0, (p.distFrac - .13) / p.distFrac) * 255;
31 | a = min(a, min(noiseAlpha, p.lifeLeft * 3 * 255));
32 | } else {
33 | a = p.distFrac <= handlesStart
34 | ? 255 * Curves.easeIn.transform(p.distFrac / handlesStart)
35 | : min(255, p.lifeLeft * 10 * 255);
36 | }
37 |
38 | var alpha = a.floor();
39 |
40 | var circlePaint = Paint()
41 | ..style = PaintingStyle.stroke
42 | ..strokeWidth = p.size / 4
43 | ..color = p.color.withAlpha(alpha);
44 |
45 | var particlePosition = Offset(p.x, p.y);
46 |
47 | // Fill some particles.
48 | if (p.isFilled) {
49 | circlePaint.style = PaintingStyle.fill;
50 |
51 | // Draw gradient arc stroke on some filled particles.
52 | if (p.isFlowing!) {
53 | var threshold = p.distribution < 2 ? .2 : .4;
54 | var flowAlpha = max(0, (p.distFrac - threshold) / p.distFrac) * alpha;
55 | var shader = SweepGradient(
56 | colors: [
57 | p.color.withAlpha(0),
58 | p.color.withAlpha(flowAlpha.floor()),
59 | ],
60 | startAngle: pi,
61 | endAngle: 2 * pi,
62 | transform: GradientRotation(p.a),
63 | stops: const [.6, 1],
64 | ).createShader(Rect.fromCircle(
65 | center: fx!.center,
66 | radius: p.dist,
67 | ));
68 |
69 | var gradientPaint = Paint()
70 | ..strokeWidth = p.size / 2
71 | ..shader = shader
72 | ..style = PaintingStyle.stroke;
73 |
74 | canvas.drawCircle(fx!.center, p.dist, gradientPaint);
75 | }
76 | }
77 |
78 | canvas.drawCircle(particlePosition, p.size, circlePaint);
79 | }
80 | }
81 |
82 | @override
83 | bool shouldRepaint(oldDelegate) => true;
84 | }
85 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/utils/rnd.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/painting.dart';
4 |
5 | import '../palette.dart';
6 |
7 | class Rnd {
8 | static int _seed = DateTime.now().millisecondsSinceEpoch;
9 | static Random random = Random(_seed);
10 |
11 | static set seed(int val) => random = Random(_seed = val);
12 | static int get seed => _seed;
13 |
14 | /// Gets the next double.
15 | static get ratio => random.nextDouble();
16 |
17 | /// Gets a random int between [min] and [max].
18 | static int getInt(int min, int max) {
19 | return min + random.nextInt(max - min);
20 | }
21 |
22 | /// Gets a random double between [min] and [max].
23 | static double getDouble(double min, double max) {
24 | return min + random.nextDouble() * (max - min);
25 | }
26 |
27 | /// Gets a random boolean with chance [chance].
28 | static bool getBool([double chance = 0.5]) {
29 | return random.nextDouble() < chance;
30 | }
31 |
32 | /// Randomize the positions of items in a list.
33 | static List shuffle(List list) {
34 | for (int i = 0, l = list.length; i < l; i++) {
35 | int j = random.nextInt(l);
36 | if (j == i) {
37 | continue;
38 | }
39 | dynamic item = list[j];
40 | list[j] = list[i];
41 | list[i] = item;
42 | }
43 | return list;
44 | }
45 |
46 | /// Randomly selects an item from a list.
47 | static dynamic getItem(List list) {
48 | return list[random.nextInt(list.length)];
49 | }
50 |
51 | /// Gets a random palette from a list of palettes and sorts its' colors by luminance.
52 | ///
53 | /// Given if [dark] or not, this method makes sure the luminance of the background color is valid.
54 | static Palette getPalette(List? palettes, bool dark) {
55 | Palette? result;
56 |
57 | while (result == null) {
58 | Palette palette = Rnd.getItem(palettes!);
59 | List colors = Rnd.shuffle(palette.components!) as List;
60 |
61 | var luminance = colors[0].computeLuminance();
62 |
63 | if (dark ? luminance <= .1 : luminance >= .1) {
64 | var lumDiff = colors
65 | .sublist(1)
66 | .asMap()
67 | .map(
68 | (i, color) => MapEntry(
69 | i,
70 | [i, (luminance - color.computeLuminance()).abs()],
71 | ),
72 | )
73 | .values
74 | .toList();
75 |
76 | lumDiff.sort((List a, List b) {
77 | return a[1].compareTo(b[1]);
78 | });
79 |
80 | List sortedColors =
81 | lumDiff.map((d) => colors[d[0] + 1 as int]).toList();
82 |
83 | result = Palette(
84 | components: [colors[0]] + sortedColors,
85 | );
86 | }
87 | }
88 | return result;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/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 | def keystoreProperties = new Properties()
29 | def keystorePropertiesFile = rootProject.file('key.properties')
30 | if (keystorePropertiesFile.exists()) {
31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
32 | }
33 |
34 | android {
35 | compileSdkVersion flutter.compileSdkVersion
36 | ndkVersion flutter.ndkVersion
37 |
38 | compileOptions {
39 | sourceCompatibility JavaVersion.VERSION_1_8
40 | targetCompatibility JavaVersion.VERSION_1_8
41 | }
42 |
43 | kotlinOptions {
44 | jvmTarget = '1.8'
45 | }
46 |
47 | sourceSets {
48 | main.java.srcDirs += 'src/main/kotlin'
49 | }
50 |
51 | defaultConfig {
52 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
53 | applicationId "dev.shorebird.u_shorebird_clock"
54 | // You can update the following values to match your application needs.
55 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
56 | minSdkVersion flutter.minSdkVersion
57 | targetSdkVersion flutter.targetSdkVersion
58 | versionCode flutterVersionCode.toInteger()
59 | versionName flutterVersionName
60 | }
61 |
62 | signingConfigs {
63 | release {
64 | keyAlias keystoreProperties['keyAlias']
65 | keyPassword keystoreProperties['keyPassword']
66 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
67 | storePassword keystoreProperties['storePassword']
68 | }
69 | }
70 | buildTypes {
71 | release {
72 | signingConfig signingConfigs.release
73 | }
74 | }
75 | }
76 |
77 | flutter {
78 | source '../..'
79 | }
80 |
81 | dependencies {
82 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
83 | }
84 |
--------------------------------------------------------------------------------
/lib/update_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:restart_app/restart_app.dart';
3 | import 'package:shorebird_code_push/shorebird_code_push.dart';
4 |
5 | class UpdateButton extends StatefulWidget {
6 | const UpdateButton({super.key});
7 |
8 | @override
9 | State createState() => _UpdateButtonState();
10 | }
11 |
12 | enum UpdateStatus {
13 | idle,
14 | checking,
15 | finishingCheck,
16 | }
17 |
18 | class _UpdateButtonState extends State {
19 | UpdateStatus status = UpdateStatus.idle;
20 | bool _haveUpdate = false;
21 | final _shorebirdCodePush = ShorebirdCodePush();
22 |
23 | Future _doUpdateCheck() {
24 | // Ask the Shorebird servers if there is a new patch available.
25 | return _shorebirdCodePush.isNewPatchAvailableForDownload();
26 | }
27 |
28 | Future _checkForUpdate() async {
29 | setState(() {
30 | status = UpdateStatus.checking;
31 | });
32 | final isUpdateAvailable = await _doUpdateCheck();
33 |
34 | if (!mounted) return;
35 |
36 | if (isUpdateAvailable) {
37 | await _shorebirdCodePush.downloadUpdateIfAvailable();
38 | }
39 | setState(() {
40 | _haveUpdate = isUpdateAvailable;
41 | status = UpdateStatus.finishingCheck;
42 | });
43 | Future.delayed(const Duration(milliseconds: 500), () {
44 | setState(() {
45 | status = UpdateStatus.idle;
46 | });
47 | });
48 | }
49 |
50 | Widget widgetForStatus(UpdateStatus status) {
51 | switch (status) {
52 | case UpdateStatus.idle:
53 | if (_haveUpdate) {
54 | return const Icon(Icons.restart_alt);
55 | } else {
56 | // TODO(eseidel): Use a color so the rocket isn't blue, maybe a
57 | // partially tranparent gray instead? Maybe get a color from the
58 | // current clock face?
59 | return const Icon(Icons.rocket);
60 | }
61 | case UpdateStatus.checking:
62 | return const SizedBox(
63 | width: 25,
64 | height: 25,
65 | child: CircularProgressIndicator(
66 | strokeWidth: 2,
67 | ),
68 | );
69 | case UpdateStatus.finishingCheck:
70 | return const Icon(Icons.thumb_up_outlined);
71 | }
72 | }
73 |
74 | void Function()? actionForStatus(UpdateStatus status) {
75 | switch (status) {
76 | case UpdateStatus.idle:
77 | if (_haveUpdate) {
78 | return Restart.restartApp;
79 | } else {
80 | return _checkForUpdate;
81 | }
82 | case UpdateStatus.checking:
83 | return null;
84 | case UpdateStatus.finishingCheck:
85 | return null;
86 | }
87 | }
88 |
89 | @override
90 | Widget build(BuildContext context) {
91 | final widget = widgetForStatus(status);
92 | final action = actionForStatus(status);
93 |
94 | return TextButton(
95 | onPressed: action,
96 | child: widget,
97 | );
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/clock_fx.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/widgets.dart';
4 |
5 | import 'palette.dart';
6 | import 'particle.dart';
7 | import 'utils/rnd.dart';
8 |
9 | const Color transparent = Color.fromARGB(0, 0, 0, 0);
10 |
11 | abstract class ClockFx with ChangeNotifier {
12 | /// The available canvas width for the FX.
13 | late double width;
14 |
15 | /// The available canvas height for the FX.
16 | late double height;
17 |
18 | /// The minimum value of [width] and [height].
19 | late double sizeMin;
20 |
21 | /// The center of the canvas.
22 | late Offset center;
23 |
24 | /// The area from wich to spawn new particles.
25 | late Rect spawnArea;
26 |
27 | /// The colors used for painting.
28 | Palette? palette;
29 |
30 | /// All the FX's particles.
31 | late List particles;
32 |
33 | /// The maximum number of particles.
34 | int numParticles;
35 |
36 | /// Date and time used for rendering time-specific effects.
37 | DateTime? time;
38 |
39 | ClockFx({
40 | required Size size,
41 | this.time,
42 | this.numParticles = 5000,
43 | }) {
44 | particles = List.filled(numParticles, null);
45 | palette = Palette(components: [transparent, transparent]);
46 | setSize(size);
47 | }
48 |
49 | /// Initializes the particle effect by resetting all the particles and assigning a random color from the palette.
50 | void init() {
51 | if (palette == null) return;
52 | for (int i = 0; i < numParticles; i++) {
53 | var color = Rnd.getItem(palette!.components!);
54 | particles[i] = Particle(color: color);
55 | resetParticle(i);
56 | }
57 | }
58 |
59 | /// Sets the palette used for coloring.
60 | void setPalette(Palette palette) {
61 | this.palette = palette;
62 | var colors = palette.components!.sublist(1);
63 | var accentColor = colors[colors.length - 1];
64 | particles.where((p) => p != null).forEach((p) => p!.color =
65 | p.type == ParticleType.noise ? Rnd.getItem(colors) : accentColor);
66 | }
67 |
68 | /// Sets the time used for time-specific effects.
69 | void setTime(DateTime? time) {
70 | this.time = time;
71 | }
72 |
73 | /// Sets the canvas size and updates dependent values.
74 | void setSize(Size size) {
75 | width = size.width;
76 | height = size.height;
77 | sizeMin = min(width, height);
78 | center = Offset(width / 2, height / 2);
79 | spawnArea = Rect.fromLTRB(
80 | center.dx - sizeMin / 100,
81 | center.dy - sizeMin / 100,
82 | center.dx + sizeMin / 100,
83 | center.dy + sizeMin / 100,
84 | );
85 | init();
86 | }
87 |
88 | /// Resets a particle's values.
89 | Particle resetParticle(int i) {
90 | Particle p = particles[i]!;
91 | p.size = p.a = p.vx = p.vy = p.life = p.lifeLeft = 0;
92 | p.x = center.dx;
93 | p.y = center.dy;
94 | return p;
95 | }
96 |
97 | void tick(Duration duration) {
98 | notifyListeners();
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "40.png",
5 | "idiom" : "iphone",
6 | "scale" : "2x",
7 | "size" : "20x20"
8 | },
9 | {
10 | "filename" : "60.png",
11 | "idiom" : "iphone",
12 | "scale" : "3x",
13 | "size" : "20x20"
14 | },
15 | {
16 | "filename" : "29.png",
17 | "idiom" : "iphone",
18 | "scale" : "1x",
19 | "size" : "29x29"
20 | },
21 | {
22 | "filename" : "58.png",
23 | "idiom" : "iphone",
24 | "scale" : "2x",
25 | "size" : "29x29"
26 | },
27 | {
28 | "filename" : "87.png",
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "29x29"
32 | },
33 | {
34 | "filename" : "80.png",
35 | "idiom" : "iphone",
36 | "scale" : "2x",
37 | "size" : "40x40"
38 | },
39 | {
40 | "filename" : "120.png",
41 | "idiom" : "iphone",
42 | "scale" : "3x",
43 | "size" : "40x40"
44 | },
45 | {
46 | "filename" : "57.png",
47 | "idiom" : "iphone",
48 | "scale" : "1x",
49 | "size" : "57x57"
50 | },
51 | {
52 | "filename" : "114.png",
53 | "idiom" : "iphone",
54 | "scale" : "2x",
55 | "size" : "57x57"
56 | },
57 | {
58 | "filename" : "120.png",
59 | "idiom" : "iphone",
60 | "scale" : "2x",
61 | "size" : "60x60"
62 | },
63 | {
64 | "filename" : "180.png",
65 | "idiom" : "iphone",
66 | "scale" : "3x",
67 | "size" : "60x60"
68 | },
69 | {
70 | "filename" : "20.png",
71 | "idiom" : "ipad",
72 | "scale" : "1x",
73 | "size" : "20x20"
74 | },
75 | {
76 | "filename" : "40.png",
77 | "idiom" : "ipad",
78 | "scale" : "2x",
79 | "size" : "20x20"
80 | },
81 | {
82 | "filename" : "29.png",
83 | "idiom" : "ipad",
84 | "scale" : "1x",
85 | "size" : "29x29"
86 | },
87 | {
88 | "filename" : "58.png",
89 | "idiom" : "ipad",
90 | "scale" : "2x",
91 | "size" : "29x29"
92 | },
93 | {
94 | "filename" : "40.png",
95 | "idiom" : "ipad",
96 | "scale" : "1x",
97 | "size" : "40x40"
98 | },
99 | {
100 | "filename" : "80.png",
101 | "idiom" : "ipad",
102 | "scale" : "2x",
103 | "size" : "40x40"
104 | },
105 | {
106 | "filename" : "50.png",
107 | "idiom" : "ipad",
108 | "scale" : "1x",
109 | "size" : "50x50"
110 | },
111 | {
112 | "filename" : "100.png",
113 | "idiom" : "ipad",
114 | "scale" : "2x",
115 | "size" : "50x50"
116 | },
117 | {
118 | "filename" : "72.png",
119 | "idiom" : "ipad",
120 | "scale" : "1x",
121 | "size" : "72x72"
122 | },
123 | {
124 | "filename" : "144.png",
125 | "idiom" : "ipad",
126 | "scale" : "2x",
127 | "size" : "72x72"
128 | },
129 | {
130 | "filename" : "76.png",
131 | "idiom" : "ipad",
132 | "scale" : "1x",
133 | "size" : "76x76"
134 | },
135 | {
136 | "filename" : "152.png",
137 | "idiom" : "ipad",
138 | "scale" : "2x",
139 | "size" : "76x76"
140 | },
141 | {
142 | "filename" : "167.png",
143 | "idiom" : "ipad",
144 | "scale" : "2x",
145 | "size" : "83.5x83.5"
146 | },
147 | {
148 | "filename" : "1024.png",
149 | "idiom" : "ios-marketing",
150 | "scale" : "1x",
151 | "size" : "1024x1024"
152 | }
153 | ],
154 | "info" : {
155 | "author" : "xcode",
156 | "version" : 1
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/lib/clocks/generative_clock/node.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 | import 'globals.dart';
3 |
4 | /// [Node] represents one of the moving elements in the net
5 | /// [coordinate] of the node
6 | /// the coordinate system we use has its origin in the middle of the screen for convinience
7 | /// the maximal y range is -0.5 to 0.5
8 | /// the maximal x range is -0.5 * aspectRatio to 0.5 * aspectRatio
9 | /// [velocity]: how does the node move every timestep
10 | class Node {
11 | late Point coordinate;
12 | Point velocity = randomizer.point(-0.5, 0.5, -0.5, 0.5);
13 | int quadrant = 0;
14 | int index = 0;
15 | bool isStatic;
16 |
17 | /// returns a node with randomized values
18 | /// if [isStatic] is true, then the node does not move and its coordinate is more more likely to be around the (0,0)
19 | Node(this.isStatic) {
20 | if (!isStatic) {
21 | coordinate =
22 | randomizer.point(-aspectRatio / 2, aspectRatio / 2, -0.5, 0.5);
23 | // velocity = randomizer.doubleInRange(0.2, 1.0);
24 | velocity = randomizer.point(-0.5, 0.5, -0.5, 0.5);
25 | } else {
26 | Point randomPoint = randomizer.point(
27 | -aspectRatio / 2 + 1.5 / 12.0,
28 | aspectRatio / 2 - 1.5 / 12.0,
29 | -0.5 + 3.0 / 12.0,
30 | 0.5 - 3.0 / 12.0,
31 | );
32 | coordinate = randomPoint;
33 | //velocity = 0.0;
34 | velocity = const Point(0, 0);
35 | }
36 | }
37 |
38 | // update the position of the node and compute in which quadrant it is located
39 | void update() {
40 | if (!isStatic) {
41 | //update position
42 | coordinate += velocity * 0.004;
43 |
44 | double maxX = aspectRatio / 2, maxY = 0.5;
45 |
46 | // if the point has reached a wall, then we move it to the opposite wall
47 | // i think this is called a periodic boundary condition
48 | if (coordinate.x > maxX || coordinate.x < -maxX) {
49 | coordinate = Point(-coordinate.x, coordinate.y);
50 | }
51 | if (coordinate.y > maxY || coordinate.y < -maxY) {
52 | coordinate = Point(coordinate.x, -coordinate.y);
53 | }
54 |
55 | //compute quadrant of node
56 | int xQuadrant =
57 | (columns * (coordinate.x + aspectRatio / 2) / aspectRatio).floor();
58 | int yQuadrant = (rows * (coordinate.y + 0.5)).floor();
59 | quadrant = max(0, min(columns * yQuadrant + xQuadrant, quadrants() - 1));
60 | }
61 | }
62 |
63 | /// gives back the result of the right Outside/Inside check, according to the [checkInside] flag
64 | bool checkCoordinate(int hour, minute, bool checkInside) {
65 | if (checkInside) {
66 | return _isInsideOfNumbers(hour, minute);
67 | } else {
68 | return _isOutsideOfNumbers(hour, minute);
69 | }
70 | }
71 |
72 | /// check if the coordinate of the node is in one of the four digits, which represent [hour] and [minute]
73 | bool _isOutsideOfNumbers(int hour, minute) {
74 | int digit1 = hour ~/ 10;
75 | int digit2 = hour % 10;
76 |
77 | int digit3 = minute ~/ 10;
78 | int digit4 = minute % 10;
79 |
80 | double width = 3 * aspectRatio / 20;
81 | double height = 5 * width / 3;
82 | double distance = width / 3;
83 |
84 | Rectangle rect1 =
85 | Rectangle(-2 * (width + distance), -height / 2, width, height);
86 | Rectangle rect2 =
87 | Rectangle(-1 * (width + distance), -height / 2, width, height);
88 | Rectangle rect3 = Rectangle(distance, -height / 2, width, height);
89 | Rectangle rect4 =
90 | Rectangle(2 * distance + width, -height / 2, width, height);
91 |
92 | return digitChecker.isOut(digit1, coordinate, rect1) &&
93 | digitChecker.isOut(digit2, coordinate, rect2) &&
94 | digitChecker.isOut(digit3, coordinate, rect3) &&
95 | digitChecker.isOut(digit4, coordinate, rect4);
96 | }
97 |
98 | // the inverse of the function above
99 | bool _isInsideOfNumbers(int hour, minute) {
100 | return !_isOutsideOfNumbers(hour, minute);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/scene.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter/scheduler.dart';
5 |
6 | import 'bg_fx.dart';
7 | import 'clock_bg_particle_painter.dart';
8 | import 'clock_face_painter.dart';
9 | import 'clock_fx.dart';
10 | import 'clock_fx_painter.dart';
11 | import 'clock_seconds_painter.dart';
12 | import 'palette.dart';
13 | import 'particle_clock_fx.dart';
14 | import 'utils/rnd.dart';
15 |
16 | class Scene extends StatefulWidget {
17 | final Size? size;
18 | final List? palettes;
19 | final DateTime? time;
20 | final Brightness? brightness;
21 |
22 | const Scene({
23 | Key? key,
24 | this.size,
25 | this.palettes,
26 | this.time,
27 | this.brightness,
28 | }) : super(key: key);
29 |
30 | @override
31 | SceneState createState() => SceneState();
32 | }
33 |
34 | class SceneState extends State with SingleTickerProviderStateMixin {
35 | late Ticker _ticker;
36 | ClockFx? _fx;
37 | BgFx? _bgFx;
38 | late Palette _palette;
39 | Color? _bgColor;
40 | Color? _accentColor;
41 |
42 | @override
43 | void initState() {
44 | _ticker = createTicker(_tick)..start();
45 | _fx = ParticleClockFx(
46 | size: widget.size!,
47 | time: widget.time,
48 | );
49 | _bgFx = BgFx(
50 | size: widget.size!,
51 | time: widget.time,
52 | );
53 | _updatePalette();
54 | super.initState();
55 | }
56 |
57 | @override
58 | void dispose() {
59 | _ticker.dispose();
60 | super.dispose();
61 | }
62 |
63 | @override
64 | void didUpdateWidget(Scene oldWidget) {
65 | super.didUpdateWidget(oldWidget);
66 | if (oldWidget.time != widget.time ||
67 | oldWidget.brightness != widget.brightness) {
68 | _fx!.setTime(widget.time);
69 | _bgFx!.setTime(widget.time);
70 |
71 | // Change palette every 15th second to keep things interesting.
72 | if (widget.time!.second % 15 == 0 ||
73 | oldWidget.brightness != widget.brightness) {
74 | _updatePalette();
75 | }
76 | }
77 |
78 | if (oldWidget.size != widget.size) {
79 | _fx!.setSize(widget.size!);
80 | _bgFx!.setSize(widget.size!);
81 | _bgFx!.tick(const Duration(days: 0)); // Trigger repaint.
82 | }
83 | }
84 |
85 | /// Randomly selects a new palette that conforms to the clock's brightness setting.
86 | /// The background color is set to the first component of the palette, and the accent
87 | /// color is set to the color of the palette that is furthest from the background color
88 | /// in terms of luminosity.
89 | void _updatePalette() {
90 | var isDarkMode = widget.brightness == Brightness.dark;
91 | _palette = Rnd.getPalette(widget.palettes, isDarkMode);
92 | _bgColor = _palette.components![0];
93 | _accentColor = _palette.components![_palette.components!.length - 1];
94 | _fx!.setPalette(_palette);
95 | _bgFx!.setPalette(_palette);
96 | }
97 |
98 | void _tick(Duration duration) {
99 | _fx!.tick(duration);
100 | if (widget.time!.second % 5 == 0) _bgFx!.tick(duration);
101 | }
102 |
103 | @override
104 | Widget build(BuildContext context) {
105 | return AnimatedContainer(
106 | duration: const Duration(milliseconds: 1500),
107 | curve: Curves.easeOut,
108 | color: _bgColor,
109 | child: ClipRect(
110 | child: Stack(
111 | children: [
112 | _buildBgBlurFx(),
113 | _buildClockFace(),
114 | CustomPaint(
115 | painter: ClockFxPainter(fx: _fx),
116 | child: Container(),
117 | ),
118 | ],
119 | ),
120 | ),
121 | );
122 | }
123 |
124 | Widget _buildBgBlurFx() {
125 | return RepaintBoundary(
126 | child: Stack(
127 | children: [
128 | CustomPaint(
129 | painter: ClockBgParticlePainter(
130 | fx: _bgFx,
131 | ),
132 | child: const SizedBox(),
133 | ),
134 | BackdropFilter(
135 | filter: ImageFilter.blur(
136 | sigmaX: widget.size!.width * .05,
137 | sigmaY: 0,
138 | ),
139 | child: const Text(" "),
140 | ),
141 | ],
142 | ),
143 | );
144 | }
145 |
146 | Widget _buildClockFace() {
147 | var faceSize = widget.size!.height * .85;
148 | return Center(
149 | child: SizedBox(
150 | width: faceSize,
151 | height: faceSize,
152 | child: Stack(
153 | children: [
154 | // Paint the second hand.
155 | CustomPaint(
156 | size: Size(faceSize, faceSize),
157 | painter: ClockSecondsPainter(
158 | accentColor: _accentColor,
159 | ),
160 | ),
161 |
162 | // Paint the clock face.
163 | RepaintBoundary(
164 | key: Key(_accentColor.toString()),
165 | child: CustomPaint(
166 | size: widget.size!,
167 | painter: ClockFacePainter(
168 | accentColor: _accentColor,
169 | ),
170 | ),
171 | ),
172 | ],
173 | ),
174 | ),
175 | );
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/lib/clocks/particle_clock/particle_clock_fx.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/widgets.dart';
4 |
5 | import 'clock_fx.dart';
6 | import 'particle.dart';
7 | import 'utils/rnd.dart';
8 |
9 | const easingDelayDuration = Duration(seconds: 10);
10 |
11 | /// Probabilities of Hour, Minute, Noise.
12 | final particleDistributions = [2, 4, 100];
13 |
14 | /// Number of "arms" to emit noise particles from center.
15 | const int noiseAngles = 2000;
16 |
17 | /// Threshold for particles to go rouge. Lower = more particles.
18 | const rougeDistributionLmt = 85;
19 |
20 | /// Threshold for particles to go jelly. Lower = more particles.
21 | const jellyDistributionLmt = 97;
22 |
23 | class ParticleClockFx extends ClockFx {
24 | ParticleClockFx({required Size size, required DateTime? time})
25 | : super(
26 | size: size,
27 | time: time,
28 | );
29 |
30 | @override
31 | void tick(Duration duration) {
32 | var secFrac = DateTime.now().millisecond / 1000;
33 |
34 | var vecSpeed = duration.compareTo(easingDelayDuration) > 0
35 | ? max(.2, Curves.easeInOutSine.transform(1 - secFrac))
36 | : 1;
37 |
38 | var vecSpeedInv = Curves.easeInSine.transform(secFrac);
39 |
40 | // Used to avoid emitting all particles at once.
41 | var maxSpawnPerTick = 10;
42 |
43 | particles.asMap().forEach((i, p) {
44 | // Movement
45 | p!.x -= p.vx * vecSpeed;
46 | p.y -= p.vy * vecSpeed;
47 |
48 | p.dist = _getDistanceFromCenter(p);
49 | p.distFrac = p.dist / (sizeMin / 2);
50 |
51 | p.lifeLeft = p.life - p.distFrac;
52 |
53 | // Gradually reduce the speed of Noise particles.
54 | if (p.type == ParticleType.noise) {
55 | p.vx -= p.lifeLeft * p.vx * .001;
56 | p.vy -= p.lifeLeft * p.vy * .001;
57 | }
58 |
59 | // Gradually reduce the size of all particles.
60 | if (p.lifeLeft < .3) {
61 | p.size -= p.size * .0015;
62 | }
63 |
64 | // Make some of the particles go rouge.
65 | if (p.distribution > rougeDistributionLmt &&
66 | p.distribution < jellyDistributionLmt) {
67 | var r = Rnd.getDouble(.2, 2.5) * vecSpeedInv * p.distFrac;
68 | p.x -= p.vx * r + (p.distFrac * Rnd.getDouble(-.4, .4));
69 | p.y -= p.vy * r + (p.distFrac * Rnd.getDouble(-.4, .4));
70 | }
71 |
72 | // Make some of the particles ease back, for a jelly effect.
73 | if (p.distribution >= jellyDistributionLmt) {
74 | var r = Rnd.getDouble(.1, .9) * vecSpeedInv * (1 - p.lifeLeft);
75 | p.x += p.vx * r;
76 | p.y += p.vy * r;
77 | }
78 |
79 | // Reset particles once they are invisible or at the edge.
80 | if (p.lifeLeft <= 0 || p.size <= .5) {
81 | resetParticle(i);
82 | if (maxSpawnPerTick > 0) {
83 | _activateParticle(p);
84 | maxSpawnPerTick--;
85 | }
86 | }
87 | });
88 |
89 | super.tick(duration);
90 | }
91 |
92 | void _activateParticle(Particle p) {
93 | p.x = Rnd.getDouble(spawnArea.left, spawnArea.right);
94 | p.y = Rnd.getDouble(spawnArea.top, spawnArea.bottom);
95 | p.isFilled = Rnd.getBool();
96 | p.size = Rnd.getDouble(3, 8);
97 | p.isFlowing = false;
98 | p.distFrac = 0;
99 |
100 | p.distribution = Rnd.getInt(1, particleDistributions[2]);
101 |
102 | if (p.distribution < particleDistributions[0]) {
103 | p.type = ParticleType.hour;
104 | } else if (p.distribution < particleDistributions[1]) {
105 | p.type = ParticleType.minute;
106 | } else {
107 | p.type = ParticleType.noise;
108 | }
109 |
110 | double? angle;
111 |
112 | switch (p.type) {
113 | case ParticleType.hour:
114 | angle = _getHourRadians();
115 | p.life = Rnd.getDouble(.5, .55);
116 | p.size = sizeMin * .010;
117 | p.isFlowing = Rnd.ratio > .85;
118 | p.color = palette!.components![palette!.components!.length - 1];
119 | break;
120 |
121 | case ParticleType.minute:
122 | angle = _getMinuteRadians();
123 | p.life = Rnd.getDouble(.68, .73);
124 | p.size = sizeMin * .008;
125 | p.isFlowing = Rnd.ratio > .6;
126 | p.color = palette!.components![palette!.components!.length - 1];
127 | break;
128 |
129 | case ParticleType.noise:
130 | // Find a random angle while avoiding clutter at the hour & minute hands.
131 | var am = _getMinuteRadians();
132 | var ah = _getHourRadians() % (pi * 2);
133 | var d = pi / 18;
134 |
135 | // Probably not the most efficient solution right here.
136 | do {
137 | angle = Rnd.ratio * pi * 2;
138 | } while (_isBetween(angle!, am - d, am + d) ||
139 | _isBetween(angle, ah - d, ah + d));
140 |
141 | p.life = Rnd.getDouble(0.75, .8);
142 | p.size = sizeMin *
143 | (Rnd.ratio > .8
144 | ? Rnd.getDouble(.0015, .003)
145 | : Rnd.getDouble(.002, .006));
146 | break;
147 | }
148 |
149 | // Particle movement vector.
150 | p.vx = sin(-angle);
151 | p.vy = cos(-angle);
152 |
153 | // Particle movement angle.
154 | p.a = atan2(p.vy, p.vx) + pi;
155 |
156 | // Add some speed randomeness.
157 | double v = p.type == ParticleType.noise
158 | ? Rnd.getDouble(.5, 1)
159 | : Rnd.getDouble(.3, .4);
160 |
161 | p.vx *= v;
162 | p.vy *= v;
163 | }
164 |
165 | /// Gets the radians of the hour hand.
166 | double _getHourRadians() =>
167 | (time!.hour * pi / 6) +
168 | (time!.minute * pi / (6 * 60)) +
169 | (time!.second * pi / (360 * 60));
170 |
171 | /// Gets the radians of the minute hand.
172 | double _getMinuteRadians() =>
173 | (time!.minute * (2 * pi) / 60) + (time!.second * pi / (30 * 60));
174 |
175 | /// Checks if a value is between two other values.
176 | bool _isBetween(double value, double min, double max) {
177 | return value >= min && value <= max;
178 | }
179 |
180 | /// Calculates the distance from center using pythagoras rate.
181 | double _getDistanceFromCenter(Particle p) {
182 | var a = pow(center.dx - p.x, 2);
183 | var b = pow(center.dy - p.y, 2);
184 | return sqrt(a + b);
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/assets/palettes.json:
--------------------------------------------------------------------------------
1 | [["0xffCC5D4C","0xffFFFEC6","0xffC7D1AF","0xff96B49C","0xff5B5847"],["0xffE4E4C5","0xffB9D48B","0xff8D2036","0xffCE0A31","0xffD3E4C5"],["0xffEB9C4D","0xffF2D680","0xffF3FFCF","0xffBAC9A9","0xff697060"],["0xff360745","0xffD61C59","0xffE7D84B","0xffEFEAC5","0xff1B8798"],["0xffF2E8C4","0xff98D9B6","0xff3EC9A7","0xff2B879E","0xff616668"],["0xffFFF3DB","0xffE7E4D5","0xffD3C8B4","0xffC84648","0xff703E3B"],["0xffF5DD9D","0xffBCC499","0xff92A68A","0xff7B8F8A","0xff506266"],["0xff041122","0xff259073","0xff7FDA89","0xffC8E98E","0xffE6F99D"],["0xff1D1313","0xff24B694","0xffD22042","0xffA3B808","0xff30C4C9"],["0xffF0FFC9","0xffA9DA88","0xff62997A","0xff72243D","0xff3B0819"],["0xffC6CCA5","0xff8AB8A8","0xff6B9997","0xff54787D","0xff615145"],["0xff429398","0xff6B5D4D","0xffB0A18F","0xffDFCDB4","0xffFBEED3"],["0xff2D1B33","0xffF36A71","0xffEE887A","0xffE4E391","0xff9ABC8A"],["0xff4B1139","0xff3B4058","0xff2A6E78","0xff7A907C","0xffC9B180"],["0xff322938","0xff89A194","0xffCFC89A","0xffCC883A","0xffA14016"],["0xff540045","0xffC60052","0xffFF714B","0xffEAFF87","0xffACFFE9"],["0xff79254A","0xff795C64","0xff79927D","0xffAEB18E","0xffE3CF9E"],["0xff452E3C","0xffFF3D5A","0xffFFB969","0xffEAF27E","0xff3B8C88"],["0xff2B2726","0xff0A516D","0xff018790","0xff7DAD93","0xffBACCA4"],["0xff027B7F","0xffFFA588","0xffD62957","0xffBF1E62","0xff572E4F"],["0xffFA6A64","0xff7A4E48","0xff4A4031","0xffF6E2BB","0xff9EC6B8"],["0xff23192D","0xffFD0A54","0xffF57576","0xffFEBF97","0xffF5ECB7"],["0xffA3C68C","0xff879676","0xff6E6662","0xff4F364A","0xff340735"],["0xffA32C28","0xff1C090B","0xff384030","0xff7B8055","0xffBCA875"],["0xff6D9788","0xff1E2528","0xff7E1C13","0xffBF0A0D","0xffE6E1C2"],["0xff373737","0xff8DB986","0xffACCE91","0xffBADB73","0xffEFEAE4"],["0xff280904","0xff680E34","0xff9A151A","0xffC21B12","0xffFC4B2A"],["0xff4B3E4D","0xff1E8C93","0xffDBD8A2","0xffC4AC30","0xffD74F33"],["0xffA69E80","0xffE0BA9B","0xffE7A97E","0xffD28574","0xff3B1922"],["0xff161616","0xffC94D65","0xffE7C049","0xff92B35A","0xff1F6764"],["0xff234D20","0xff36802D","0xff77AB59","0xffC9DF8A","0xffF0F7DA"],["0xffE6EBA9","0xffABBB9F","0xff6F8B94","0xff706482","0xff703D6F"],["0xffFDCFBF","0xffFEB89F","0xffE23D75","0xff5F0D3B","0xff742365"],["0xff641F5E","0xff676077","0xff65AC92","0xffC2C092","0xffEDD48E"],["0xff26251C","0xffEB0A44","0xffF2643D","0xffF2A73D","0xffA0E8B7"],["0xff4F364C","0xff5E405F","0xff6B6B6B","0xff8F9E6F","0xffB1CF72"],["0xff230B00","0xffA29D7F","0xffD4CFA5","0xffF8ECD4","0xffAABE9B"],["0xff85847E","0xffAB6A6E","0xffF7345B","0xff353130","0xffCBCFB4"],["0xffD24858","0xffEA8676","0xffEAB05E","0xffFDEECD","0xff493831"],["0xffD3D5B0","0xffB5CEA4","0xff9DC19D","0xff8C7C62","0xff71443F"],["0xffFFFF99","0xffD9CC8C","0xffB39980","0xff8C6673","0xff663366"],["0xffED6464","0xffBF6370","0xff87586C","0xff574759","0xff1A1B1C"],["0xffCCB24C","0xffF7D683","0xffFFFDC0","0xffFFFFFD","0xff457D97"],["0xffF0F0D8","0xffB4DEBE","0xff77CCA4","0xff666666","0xffB4DF37"],["0xff1A081F","0xff4D1D4D","0xff05676E","0xff489C79","0xffEBC288"],["0xff7A5B3E","0xffFAFAFA","0xffFA4B00","0xffCDBDAE","0xff1F1F1F"],["0xffD31900","0xffFF6600","0xffFFF2AF","0xff7CB490","0xff000000"],["0xffE8C382","0xffB39D69","0xffA86B4C","0xff7D1A0C","0xff340A0B"],["0xffEBEAA9","0xffEBC588","0xff7D2948","0xff3B0032","0xff0E0B29"],["0xff063940","0xff195E63","0xff3E838C","0xff8EBDB6","0xffECE1C3"],["0xff595B5A","0xff14C3A2","0xff0DE5A8","0xff7CF49A","0xffB8FD99"],["0xff411F2D","0xffAC4147","0xffF88863","0xffFFC27F","0xffFFE29A"],["0xffE7E79D","0xffC0D890","0xff78A890","0xff606078","0xffD8A878"],["0xff9DBCBC","0xffF0F0AF","0xffFF370F","0xff332717","0xff6BACBF"],["0xff94654C","0xffF89FA1","0xffFABDBD","0xffFAD6D6","0xffFEFCD0"],["0xff1F1F20","0xff2B4C7E","0xff567EBB","0xff606D80","0xffDCE0E6"],["0xffCDDBC2","0xffF7E4C6","0xffFB9274","0xffF5565B","0xff875346"],["0xffECBE13","0xff738C79","0xff6A6B5F","0xff2C2B26","0xffA43955"],["0xffFFF5DE","0xffB8D9C8","0xff917081","0xff750E49","0xff4D002B"],["0xffF0DDBD","0xffBA3622","0xff851E25","0xff520C30","0xff1C997F"],["0xff312C20","0xff494D4B","0xff7C7052","0xffB3A176","0xffE2CB92"],["0xff3F2C26","0xffDD423E","0xffA2A384","0xffEAC388","0xffC5AD4B"],["0xff0A0310","0xff49007E","0xffFF005B","0xffFF7D10","0xffFFB238"],["0xffCDECCC","0xffEDD269","0xffE88460","0xffF23460","0xff321D2E"],["0xff574C41","0xffE36B6B","0xffE3A56B","0xffE3C77B","0xff96875A"],["0xffE7DD96","0xffE16639","0xffAD860A","0xffB7023F","0xff55024A"],["0xff213435","0xff46685B","0xff648A64","0xffA6B985","0xffE1E3AC"],["0xff181419","0xff4A073C","0xff9E0B41","0xffCC3E18","0xffF0971C"],["0xff413040","0xff6C6368","0xffB9A173","0xffEAA353","0xffFFEFA9"],["0xff4D3B36","0xffEB613B","0xffF98F6F","0xffC1D9CD","0xffF7EADC"],["0xffFFCDB8","0xffFDEECF","0xffC8C696","0xff97BEA9","0xff37260C"],["0xff130912","0xff3E1C33","0xff602749","0xffB14623","0xffF6921D"],["0xffFFFF00","0xffCCD91A","0xff99B333","0xff668C4D","0xff336666"],["0xff001449","0xff012677","0xff005BC5","0xff00B4FC","0xff17F9FF"],["0xffED5672","0xff160E32","0xff9EAE8A","0xffCDBB93","0xffFBC599"],["0xff785D56","0xffBE4C54","0xffC6B299","0xffE6D5C1","0xffFFF4E3"],["0xff2B1719","0xff02483E","0xff057C46","0xff9BB61B","0xffF8BE00"],["0xffE6A06F","0xff9E9C71","0xff5E8271","0xff33454E","0xff242739"],["0xffF7F3D5","0xffFFDABF","0xffFA9B9B","0xffE88087","0xff635063"],["0xffE7EED0","0xffCAD1C3","0xff948E99","0xff51425F","0xff2E1437"],["0xffE25858","0xffE9D6AF","0xffFFFFDD","0xffC0EFD2","0xff384252"],["0xffACDEB2","0xffE1EAB5","0xffEDAD9E","0xffFE4B74","0xff390D2D"],["0xff42282C","0xff6CA19E","0xff84ABAA","0xffDED1B6","0xff6D997A"],["0xffFDFFD9","0xff73185E","0xff36BBA6","0xff0C0D02","0xff8B911A"],["0xff9F0A28","0xffD55C2B","0xffF6E7D3","0xff89A46F","0xff55203C"],["0xffA69A90","0xff4A403D","0xffFFF1C1","0xffFACF7D","0xffEA804C"],["0xff418E8E","0xff5A4E3C","0xffC4D428","0xffD8E472","0xffE9EBBF"],["0xffC9D1D3","0xffF7F7F7","0xff9DD3DF","0xff3B3737","0xff991818"],["0xffFAF6D0","0xffC7D8AB","0xff909A92","0xff744F78","0xff30091E"],["0xffF6C7B7","0xffF7A398","0xffFA7F77","0xffB42529","0xff000000"],["0xffA7321C","0xffFFDC68","0xffCC982A","0xff928941","0xff352504"],["0xffF0371A","0xff000000","0xffF7E6A6","0xff3E6B48","0xffB5B479"],["0xffF4F4F4","0xff9BA657","0xffF0E5C9","0xffA68C69","0xff594433"],["0xffF1E8B4","0xffB2BB91","0xffD7BF5E","0xffD16344","0xff83555E"],["0xff000000","0xff001F36","0xff1C5560","0xff79AE92","0xffFBFFCD"],["0xffE0DC8B","0xffF6AA3D","0xffED4C57","0xff574435","0xff6CC4B9"],["0xff42393B","0xff75C9A3","0xffBAC99A","0xffFFC897","0xffF7EFA2"],["0xffF2CC67","0xffF38264","0xffF40034","0xff5F051F","0xff75BAA8"],["0xffFBFEE5","0xffC91842","0xff98173D","0xff25232D","0xffA8E7CA"],["0xff998496","0xffF7E0AE","0xffFA748F","0xff2D2C26","0xffC3B457"],["0xffDBD9B7","0xffC1C9C8","0xffA5B5AB","0xff949A8E","0xff615566"],["0xffF3E6BC","0xffF1C972","0xffF5886B","0xff72AE95","0xff5A3226"],["0xffD9D4A8","0xffD15C57","0xffCC3747","0xff5C374B","0xff4A5F67"],["0xff674F23","0xffE48B69","0xffE1B365","0xffE5DB84","0xffFFEEAC"],["0xffBF2A23","0xffA6AD3C","0xffF0CE4E","0xffCF872E","0xff8A211D"],["0xff84C1B1","0xffAD849A","0xffD64783","0xffFD135A","0xff40202A"],["0xff020304","0xff541F14","0xff938172","0xffCC9E61","0xff626266"],["0xff002C2B","0xffFF3D00","0xffFFBC11","0xff0A837F","0xff076461"]]
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: transitive
38 | description:
39 | name: collection
40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.18.0"
44 | fake_async:
45 | dependency: transitive
46 | description:
47 | name: fake_async
48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.3.1"
52 | ffi:
53 | dependency: transitive
54 | description:
55 | name: ffi
56 | sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "2.0.2"
60 | flutter:
61 | dependency: "direct main"
62 | description: flutter
63 | source: sdk
64 | version: "0.0.0"
65 | flutter_lints:
66 | dependency: "direct dev"
67 | description:
68 | name: flutter_lints
69 | sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
70 | url: "https://pub.dev"
71 | source: hosted
72 | version: "2.0.1"
73 | flutter_test:
74 | dependency: "direct dev"
75 | description: flutter
76 | source: sdk
77 | version: "0.0.0"
78 | flutter_web_plugins:
79 | dependency: transitive
80 | description: flutter
81 | source: sdk
82 | version: "0.0.0"
83 | leak_tracker:
84 | dependency: transitive
85 | description:
86 | name: leak_tracker
87 | sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
88 | url: "https://pub.dev"
89 | source: hosted
90 | version: "10.0.0"
91 | leak_tracker_flutter_testing:
92 | dependency: transitive
93 | description:
94 | name: leak_tracker_flutter_testing
95 | sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
96 | url: "https://pub.dev"
97 | source: hosted
98 | version: "2.0.1"
99 | leak_tracker_testing:
100 | dependency: transitive
101 | description:
102 | name: leak_tracker_testing
103 | sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
104 | url: "https://pub.dev"
105 | source: hosted
106 | version: "2.0.1"
107 | lints:
108 | dependency: transitive
109 | description:
110 | name: lints
111 | sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
112 | url: "https://pub.dev"
113 | source: hosted
114 | version: "2.0.1"
115 | matcher:
116 | dependency: transitive
117 | description:
118 | name: matcher
119 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
120 | url: "https://pub.dev"
121 | source: hosted
122 | version: "0.12.16+1"
123 | material_color_utilities:
124 | dependency: transitive
125 | description:
126 | name: material_color_utilities
127 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
128 | url: "https://pub.dev"
129 | source: hosted
130 | version: "0.8.0"
131 | meta:
132 | dependency: transitive
133 | description:
134 | name: meta
135 | sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
136 | url: "https://pub.dev"
137 | source: hosted
138 | version: "1.11.0"
139 | path:
140 | dependency: transitive
141 | description:
142 | name: path
143 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
144 | url: "https://pub.dev"
145 | source: hosted
146 | version: "1.9.0"
147 | plugin_platform_interface:
148 | dependency: transitive
149 | description:
150 | name: plugin_platform_interface
151 | sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "2.1.4"
155 | restart_app:
156 | dependency: "direct main"
157 | description:
158 | name: restart_app
159 | sha256: b37daeb1c02fcab30e19d9e30b6fdd215bd53577efd927042eb77cf6f09daadb
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "1.2.1"
163 | shorebird_code_push:
164 | dependency: "direct main"
165 | description:
166 | name: shorebird_code_push
167 | sha256: "872839ac1e9b86f97db99c6456c4886620246f15e1cff9d29d4101e2beb991eb"
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "1.1.3"
171 | sky_engine:
172 | dependency: transitive
173 | description: flutter
174 | source: sdk
175 | version: "0.0.99"
176 | source_span:
177 | dependency: transitive
178 | description:
179 | name: source_span
180 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
181 | url: "https://pub.dev"
182 | source: hosted
183 | version: "1.10.0"
184 | stack_trace:
185 | dependency: transitive
186 | description:
187 | name: stack_trace
188 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
189 | url: "https://pub.dev"
190 | source: hosted
191 | version: "1.11.1"
192 | stream_channel:
193 | dependency: transitive
194 | description:
195 | name: stream_channel
196 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
197 | url: "https://pub.dev"
198 | source: hosted
199 | version: "2.1.2"
200 | string_scanner:
201 | dependency: transitive
202 | description:
203 | name: string_scanner
204 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
205 | url: "https://pub.dev"
206 | source: hosted
207 | version: "1.2.0"
208 | term_glyph:
209 | dependency: transitive
210 | description:
211 | name: term_glyph
212 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
213 | url: "https://pub.dev"
214 | source: hosted
215 | version: "1.2.1"
216 | test_api:
217 | dependency: transitive
218 | description:
219 | name: test_api
220 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
221 | url: "https://pub.dev"
222 | source: hosted
223 | version: "0.6.1"
224 | vector_math:
225 | dependency: transitive
226 | description:
227 | name: vector_math
228 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
229 | url: "https://pub.dev"
230 | source: hosted
231 | version: "2.1.4"
232 | vm_service:
233 | dependency: transitive
234 | description:
235 | name: vm_service
236 | sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
237 | url: "https://pub.dev"
238 | source: hosted
239 | version: "13.0.0"
240 | sdks:
241 | dart: ">=3.2.0-0 <4.0.0"
242 | flutter: ">=3.3.0"
243 |
--------------------------------------------------------------------------------
/lib/clocks/generative_clock/generative_clock.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The Chromium Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | import 'dart:math';
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter/scheduler.dart';
9 | import 'package:flutter/services.dart';
10 |
11 | import 'globals.dart';
12 | import 'node.dart';
13 |
14 | // An array of all the nodes which are animated
15 | // The fluidity of the animation is highly dependent on the size of this array
16 | // change for convenience
17 | List nodes = List.filled(2500, null);
18 |
19 | /// A digital clock.
20 | ///
21 | /// You can do better than this!
22 | class GenerativeClock extends StatefulWidget {
23 | const GenerativeClock({super.key});
24 |
25 | @override
26 | State createState() => _GenerativeClockState();
27 | }
28 |
29 | class _GenerativeClockState extends State {
30 | DateTime _dateTime = DateTime.now();
31 | late Ticker _ticker;
32 |
33 | @override
34 | void initState() {
35 | super.initState();
36 |
37 | SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
38 |
39 | //setup Nodes: one half of the nodes are static and do not move, the other have does
40 | for (int i = 0; i < 600; i++) {
41 | nodes[i] = Node(true);
42 | }
43 |
44 | for (int i = 600; i < nodes.length; i++) {
45 | nodes[i] = Node(false);
46 | }
47 |
48 | for (int i = 0; i < quadrantDescriptors.length; i++) {
49 | quadrantDescriptors[i] = QuadrantDescriptor();
50 | }
51 |
52 | //we use a ticker to regularly update the state, so that the clock is redrawn
53 | _ticker = Ticker(_updateTime);
54 | _ticker.start();
55 | }
56 |
57 | ///the method invoked by the ticker, which just updates the current time
58 | void _updateTime(Duration d) {
59 | setState(() {
60 | _dateTime = DateTime.now();
61 | });
62 | }
63 |
64 | @override
65 | Widget build(BuildContext context) {
66 | int hour = _dateTime.hour;
67 | if (!MediaQuery.of(context).alwaysUse24HourFormat) {
68 | hour %= 12;
69 | }
70 | int minute = _dateTime.minute;
71 |
72 | bool inside =
73 | Theme.of(context).brightness == Brightness.light ? true : false;
74 |
75 | double percentageOfMinute = _dateTime.millisecond.toDouble() / 60000.0 +
76 | _dateTime.second.toDouble() / 60.0;
77 |
78 | return Container(
79 | color: Colors.black,
80 | child: CustomPaint(
81 | painter: Painter(hour, minute, percentageOfMinute, inside),
82 | // We give the CustomPaint a child, so that it can use the child's size
83 | // to determine its own size. If we don't do this, CustomPaint will
84 | // size itself wrong when inside a Scaffold.
85 | // https://github.com/flutter/flutter/issues/76230#issuecomment-781119751
86 | child: Container(),
87 | ),
88 | );
89 | }
90 | }
91 |
92 | /// [QuadrantDescriptor] a helper class, which we use to keep track, how many nodes are in a quadrant
93 | /// the real information is stored in the indexs array, the QuadrantDescriptors, just describe the structural information of the array
94 | /// [size] how many nodes are in the quadrant
95 | /// [beginningIndex] in the
96 | /// [filled] how many nodes of the quadrant are already found
97 | /// in this way we define a slice of the indexs array, which only holds indexs of nodes, which belong to one quadrant
98 | class QuadrantDescriptor {
99 | int size = 0;
100 | int beginningIndex = 0;
101 | int filled = 0;
102 |
103 | void reset() {
104 | size = 0;
105 | beginningIndex = 0;
106 | filled = 0;
107 | }
108 | }
109 |
110 | // Maintain a QuadrantDescriptor for every quadrant
111 | List quadrantDescriptors = List.filled(
112 | quadrants(),
113 | QuadrantDescriptor(),
114 | );
115 |
116 | // aspect ratio of the screen is 5/3
117 | double aspectRatio = 5 / 3;
118 |
119 | /// A custom painter, which is used to animate the clockface
120 | class Painter extends CustomPainter {
121 | final int _hour;
122 | final int _minute;
123 | final double _percentageOfMinute;
124 | final bool _drawInside;
125 |
126 | Painter(
127 | this._hour,
128 | this._minute,
129 | this._percentageOfMinute,
130 | this._drawInside,
131 | );
132 |
133 | /// the paint function is used in a similar way to the main loop of a game:
134 | /// first the state of the world (in this case mainly the position of the nodes) is simulated
135 | /// second the world state is drawn, i.e. each node tests if it has close neightbors and draws a line to them
136 | @override
137 | void paint(Canvas canvas, Size size) {
138 | /// translate the point [p] from our local coordinate system into that of the canvas
139 | Point global(Point p) {
140 | return Point(
141 | p.x * size.height + size.width / 2,
142 | p.y * size.height + size.height / 2,
143 | );
144 | }
145 |
146 | /// translates the point [p] into an equivalent offset
147 | Offset offset(Point p) {
148 | return Offset(p.x, p.y);
149 | }
150 |
151 | //reset the quadrantDescriptors
152 | for (var quadrantDescriptor in quadrantDescriptors) {
153 | quadrantDescriptor.reset();
154 | }
155 |
156 | // update the positions of the node, we use the quadrant assoziations to count the amount of nodes in each quadrant
157 | for (var node in nodes) {
158 | node!.update();
159 | quadrantDescriptors[node.quadrant].size++;
160 | }
161 |
162 | // fill out the beginningIndexs of the quadrantDescriptors, by summing over the sizes
163 | int counter = 0;
164 | for (var quadrantDescriptor in quadrantDescriptors) {
165 | quadrantDescriptor.beginningIndex = counter;
166 | counter += quadrantDescriptor.size;
167 | }
168 |
169 | {
170 | // This is a list of indexes of the nodes, it is ordered by the quadrant association of the nodes,
171 | // i.e. first all indexes of nodes in the first quadrant appear, then of the second and so forth
172 | List indexs = List.filled(nodes.length, 0);
173 |
174 | // fill out the indexs array, by iterating over nodes, and saving the node at appropriate index according to the quadrantDescriptor
175 | for (int i = 0; i < nodes.length; i++) {
176 | var node = nodes[i];
177 | if (node == null) {
178 | continue;
179 | }
180 |
181 | var listDescriptor = quadrantDescriptors[node.quadrant];
182 | int index = listDescriptor.beginningIndex + listDescriptor.filled;
183 | listDescriptor.filled++;
184 | indexs[index] = i;
185 | nodes[i]!.index = index;
186 | }
187 |
188 | //sort the nodes array, using the indexs array
189 | for (int i = 0; i < nodes.length; i++) {
190 | int index = indexs[i];
191 |
192 | //swap
193 | Node temp = nodes[i]!;
194 | int indexOfSwap = temp.index;
195 |
196 | nodes[i] = nodes[index];
197 | nodes[index] = temp;
198 |
199 | indexs[i] = i;
200 | indexs[indexOfSwap] = index;
201 | }
202 | }
203 |
204 | /// get the following, surrounding quadrants of [quadrant] are the return vakze:
205 | /// quadrant, right
206 | ///left under, under , right under
207 | List surroundingQuadrants(int quadrant) {
208 | List result = [];
209 |
210 | int x = quadrant % columns;
211 |
212 | int nQuadrants = quadrants();
213 |
214 | if (quadrant < nQuadrants) {
215 | result.add(quadrant);
216 | }
217 |
218 | if (x + 1 < columns && quadrant + 1 < nQuadrants) {
219 | result.add(quadrant + 1);
220 | }
221 |
222 | if (x - 1 > 0 && quadrant + columns - 1 < nQuadrants) {
223 | result.add(quadrant + columns - 1);
224 | }
225 |
226 | if (quadrant + columns < nQuadrants) {
227 | result.add(quadrant + columns);
228 | }
229 |
230 | if (x + 1 < columns && quadrant + columns + 1 < nQuadrants) {
231 | result.add(quadrant + columns + 1);
232 | }
233 | return result;
234 | }
235 |
236 | // ----- Draw step ----- //
237 |
238 | //draw half the screen black
239 | Paint white = Paint()
240 | ..style = PaintingStyle.fill
241 | ..color = Colors.white70;
242 |
243 | // we compute a four sided polygon, which tints have the screen white (the other half is white)
244 | // it acts as a clockhand for the seconds
245 | Path path = Path();
246 | double angle = _percentageOfMinute * 2 * pi;
247 |
248 | double x, y;
249 | double tangensAngle = atan((size.width / 2) / (size.height / 2));
250 | if (angle < tangensAngle || angle > 2 * pi - tangensAngle) {
251 | x = tan(angle) * size.height / 2;
252 | path.moveTo(size.width / 2 + x, 0.0);
253 | path.lineTo(size.width, 0.0);
254 | path.lineTo(size.width, size.height);
255 | path.lineTo(size.width / 2 - x, size.height);
256 | } else if (angle < pi - tangensAngle) {
257 | y = tan(angle - pi / 2) * size.width / 2;
258 | path.moveTo(size.width, size.height / 2 + y);
259 | path.lineTo(size.width, size.height);
260 | path.lineTo(0.0, size.height);
261 | path.lineTo(0.0, size.height / 2 - y);
262 | } else if (angle < pi + tangensAngle) {
263 | x = tan(angle - pi) * size.height / 2;
264 | path.moveTo(size.width / 2 - x, size.height);
265 | path.lineTo(0.0, size.height);
266 | path.lineTo(0.0, 0.0);
267 | path.lineTo(size.width / 2 + x, 0.0);
268 | } else if (angle < 2 * pi - tangensAngle) {
269 | y = tan(angle - 3 * pi / 2) * size.width / 2;
270 | path.moveTo(0.0, size.height / 2 - y);
271 | path.lineTo(0.0, 0.0);
272 | path.lineTo(size.width, 0.0);
273 | path.lineTo(size.width, size.height / 2 + y);
274 | }
275 | path.close();
276 | canvas.drawPath(path, white);
277 |
278 | // this is a function that computes if a point is in the white four sided figure drawn above or not
279 | bool isBelowAngle(Point p) {
280 | double pointAngle;
281 | if (p.x > 0 && p.y < 0) {
282 | pointAngle = atan(p.x / -p.y);
283 | } else if (p.x > 0 && p.y > 0) {
284 | pointAngle = pi / 2 + atan(p.y / p.x);
285 | } else if (p.x <= 0 && p.y > 0) {
286 | pointAngle = pi + atan(-p.x / p.y);
287 | } else {
288 | pointAngle = 3 * pi / 2 + atan(-p.y / -p.x);
289 | }
290 |
291 | if (angle > pi && (pointAngle > angle || pointAngle < angle - pi)) {
292 | return true;
293 | } else if (angle < pi && pointAngle > angle && pointAngle < angle + pi) {
294 | return true;
295 | } else {
296 | return false;
297 | }
298 | }
299 |
300 | // iterate over the nodes and check every other node if it is near
301 | // as is appearent, algorithmn scales quadratically with the numbers of nodes,
302 | // because of this we have the optimization with the quadrants
303 | for (var node in nodes) {
304 | if (node == null) {
305 | continue;
306 | }
307 |
308 | //do we have to draw things for this node?
309 | if (node.checkCoordinate(_hour, _minute, _drawInside)) {
310 | Point point1 = node.coordinate;
311 | Point globalPoint = global(point1);
312 |
313 | /* debug code: draws the nodes
314 | Offset off = Offset(globalPoint.x, globalPoint.y);
315 | Paint paint = Paint()
316 | ..color = Colors.red;
317 | canvas.drawCircle(off, 1, paint);
318 | */
319 |
320 | var quadrantsToCheck = surroundingQuadrants(node.quadrant);
321 |
322 | for (var index in quadrantsToCheck) {
323 | QuadrantDescriptor quadrantDescriptor = quadrantDescriptors[index];
324 | int startingIndex =
325 | max(quadrantDescriptor.beginningIndex, node.index);
326 |
327 | //iterate over the following nodes in the surrounding quadrant, which we can get by looking at the quadrantDescriptors
328 | for (int i = startingIndex;
329 | i < quadrantDescriptor.beginningIndex + quadrantDescriptor.size;
330 | i++) {
331 | // a node in the surrounding quadrants
332 | Node node2 = nodes[i]!;
333 | Point point2 = node2.coordinate;
334 | double distance = point1.distanceTo(point2);
335 |
336 | //do we have to draw things for this node
337 | if (node2.checkCoordinate(_hour, _minute, _drawInside)) {
338 | Point globalPoint2 = global(point2);
339 |
340 | //if the distance between nodes is small enough draw a line between them
341 | if (distance < maxDistanceBetweenNodes) {
342 | // we use a more pronaunced line, for close nodes
343 | // in technical terms: the alpha value of the linepaint is antiproportionally defined to the distance between the nodes
344 | double alpha = min(0.015 / (distance + 0.0001), 1.0);
345 |
346 | Paint paint;
347 | if (isBelowAngle(point1)) {
348 | paint = Paint()
349 | ..color = Colors.black.withAlpha((alpha * 255).floor());
350 | } else {
351 | paint = Paint()
352 | ..color = Colors.white.withAlpha((alpha * 255).floor());
353 | }
354 |
355 | var offset1 = offset(globalPoint);
356 | var offset2 = offset(globalPoint2);
357 | canvas.drawLine(offset1, offset2, paint);
358 | }
359 | }
360 | }
361 | }
362 | }
363 | }
364 | }
365 |
366 | //always repaint, we want an animation, which is as smooth as possible
367 | @override
368 | bool shouldRepaint(CustomPainter oldDelegate) {
369 | return true;
370 | }
371 | }
372 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
14 | 813818BFB9BB49F5BB60D6AE /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5C3DE064D1A08F8E6A7DC11 /* Pods_Runner.framework */; };
15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
18 | 9CF2981888CBC82D40B8C39D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33EB2062AC918C21D323D4E6 /* Pods_RunnerTests.framework */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXContainerItemProxy section */
22 | 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
23 | isa = PBXContainerItemProxy;
24 | containerPortal = 97C146E61CF9000F007C117D /* Project object */;
25 | proxyType = 1;
26 | remoteGlobalIDString = 97C146ED1CF9000F007C117D;
27 | remoteInfo = Runner;
28 | };
29 | /* End PBXContainerItemProxy section */
30 |
31 | /* Begin PBXCopyFilesBuildPhase section */
32 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
33 | isa = PBXCopyFilesBuildPhase;
34 | buildActionMask = 2147483647;
35 | dstPath = "";
36 | dstSubfolderSpec = 10;
37 | files = (
38 | );
39 | name = "Embed Frameworks";
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXCopyFilesBuildPhase section */
43 |
44 | /* Begin PBXFileReference section */
45 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
46 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
47 | 284DD404BFF3A339E0E6BBCF /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
48 | 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
49 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
50 | 33EB2062AC918C21D323D4E6 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
51 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
52 | 68A8ECFC7D852E2C808E1C60 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
53 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
54 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
55 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
56 | 880A0F2F16AD4B996A80883C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
57 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
58 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
59 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
60 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
61 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
62 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
63 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
64 | 9ADA8FDFC9C6E3A6BC1FD046 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
65 | B5C3DE064D1A08F8E6A7DC11 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
66 | DDE62DB222EE666E0CBCB768 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
67 | F6F27CCFAD36F2DC3F349CFC /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
68 | /* End PBXFileReference section */
69 |
70 | /* Begin PBXFrameworksBuildPhase section */
71 | 12034162D0311F0302B82CE9 /* Frameworks */ = {
72 | isa = PBXFrameworksBuildPhase;
73 | buildActionMask = 2147483647;
74 | files = (
75 | 9CF2981888CBC82D40B8C39D /* Pods_RunnerTests.framework in Frameworks */,
76 | );
77 | runOnlyForDeploymentPostprocessing = 0;
78 | };
79 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
80 | isa = PBXFrameworksBuildPhase;
81 | buildActionMask = 2147483647;
82 | files = (
83 | 813818BFB9BB49F5BB60D6AE /* Pods_Runner.framework in Frameworks */,
84 | );
85 | runOnlyForDeploymentPostprocessing = 0;
86 | };
87 | /* End PBXFrameworksBuildPhase section */
88 |
89 | /* Begin PBXGroup section */
90 | 10A61520B39F1D545D584A8C /* Pods */ = {
91 | isa = PBXGroup;
92 | children = (
93 | 9ADA8FDFC9C6E3A6BC1FD046 /* Pods-Runner.debug.xcconfig */,
94 | 68A8ECFC7D852E2C808E1C60 /* Pods-Runner.release.xcconfig */,
95 | 880A0F2F16AD4B996A80883C /* Pods-Runner.profile.xcconfig */,
96 | 284DD404BFF3A339E0E6BBCF /* Pods-RunnerTests.debug.xcconfig */,
97 | DDE62DB222EE666E0CBCB768 /* Pods-RunnerTests.release.xcconfig */,
98 | F6F27CCFAD36F2DC3F349CFC /* Pods-RunnerTests.profile.xcconfig */,
99 | );
100 | path = Pods;
101 | sourceTree = "";
102 | };
103 | 331C8082294A63A400263BE5 /* RunnerTests */ = {
104 | isa = PBXGroup;
105 | children = (
106 | 331C807B294A618700263BE5 /* RunnerTests.swift */,
107 | );
108 | path = RunnerTests;
109 | sourceTree = "";
110 | };
111 | 3DCE6269940FD62C12D73331 /* Frameworks */ = {
112 | isa = PBXGroup;
113 | children = (
114 | B5C3DE064D1A08F8E6A7DC11 /* Pods_Runner.framework */,
115 | 33EB2062AC918C21D323D4E6 /* Pods_RunnerTests.framework */,
116 | );
117 | name = Frameworks;
118 | sourceTree = "";
119 | };
120 | 9740EEB11CF90186004384FC /* Flutter */ = {
121 | isa = PBXGroup;
122 | children = (
123 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
124 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
125 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
126 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
127 | );
128 | name = Flutter;
129 | sourceTree = "";
130 | };
131 | 97C146E51CF9000F007C117D = {
132 | isa = PBXGroup;
133 | children = (
134 | 9740EEB11CF90186004384FC /* Flutter */,
135 | 97C146F01CF9000F007C117D /* Runner */,
136 | 97C146EF1CF9000F007C117D /* Products */,
137 | 331C8082294A63A400263BE5 /* RunnerTests */,
138 | 10A61520B39F1D545D584A8C /* Pods */,
139 | 3DCE6269940FD62C12D73331 /* Frameworks */,
140 | );
141 | sourceTree = "";
142 | };
143 | 97C146EF1CF9000F007C117D /* Products */ = {
144 | isa = PBXGroup;
145 | children = (
146 | 97C146EE1CF9000F007C117D /* Runner.app */,
147 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
148 | );
149 | name = Products;
150 | sourceTree = "";
151 | };
152 | 97C146F01CF9000F007C117D /* Runner */ = {
153 | isa = PBXGroup;
154 | children = (
155 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
156 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
157 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
158 | 97C147021CF9000F007C117D /* Info.plist */,
159 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
160 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
161 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
162 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
163 | );
164 | path = Runner;
165 | sourceTree = "";
166 | };
167 | /* End PBXGroup section */
168 |
169 | /* Begin PBXNativeTarget section */
170 | 331C8080294A63A400263BE5 /* RunnerTests */ = {
171 | isa = PBXNativeTarget;
172 | buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
173 | buildPhases = (
174 | F154AAB717774527F1005029 /* [CP] Check Pods Manifest.lock */,
175 | 331C807D294A63A400263BE5 /* Sources */,
176 | 331C807F294A63A400263BE5 /* Resources */,
177 | 12034162D0311F0302B82CE9 /* Frameworks */,
178 | );
179 | buildRules = (
180 | );
181 | dependencies = (
182 | 331C8086294A63A400263BE5 /* PBXTargetDependency */,
183 | );
184 | name = RunnerTests;
185 | productName = RunnerTests;
186 | productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
187 | productType = "com.apple.product-type.bundle.unit-test";
188 | };
189 | 97C146ED1CF9000F007C117D /* Runner */ = {
190 | isa = PBXNativeTarget;
191 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
192 | buildPhases = (
193 | 9F38765FC48E36B59996C5CB /* [CP] Check Pods Manifest.lock */,
194 | 9740EEB61CF901F6004384FC /* Run Script */,
195 | 97C146EA1CF9000F007C117D /* Sources */,
196 | 97C146EB1CF9000F007C117D /* Frameworks */,
197 | 97C146EC1CF9000F007C117D /* Resources */,
198 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
199 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
200 | 91FA61AD0A9F9F3BADE0FD10 /* [CP] Embed Pods Frameworks */,
201 | );
202 | buildRules = (
203 | );
204 | dependencies = (
205 | );
206 | name = Runner;
207 | productName = Runner;
208 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
209 | productType = "com.apple.product-type.application";
210 | };
211 | /* End PBXNativeTarget section */
212 |
213 | /* Begin PBXProject section */
214 | 97C146E61CF9000F007C117D /* Project object */ = {
215 | isa = PBXProject;
216 | attributes = {
217 | LastUpgradeCheck = 1430;
218 | ORGANIZATIONNAME = "";
219 | TargetAttributes = {
220 | 331C8080294A63A400263BE5 = {
221 | CreatedOnToolsVersion = 14.0;
222 | TestTargetID = 97C146ED1CF9000F007C117D;
223 | };
224 | 97C146ED1CF9000F007C117D = {
225 | CreatedOnToolsVersion = 7.3.1;
226 | LastSwiftMigration = 1100;
227 | };
228 | };
229 | };
230 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
231 | compatibilityVersion = "Xcode 9.3";
232 | developmentRegion = en;
233 | hasScannedForEncodings = 0;
234 | knownRegions = (
235 | en,
236 | Base,
237 | );
238 | mainGroup = 97C146E51CF9000F007C117D;
239 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
240 | projectDirPath = "";
241 | projectRoot = "";
242 | targets = (
243 | 97C146ED1CF9000F007C117D /* Runner */,
244 | 331C8080294A63A400263BE5 /* RunnerTests */,
245 | );
246 | };
247 | /* End PBXProject section */
248 |
249 | /* Begin PBXResourcesBuildPhase section */
250 | 331C807F294A63A400263BE5 /* Resources */ = {
251 | isa = PBXResourcesBuildPhase;
252 | buildActionMask = 2147483647;
253 | files = (
254 | );
255 | runOnlyForDeploymentPostprocessing = 0;
256 | };
257 | 97C146EC1CF9000F007C117D /* Resources */ = {
258 | isa = PBXResourcesBuildPhase;
259 | buildActionMask = 2147483647;
260 | files = (
261 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
262 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
263 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
264 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
265 | );
266 | runOnlyForDeploymentPostprocessing = 0;
267 | };
268 | /* End PBXResourcesBuildPhase section */
269 |
270 | /* Begin PBXShellScriptBuildPhase section */
271 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
272 | isa = PBXShellScriptBuildPhase;
273 | alwaysOutOfDate = 1;
274 | buildActionMask = 2147483647;
275 | files = (
276 | );
277 | inputPaths = (
278 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
279 | );
280 | name = "Thin Binary";
281 | outputPaths = (
282 | );
283 | runOnlyForDeploymentPostprocessing = 0;
284 | shellPath = /bin/sh;
285 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
286 | };
287 | 91FA61AD0A9F9F3BADE0FD10 /* [CP] Embed Pods Frameworks */ = {
288 | isa = PBXShellScriptBuildPhase;
289 | buildActionMask = 2147483647;
290 | files = (
291 | );
292 | inputFileListPaths = (
293 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
294 | );
295 | name = "[CP] Embed Pods Frameworks";
296 | outputFileListPaths = (
297 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
298 | );
299 | runOnlyForDeploymentPostprocessing = 0;
300 | shellPath = /bin/sh;
301 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
302 | showEnvVarsInLog = 0;
303 | };
304 | 9740EEB61CF901F6004384FC /* Run Script */ = {
305 | isa = PBXShellScriptBuildPhase;
306 | alwaysOutOfDate = 1;
307 | buildActionMask = 2147483647;
308 | files = (
309 | );
310 | inputPaths = (
311 | );
312 | name = "Run Script";
313 | outputPaths = (
314 | );
315 | runOnlyForDeploymentPostprocessing = 0;
316 | shellPath = /bin/sh;
317 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
318 | };
319 | 9F38765FC48E36B59996C5CB /* [CP] Check Pods Manifest.lock */ = {
320 | isa = PBXShellScriptBuildPhase;
321 | buildActionMask = 2147483647;
322 | files = (
323 | );
324 | inputFileListPaths = (
325 | );
326 | inputPaths = (
327 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
328 | "${PODS_ROOT}/Manifest.lock",
329 | );
330 | name = "[CP] Check Pods Manifest.lock";
331 | outputFileListPaths = (
332 | );
333 | outputPaths = (
334 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
335 | );
336 | runOnlyForDeploymentPostprocessing = 0;
337 | shellPath = /bin/sh;
338 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
339 | showEnvVarsInLog = 0;
340 | };
341 | F154AAB717774527F1005029 /* [CP] Check Pods Manifest.lock */ = {
342 | isa = PBXShellScriptBuildPhase;
343 | buildActionMask = 2147483647;
344 | files = (
345 | );
346 | inputFileListPaths = (
347 | );
348 | inputPaths = (
349 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
350 | "${PODS_ROOT}/Manifest.lock",
351 | );
352 | name = "[CP] Check Pods Manifest.lock";
353 | outputFileListPaths = (
354 | );
355 | outputPaths = (
356 | "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
357 | );
358 | runOnlyForDeploymentPostprocessing = 0;
359 | shellPath = /bin/sh;
360 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
361 | showEnvVarsInLog = 0;
362 | };
363 | /* End PBXShellScriptBuildPhase section */
364 |
365 | /* Begin PBXSourcesBuildPhase section */
366 | 331C807D294A63A400263BE5 /* Sources */ = {
367 | isa = PBXSourcesBuildPhase;
368 | buildActionMask = 2147483647;
369 | files = (
370 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
371 | );
372 | runOnlyForDeploymentPostprocessing = 0;
373 | };
374 | 97C146EA1CF9000F007C117D /* Sources */ = {
375 | isa = PBXSourcesBuildPhase;
376 | buildActionMask = 2147483647;
377 | files = (
378 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
379 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
380 | );
381 | runOnlyForDeploymentPostprocessing = 0;
382 | };
383 | /* End PBXSourcesBuildPhase section */
384 |
385 | /* Begin PBXTargetDependency section */
386 | 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
387 | isa = PBXTargetDependency;
388 | target = 97C146ED1CF9000F007C117D /* Runner */;
389 | targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
390 | };
391 | /* End PBXTargetDependency section */
392 |
393 | /* Begin PBXVariantGroup section */
394 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
395 | isa = PBXVariantGroup;
396 | children = (
397 | 97C146FB1CF9000F007C117D /* Base */,
398 | );
399 | name = Main.storyboard;
400 | sourceTree = "";
401 | };
402 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
403 | isa = PBXVariantGroup;
404 | children = (
405 | 97C147001CF9000F007C117D /* Base */,
406 | );
407 | name = LaunchScreen.storyboard;
408 | sourceTree = "";
409 | };
410 | /* End PBXVariantGroup section */
411 |
412 | /* Begin XCBuildConfiguration section */
413 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
414 | isa = XCBuildConfiguration;
415 | buildSettings = {
416 | ALWAYS_SEARCH_USER_PATHS = NO;
417 | CLANG_ANALYZER_NONNULL = YES;
418 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
419 | CLANG_CXX_LIBRARY = "libc++";
420 | CLANG_ENABLE_MODULES = YES;
421 | CLANG_ENABLE_OBJC_ARC = YES;
422 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
423 | CLANG_WARN_BOOL_CONVERSION = YES;
424 | CLANG_WARN_COMMA = YES;
425 | CLANG_WARN_CONSTANT_CONVERSION = YES;
426 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
427 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
428 | CLANG_WARN_EMPTY_BODY = YES;
429 | CLANG_WARN_ENUM_CONVERSION = YES;
430 | CLANG_WARN_INFINITE_RECURSION = YES;
431 | CLANG_WARN_INT_CONVERSION = YES;
432 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
433 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
434 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
435 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
436 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
437 | CLANG_WARN_STRICT_PROTOTYPES = YES;
438 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
439 | CLANG_WARN_UNREACHABLE_CODE = YES;
440 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
441 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
442 | COPY_PHASE_STRIP = NO;
443 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
444 | ENABLE_NS_ASSERTIONS = NO;
445 | ENABLE_STRICT_OBJC_MSGSEND = YES;
446 | GCC_C_LANGUAGE_STANDARD = gnu99;
447 | GCC_NO_COMMON_BLOCKS = YES;
448 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
449 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
450 | GCC_WARN_UNDECLARED_SELECTOR = YES;
451 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
452 | GCC_WARN_UNUSED_FUNCTION = YES;
453 | GCC_WARN_UNUSED_VARIABLE = YES;
454 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
455 | MTL_ENABLE_DEBUG_INFO = NO;
456 | SDKROOT = iphoneos;
457 | SUPPORTED_PLATFORMS = iphoneos;
458 | TARGETED_DEVICE_FAMILY = "1,2";
459 | VALIDATE_PRODUCT = YES;
460 | };
461 | name = Profile;
462 | };
463 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
464 | isa = XCBuildConfiguration;
465 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
466 | buildSettings = {
467 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
468 | CLANG_ENABLE_MODULES = YES;
469 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
470 | DEVELOPMENT_TEAM = 6V53YACS2W;
471 | ENABLE_BITCODE = NO;
472 | INFOPLIST_FILE = Runner/Info.plist;
473 | INFOPLIST_KEY_CFBundleDisplayName = "Shorebird Clock";
474 | LD_RUNPATH_SEARCH_PATHS = (
475 | "$(inherited)",
476 | "@executable_path/Frameworks",
477 | );
478 | PRODUCT_BUNDLE_IDENTIFIER = com.shorebird.timeShift;
479 | PRODUCT_NAME = "$(TARGET_NAME)";
480 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
481 | SWIFT_VERSION = 5.0;
482 | VERSIONING_SYSTEM = "apple-generic";
483 | };
484 | name = Profile;
485 | };
486 | 331C8088294A63A400263BE5 /* Debug */ = {
487 | isa = XCBuildConfiguration;
488 | baseConfigurationReference = 284DD404BFF3A339E0E6BBCF /* Pods-RunnerTests.debug.xcconfig */;
489 | buildSettings = {
490 | BUNDLE_LOADER = "$(TEST_HOST)";
491 | CODE_SIGN_STYLE = Automatic;
492 | CURRENT_PROJECT_VERSION = 1;
493 | GENERATE_INFOPLIST_FILE = YES;
494 | MARKETING_VERSION = 1.0;
495 | PRODUCT_BUNDLE_IDENTIFIER = dev.shorebird.timeShift.RunnerTests;
496 | PRODUCT_NAME = "$(TARGET_NAME)";
497 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
498 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
499 | SWIFT_VERSION = 5.0;
500 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
501 | };
502 | name = Debug;
503 | };
504 | 331C8089294A63A400263BE5 /* Release */ = {
505 | isa = XCBuildConfiguration;
506 | baseConfigurationReference = DDE62DB222EE666E0CBCB768 /* Pods-RunnerTests.release.xcconfig */;
507 | buildSettings = {
508 | BUNDLE_LOADER = "$(TEST_HOST)";
509 | CODE_SIGN_STYLE = Automatic;
510 | CURRENT_PROJECT_VERSION = 1;
511 | GENERATE_INFOPLIST_FILE = YES;
512 | MARKETING_VERSION = 1.0;
513 | PRODUCT_BUNDLE_IDENTIFIER = dev.shorebird.timeShift.RunnerTests;
514 | PRODUCT_NAME = "$(TARGET_NAME)";
515 | SWIFT_VERSION = 5.0;
516 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
517 | };
518 | name = Release;
519 | };
520 | 331C808A294A63A400263BE5 /* Profile */ = {
521 | isa = XCBuildConfiguration;
522 | baseConfigurationReference = F6F27CCFAD36F2DC3F349CFC /* Pods-RunnerTests.profile.xcconfig */;
523 | buildSettings = {
524 | BUNDLE_LOADER = "$(TEST_HOST)";
525 | CODE_SIGN_STYLE = Automatic;
526 | CURRENT_PROJECT_VERSION = 1;
527 | GENERATE_INFOPLIST_FILE = YES;
528 | MARKETING_VERSION = 1.0;
529 | PRODUCT_BUNDLE_IDENTIFIER = dev.shorebird.timeShift.RunnerTests;
530 | PRODUCT_NAME = "$(TARGET_NAME)";
531 | SWIFT_VERSION = 5.0;
532 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
533 | };
534 | name = Profile;
535 | };
536 | 97C147031CF9000F007C117D /* Debug */ = {
537 | isa = XCBuildConfiguration;
538 | buildSettings = {
539 | ALWAYS_SEARCH_USER_PATHS = NO;
540 | CLANG_ANALYZER_NONNULL = YES;
541 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
542 | CLANG_CXX_LIBRARY = "libc++";
543 | CLANG_ENABLE_MODULES = YES;
544 | CLANG_ENABLE_OBJC_ARC = YES;
545 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
546 | CLANG_WARN_BOOL_CONVERSION = YES;
547 | CLANG_WARN_COMMA = YES;
548 | CLANG_WARN_CONSTANT_CONVERSION = YES;
549 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
550 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
551 | CLANG_WARN_EMPTY_BODY = YES;
552 | CLANG_WARN_ENUM_CONVERSION = YES;
553 | CLANG_WARN_INFINITE_RECURSION = YES;
554 | CLANG_WARN_INT_CONVERSION = YES;
555 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
556 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
557 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
558 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
559 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
560 | CLANG_WARN_STRICT_PROTOTYPES = YES;
561 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
562 | CLANG_WARN_UNREACHABLE_CODE = YES;
563 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
564 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
565 | COPY_PHASE_STRIP = NO;
566 | DEBUG_INFORMATION_FORMAT = dwarf;
567 | ENABLE_STRICT_OBJC_MSGSEND = YES;
568 | ENABLE_TESTABILITY = YES;
569 | GCC_C_LANGUAGE_STANDARD = gnu99;
570 | GCC_DYNAMIC_NO_PIC = NO;
571 | GCC_NO_COMMON_BLOCKS = YES;
572 | GCC_OPTIMIZATION_LEVEL = 0;
573 | GCC_PREPROCESSOR_DEFINITIONS = (
574 | "DEBUG=1",
575 | "$(inherited)",
576 | );
577 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
578 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
579 | GCC_WARN_UNDECLARED_SELECTOR = YES;
580 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
581 | GCC_WARN_UNUSED_FUNCTION = YES;
582 | GCC_WARN_UNUSED_VARIABLE = YES;
583 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
584 | MTL_ENABLE_DEBUG_INFO = YES;
585 | ONLY_ACTIVE_ARCH = YES;
586 | SDKROOT = iphoneos;
587 | TARGETED_DEVICE_FAMILY = "1,2";
588 | };
589 | name = Debug;
590 | };
591 | 97C147041CF9000F007C117D /* Release */ = {
592 | isa = XCBuildConfiguration;
593 | buildSettings = {
594 | ALWAYS_SEARCH_USER_PATHS = NO;
595 | CLANG_ANALYZER_NONNULL = YES;
596 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
597 | CLANG_CXX_LIBRARY = "libc++";
598 | CLANG_ENABLE_MODULES = YES;
599 | CLANG_ENABLE_OBJC_ARC = YES;
600 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
601 | CLANG_WARN_BOOL_CONVERSION = YES;
602 | CLANG_WARN_COMMA = YES;
603 | CLANG_WARN_CONSTANT_CONVERSION = YES;
604 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
605 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
606 | CLANG_WARN_EMPTY_BODY = YES;
607 | CLANG_WARN_ENUM_CONVERSION = YES;
608 | CLANG_WARN_INFINITE_RECURSION = YES;
609 | CLANG_WARN_INT_CONVERSION = YES;
610 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
611 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
612 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
613 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
614 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
615 | CLANG_WARN_STRICT_PROTOTYPES = YES;
616 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
617 | CLANG_WARN_UNREACHABLE_CODE = YES;
618 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
619 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
620 | COPY_PHASE_STRIP = NO;
621 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
622 | ENABLE_NS_ASSERTIONS = NO;
623 | ENABLE_STRICT_OBJC_MSGSEND = YES;
624 | GCC_C_LANGUAGE_STANDARD = gnu99;
625 | GCC_NO_COMMON_BLOCKS = YES;
626 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
627 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
628 | GCC_WARN_UNDECLARED_SELECTOR = YES;
629 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
630 | GCC_WARN_UNUSED_FUNCTION = YES;
631 | GCC_WARN_UNUSED_VARIABLE = YES;
632 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
633 | MTL_ENABLE_DEBUG_INFO = NO;
634 | SDKROOT = iphoneos;
635 | SUPPORTED_PLATFORMS = iphoneos;
636 | SWIFT_COMPILATION_MODE = wholemodule;
637 | SWIFT_OPTIMIZATION_LEVEL = "-O";
638 | TARGETED_DEVICE_FAMILY = "1,2";
639 | VALIDATE_PRODUCT = YES;
640 | };
641 | name = Release;
642 | };
643 | 97C147061CF9000F007C117D /* Debug */ = {
644 | isa = XCBuildConfiguration;
645 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
646 | buildSettings = {
647 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
648 | CLANG_ENABLE_MODULES = YES;
649 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
650 | DEVELOPMENT_TEAM = 6V53YACS2W;
651 | ENABLE_BITCODE = NO;
652 | INFOPLIST_FILE = Runner/Info.plist;
653 | INFOPLIST_KEY_CFBundleDisplayName = "Shorebird Clock";
654 | LD_RUNPATH_SEARCH_PATHS = (
655 | "$(inherited)",
656 | "@executable_path/Frameworks",
657 | );
658 | PRODUCT_BUNDLE_IDENTIFIER = com.shorebird.timeShift;
659 | PRODUCT_NAME = "$(TARGET_NAME)";
660 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
661 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
662 | SWIFT_VERSION = 5.0;
663 | VERSIONING_SYSTEM = "apple-generic";
664 | };
665 | name = Debug;
666 | };
667 | 97C147071CF9000F007C117D /* Release */ = {
668 | isa = XCBuildConfiguration;
669 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
670 | buildSettings = {
671 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
672 | CLANG_ENABLE_MODULES = YES;
673 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
674 | DEVELOPMENT_TEAM = 6V53YACS2W;
675 | ENABLE_BITCODE = NO;
676 | INFOPLIST_FILE = Runner/Info.plist;
677 | INFOPLIST_KEY_CFBundleDisplayName = "Shorebird Clock";
678 | LD_RUNPATH_SEARCH_PATHS = (
679 | "$(inherited)",
680 | "@executable_path/Frameworks",
681 | );
682 | PRODUCT_BUNDLE_IDENTIFIER = com.shorebird.timeShift;
683 | PRODUCT_NAME = "$(TARGET_NAME)";
684 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
685 | SWIFT_VERSION = 5.0;
686 | VERSIONING_SYSTEM = "apple-generic";
687 | };
688 | name = Release;
689 | };
690 | /* End XCBuildConfiguration section */
691 |
692 | /* Begin XCConfigurationList section */
693 | 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
694 | isa = XCConfigurationList;
695 | buildConfigurations = (
696 | 331C8088294A63A400263BE5 /* Debug */,
697 | 331C8089294A63A400263BE5 /* Release */,
698 | 331C808A294A63A400263BE5 /* Profile */,
699 | );
700 | defaultConfigurationIsVisible = 0;
701 | defaultConfigurationName = Release;
702 | };
703 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
704 | isa = XCConfigurationList;
705 | buildConfigurations = (
706 | 97C147031CF9000F007C117D /* Debug */,
707 | 97C147041CF9000F007C117D /* Release */,
708 | 249021D3217E4FDB00AE95B9 /* Profile */,
709 | );
710 | defaultConfigurationIsVisible = 0;
711 | defaultConfigurationName = Release;
712 | };
713 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
714 | isa = XCConfigurationList;
715 | buildConfigurations = (
716 | 97C147061CF9000F007C117D /* Debug */,
717 | 97C147071CF9000F007C117D /* Release */,
718 | 249021D4217E4FDB00AE95B9 /* Profile */,
719 | );
720 | defaultConfigurationIsVisible = 0;
721 | defaultConfigurationName = Release;
722 | };
723 | /* End XCConfigurationList section */
724 | };
725 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
726 | }
727 |
--------------------------------------------------------------------------------