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.
# 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