Repository: happyharis/neumorphic Branch: master Commit: 5f2d23768f09 Files: 63 Total size: 128.1 KB Directory structure: gitextract_6ie4cbff/ ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── android/ │ ├── .gitignore │ ├── app/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── debug/ │ │ │ └── AndroidManifest.xml │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin/ │ │ │ │ └── com/ │ │ │ │ └── example/ │ │ │ │ └── neumorphism_web/ │ │ │ │ └── MainActivity.kt │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ └── launch_background.xml │ │ │ └── values/ │ │ │ └── styles.xml │ │ └── profile/ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ └── settings.gradle ├── ios/ │ ├── .gitignore │ ├── Flutter/ │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.imageset/ │ │ │ ├── Contents.json │ │ │ └── README.md │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ ├── Runner.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── Runner.xcscheme │ └── Runner.xcworkspace/ │ └── contents.xcworkspacedata ├── lib/ │ ├── calculator/ │ │ ├── calculator_logic.dart │ │ ├── calculator_view.dart │ │ ├── concave_decoration.dart │ │ ├── neu_calculator_button.dart │ │ └── neumorphic_theme.dart │ ├── main.dart │ ├── neumorphic_bar/ │ │ └── neumorphic_bar.dart │ ├── neumorphic_expenses/ │ │ ├── categories_row.dart │ │ ├── monthly_expenses_view.dart │ │ ├── pie_chart.dart │ │ └── pie_chart_view.dart │ ├── neumorphic_pie/ │ │ ├── middle_ring.dart │ │ ├── neumorphic_pie.dart │ │ └── progress_rings.dart │ ├── neumorphic_start_page/ │ │ ├── bottom_left_clipper.dart │ │ ├── bottom_left_clipper_bottom.dart │ │ ├── clip_shadow_path.dart │ │ ├── neumophic_start_page.dart │ │ ├── top_right_cipper_bottom.dart │ │ └── top_right_clipper.dart │ └── timer/ │ ├── digital_font/ │ │ ├── digital_colon.dart │ │ └── digital_number.dart │ ├── neu_digital_clock.dart │ ├── neu_hamburger_button.dart │ ├── neu_progress_painter.dart │ ├── neu_progress_pie_bar.dart │ ├── neu_reset_button.dart │ └── screen.dart ├── pubspec.yaml ├── test/ │ └── widget_test.dart └── web/ └── index.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Miscellaneous *.class *.log *.pyc *.swp .DS_Store .atom/ .buildlog/ .history .svn/ # IntelliJ related *.iml *.ipr *.iws .idea/ # The .vscode folder contains launch configuration and tasks you configure in # VS Code which you may wish to be included in version control, so this line # is commented out by default. #.vscode/ # Flutter/Dart/Pub related **/doc/api/ .dart_tool/ .flutter-plugins .flutter-plugins-dependencies .packages .pub-cache/ .pub/ /build/ # Web related lib/generated_plugin_registrant.dart # Exceptions to above rules. !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages ================================================ FILE: .metadata ================================================ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # # This file should be version controlled and should not be manually edited. version: revision: 18cd7a3601bcffb36fdf2f679f763b5e827c2e8e channel: beta project_type: app ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 happyharis 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. ================================================ FILE: README.md ================================================ This project is licensed under the terms of the MIT license. Buy Me A Coffee # Flutter Neumorphic Widgets Just a repo of neumorphic widgets. Neumorphic Bars | Neumorphic Pie Chart | Neumorphic Timer | Neumorphic Expense Pie Chart --- | --- | --- | --- | | | | | [📹 Video](https://youtu.be/0um8Pxs73xI) | [💻 Video](https://youtu.be/uGS-qVUCByQ) | [📹 Video](https://youtu.be/L6g4eRlAsh0) | [💻 Video](https://youtu.be/rkOc8WbgPqw) | Neumorphic Homepage (with SVG) | Neumorphic Calculator --- | --- | [📹 Video](https://youtu.be/KKO5PPkdKQg) | [📹 Video](https://youtu.be/8psTnM6RGxU) ================================================ FILE: android/.gitignore ================================================ gradle-wrapper.jar /.gradle /captures/ /gradlew /gradlew.bat /local.properties GeneratedPluginRegistrant.java ================================================ FILE: android/app/build.gradle ================================================ def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) } } def flutterRoot = localProperties.getProperty('flutter.sdk') if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { flutterVersionName = '1.0' } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion 28 sourceSets { main.java.srcDirs += 'src/main/kotlin' } lintOptions { disable 'InvalidPackage' } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.neumorphism_web" minSdkVersion 16 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } } } flutter { source '../..' } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } ================================================ FILE: android/app/src/debug/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/main/kotlin/com/example/neumorphism_web/MainActivity.kt ================================================ package com.example.neumorphism_web import androidx.annotation.NonNull; import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugins.GeneratedPluginRegistrant class MainActivity: FlutterActivity() { override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); } } ================================================ FILE: android/app/src/main/res/drawable/launch_background.xml ================================================ ================================================ FILE: android/app/src/main/res/values/styles.xml ================================================ ================================================ FILE: android/app/src/profile/AndroidManifest.xml ================================================ ================================================ FILE: android/build.gradle ================================================ buildscript { ext.kotlin_version = '1.3.50' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.5.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } allprojects { repositories { google() jcenter() } } rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { project.evaluationDependsOn(':app') } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: android/gradle/wrapper/gradle-wrapper.properties ================================================ #Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip ================================================ FILE: android/gradle.properties ================================================ org.gradle.jvmargs=-Xmx1536M android.enableR8=true android.useAndroidX=true android.enableJetifier=true ================================================ FILE: android/settings.gradle ================================================ include ':app' def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() def plugins = new Properties() def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') if (pluginsFile.exists()) { pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } } plugins.each { name, path -> def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() include ":$name" project(":$name").projectDir = pluginDirectory } ================================================ FILE: ios/.gitignore ================================================ *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 **/*sync/ .sconsign.dblite .tags* **/.vagrant/ **/DerivedData/ Icon? **/Pods/ **/.symlinks/ profile xcuserdata **/.generated/ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ Flutter/flutter_export_environment.sh ServiceDefinitions.json Runner/GeneratedPluginRegistrant.* # Exceptions to above rules. !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 ================================================ FILE: ios/Flutter/AppFrameworkInfo.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable App CFBundleIdentifier io.flutter.flutter.app CFBundleInfoDictionaryVersion 6.0 CFBundleName App CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 MinimumOSVersion 8.0 ================================================ FILE: ios/Flutter/Debug.xcconfig ================================================ #include "Generated.xcconfig" ================================================ FILE: ios/Flutter/Release.xcconfig ================================================ #include "Generated.xcconfig" ================================================ FILE: ios/Runner/AppDelegate.swift ================================================ import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } ================================================ FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "20x20", "idiom" : "iphone", "filename" : "Icon-App-20x20@2x.png", "scale" : "2x" }, { "size" : "20x20", "idiom" : "iphone", "filename" : "Icon-App-20x20@3x.png", "scale" : "3x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "Icon-App-29x29@1x.png", "scale" : "1x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "Icon-App-29x29@2x.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "Icon-App-29x29@3x.png", "scale" : "3x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "Icon-App-40x40@2x.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "Icon-App-40x40@3x.png", "scale" : "3x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "Icon-App-60x60@2x.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "Icon-App-60x60@3x.png", "scale" : "3x" }, { "size" : "20x20", "idiom" : "ipad", "filename" : "Icon-App-20x20@1x.png", "scale" : "1x" }, { "size" : "20x20", "idiom" : "ipad", "filename" : "Icon-App-20x20@2x.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "Icon-App-29x29@1x.png", "scale" : "1x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "Icon-App-29x29@2x.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "Icon-App-40x40@1x.png", "scale" : "1x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "Icon-App-40x40@2x.png", "scale" : "2x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "Icon-App-76x76@1x.png", "scale" : "1x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "Icon-App-76x76@2x.png", "scale" : "2x" }, { "size" : "83.5x83.5", "idiom" : "ipad", "filename" : "Icon-App-83.5x83.5@2x.png", "scale" : "2x" }, { "size" : "1024x1024", "idiom" : "ios-marketing", "filename" : "Icon-App-1024x1024@1x.png", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "LaunchImage.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "LaunchImage@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "LaunchImage@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md ================================================ # Launch Screen Assets You can customize the launch screen with your own desired assets by replacing the image files in this directory. 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. ================================================ FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: ios/Runner/Base.lproj/Main.storyboard ================================================ ================================================ FILE: ios/Runner/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName neumorphism_web CFBundlePackageType APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) CFBundleSignature ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance ================================================ FILE: ios/Runner/Runner-Bridging-Header.h ================================================ #import "GeneratedPluginRegistrant.h" ================================================ FILE: ios/Runner.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, ); name = Flutter; sourceTree = ""; }; 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, ); sourceTree = ""; }; 97C146EF1CF9000F007C117D /* Products */ = { isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, ); name = Products; sourceTree = ""; }; 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; sourceTree = ""; }; 97C146F11CF9000F007C117D /* Supporting Files */ = { isa = PBXGroup; children = ( ); name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, ); buildRules = ( ); dependencies = ( ); name = Runner; productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1020; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 97C146E51CF9000F007C117D; productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Thin Binary"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Run Script"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 97C146FB1CF9000F007C117D /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 97C147001CF9000F007C117D /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Profile; }; 249021D4217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = com.example.neumorphismWeb; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; 97C147061CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = com.example.neumorphismWeb; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; 97C147071CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = com.example.neumorphismWeb; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( 97C147031CF9000F007C117D /* Debug */, 97C147041CF9000F007C117D /* Release */, 249021D3217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( 97C147061CF9000F007C117D /* Debug */, 97C147071CF9000F007C117D /* Release */, 249021D4217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } ================================================ FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme ================================================ ================================================ FILE: ios/Runner.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: lib/calculator/calculator_logic.dart ================================================ import 'package:flutter/material.dart'; import 'package:math_expressions/math_expressions.dart'; class Calculator extends ChangeNotifier { num _value = 0; List _actions = [CalculatorNumber('0')]; num get value => _value; CalculatorVariable get currentVariable => _actions.last; void add() { takeAction( CalculatorAdd(), when: _actions.last is! CalculatorAdd, ); } void deduct() { takeAction( CalculatorDeduct(), when: _actions.last is! CalculatorDeduct, ); } void multiply() { takeAction( CalculatorMultiply(), when: _actions.last is! CalculatorMultiply, ); } void divide() { takeAction( CalculatorDivide(), when: _actions.last is! CalculatorDivide, ); } void takeAction( CalculatorVariable action, { @required bool when, }) { if (when) { if (_actions.last is MathOperator) { _actions.removeLast(); } else { _value = parseCalculatorActions(_actions); } _actions.add(action); } notifyListeners(); } void reset() { _actions = [CalculatorNumber('0')]; _value = 0; notifyListeners(); } void showResult() { _value = parseCalculatorActions(_actions); notifyListeners(); } void setValue(num number) { if (_actions.last is! CalculatorNumber) _value = 0; final stringifyedValue = _value.toString(); if (_value == 0) { _value = number; } else { _value = int.parse(stringifyedValue + number.toString()); } notifyListeners(); final lastAction = _actions.last; if (lastAction is CalculatorNumber) _actions.removeLast(); _actions.add(CalculatorNumber(_value.toString())); } } abstract class CalculatorVariable { CalculatorVariable({this.value}); final String value; } abstract class MathOperator {} class CalculatorAdd extends CalculatorVariable with MathOperator { String value = '+'; } class CalculatorMultiply extends CalculatorVariable with MathOperator { String value = '*'; } class CalculatorDivide extends CalculatorVariable with MathOperator { String value = '/'; } class CalculatorDeduct extends CalculatorVariable with MathOperator { String value = '-'; } class CalculatorNumber extends CalculatorVariable { CalculatorNumber(this.value); final String value; } num parseCalculatorActions(List actions) { final List mathVariables = []; actions.forEach((action) => mathVariables.add(action.value)); final variablesLength = mathVariables.length; if (variablesLength.isEven) mathVariables.removeLast(); final equation = mathVariables.join(' '); final num result = Parser().parse(equation).evaluate( EvaluationType.REAL, ContextModel(), ); print('$equation = $result'); final prettierResult = isInteger(result) ? result.round() : result; return prettierResult; } bool isInteger(num value) { return value is int || value == value.roundToDouble(); } ================================================ FILE: lib/calculator/calculator_view.dart ================================================ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:neumorphism_web/calculator/calculator_logic.dart'; import 'package:neumorphism_web/calculator/neu_calculator_button.dart'; import 'package:neumorphism_web/calculator/neumorphic_theme.dart'; import 'package:provider/provider.dart'; class CalculatorView extends StatelessWidget { @override Widget build(BuildContext context) { final calculator = Provider.of(context); return Material( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15.0), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Spacer(), Text( calculator.value.toString(), style: GoogleFonts.montserrat( fontSize: 100, fontWeight: FontWeight.w200, ), ), SizedBox(height: 150), ButtonRow(children: [ NeuCalculatorButton( text: 'AC', onPressed: calculator.reset, ), NeuCalculatorButton( text: '+/-', onPressed: () {}, ), NeuCalculatorButton( text: '%', onPressed: () {}, ), NeuCalculatorButton( text: '÷', textColor: kOrange, textSize: 45, onPressed: calculator.divide, isChosen: calculator.currentVariable is CalculatorDivide, ), ]), ButtonRow( children: [ NeuCalculatorButton( text: '7', onPressed: () => calculator.setValue(7), ), NeuCalculatorButton( text: '8', onPressed: () => calculator.setValue(8), ), NeuCalculatorButton( text: '9', onPressed: () => calculator.setValue(9), ), NeuCalculatorButton( text: 'x', textColor: kOrange, onPressed: calculator.multiply, isChosen: calculator.currentVariable is CalculatorMultiply, ), ], ), ButtonRow( children: [ NeuCalculatorButton( text: '4', onPressed: () => calculator.setValue(4), ), NeuCalculatorButton( text: '5', onPressed: () => calculator.setValue(5), ), NeuCalculatorButton( text: '6', onPressed: () => calculator.setValue(6), ), NeuCalculatorButton( text: '-', textColor: kOrange, textSize: 50, onPressed: calculator.deduct, isChosen: calculator.currentVariable is CalculatorDeduct, ), ], ), ButtonRow( children: [ NeuCalculatorButton( text: '1', onPressed: () => calculator.setValue(1), ), NeuCalculatorButton( text: '2', onPressed: () => calculator.setValue(2), ), NeuCalculatorButton( text: '3', onPressed: () => calculator.setValue(3), ), NeuCalculatorButton( text: '+', textColor: kOrange, textSize: 45, onPressed: calculator.add, isChosen: calculator.currentVariable is CalculatorAdd, ), ], ), ButtonRow( children: [ NeuCalculatorButton( text: '0', onPressed: () => calculator.setValue(0), isPill: true, ), NeuCalculatorButton( text: '.', onPressed: () {}, ), NeuCalculatorButton( text: '=', textColor: kOrange, textSize: 45, onPressed: calculator.showResult, ), ], ), SizedBox( height: MediaQuery.of(context).padding.bottom, ) ], ), ), ); } } class ButtonRow extends StatelessWidget { const ButtonRow({ Key key, @required this.children, }) : super(key: key); final List children; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: children, ), ); } } ================================================ FILE: lib/calculator/concave_decoration.dart ================================================ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; class ConcaveDecoration extends Decoration { final ShapeBorder shape; final double depression; final List colors; ConcaveDecoration({ @required this.shape, @required this.depression, this.colors, }) : assert(shape != null), assert(depression >= 0), assert(colors == null || colors.length == 2); @override BoxPainter createBoxPainter([onChanged]) => _ConcaveDecorationPainter(shape, depression, colors); @override EdgeInsetsGeometry get padding => shape.dimensions; } class _ConcaveDecorationPainter extends BoxPainter { ShapeBorder shape; double depression; List colors; _ConcaveDecorationPainter(this.shape, this.depression, this.colors) { colors ??= [Colors.black87, Colors.white]; } @override void paint( ui.Canvas canvas, ui.Offset offset, ImageConfiguration configuration) { final rect = offset & configuration.size; final shapePath = shape.getOuterPath(rect); final delta = 16 / rect.longestSide; final stops = [0.5 - delta, 0.5 + delta]; final path = Path() ..fillType = PathFillType.evenOdd ..addRect(rect.inflate(depression * 2)) ..addPath(shapePath, Offset.zero); canvas.save(); canvas.clipPath(shapePath); final paint = Paint() ..maskFilter = MaskFilter.blur(BlurStyle.normal, depression); final clipSize = rect.size.aspectRatio > 1 ? Size(rect.width, rect.height / 2) : Size(rect.width / 2, rect.height); for (final alignment in [Alignment.topLeft, Alignment.bottomRight]) { final shaderRect = alignment.inscribe(Size.square(rect.longestSide), rect); paint ..shader = ui.Gradient.linear( shaderRect.topLeft, shaderRect.bottomRight, colors, stops); canvas.save(); canvas.clipRect(alignment.inscribe(clipSize, rect)); canvas.drawPath(path, paint); canvas.restore(); } canvas.restore(); } } ================================================ FILE: lib/calculator/neu_calculator_button.dart ================================================ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:neumorphism_web/calculator/concave_decoration.dart'; import 'package:neumorphism_web/calculator/neumorphic_theme.dart'; import 'package:provider/provider.dart'; class NeuCalculatorButton extends StatefulWidget { NeuCalculatorButton({ Key key, @required this.text, this.textColor, this.textSize, this.isPill = false, @required this.onPressed, this.isChosen = false, }) : super(key: key); final bool isChosen; final bool isPill; final VoidCallback onPressed; final String text; final Color textColor; final double textSize; @override _NeuCalculatorButtonState createState() => _NeuCalculatorButtonState(); } class _NeuCalculatorButtonState extends State { bool _isPressed = false; @override void didUpdateWidget(NeuCalculatorButton oldWidget) { if (oldWidget.isChosen != widget.isChosen) { setState(() => _isPressed = widget.isChosen); } super.didUpdateWidget(oldWidget); } void _onPointerDown(PointerDownEvent event) { setState(() => _isPressed = true); widget.onPressed(); } void _onPointerUp(PointerUpEvent event) { setState(() => _isPressed = widget.isChosen); } @override Widget build(BuildContext context) { final neumorphicTheme = Provider.of(context); final width = MediaQuery.of(context).size.width; final squareSideLength = width / 5; final buttonWidth = squareSideLength * (widget.isPill ? 2.2 : 1); final buttonSize = Size(buttonWidth, squareSideLength); final innerShadow = ConcaveDecoration( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(buttonSize.width), ), colors: neumorphicTheme.innerShadowColors, depression: 10, ); final outerShadow = BoxDecoration( border: Border.all(color: neumorphicTheme.borderColor), borderRadius: BorderRadius.circular(buttonSize.width), color: neumorphicTheme.buttonColor, boxShadow: neumorphicTheme.outerShadow, ); return SizedBox( height: buttonSize.height, width: buttonSize.width, child: Listener( onPointerDown: _onPointerDown, onPointerUp: _onPointerUp, child: Stack( children: [ AnimatedContainer( duration: const Duration(milliseconds: 50), padding: EdgeInsets.all(width / 12), decoration: _isPressed ? innerShadow : outerShadow, ), Align( alignment: Alignment(widget.isPill ? -0.6 : 0, 0), child: Text( widget.text, style: GoogleFonts.montserrat( fontSize: widget.textSize ?? 30, fontWeight: FontWeight.w200, color: widget.textColor ?? Theme.of(context).textTheme.bodyText1.color, ), ), ), ], ), ), ); } } ================================================ FILE: lib/calculator/neumorphic_theme.dart ================================================ import 'package:flutter/material.dart'; class NeumorphicTheme { NeumorphicTheme({ this.outerShadow, this.innerShadowColors, this.borderColor, this.buttonColor, this.isDark = false, }); final List outerShadow; final List innerShadowColors; final Color borderColor; final Color buttonColor; final bool isDark; } final darkNeumorphicTheme = NeumorphicTheme( innerShadowColors: [ kDarkBackgroundShadowColour, kOutline, ], borderColor: kOutline, buttonColor: kDarkBackgroundColour, outerShadow: kDarkBackgroundShadow, isDark: true, ); final lightNeumorphicTheme = NeumorphicTheme( innerShadowColors: [kDarkShadow, Colors.white], borderColor: Colors.transparent, buttonColor: kBackgroundColour, outerShadow: kShadow, ); final kBackgroundColour = Color.fromRGBO(239, 238, 238, 1); final kOrange = Color.fromRGBO(238, 134, 48, 1); // rgb(238, 134, 48) final kDarkShadow = Color.fromRGBO(216, 213, 208, 1); // rgb(216, 213, 208) final kDarkBackgroundColour = Color.fromRGBO(38, 38, 38, 1); // rgb(38,38,38) final kDarkBackgroundShadowColour = Color.fromRGBO( 30, 30, 30, 1, ); // rgb(30,30,30) final kOutline = Color.fromRGBO(46, 46, 46, 1); // rgb(46,46,46) final kShadow = [ BoxShadow( blurRadius: 15, offset: -Offset(5, 5), color: Colors.white, ), BoxShadow( blurRadius: 15, offset: Offset(4.5, 4.5), color: kDarkShadow, ) ]; final kDarkBackgroundShadow = [ BoxShadow( blurRadius: 15, offset: Offset(4.5, 4.5), color: kDarkBackgroundShadowColour, ) ]; ================================================ FILE: lib/main.dart ================================================ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:neumorphism_web/calculator/calculator_logic.dart'; import 'package:neumorphism_web/calculator/calculator_view.dart'; import 'package:neumorphism_web/calculator/neumorphic_theme.dart'; import 'package:neumorphism_web/neumorphic_bar/neumorphic_bar.dart'; import 'package:neumorphism_web/neumorphic_pie/neumorphic_pie.dart'; import 'package:provider/provider.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Neumorphic Widgets', darkTheme: ThemeData( brightness: Brightness.dark, canvasColor: kDarkBackgroundColour, ), theme: ThemeData( canvasColor: kBackgroundColour, backgroundColor: Color.fromRGBO(231, 240, 247, 1), scaffoldBackgroundColor: Color.fromRGBO(231, 240, 247, 1), textTheme: TextTheme( headline1: GoogleFonts.dmSans( textStyle: TextStyle( fontSize: 43, fontWeight: FontWeight.w900, color: Color.fromRGBO(49, 68, 105, 1), ), ), headline4: GoogleFonts.dmSans( textStyle: TextStyle( fontSize: 28, fontWeight: FontWeight.w800, color: Color.fromRGBO(49, 68, 105, 1), ), ), ), ), home: Builder( builder: (BuildContext context) { final brightnessValue = MediaQuery.of(context).platformBrightness; bool isDark = brightnessValue == Brightness.dark; final theme = isDark ? darkNeumorphicTheme : lightNeumorphicTheme; // Intro, need to show the first use case: 1+1 = 2 (iphone case) // part of -1 Since got 2 providers, use multiprovider return MultiProvider( providers: [ ProxyProvider0(update: (_, __) => theme), // part of -1. Explain wanted to do on mobx and did not pursue // and how it is not relevant. However, very good async stuff, // to the non-async part. Yeah. So, use what's availabe, which // is change notifier and change notifier provider ChangeNotifierProvider(create: (_) => Calculator()) ], child: CalculatorView(), ); }, ), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key}) : super(key: key); @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: backgroundColor, body: Column( mainAxisSize: MainAxisSize.min, children: [ BarDays(), SizedBox(height: 40), NeumorphicPie(), ], ), ); } } class BarDays extends StatelessWidget { const BarDays({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ NeumorphicBar( width: 200, height: 400, value: 0.5, text: 'Mon', ), NeumorphicBar( width: 200, height: 400, value: 0.9, text: 'Tue', color: Color.fromRGBO(0, 200, 156, 1), ), NeumorphicBar( width: 200, height: 400, value: 0.7, text: 'Wed', ), NeumorphicBar( width: 200, height: 400, value: 1, text: 'Thur', ), ], ); } } ================================================ FILE: lib/neumorphic_bar/neumorphic_bar.dart ================================================ import 'package:flutter/material.dart'; class NeumorphicBar extends StatelessWidget { const NeumorphicBar({ Key key, @required this.width, @required this.height, @required this.value, @required this.text, this.color, }) : super(key: key); final num width; final num height; /// Value from 1.0 to 0.0 final num value; final String text; final Color color; @override Widget build(BuildContext context) { final innerContainerWidth = width * 0.95; final innerContainerHeight = height * value * 0.96; return Column( mainAxisSize: MainAxisSize.min, children: [ Container( height: height * 1.01, width: width.toDouble() / 4, child: Stack( children: [ DugContainer( width: width, height: height, ), InnerContainer( width: innerContainerWidth, height: innerContainerHeight, color: color), ], ), ), SizedBox(height: 10), Text( text, style: TextStyle( color: Colors.blueGrey[300], ), ), ], ); } } class InnerContainer extends StatelessWidget { const InnerContainer({ Key key, @required this.height, @required this.width, this.color, }) : super(key: key); final num height; final num width; final Color color; @override Widget build(BuildContext context) { return Align( alignment: Alignment.bottomCenter, child: Padding( padding: const EdgeInsets.only(bottom: 5.0), child: Container( height: height * 600 / 896, width: width * 85 / 414, decoration: BoxDecoration( borderRadius: BorderRadius.circular(95.0), color: color ?? Color.fromRGBO(235, 233, 232, 1), boxShadow: [ BoxShadow( offset: Offset(1.5, 1.5), color: Colors.black38, blurRadius: 2, ), BoxShadow( offset: Offset(-1.5, -1.5), color: color?.withOpacity(0.95) ?? Colors.white.withOpacity(0.85), blurRadius: 2, ) ], ), ), ), ); } } class DugContainer extends StatelessWidget { const DugContainer({ Key key, @required this.height, @required this.width, }) : super(key: key); final num height; final num width; @override Widget build(BuildContext context) { return Align( alignment: Alignment.bottomCenter, child: Container( height: height * 600 / 896, width: width * 100 / 414, decoration: BoxDecoration( boxShadow: [ BoxShadow( color: exteriorShadow, offset: Offset(0.0, 0.0), ), BoxShadow( color: interiorShadow, offset: Offset(0.0, 0.0), spreadRadius: -1.0, blurRadius: 11.0, ), ], borderRadius: BorderRadius.circular(100.0), ), ), ); } } const exteriorShadow = Color.fromRGBO(209, 207, 205, 1); const interiorShadow = Color.fromRGBO(224, 221, 217, 1); const backgroundColor = Color.fromRGBO(235, 235, 234, 1); ================================================ FILE: lib/neumorphic_expenses/categories_row.dart ================================================ import 'package:flutter/material.dart'; import 'package:neumorphism_web/neumorphic_expenses/pie_chart.dart'; class CategoriesRow extends StatelessWidget { const CategoriesRow({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { return Expanded( flex: 3, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ for (var category in kCategories) ExpenseCategory( text: category.name, index: kCategories.indexOf(category)) ], ), ); } } class ExpenseCategory extends StatelessWidget { const ExpenseCategory({ Key key, @required this.index, @required this.text, }) : super(key: key); final int index; final String text; @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.only(bottom: 10), child: Row( children: [ Container( width: 7, height: 7, decoration: BoxDecoration( shape: BoxShape.circle, color: kNeumorphicColors.elementAt(index % kNeumorphicColors.length), ), ), SizedBox(width: 20), Text(text.capitalize()), ], ), ); } } extension StringExtension on String { String capitalize() { return "${this[0].toUpperCase()}${this.substring(1)}"; } } ================================================ FILE: lib/neumorphic_expenses/monthly_expenses_view.dart ================================================ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:neumorphism_web/neumorphic_expenses/categories_row.dart'; import 'package:neumorphism_web/neumorphic_expenses/pie_chart_view.dart'; class MontlyExpensesView extends StatelessWidget { @override Widget build(BuildContext context) { final height = MediaQuery.of(context).size.height; return Scaffold( backgroundColor: Color.fromRGBO(193, 214, 233, 1), body: SafeArea( child: Column( children: [ Spacer(), SizedBox( height: height * 0.43, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 25.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: height * 0.065), Text( 'Monthly Expenses', style: GoogleFonts.rubik( fontWeight: FontWeight.w400, fontSize: 18), ), Expanded( child: Row( children: [ CategoriesRow(), PieChartView(), ], ), ), ], ), ), ) ], ), ), ); } } ================================================ FILE: lib/neumorphic_expenses/pie_chart.dart ================================================ import 'dart:math'; import 'package:flutter/material.dart'; class PieChart extends CustomPainter { PieChart({@required this.categories, @required this.width}); final List categories; final double width; @override void paint(Canvas canvas, Size size) { Offset center = Offset(size.width / 2, size.height / 2); double radius = min(size.width / 2, size.height / 2); var paint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = width / 2; double total = 0; // Calculate total amount from each category categories.forEach((expense) => total += expense.amount); // The angle/radian at 12 o'clcok double startRadian = -pi / 2; for (var index = 0; index < categories.length; index++) { final currentCategory = categories.elementAt(index); // Amount of length to paint is a percentage of the perimeter of a circle (2 x pi) final sweepRadian = currentCategory.amount / total * 2 * pi; // Used modulo/remainder to catch use case if there is more than 6 colours paint.color = kNeumorphicColors.elementAt(index % categories.length); canvas.drawArc( Rect.fromCircle(center: center, radius: radius), startRadian, sweepRadian, false, paint, ); // The new startRadian starts from where the previous sweepRadian. // Example, a circle perimeter is 10. // Category A takes a startRadian 0 and ends at sweepRadian 5. // Category B takes the startRadian where Category A left off, which is 5 // and ends at sweepRadian 7. // Category C takes the startRadian where Category B left off, which is 7 // and so on. startRadian += sweepRadian; } } @override bool shouldRepaint(CustomPainter oldDelegate) => true; } class Category { Category(this.name, {@required this.amount}); final String name; final double amount; } final kCategories = [ Category('groceries', amount: 500.00), Category('online Shopping', amount: 150.00), Category('eating', amount: 90.00), Category('bills', amount: 90.00), Category('subscriptions', amount: 40.00), Category('fees', amount: 20.00), ]; final kNeumorphicColors = [ Color.fromRGBO(82, 98, 255, 1), // rgb(82, 98, 255) Color.fromRGBO(46, 198, 255, 1), // rgb(46, 198, 255) Color.fromRGBO(123, 201, 82, 1), // rgb(123, 201, 82) Color.fromRGBO(255, 171, 67, 1), // rgb(255, 171, 67) Color.fromRGBO(252, 91, 57, 1), // rgb(252, 91, 57) Color.fromRGBO(139, 135, 130, 1), //rgb(139, 135, 130) ]; ================================================ FILE: lib/neumorphic_expenses/pie_chart_view.dart ================================================ import 'package:flutter/material.dart'; import 'package:neumorphism_web/neumorphic_expenses/pie_chart.dart'; class PieChartView extends StatelessWidget { const PieChartView({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { return Expanded( flex: 4, child: LayoutBuilder( builder: (context, constraint) => Container( decoration: BoxDecoration( color: Color.fromRGBO(193, 214, 233, 1), shape: BoxShape.circle, boxShadow: [ BoxShadow( spreadRadius: -10, blurRadius: 17, offset: Offset(-5, -5), color: Colors.white, ), BoxShadow( spreadRadius: -2, blurRadius: 10, offset: Offset(7, 7), color: Color.fromRGBO(146, 182, 216, 1), ) ], ), child: Stack( children: [ Center( child: SizedBox( width: constraint.maxWidth * 0.6, child: CustomPaint( child: Center(), foregroundPainter: PieChart( width: constraint.maxWidth * 0.5, categories: kCategories, ), ), ), ), Center( child: Container( height: constraint.maxWidth * 0.4, decoration: BoxDecoration( color: Color.fromRGBO(193, 214, 233, 1), shape: BoxShape.circle, boxShadow: [ BoxShadow( blurRadius: 1, offset: Offset(-1, -1), color: Colors.white, ), BoxShadow( spreadRadius: -2, blurRadius: 10, offset: Offset(5, 5), color: Colors.black.withOpacity(0.5), ) ], ), child: Center( child: Text('\$1280.20'), ), ), ), ], ), ), ), ); } } ================================================ FILE: lib/neumorphic_pie/middle_ring.dart ================================================ import 'package:flutter/material.dart'; import 'package:neumorphism_web/neumorphic_pie/neumorphic_pie.dart'; class MiddleRing extends StatelessWidget { final num width; const MiddleRing({Key key, @required this.width}) : super(key: key); @override Widget build(BuildContext context) { return Container( height: width, width: width, decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, ), child: Center( child: Container( height: width * 0.3, width: width * 0.3, decoration: BoxDecoration( shape: BoxShape.circle, boxShadow: [ // Edge shadow BoxShadow( offset: Offset(-1.5, -1.5), color: shadowColor, spreadRadius: 2.0, // blurRadius: 0, ), // Circular shadow BoxShadow( offset: Offset(1.5, 1.5), color: Colors.white, spreadRadius: 2.0, blurRadius: 4, ) ], ), ), ), ); } } ================================================ FILE: lib/neumorphic_pie/neumorphic_pie.dart ================================================ import 'dart:math'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:neumorphism_web/neumorphic_pie/middle_ring.dart'; import 'package:neumorphism_web/neumorphic_pie/progress_rings.dart'; class NeumorphicPie extends StatelessWidget { @override Widget build(BuildContext context) { // Outer white circle return Container( height: 290.0, width: 290.0, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white24, ), child: Center( // Container of the pie chart child: Container( height: 200.0, width: 200.0, decoration: BoxDecoration( shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.8), spreadRadius: 20, blurRadius: 45, offset: Offset(0, 7), // changes position of shadow ), ], ), child: Stack( children: [ Center(child: MiddleRing(width: 300.0)), Transform.rotate( angle: pi * 1.6, child: CustomPaint( child: Center(), painter: ProgressRings( completedPercentage: 0.65, circleWidth: 50.0, gradient: greenGradient, gradientStartAngle: 0.0, gradientEndAngle: pi / 3, progressStartAngle: 1.85, lengthToRemove: 3, ), ), ), Transform.rotate( angle: pi / 1.8, child: CustomPaint( child: Center(), painter: ProgressRings( completedPercentage: 0.20, circleWidth: 50.0, gradient: turqoiseGradient, ), ), ), Transform.rotate( angle: pi / 2.6, child: CustomPaint( child: Center(), painter: ProgressRings( completedPercentage: 0.35, circleWidth: 50.0, gradient: redGradient, gradientStartAngle: 0.0, gradientEndAngle: pi / 2, progressStartAngle: 1.85, ), ), ), Transform.rotate( angle: pi * 1.1, child: CustomPaint( child: Center(), painter: ProgressRings( completedPercentage: 0.24, circleWidth: 50.0, gradient: orangeGradient, gradientStartAngle: 0.0, gradientEndAngle: pi / 2, progressStartAngle: 1.85, ), ), ), ], ), ), ), ); } } final innerColor = Color.fromRGBO(233, 242, 249, 1); final shadowColor = Color.fromRGBO(220, 227, 234, 1); const greenGradient = [ Color.fromRGBO(223, 250, 92, 1), Color.fromRGBO(129, 250, 112, 1) ]; const turqoiseGradient = [ Color.fromRGBO(91, 253, 199, 1), Color.fromRGBO(129, 182, 205, 1) ]; const redGradient = [ Color.fromRGBO(255, 93, 91, 1), Color.fromRGBO(254, 154, 92, 1), ]; const orangeGradient = [ Color.fromRGBO(251, 173, 86, 1), Color.fromRGBO(253, 255, 93, 1), ]; ================================================ FILE: lib/neumorphic_pie/progress_rings.dart ================================================ import 'dart:math'; import 'package:flutter/material.dart'; class ProgressRings extends CustomPainter { /// From 0.0 to 1.0 final double completedPercentage; final double circleWidth; final List gradient; final num gradientStartAngle; final num gradientEndAngle; final double progressStartAngle; final double lengthToRemove; ProgressRings({ this.completedPercentage, this.circleWidth, this.gradient, this.gradientStartAngle = 3 * pi / 2, this.gradientEndAngle = 4 * pi / 2, this.progressStartAngle = 0, this.lengthToRemove = 0, }); @override void paint(Canvas canvas, Size size) { Offset center = Offset(size.width / 2, size.height / 2); double radius = min(size.width / 2, size.height / 2); double arcAngle = 2 * pi * (completedPercentage); Rect boundingSquare = Rect.fromCircle(center: center, radius: radius); paint(List colors, {double startAngle = 0.0, double endAngle = pi * 2}) { final Gradient gradient = SweepGradient( startAngle: startAngle, endAngle: endAngle, colors: colors, ); return Paint() ..strokeCap = StrokeCap.round ..style = PaintingStyle.stroke ..strokeWidth = circleWidth ..shader = gradient.createShader(boundingSquare); } canvas.drawArc( boundingSquare, -pi / 2 + progressStartAngle, arcAngle - lengthToRemove, false, paint( gradient, startAngle: gradientStartAngle, endAngle: gradientEndAngle, ), ); } @override bool shouldRepaint(CustomPainter painter) => true; } ================================================ FILE: lib/neumorphic_start_page/bottom_left_clipper.dart ================================================ import 'package:flutter/material.dart'; /// Neumorphic clipper that is placed the top most on the bottom left class BottomLeftNeuClipper extends CustomClipper { @override Path getClip(Size size) { Path path = Path(); final double _xScaling = size.width / 375; final double _yScaling = size.height / 812; path.lineTo(15.8844 * _xScaling, 194.032 * _yScaling); path.cubicTo( 41.7322 * _xScaling, 187.818 * _yScaling, 64.4102 * _xScaling, 168.631 * _yScaling, 91.895 * _xScaling, 173.507 * _yScaling, ); path.cubicTo( 119.396 * _xScaling, 178.385 * _yScaling, 142.116 * _xScaling, 203.325 * _yScaling, 166.569 * _xScaling, 220.764 * _yScaling, ); path.cubicTo( 190.591 * _xScaling, 237.895 * _yScaling, 218.969 * _xScaling, 250.109 * _yScaling, 235.467 * _xScaling, 275.895 * _yScaling, ); path.cubicTo( 251.907 * _xScaling, 301.59 * _yScaling, 251.874 * _xScaling, 333.261 * _yScaling, 257.411 * _xScaling, 362.746 * _yScaling, ); path.cubicTo( 262.655 * _xScaling, 390.668 * _yScaling, 268.591 * _xScaling, 418.289 * _yScaling, 267.371 * _xScaling, 445.781 * _yScaling, ); path.cubicTo( 266.093 * _xScaling, 474.598 * _yScaling, 264.341 * _xScaling, 504.85 * _yScaling, 250.113 * _xScaling, 527.259 * _yScaling, ); path.cubicTo( 235.895 * _xScaling, 549.652 * _yScaling, 208.962 * _xScaling, 556.46 * _yScaling, 187.856 * _xScaling, 571.016 * _yScaling, ); path.cubicTo( 165.513 * _xScaling, 586.425 * _yScaling, 149.084 * _xScaling, 615.374 * _yScaling, 121.194 * _xScaling, 616.171 * _yScaling, ); path.cubicTo( 93.2682 * _xScaling, 616.968 * _yScaling, 68.0885 * _xScaling, 589.718 * _yScaling, 41.4531 * _xScaling, 575.197 * _yScaling, ); path.cubicTo( 17.0259 * _xScaling, 561.879 * _yScaling, -7.81309 * _xScaling, 551.216 * _yScaling, -30.2651 * _xScaling, 533.596 * _yScaling, ); path.cubicTo( -54.5775 * _xScaling, 514.517 * _yScaling, -82.7712 * _xScaling, 496.531 * _yScaling, -95.8271 * _xScaling, 467.465 * _yScaling, ); path.cubicTo( -108.883 * _xScaling, 438.398 * _yScaling, -100.086 * _xScaling, 406.852 * _yScaling, -100.977 * _xScaling, 376.171 * _yScaling, ); path.cubicTo( -101.84 * _xScaling, 346.477 * _yScaling, -107.228 * _xScaling, 316.252 * _yScaling, -101.029 * _xScaling, 288.404 * _yScaling, ); path.cubicTo( -94.5197 * _xScaling, 259.162 * _yScaling, -85.6289 * _xScaling, 228.319 * _yScaling, -64.217 * _xScaling, 211.036 * _yScaling, ); path.cubicTo( -42.8756 * _xScaling, 193.809 * _yScaling, -10.8945 * _xScaling, 200.469 * _yScaling, 15.8844 * _xScaling, 194.032 * _yScaling, ); path.cubicTo( 15.8844 * _xScaling, 194.032 * _yScaling, 15.8844 * _xScaling, 194.032 * _yScaling, 15.8844 * _xScaling, 194.032 * _yScaling, ); return path; } @override bool shouldReclip(CustomClipper oldClipper) => true; } ================================================ FILE: lib/neumorphic_start_page/bottom_left_clipper_bottom.dart ================================================ import 'package:flutter/material.dart'; /// Neumorphic clipper that is placed the bottom most on the bottom left /// Btm just stands for the bottom most class BottomLeftNeuClipperBtm extends CustomClipper { @override Path getClip(Size size) { Path path = Path(); final double _xScaling = size.width / 375; final double _yScaling = size.height / 812; path.lineTo(-85.8912 * _xScaling, 289.388 * _yScaling); path.cubicTo( -34.0201 * _xScaling, 277.413 * _yScaling, 11.0669 * _xScaling, 238.676 * _yScaling, 66.5766 * _xScaling, 249.544 * _yScaling, ); path.cubicTo( 122.119 * _xScaling, 260.418 * _yScaling, 168.648 * _xScaling, 312.375 * _yScaling, 218.439 * _xScaling, 348.97 * _yScaling, ); path.cubicTo( 267.351 * _xScaling, 384.919 * _yScaling, 324.885 * _xScaling, 410.894 * _yScaling, 358.91 * _xScaling, 464.401 * _yScaling, ); path.cubicTo( 392.814 * _xScaling, 517.716 * _yScaling, 393.723 * _xScaling, 582.808 * _yScaling, 405.784 * _xScaling, 643.579 * _yScaling, ); path.cubicTo( 417.205 * _xScaling, 701.128 * _yScaling, 430.011 * _xScaling, 758.078 * _yScaling, 428.401 * _xScaling, 814.544 * _yScaling, ); path.cubicTo( 426.713 * _xScaling, 873.733 * _yScaling, 424.117 * _xScaling, 935.854 * _yScaling, 396.149 * _xScaling, 981.472 * _yScaling, ); path.cubicTo( 368.2 * _xScaling, 1027.06 * _yScaling, 314.161 * _xScaling, 1040.22 * _yScaling, 272.099 * _xScaling, 1069.49 * _yScaling, ); path.cubicTo( 227.569 * _xScaling, 1100.47 * _yScaling, 195.369 * _xScaling, 1159.46 * _yScaling, 139.219 * _xScaling, 1160.24 * _yScaling, ); path.cubicTo( 82.9956 * _xScaling, 1161.02 * _yScaling, 31.44 * _xScaling, 1104.24 * _yScaling, -22.6558 * _xScaling, 1073.57 * _yScaling, ); path.cubicTo( -72.2668 * _xScaling, 1045.45 * _yScaling, -122.625 * _xScaling, 1022.77 * _yScaling, -168.39 * _xScaling, 985.865 * _yScaling, ); path.cubicTo( -217.948 * _xScaling, 945.903 * _yScaling, -275.289 * _xScaling, 908.069 * _yScaling, -302.481 * _xScaling, 847.928 * _yScaling, ); path.cubicTo( -329.673 * _xScaling, 787.785 * _yScaling, -312.924 * _xScaling, 723.222 * _yScaling, -315.664 * _xScaling, 660.137 * _yScaling, ); path.cubicTo( -318.316 * _xScaling, 599.081 * _yScaling, -330.1 * _xScaling, 536.794 * _yScaling, -318.471 * _xScaling, 479.749 * _yScaling, ); path.cubicTo( -306.26 * _xScaling, 419.851 * _yScaling, -289.302 * _xScaling, 356.733 * _yScaling, -246.707 * _xScaling, 321.87 * _yScaling, ); path.cubicTo( -204.252 * _xScaling, 287.122 * _yScaling, -139.631 * _xScaling, 301.795 * _yScaling, -85.8912 * _xScaling, 289.388 * _yScaling, ); path.cubicTo( -85.8912 * _xScaling, 289.388 * _yScaling, -85.8912 * _xScaling, 289.388 * _yScaling, -85.8912 * _xScaling, 289.388 * _yScaling, ); return path; } @override bool shouldReclip(CustomClipper oldClipper) => true; } ================================================ FILE: lib/neumorphic_start_page/clip_shadow_path.dart ================================================ import 'package:flutter/material.dart'; @immutable class ClipShadowPath extends StatelessWidget { final BoxShadow shadow; final CustomClipper clipper; final Widget child; ClipShadowPath({ @required this.shadow, @required this.clipper, @required this.child, }); @override Widget build(BuildContext context) { return CustomPaint( painter: _ClipShadowShadowPainter( clipper: this.clipper, shadow: this.shadow, ), child: ClipPath(child: child, clipper: this.clipper), ); } } class _ClipShadowShadowPainter extends CustomPainter { final BoxShadow shadow; final CustomClipper clipper; _ClipShadowShadowPainter({@required this.shadow, @required this.clipper}); @override void paint(Canvas canvas, Size size) { var paint = shadow.toPaint() ..maskFilter = MaskFilter.blur( BlurStyle.normal, shadow.spreadRadius, ); var clipPath = clipper.getClip(size).shift(shadow.offset); canvas.drawPath(clipPath, paint); } @override bool shouldRepaint(CustomPainter oldDelegate) { return true; } } ================================================ FILE: lib/neumorphic_start_page/neumophic_start_page.dart ================================================ import 'package:flutter/material.dart'; import 'package:neumorphism_web/neumorphic_start_page/bottom_left_clipper.dart'; import 'package:neumorphism_web/neumorphic_start_page/bottom_left_clipper_bottom.dart'; import 'package:neumorphism_web/neumorphic_start_page/clip_shadow_path.dart'; import 'package:neumorphism_web/neumorphic_start_page/top_right_cipper_bottom.dart'; import 'package:neumorphism_web/neumorphic_start_page/top_right_clipper.dart'; import 'package:transparent_image/transparent_image.dart'; class NeumorphicStartPage extends StatelessWidget { @override Widget build(BuildContext context) { final width = MediaQuery.of(context).size.width; final height = MediaQuery.of(context).size.height; final boxShadow = BoxShadow( color: Colors.grey, offset: Offset(-5, 3), blurRadius: 5, spreadRadius: 10, ); // Neumorphic colored container with 99% app width final widthNeuContainer = Container( width: width * 0.99, color: kNeumorphicColor, ); // Neumorphic colored container with 99% app height final heightNeuContainer = Container( height: height * 0.99, color: kNeumorphicColor, ); return Material( child: Stack( children: [ Align( child: ClipShadowPath( shadow: boxShadow, clipper: TopRightNeuClipperBtm(), child: widthNeuContainer, ), ), Align( alignment: Alignment(30, -1), child: ClipShadowPath( shadow: boxShadow, clipper: TopRightNeuClipper(), child: widthNeuContainer, ), ), Align( alignment: Alignment(0, 30.5), child: ClipShadowPath( shadow: boxShadow, clipper: BottomLeftNeuClipperBtm(), child: heightNeuContainer, ), ), Align( alignment: Alignment(0, 9), child: SizedBox( width: width, height: height * 0.99, child: FadeInImage.memoryNetwork( placeholder: kTransparentImage, image: kHomeImage, fit: BoxFit.fitWidth, ), ), ), Align( alignment: Alignment(0, 80.5), child: ClipShadowPath( shadow: boxShadow, clipper: BottomLeftNeuClipper(), child: heightNeuContainer, ), ), Align( alignment: Alignment.topCenter, child: Padding( padding: const EdgeInsets.only(top: 100), child: Text( 'Neumorphic', style: Theme.of(context).textTheme.headline1, ), ), ), Align( alignment: Alignment.bottomCenter, child: Padding( padding: const EdgeInsets.only(bottom: 58.0), child: SizedBox( width: width * 0.8, child: MaterialButton( onPressed: () {}, padding: const EdgeInsets.symmetric(vertical: 10), color: Theme.of(context).textTheme.headline1.color, child: Text( 'Let\'s Get Started', style: TextStyle(fontSize: 24, color: Colors.white), ), ), ), ), ) ], ), ); } } final kNeumorphicColor = Color.fromRGBO(235, 228, 229, 1); // rgb(235, 228, 229) final kHomeImage = 'https://user-images.githubusercontent.com/31005114/78748465-b5327d00-799e-11ea-9f40-38d322a9531a.png'; ================================================ FILE: lib/neumorphic_start_page/top_right_cipper_bottom.dart ================================================ import 'package:flutter/material.dart'; /// Neumorphic clipper that is placed the bottom most on the top right /// Btm just stands for the bottom most class TopRightNeuClipperBtm extends CustomClipper { @override Path getClip(Size size) { Path path = Path(); final double _xScaling = size.width / 375; final double _yScaling = size.height / 812; path.lineTo(272.31 * _xScaling, -94.0542 * _yScaling); path.cubicTo( 293.384 * _xScaling, -98.9195 * _yScaling, 311.702 * _xScaling, -114.658 * _yScaling, 334.255 * _xScaling, -110.242 * _yScaling, ); path.cubicTo( 356.821 * _xScaling, -105.825 * _yScaling, 375.725 * _xScaling, -84.715 * _yScaling, 395.955 * _xScaling, -69.847 * _yScaling, ); path.cubicTo( 415.827 * _xScaling, -55.2416 * _yScaling, 439.202 * _xScaling, -44.688 * _yScaling, 453.026 * _xScaling, -22.9491 * _yScaling, ); path.cubicTo( 466.801 * _xScaling, -1.28773 * _yScaling, 467.17 * _xScaling, 25.1582 * _yScaling, 472.07 * _xScaling, 49.8486 * _yScaling, ); path.cubicTo( 476.71 * _xScaling, 73.2298 * _yScaling, 481.913 * _xScaling, 96.368 * _yScaling, 481.259 * _xScaling, 119.309 * _yScaling, ); path.cubicTo( 480.574 * _xScaling, 143.357 * _yScaling, 479.519 * _xScaling, 168.596 * _yScaling, 468.156 * _xScaling, 187.13 * _yScaling, ); path.cubicTo( 456.801 * _xScaling, 205.651 * _yScaling, 434.845 * _xScaling, 210.999 * _yScaling, 417.756 * _xScaling, 222.889 * _yScaling, ); path.cubicTo( 399.664 * _xScaling, 235.477 * _yScaling, 386.582 * _xScaling, 259.445 * _yScaling, 363.769 * _xScaling, 259.761 * _yScaling, ); path.cubicTo( 340.926 * _xScaling, 260.078 * _yScaling, 319.98 * _xScaling, 237.008 * _yScaling, 298.001 * _xScaling, 224.549 * _yScaling, ); path.cubicTo( 277.845 * _xScaling, 213.123 * _yScaling, 257.385 * _xScaling, 203.908 * _yScaling, 238.791 * _xScaling, 188.915 * _yScaling, ); path.cubicTo( 218.657 * _xScaling, 172.679 * _yScaling, 195.36 * _xScaling, 157.307 * _yScaling, 184.312 * _xScaling, 132.873 * _yScaling, ); path.cubicTo( 173.264 * _xScaling, 108.438 * _yScaling, 180.069 * _xScaling, 82.2063 * _yScaling, 178.956 * _xScaling, 56.5758 * _yScaling, ); path.cubicTo( 177.879 * _xScaling, 31.7698 * _yScaling, 173.091 * _xScaling, 6.46339 * _yScaling, 177.816 * _xScaling, -16.7134 * _yScaling, ); path.cubicTo( 182.777 * _xScaling, -41.0492 * _yScaling, 189.666 * _xScaling, -66.6928 * _yScaling, 206.972 * _xScaling, -80.8573 * _yScaling, ); path.cubicTo( 224.221 * _xScaling, -94.9751 * _yScaling, 250.476 * _xScaling, -89.0135 * _yScaling, 272.31 * _xScaling, -94.0542 * _yScaling, ); path.cubicTo( 272.31 * _xScaling, -94.0542 * _yScaling, 272.31 * _xScaling, -94.0542 * _yScaling, 272.31 * _xScaling, -94.0542 * _yScaling, ); return path; } @override bool shouldReclip(CustomClipper oldClipper) => true; } ================================================ FILE: lib/neumorphic_start_page/top_right_clipper.dart ================================================ import 'package:flutter/material.dart'; /// Neumorphic clipper that is placed the top most on the top right class TopRightNeuClipper extends CustomClipper { @override Path getClip(Size size) { Path path = Path(); final double _xScaling = size.width / 375; final double _yScaling = size.height / 812; path.lineTo(304.51 * _xScaling, -174.308 * _yScaling); path.cubicTo( 329.223 * _xScaling, -181.119 * _yScaling, 351.649 * _xScaling, -199.055 * _yScaling, 377.304 * _xScaling, -196.299 * _yScaling, ); path.cubicTo( 402.975 * _xScaling, -193.542 * _yScaling, 423.049 * _xScaling, -173.058 * _yScaling, 445.164 * _xScaling, -159.199 * _yScaling, ); path.cubicTo( 466.889 * _xScaling, -145.585 * _yScaling, 492.989 * _xScaling, -136.488 * _yScaling, 507.15 * _xScaling, -114.93 * _yScaling, ); path.cubicTo( 521.261 * _xScaling, -93.4484 * _yScaling, 519.513 * _xScaling, -65.87 * _yScaling, 523.137 * _xScaling, -40.4969 * _yScaling, ); path.cubicTo( 526.568 * _xScaling, -16.469 * _yScaling, 530.669 * _xScaling, 7.25881 * _yScaling, 528.028 * _xScaling, 31.2624 * _yScaling, ); path.cubicTo( 525.261 * _xScaling, 56.4238 * _yScaling, 521.969 * _xScaling, 82.8593 * _yScaling, 507.337 * _xScaling, 103.142 * _yScaling, ); path.cubicTo( 492.715 * _xScaling, 123.411 * _yScaling, 466.947 * _xScaling, 130.798 * _yScaling, 446.254 * _xScaling, 144.616 * _yScaling, ); path.cubicTo( 424.348 * _xScaling, 159.245 * _yScaling, 407.286 * _xScaling, 185.341 * _yScaling, 380.941 * _xScaling, 187.547 * _yScaling, ); path.cubicTo( 354.563 * _xScaling, 189.755 * _yScaling, 332.294 * _xScaling, 167.393 * _yScaling, 307.963 * _xScaling, 156.192 * _yScaling, ); path.cubicTo( 285.649 * _xScaling, 145.921 * _yScaling, 262.802 * _xScaling, 137.982 * _yScaling, 242.584 * _xScaling, 123.857 * _yScaling, ); path.cubicTo( 220.69 * _xScaling, 108.562 * _yScaling, 195.077 * _xScaling, 94.4301 * _yScaling, 184.341 * _xScaling, 69.8294 * _yScaling, ); path.cubicTo( 173.603 * _xScaling, 45.2279 * _yScaling, 183.61 * _xScaling, 17.2839 * _yScaling, 184.432 * _xScaling, -9.38204 * _yScaling, ); path.cubicTo( 185.228 * _xScaling, -35.1901 * _yScaling, 181.785 * _xScaling, -61.2157 * _yScaling, 189.141 * _xScaling, -85.7998 * _yScaling, ); path.cubicTo( 196.864 * _xScaling, -111.613 * _yScaling, 206.92 * _xScaling, -138.951 * _yScaling, 228.049 * _xScaling, -155.16 * _yScaling, ); path.cubicTo( 249.109 * _xScaling, -171.317 * _yScaling, 278.908 * _xScaling, -167.251 * _yScaling, 304.51 * _xScaling, -174.308 * _yScaling, ); path.cubicTo( 304.51 * _xScaling, -174.308 * _yScaling, 304.51 * _xScaling, -174.308 * _yScaling, 304.51 * _xScaling, -174.308 * _yScaling, ); return path; } @override bool shouldReclip(CustomClipper oldClipper) => true; } ================================================ FILE: lib/timer/digital_font/digital_colon.dart ================================================ import 'package:flutter/material.dart'; class DigitalColon extends StatelessWidget { final double height; final Color color; DigitalColon({Key key, @required this.height, @required this.color}) : assert(height != null), assert(color != null), super(key: key); @override Widget build(BuildContext context) { return CustomPaint( size: Size(height / 2.0, height), painter: _DigitalColonPainter(height, color), ); } } class _DigitalColonPainter extends CustomPainter { final double height; final Color color; _DigitalColonPainter(this.height, this.color); @override bool shouldRepaint(_DigitalColonPainter oldDelegate) { return height != oldDelegate.height || color != oldDelegate.color; } @override void paint(Canvas canvas, Size size) { final double width = height / 2; final double thickness = width / 5; final Paint paint = Paint() ..color = color ..style = PaintingStyle.fill; // Top dot canvas.drawRect( Rect.fromLTWH( width / 2 - thickness / 2, height / 3 - thickness / 2, thickness, thickness, ), paint); // Bottom dot canvas.drawRect( Rect.fromLTWH( width / 2 - thickness / 2, height * 2 / 3 - thickness / 2, thickness, thickness, ), paint); } } ================================================ FILE: lib/timer/digital_font/digital_number.dart ================================================ import 'package:flutter/material.dart'; class DigitalNumber extends StatelessWidget { final int value; final int padLeft; final double height; final Color color; DigitalNumber({ Key key, @required this.value, @required this.height, @required this.color, this.padLeft = 0, }) : assert(value != null), assert(height != null), assert(color != null), super(key: key); @override Widget build(BuildContext context) { Widget digitPainter(int digit) { return new CustomPaint( size: new Size(height / 2.0, height), painter: new _DigitalDigitPainter(digit, height, color), ); } final Widget digitPadding = new SizedBox(width: height / 10.0); List children = []; int digits = 0; int remaining = value; // do-while required for when [value] is 0 do { int digit = remaining.remainder(10); // If this is not our first entry, add padding if (remaining != value) { children.add(digitPadding); } children.add(digitPainter(digit)); remaining ~/= 10; digits++; } while (remaining > 0); // If need to pad this number with zeros while (digits < padLeft) { children.add(digitPadding); children.add(digitPainter(0)); digits++; } return new Row( crossAxisAlignment: CrossAxisAlignment.center, children: new List.from(children.reversed), ); } } class _DigitalDigitPainter extends CustomPainter { final int value; final double height; final Color color; _DigitalDigitPainter(this.value, this.height, this.color) : assert(value >= 0), assert(value < 10); @override bool shouldRepaint(_DigitalDigitPainter oldDelegate) { return value != oldDelegate.value || height != oldDelegate.height || color != oldDelegate.color; } @override void paint(Canvas canvas, Size size) { final double width = height / 2; // Digits are half as wide as they are tall final double thickness = width / 5; // Arbitrary thickness that looks good final double bigGap = thickness * 2 / 3; // Inside angle for outer pixels final double midGap = thickness / 2; // Angle for middle bar final double smallGap = thickness / 3; // Outside angle for outer pixels final double smallPad = thickness / 10; // Spacing for middle bar final double bigPad = smallGap + smallPad; // Spacing for outer pixels // Alias/pre-calculate convenient locations final double top = size.height - height; final double left = size.width - width; final double right = size.width; final double bottom = size.height; final double middle = size.height - width; final Paint paint = new Paint() ..color = color ..style = PaintingStyle.fill; /// Build a polygon for the left side of the digit List leftPolygon(top, bottom) { return [ new Offset(left + smallGap, top), new Offset(left, top + smallGap), new Offset(left, bottom - smallGap), new Offset(left + smallGap, bottom), new Offset(left + thickness, bottom - bigGap), new Offset(left + thickness, top + bigGap), ]; } /// Build a polygon for the right side of the digit List rightPolygon(top, bottom) { return [ new Offset(right - smallGap, top), new Offset(right - thickness, top + bigGap), new Offset(right - thickness, bottom - bigGap), new Offset(right - smallGap, bottom), new Offset(right, bottom - smallGap), new Offset(right, top + smallGap), new Offset(right - smallGap, top), ]; } Path p = new Path(); // Top if (value != 1 && value != 4) { final tleft = left + bigPad; final tright = right - bigPad; p.addPolygon([ new Offset(tleft, top + smallGap), new Offset(tleft + smallGap, top), new Offset(tright - smallGap, top), new Offset(tright, top + smallGap), new Offset(tright - bigGap, top + thickness), new Offset(tleft + bigGap, top + thickness), ], true); } // Left Top if (value == 0 || (value > 3 && value != 7)) { p.addPolygon(leftPolygon(top + bigPad, middle - smallPad), true); } // Right Top if (value != 5 && value != 6) { p.addPolygon(rightPolygon(top + bigPad, middle - smallPad), true); } // Middle if (value > 1 && value != 7) { final mleft = left + bigPad; final mright = right - bigPad; final halfThick = thickness / 2; p.addPolygon([ new Offset(mleft, middle), new Offset(mleft + midGap, middle - halfThick), new Offset(mright - midGap, middle - halfThick), new Offset(mright, middle), new Offset(mright - midGap, middle + halfThick), new Offset(mleft + midGap, middle + halfThick), new Offset(mleft, middle), ], false); } // Left Bottom if (value == 0 || value == 2 || value == 6 || value == 8) { p.addPolygon(leftPolygon(middle + smallPad, bottom - bigPad), true); } // Right bottom if (value != 2) { p.addPolygon(rightPolygon(middle + smallPad, bottom - bigPad), true); } // Bottom if (value != 1 && value != 4 && value != 7) { final bleft = left + bigPad; final bright = right - bigPad; p.addPolygon([ new Offset(bleft, bottom - smallGap), new Offset(bleft + bigGap, bottom - thickness), new Offset(bright - bigGap, bottom - thickness), new Offset(bright, bottom - smallGap), new Offset(bright - smallGap, bottom), new Offset(bleft + smallGap, bottom), new Offset(bleft, bottom - smallGap), ], false); } canvas.drawPath(p, paint); } } ================================================ FILE: lib/timer/neu_digital_clock.dart ================================================ import 'package:flutter/material.dart'; import 'package:neumorphism_web/timer/digital_font/digital_colon.dart'; import 'package:neumorphism_web/timer/screen.dart'; import 'package:provider/provider.dart'; import 'digital_font/digital_number.dart'; class NeuDigitalClock extends StatelessWidget { const NeuDigitalClock({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { final currentDuration = Provider.of(context).currentDuration; final seconds = currentDuration.inSeconds; final minutes = currentDuration.inMinutes; final hours = currentDuration.inHours; // Outer white container return Container( height: 145, decoration: BoxDecoration( color: Color.fromRGBO(217, 230, 243, 1), borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( blurRadius: 15, offset: Offset(-5, -5), color: Colors.white, ), BoxShadow( blurRadius: 15, offset: Offset(10.5, 10.5), color: Color.fromRGBO(214, 223, 230, 1), ) ], ), // Digital green background child: Center( child: LayoutBuilder( builder: (context, constraints) => Container( height: constraints.maxHeight * 0.87, width: constraints.maxWidth * 0.95, decoration: BoxDecoration( gradient: LinearGradient(colors: [ Color.fromRGBO(203, 211, 196, 1), Color.fromRGBO(176, 188, 163, 1) ]), borderRadius: BorderRadius.circular(10), border: Border.all( color: Color.fromRGBO(168, 168, 168, 1), width: 2, ), ), child: DigitalClock( height: constraints.maxHeight, width: constraints.maxWidth, seconds: seconds, minutes: minutes, hours: hours, ), ), ), ), ); } } class DigitalClock extends StatelessWidget { const DigitalClock({ Key key, @required this.height, @required this.width, this.hours = 0, this.minutes = 0, this.seconds = 0, }) : super(key: key); final num height; final num width; final int hours; final int minutes; final int seconds; @override Widget build(BuildContext context) { List hourNumber = createNumberTime(hours); List minuteNumber = createNumberTime(minutes); List secondNumber = createNumberTime(seconds); return Center( child: Container( // color: Colors.green, height: height * 0.47, width: width * 0.70, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ...hourNumber, DigitalColon(height: height * 0.30, color: Colors.black87), ...minuteNumber, DigitalColon(height: height * 0.30, color: Colors.black87), ...secondNumber, ], ), ), ); } List createNumberTime(int numberTime) { final parsedNumberTime = numberTime % 60; final isNumberTimeTwoDigits = isNumberTwoDigits(parsedNumberTime); final firstNumber = firstDigit(parsedNumberTime); final tenDigit = isNumberTimeTwoDigits ? firstNumber : 0; final digit = isNumberTimeTwoDigits ? int.parse(parsedNumberTime.toString()[1]) : firstNumber; return [ DigitalNumberWithBG( height: height * 0.35, value: tenDigit, ), DigitalNumberWithBG( height: height * 0.35, value: digit, ), ]; } } class DigitalNumberWithBG extends StatelessWidget { const DigitalNumberWithBG({ Key key, this.value = 0, this.padLeft, this.height, this.color, this.backgroundValue = 8, }) : super(key: key); final int value; final int backgroundValue; final int padLeft; final double height; final Color color; @override Widget build(BuildContext context) { return Stack( children: [ //Foreground DigitalNumber( value: value, color: Colors.black, height: height, ), // Background DigitalNumber( value: backgroundValue, color: Colors.black12, height: height, ), ], ); } } bool isNumberTwoDigits(int number) { return number.toString().length == 2; } int firstDigit(int number) { return int.parse(number.toString()[0]); } ================================================ FILE: lib/timer/neu_hamburger_button.dart ================================================ import 'package:flutter/material.dart'; class NeuHamburgerButton extends StatelessWidget { const NeuHamburgerButton({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { return NeumorphicContainer( color: Color.fromRGBO(227, 237, 247, 1), child: SizedBox( child: Column( mainAxisSize: MainAxisSize.min, children: [ for (var i = 0; i < 3; i++) Padding( padding: const EdgeInsets.symmetric(vertical: 2.5), child: Container( height: 1.3, width: 20, decoration: BoxDecoration( borderRadius: BorderRadius.all( Radius.circular(1), ), boxShadow: [ // Edge shadow BoxShadow( offset: Offset(-1, -1), color: Color.fromRGBO(176, 193, 209, 1), spreadRadius: 1.0, ), // Circular shadow BoxShadow( color: Colors.white, // spreadRadius: 0.5, ) ], ), ), ) ], ), ), ); } } class NeumorphicContainer extends StatefulWidget { final Widget child; final double bevel; final Offset blurOffset; final Color color; final EdgeInsets padding; NeumorphicContainer({ Key key, this.child, this.bevel = 10.0, this.color, this.padding = const EdgeInsets.all(16.0), }) : this.blurOffset = Offset(bevel / 2, bevel / 2), super(key: key); @override _NeumorphicContainerState createState() => _NeumorphicContainerState(); } class _NeumorphicContainerState extends State { bool _isPressed = false; void _onPointerDown(PointerDownEvent event) { setState(() { _isPressed = true; }); } void _onPointerUp(PointerUpEvent event) { setState(() { _isPressed = false; }); } @override Widget build(BuildContext context) { return Listener( onPointerDown: _onPointerDown, onPointerUp: _onPointerUp, child: AnimatedContainer( duration: const Duration(milliseconds: 150), padding: widget.padding, decoration: BoxDecoration( shape: BoxShape.circle, color: Color.fromRGBO(227, 237, 247, 1), boxShadow: _isPressed ? null : [ BoxShadow( blurRadius: 15, offset: -widget.blurOffset, color: Colors.white, ), BoxShadow( blurRadius: 15, offset: Offset(10.5, 10.5), color: Color.fromRGBO(214, 223, 230, 1), ) ], ), child: widget.child, ), ); } } extension ColorUtils on Color { Color mix(Color another, double amount) { return Color.lerp(this, another, amount); } } ================================================ FILE: lib/timer/neu_progress_painter.dart ================================================ import 'package:flutter/material.dart'; import 'dart:math'; class NeuProgressPainter extends CustomPainter { // Color defaultCircleColor; Color percentageCompletedCircleColor; double completedPercentage; double circleWidth; NeuProgressPainter( {this.defaultCircleColor, this.percentageCompletedCircleColor, this.completedPercentage, this.circleWidth}); getPaint(Color color) { return Paint() ..color = color ..strokeCap = StrokeCap.round ..style = PaintingStyle.stroke ..strokeWidth = circleWidth; } @override void paint(Canvas canvas, Size size) { Paint defaultCirclePaint = getPaint(defaultCircleColor); Offset center = Offset(size.width / 2, size.height / 2); double radius = min(size.width / 2, size.height / 2); Rect boundingSquare = Rect.fromCircle(center: center, radius: radius); paint( List colors, ) { final Gradient gradient = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomRight, colors: colors, ); return Paint() ..strokeCap = StrokeCap.round ..style = PaintingStyle.stroke ..strokeWidth = circleWidth ..shader = gradient.createShader(boundingSquare); } canvas.drawCircle(center, radius, defaultCirclePaint); double arcAngle = 2 * pi * (completedPercentage / 100); canvas.drawArc( Rect.fromCircle(center: center, radius: radius), -pi / 2, arcAngle, false, paint( [ Color.fromRGBO(255, 219, 129, 1), Color.fromRGBO(255, 126, 29, 1), ], ), ); } @override bool shouldRepaint(CustomPainter painter) { return true; } } ================================================ FILE: lib/timer/neu_progress_pie_bar.dart ================================================ import 'package:flutter/material.dart'; import 'package:neumorphism_web/timer/neu_progress_painter.dart'; import 'package:neumorphism_web/timer/screen.dart'; import 'package:provider/provider.dart'; class NeuProgressPieBar extends StatelessWidget { const NeuProgressPieBar({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { final percentage = Provider.of(context).currentDuration.inSeconds / 60 * 100; return Container( height: 400, decoration: BoxDecoration( shape: BoxShape.circle, color: Color.fromRGBO(225, 234, 244, 1), boxShadow: [ BoxShadow( blurRadius: 15, offset: Offset(-5, -5), color: Colors.white, ), BoxShadow( blurRadius: 15, offset: Offset(10.5, 10.5), color: Color.fromRGBO(214, 223, 230, 1), ) ], border: Border.all( width: 15, color: Theme.of(context).backgroundColor, ), ), child: Stack( children: [ Center( child: SizedBox( height: 250, child: CustomPaint( painter: NeuProgressPainter( circleWidth: 60, completedPercentage: percentage, defaultCircleColor: Colors.transparent, ), child: Center(), ), ), ), Center( child: Container( height: 200, decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( begin: FractionalOffset.topCenter, end: FractionalOffset.bottomCenter, colors: [ Colors.grey.withOpacity(0.0), Colors.black54, ], stops: [0.95, 1.0], ), border: Border.all( width: 15, color: Theme.of(context).backgroundColor, ), ), child: Center(child: NeuStartButton()), ), ), ], ), ); } } class NeuStartButton extends StatefulWidget { final double bevel; final Offset blurOffset; NeuStartButton({ Key key, this.bevel = 10.0, }) : this.blurOffset = Offset(bevel / 2, bevel / 2), super(key: key); @override _NeuStartButtonState createState() => _NeuStartButtonState(); } class _NeuStartButtonState extends State { bool _isPressed = false; bool _isRunning = false; void _onPointerDown() { setState(() { _isPressed = true; }); } void _onPointerUp(PointerUpEvent event) { setState(() { _isPressed = false; }); } @override Widget build(BuildContext context) { return Listener( onPointerDown: (_) { _onPointerDown(); _isRunning ? Provider.of(context, listen: false).stop() : Provider.of(context, listen: false).start(); setState(() => _isRunning = !_isRunning); }, onPointerUp: _onPointerUp, child: AnimatedContainer( duration: const Duration(milliseconds: 150), height: 95, padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white70, boxShadow: _isPressed ? null : [ BoxShadow( blurRadius: 15, spreadRadius: 5, offset: -widget.blurOffset, color: Colors.white, ), BoxShadow( blurRadius: 15, offset: Offset(10.5, 10.5), color: Color.fromRGBO(214, 223, 230, 1), ) ], ), child: Center( child: Icon( _isRunning ? Icons.stop : Icons.play_arrow, size: 60, color: _isRunning ? Colors.redAccent.shade400 : Colors.greenAccent.shade400, )), ), ); } } extension ColorUtils on Color { Color mix(Color another, double amount) { return Color.lerp(this, another, amount); } } ================================================ FILE: lib/timer/neu_reset_button.dart ================================================ import 'package:flutter/material.dart'; import 'package:neumorphism_web/timer/screen.dart'; import 'package:provider/provider.dart'; class NeuResetButton extends StatefulWidget { final double bevel; final Offset blurOffset; NeuResetButton({ Key key, this.bevel = 10.0, }) : this.blurOffset = Offset(bevel / 2, bevel / 2), super(key: key); @override _NeuResetButtonState createState() => _NeuResetButtonState(); } class _NeuResetButtonState extends State { bool _isPressed = false; void _onPointerDown() { setState(() { _isPressed = true; }); } void _onPointerUp(PointerUpEvent event) { setState(() { _isPressed = false; }); } @override Widget build(BuildContext context) { return Listener( onPointerDown: (_) { _onPointerDown(); final isRunning = Provider.of(context, listen: false).isRunning; Provider.of(context, listen: false).reset(); // If user press reset button when timer is running, start for them if (isRunning) Provider.of(context, listen: false).start(); }, onPointerUp: _onPointerUp, child: AnimatedContainer( duration: const Duration(milliseconds: 150), height: 73, padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: Color.fromRGBO(227, 237, 247, 1), borderRadius: BorderRadius.circular(15), boxShadow: _isPressed ? null : [ BoxShadow( blurRadius: 15, offset: -widget.blurOffset, color: Colors.white, ), BoxShadow( blurRadius: 15, offset: Offset(10.5, 10.5), color: Color.fromRGBO(214, 223, 230, 1), ) ], ), child: Center( child: Text( 'Reset', style: Theme.of(context).textTheme.headline4, ), ), ), ); } } extension ColorUtils on Color { Color mix(Color another, double amount) { return Color.lerp(this, another, amount); } } ================================================ FILE: lib/timer/screen.dart ================================================ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:neumorphism_web/timer/neu_digital_clock.dart'; import 'package:neumorphism_web/timer/neu_hamburger_button.dart'; import 'package:neumorphism_web/timer/neu_progress_pie_bar.dart'; import 'package:neumorphism_web/timer/neu_reset_button.dart'; import 'package:provider/provider.dart'; class TimerScreen extends StatelessWidget { @override Widget build(BuildContext context) { final timeService = TimerService(); return ChangeNotifierProvider( create: (_) => timeService, child: Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 35.0), child: Column( children: [ SizedBox(height: MediaQuery.of(context).viewPadding.top + 20), TimerTitle(), SizedBox(height: 60), NeuDigitalClock(), SizedBox(height: 20), NeuProgressPieBar(), SizedBox(height: 25), NeuResetButton(), ], ), ), ), ); } } class TimerTitle extends StatelessWidget { const TimerTitle({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { return Row( children: [ Text( 'Timer', style: Theme.of(context).textTheme.headline1, ), Spacer(), NeuHamburgerButton() ], ); } } class TimerService extends ChangeNotifier { Stopwatch _watch; Timer _timer; Duration get currentDuration => _currentDuration; Duration _currentDuration = Duration.zero; bool get isRunning => _timer != null; TimerService() { _watch = Stopwatch(); } void _onTick(Timer timer) { _currentDuration = _watch.elapsed; // notify all listening widgets notifyListeners(); } void start() { if (_timer != null) return; _timer = Timer.periodic(Duration(seconds: 1), _onTick); _watch.start(); notifyListeners(); } void stop() { _timer?.cancel(); _timer = null; _watch.stop(); _currentDuration = _watch.elapsed; notifyListeners(); } void reset() { stop(); _watch.reset(); _currentDuration = Duration.zero; notifyListeners(); } // source: https://stackoverflow.com/questions/53228993/how-to-implement-persistent-stopwatch-in-flutter } ================================================ FILE: pubspec.yaml ================================================ name: neumorphism_web description: A new Flutter project. # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # In Android, build-name is used as versionName while build-number used as versionCode. # Read more about Android versioning at https://developer.android.com/studio/publish/versioning # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: sdk: ">=2.6.0 <3.0.0" dependencies: flutter: sdk: flutter provider: ^4.0.5 transparent_image: ^1.0.0 math_expressions: ^2.0.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.3 google_fonts: ^0.5.0+1 dev_dependencies: flutter_test: sdk: flutter # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Italic.ttf # style: italic # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages ================================================ FILE: test/widget_test.dart ================================================ // This is a basic Flutter widget test. // // To perform an interaction with a widget in your test, use the WidgetTester // utility that Flutter provides. For example, you can send tap and scroll // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:neumorphism_web/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); // Tap the '+' icon and trigger a frame. await tester.tap(find.byIcon(Icons.add)); await tester.pump(); // Verify that our counter has incremented. expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); } ================================================ FILE: web/index.html ================================================ neumorphism_web