commit fe3de11bc56554ac9385f6c2d21638537e9db84c Author: fengshengxiong Date: Thu Jan 22 16:33:07 2026 +0800 BioFlux:Smart Hydration diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3820a95 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# 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/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..9e8b9e9 --- /dev/null +++ b/.metadata @@ -0,0 +1,30 @@ +# 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: "d693b4b9dbac2acd4477aea4555ca6dcbea44ba2" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + - platform: ios + create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0a1b7c --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# hydro_flux + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/about.txt b/about.txt new file mode 100644 index 0000000..b5868bb --- /dev/null +++ b/about.txt @@ -0,0 +1,79 @@ +BioFlux - Smart Hydration Tracker + +🌊 Transform Your Hydration Habits with Science-Based Tracking + +BioFlux isn't just another water tracker - it's your personal hydration coach that adapts to your unique metabolic needs. Using advanced algorithms, BioFlux calculates your optimal daily water intake based on your weight, activity level, and lifestyle. + +✨ KEY FEATURES + +🎯 Personalized Goals +• Smart calculations based on your weight and workout intensity +• Dynamic daily targets that adjust to your activity +• Science-backed hydration recommendations + +📊 Beautiful Analytics +• Elegant charts showing your hydration trends +• Weekly and monthly progress tracking +• Visual insights into your drinking patterns + +💧 Intuitive Tracking +• One-tap logging with customizable drink sizes +• Multiple beverage types (water, coffee, tea, soda) +• Quick access fluid sphere buttons for instant logging + +🔔 Smart Reminders +• Intelligent notifications when hydration drops +• Customizable reminder frequency +• Non-intrusive gentle nudges to stay hydrated + +🎨 Stunning Design +• Beautiful glass-morphism UI design +• Dynamic color themes that respond to your hydration level +• Smooth animations and delightful interactions + +📱 Seamless Experience +• Clean, minimalist interface +• Fast and responsive performance +• Offline functionality - no internet required + +🏆 WHY CHOOSE BIOFLUX? + +Unlike generic water trackers, BioFlux understands that hydration needs are personal. Whether you're an athlete, office worker, or busy parent, BioFlux adapts to your lifestyle and helps you maintain optimal hydration for peak performance and health. + +Our metabolic-based approach ensures you're not just drinking water - you're drinking the right amount for YOUR body. Say goodbye to one-size-fits-all hydration goals and hello to personalized wellness. + +🌟 PERFECT FOR: +• Fitness enthusiasts tracking workout hydration +• Health-conscious individuals optimizing daily wellness +• Anyone wanting to build better hydration habits +• People who forget to drink water regularly +• Those seeking data-driven health insights + +💪 HEALTH BENEFITS: +• Improved energy levels and mental clarity +• Better skin health and appearance +• Enhanced physical performance +• Optimized metabolism and digestion +• Reduced headaches and fatigue + +🔒 PRIVACY FIRST +Your health data stays on your device. BioFlux respects your privacy with local data storage and no unnecessary permissions. + +📈 TRACK YOUR SUCCESS +Watch as consistent hydration transforms your daily energy, focus, and overall well-being. BioFlux makes it easy to see the connection between proper hydration and feeling your best. + +Download BioFlux today and discover the difference personalized hydration can make in your life. Your body will thank you! 💙 + +--- + +App Store Categories: Health & Fitness, Lifestyle +Age Rating: 4+ +Languages: English +Compatibility: iOS 12.0 or later +Size: ~25 MB + +Keywords: water tracker, hydration, health, fitness, wellness, drink reminder, water intake, metabolic health, personalized nutrition, daily habits + +Support: bioflux.support@example.com +Website: https://bioflux-app.com +Privacy Policy: https://bioflux-app.com/privacy \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/images/bluxicon.png b/images/bluxicon.png new file mode 100644 index 0000000..05b58fe Binary files /dev/null and b/images/bluxicon.png differ diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.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/ephemeral/ +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 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..1dc6cf7 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 13.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..6649374 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +platform :ios, '15.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..bcd0ac9 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,213 @@ +PODS: + - Firebase/CoreOnly (12.6.0): + - FirebaseCore (~> 12.6.0) + - Firebase/Crashlytics (12.6.0): + - Firebase/CoreOnly + - FirebaseCrashlytics (~> 12.6.0) + - firebase_analytics (12.1.0): + - firebase_core + - FirebaseAnalytics (= 12.6.0) + - Flutter + - firebase_core (4.3.0): + - Firebase/CoreOnly (= 12.6.0) + - Flutter + - firebase_crashlytics (5.0.6): + - Firebase/Crashlytics (= 12.6.0) + - firebase_core + - Flutter + - FirebaseAnalytics (12.6.0): + - FirebaseAnalytics/Default (= 12.6.0) + - FirebaseCore (~> 12.6.0) + - FirebaseInstallations (~> 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - FirebaseAnalytics/Default (12.6.0): + - FirebaseCore (~> 12.6.0) + - FirebaseInstallations (~> 12.6.0) + - GoogleAppMeasurement/Default (= 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - FirebaseCore (12.6.0): + - FirebaseCoreInternal (~> 12.6.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - FirebaseCoreExtension (12.6.0): + - FirebaseCore (~> 12.6.0) + - FirebaseCoreInternal (12.6.0): + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - FirebaseCrashlytics (12.6.0): + - FirebaseCore (~> 12.6.0) + - FirebaseInstallations (~> 12.6.0) + - FirebaseRemoteConfigInterop (~> 12.6.0) + - FirebaseSessions (~> 12.6.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/Environment (~> 8.1) + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) + - FirebaseInstallations (12.6.0): + - FirebaseCore (~> 12.6.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - PromisesObjC (~> 2.4) + - FirebaseRemoteConfigInterop (12.6.0) + - FirebaseSessions (12.6.0): + - FirebaseCore (~> 12.6.0) + - FirebaseCoreExtension (~> 12.6.0) + - FirebaseInstallations (~> 12.6.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - nanopb (~> 3.30910.0) + - PromisesSwift (~> 2.1) + - Flutter (1.0.0) + - GoogleAdsOnDeviceConversion (3.2.0): + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/Core (12.6.0): + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/Default (12.6.0): + - GoogleAdsOnDeviceConversion (~> 3.2.0) + - GoogleAppMeasurement/Core (= 12.6.0) + - GoogleAppMeasurement/IdentitySupport (= 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/IdentitySupport (12.6.0): + - GoogleAppMeasurement/Core (= 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleDataTransport (10.1.0): + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) + - GoogleUtilities/AppDelegateSwizzler (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (8.1.0): + - GoogleUtilities/Privacy + - GoogleUtilities/Logger (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/MethodSwizzler (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/Network (8.1.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (8.1.0)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (8.1.0) + - GoogleUtilities/Reachability (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/UserDefaults (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - PromisesObjC (2.4.0) + - PromisesSwift (2.4.0): + - PromisesObjC (= 2.4.0) + - share_plus (0.0.1): + - Flutter + - url_launcher_ios (0.0.1): + - Flutter + +DEPENDENCIES: + - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) + - Flutter (from `Flutter`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + +SPEC REPOS: + trunk: + - Firebase + - FirebaseAnalytics + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseCrashlytics + - FirebaseInstallations + - FirebaseRemoteConfigInterop + - FirebaseSessions + - GoogleAdsOnDeviceConversion + - GoogleAppMeasurement + - GoogleDataTransport + - GoogleUtilities + - nanopb + - PromisesObjC + - PromisesSwift + +EXTERNAL SOURCES: + firebase_analytics: + :path: ".symlinks/plugins/firebase_analytics/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_crashlytics: + :path: ".symlinks/plugins/firebase_crashlytics/ios" + Flutter: + :path: Flutter + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + +SPEC CHECKSUMS: + Firebase: a451a7b61536298fd5cbfe3a746fd40443a50679 + firebase_analytics: 4f9cca09e65f6c2944a862c6dc86f6bed9fb769c + firebase_core: ba00a168e719694f38960502ceb560285603d073 + firebase_crashlytics: 13f4b77e9ce2a84b1f8ea07f293db5b6213ce1cf + FirebaseAnalytics: d0a97a0db6425e5a5d966340b87f92ca7b13a557 + FirebaseCore: 0e38ad5d62d980a47a64b8e9301ffa311457be04 + FirebaseCoreExtension: 032fd6f8509e591fda8cb76f6651f20d926b121f + FirebaseCoreInternal: 69bf1306a05b8ac43004f6cc1f804bb7b05b229e + FirebaseCrashlytics: 3d6248c50726ee7832aef0e53cb84c9e64d9fa7e + FirebaseInstallations: 631b38da2e11a83daa4bfb482f79d286a5dfa7ad + FirebaseRemoteConfigInterop: 3443b8cb8fffd76bb3e03b2a84bfd3db952fcda4 + FirebaseSessions: 2e8f808347e665dff3e5843f275715f07045297d + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f + GoogleAppMeasurement: 3bf40aff49a601af5da1c3345702fcb4991d35ee + GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 + GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 + share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a + url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b + +PODFILE CHECKSUM: 53a6aebc29ccee84c41f92f409fc20cd4ca011f1 + +COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..58d5542 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,767 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 025DDA39D7E93F60F4382EA8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96F1E3BA61059604A42469AB /* Pods_Runner.framework */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 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 */; }; + CD845436FB7704CD3A90EAAA /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B45C454EF6F9C6A60758C25 /* Pods_RunnerTests.framework */; }; + D883DAF12F1B64DF004E5C0E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D883DAF02F1B64DF004E5C0E /* GoogleService-Info.plist */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + 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 = ""; }; + 16B4CEA5BEFA387FBFD14C89 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 2EFF9F269119EEAC18771BED /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 6435378FA59164E9C79CCEF3 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 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 = ""; }; + 87A40F8066F0614934B1379B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 8B45C454EF6F9C6A60758C25 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 96F1E3BA61059604A42469AB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + 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 = ""; }; + AC867E7638F997CAE9568419 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + D883DAF02F1B64DF004E5C0E /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + EA6840404EAB2C340D013FF0 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4C508CF2318623BFF826B5A9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CD845436FB7704CD3A90EAAA /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 025DDA39D7E93F60F4382EA8 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 7FDD2DBF6F8FEC7A9421D640 /* Pods */ = { + isa = PBXGroup; + children = ( + 16B4CEA5BEFA387FBFD14C89 /* Pods-Runner.debug.xcconfig */, + AC867E7638F997CAE9568419 /* Pods-Runner.release.xcconfig */, + 87A40F8066F0614934B1379B /* Pods-Runner.profile.xcconfig */, + 2EFF9F269119EEAC18771BED /* Pods-RunnerTests.debug.xcconfig */, + EA6840404EAB2C340D013FF0 /* Pods-RunnerTests.release.xcconfig */, + 6435378FA59164E9C79CCEF3 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + 7FDD2DBF6F8FEC7A9421D640 /* Pods */, + CA9ACF2465C155E10BE16347 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + D883DAF02F1B64DF004E5C0E /* GoogleService-Info.plist */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + CA9ACF2465C155E10BE16347 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 96F1E3BA61059604A42469AB /* Pods_Runner.framework */, + 8B45C454EF6F9C6A60758C25 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 586B53A258E12B9B12E82788 /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + 4C508CF2318623BFF826B5A9 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + C92652353E009CC3DC242E3B /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 23DD3CE435CDC926AA929605 /* [CP] Embed Pods Frameworks */, + ); + 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 = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + preferredProjectObjectVersion = 77; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + D883DAF12F1B64DF004E5C0E /* GoogleService-Info.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 23DD3CE435CDC926AA929605 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 586B53A258E12B9B12E82788 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + 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"; + }; + C92652353E009CC3DC242E3B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency 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; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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 = 13.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; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 77GF32A2WP; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.bioFluxSmart.bioFluxSmart; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = BioFlux; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2EFF9F269119EEAC18771BED /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.hydroFlux.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EA6840404EAB2C340D013FF0 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.hydroFlux.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6435378FA59164E9C79CCEF3 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.hydroFlux.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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 = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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 = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + 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; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 77GF32A2WP; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.bioFluxSmart.bioFluxSmart; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = BioFlux; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 77GF32A2WP; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.bioFluxSmart.bioFluxSmart; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = BioFlux; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 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 */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..e3773d4 --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..6266644 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@main +@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) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..c68df94 --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,120 @@ +{ + "images": [ + { + "size": "20x20", + "idiom": "universal", + "filename": "icon-20@2x.png", + "scale": "2x", + "platform": "ios" + }, + { + "size": "20x20", + "idiom": "universal", + "filename": "icon-20@3x.png", + "scale": "3x", + "platform": "ios" + }, + { + "size": "29x29", + "idiom": "universal", + "filename": "icon-29@2x.png", + "scale": "2x", + "platform": "ios" + }, + { + "size": "29x29", + "idiom": "universal", + "filename": "icon-29@3x.png", + "scale": "3x", + "platform": "ios" + }, + { + "size": "38x38", + "idiom": "universal", + "filename": "icon-38@2x.png", + "scale": "2x", + "platform": "ios" + }, + { + "size": "38x38", + "idiom": "universal", + "filename": "icon-38@3x.png", + "scale": "3x", + "platform": "ios" + }, + { + "size": "40x40", + "idiom": "universal", + "filename": "icon-40@2x.png", + "scale": "2x", + "platform": "ios" + }, + { + "size": "40x40", + "idiom": "universal", + "filename": "icon-40@3x.png", + "scale": "3x", + "platform": "ios" + }, + { + "size": "60x60", + "idiom": "universal", + "filename": "icon-60@2x.png", + "scale": "2x", + "platform": "ios" + }, + { + "size": "60x60", + "idiom": "universal", + "filename": "icon-60@3x.png", + "scale": "3x", + "platform": "ios" + }, + { + "size": "64x64", + "idiom": "universal", + "filename": "icon-64@2x.png", + "scale": "2x", + "platform": "ios" + }, + { + "size": "64x64", + "idiom": "universal", + "filename": "icon-64@3x.png", + "scale": "3x", + "platform": "ios" + }, + { + "size": "68x68", + "idiom": "universal", + "filename": "icon-68@2x.png", + "scale": "2x", + "platform": "ios" + }, + { + "size": "76x76", + "idiom": "universal", + "filename": "icon-76@2x.png", + "scale": "2x", + "platform": "ios" + }, + { + "size": "83.5x83.5", + "idiom": "universal", + "filename": "icon-83.5@2x.png", + "scale": "2x", + "platform": "ios" + }, + { + "size": "1024x1024", + "idiom": "universal", + "filename": "icon-1024.png", + "scale": "1x", + "platform": "ios" + } + ], + "info": { + "version": 1, + "author": "icon.wuruihong.com" + } +} \ No newline at end of file diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png new file mode 100644 index 0000000..79bd648 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png new file mode 100644 index 0000000..94ec1b2 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png new file mode 100644 index 0000000..e39b174 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png new file mode 100644 index 0000000..031f754 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png new file mode 100644 index 0000000..151ccb7 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png new file mode 100644 index 0000000..82ffda9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png new file mode 100644 index 0000000..ab74f76 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png new file mode 100644 index 0000000..e871c6b Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png new file mode 100644 index 0000000..2519618 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png new file mode 100644 index 0000000..2519618 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png new file mode 100644 index 0000000..fb42756 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png new file mode 100644 index 0000000..ae6f2f0 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png new file mode 100644 index 0000000..98e96b0 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png new file mode 100644 index 0000000..0a484f6 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png new file mode 100644 index 0000000..798ccda Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png new file mode 100644 index 0000000..5be8d07 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "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" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# 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. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 0000000..adc566a --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,30 @@ + + + + + API_KEY + AIzaSyCZhzNo-5oukRLhs8yZ2nI9_AX6yrWELq8 + GCM_SENDER_ID + 560733077413 + PLIST_VERSION + 1 + BUNDLE_ID + com.bioFluxSmart.bioFluxSmart + PROJECT_ID + bioflux-bb331 + STORAGE_BUCKET + bioflux-bb331.firebasestorage.app + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:560733077413:ios:c1e6831284a2d5f6f1324c + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..7c12c8d --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,47 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + BioFlux + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + BioFlux + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/lib/adstools/firebase_optiongs.dart b/lib/adstools/firebase_optiongs.dart new file mode 100644 index 0000000..4248830 --- /dev/null +++ b/lib/adstools/firebase_optiongs.dart @@ -0,0 +1,33 @@ +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' show defaultTargetPlatform, TargetPlatform; + +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + + default: + throw UnsupportedError('Unsupported platform'); + } + } + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'YOUR_ANDROID_API_KEY', + appId: 'YOUR_ANDROID_APP_ID', + messagingSenderId: 'YOUR_ANDROID_MESSAGING_SENDER_ID', + projectId: 'YOUR_PROJECT_ID', + storageBucket: 'YOUR_STORAGE_BUCKET', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyCZhzNo-5oukRLhs8yZ2nI9_AX6yrWELq8', + appId: '1:560733077413:ios:c1e6831284a2d5f6f1324c', + messagingSenderId: '560733077413', + projectId: 'bioflux-bb331', + storageBucket: 'bioflux-bb331.firebasestorage.app', + iosBundleId: 'com.bioFluxSmart.bioFluxSmart', + ); +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..b303562 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:firebase_analytics/observer.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hive_flutter/hive_flutter.dart'; +import 'package:provider/provider.dart'; + +import 'adstools/firebase_optiongs.dart'; +import 'services/hydration_service.dart'; +import 'widgets/main_navigation_frame.dart'; + +bool isFirebaseInitialized = false; + +void main() async { + runZonedGuarded>(() async { + WidgetsFlutterBinding.ensureInitialized(); + + try { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ).timeout(const Duration(seconds: 15)); // 设置15秒超时 + print("✅ Firebase 初始化成功。"); + isFirebaseInitialized = true; // 成功后设置标志 + } on TimeoutException { + print("⚠️ Firebase 初始化超时 (超过15秒),应用将继续运行但相关功能将不可用。"); + } catch (error, stack) { + print("!!!!!!!!!! Firebase 初始化失败 !!!!!!!!!!: $error"); + FirebaseCrashlytics.instance.recordError(error, stack, fatal: false); + } + + // Firebase 成功初始化后,配置依赖它的服务 + if (isFirebaseInitialized) { + //设置全局错误处理器 + FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError; + PlatformDispatcher.instance.onError = (error, stack) { + FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); + return true; + }; + // 使用我们的安全服务来记录 App Open + AnalyticsService.instance.logAppOpen(); + } + + await Hive.initFlutter(); + await Hive.openBox('hydrationData'); + await Hive.openBox('userSettings'); + + final analyticsObserver = SafeFirebaseAnalyticsObserver(); + runApp( + MultiProvider( + providers: [ChangeNotifierProvider(create: (_) => HydrationService())], + child: BioFluxApp(analyticsObserver: analyticsObserver), + ), + ); + }, (e, s) {}); +} + +class BioFluxApp extends StatelessWidget { + final NavigatorObserver analyticsObserver; + + const BioFluxApp({super.key, required this.analyticsObserver}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'BioFlux', + navigatorObservers: [analyticsObserver], + debugShowCheckedModeBanner: false, + theme: ThemeData( + useMaterial3: true, + scaffoldBackgroundColor: const Color(0xFFF0F8FF), + colorScheme: ColorScheme.fromSeed( + seedColor: const Color(0xFF00BFFF), + background: const Color(0xFFF0F8FF), + ), + textTheme: GoogleFonts.latoTextTheme(Theme.of(context).textTheme) + .copyWith( + displayLarge: GoogleFonts.montserrat(), + displayMedium: GoogleFonts.montserrat(), + titleLarge: GoogleFonts.montserrat(fontWeight: FontWeight.w600), + titleMedium: GoogleFonts.montserrat(fontWeight: FontWeight.w600), + bodyLarge: GoogleFonts.lato(), + bodyMedium: GoogleFonts.lato(), + ), + ), + home: const MainNavigationFrame(), + ); + } +} + +class AnalyticsService { + // 单例模式 + AnalyticsService._(); + + static final instance = AnalyticsService._(); + + final _analytics = FirebaseAnalytics.instance; + + /// 安全地记录一个 App Open 事件。 + void logAppOpen() { + if (isFirebaseInitialized) { + _analytics.logAppOpen(); + } else { + print("[AnalyticsService] Skipped logAppOpen: Firebase not initialized."); + } + } +} + +/// 一个安全的 FirebaseAnalyticsObserver 包装器。它只在 Firebase 初始化成功时才转发导航事件。 +class SafeFirebaseAnalyticsObserver extends NavigatorObserver { + late final FirebaseAnalyticsObserver _observer; + + SafeFirebaseAnalyticsObserver() { + if (isFirebaseInitialized) { + _observer = FirebaseAnalyticsObserver( + analytics: AnalyticsService.instance._analytics, + ); + } + } + + @override + void didPush(Route route, Route? previousRoute) { + if (isFirebaseInitialized) { + _observer.didPush(route, previousRoute); + } + } + + @override + void didPop(Route route, Route? previousRoute) { + if (isFirebaseInitialized) { + _observer.didPop(route, previousRoute); + } + } + + @override + void didReplace({Route? newRoute, Route? oldRoute}) { + if (isFirebaseInitialized) { + _observer.didReplace(newRoute: newRoute, oldRoute: oldRoute); + } + } +} diff --git a/lib/models/drink_type.dart b/lib/models/drink_type.dart new file mode 100644 index 0000000..4979e92 --- /dev/null +++ b/lib/models/drink_type.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +enum DrinkType { + Water, Coffee, Tea, Soda; + + double get hydrationFactor { + switch (this) { + case DrinkType.Water: return 1.0; + case DrinkType.Coffee: return 0.85; + case DrinkType.Tea: return 0.90; + case DrinkType.Soda: return 0.80; + } + } + + double get purityFactor { + switch (this) { + case DrinkType.Water: return 1.0; + case DrinkType.Coffee: return 0.7; + case DrinkType.Tea: return 0.85; + case DrinkType.Soda: return 0.6; + } + } + + String get label => toString().split('.').last; + + Color get color { + switch (this) { + case DrinkType.Water: return const Color(0xFF00BFFF); + case DrinkType.Coffee: return const Color(0xFF8D6E63); + case DrinkType.Tea: return const Color(0xFF9CCC65); + case DrinkType.Soda: return const Color(0xFFFF7043); + } + } +} diff --git a/lib/screens/feedback_screen.dart b/lib/screens/feedback_screen.dart new file mode 100644 index 0000000..db49877 --- /dev/null +++ b/lib/screens/feedback_screen.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../widgets/glass_card.dart'; + +class FeedbackScreen extends StatefulWidget { + const FeedbackScreen({super.key}); + + @override + State createState() => _FeedbackScreenState(); +} + +class _FeedbackScreenState extends State { + final TextEditingController _controller = TextEditingController(); + bool _isSubmitting = false; + + void _submit() async { + if (_controller.text.isEmpty) return; + + setState(() => _isSubmitting = true); + + // 模拟提交过程 + // 显示成功提示 + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("Feedback sent! Thank you.", style: GoogleFonts.lato()), + backgroundColor: const Color(0xFF00BFFF), + behavior: SnackBarBehavior.floating, + duration: const Duration(milliseconds: 1500), + ) + ); + + // 0.8秒后返回 + await Future.delayed(const Duration(milliseconds: 800)); + + if (mounted) { + Navigator.pop(context); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Send Feedback", style: GoogleFonts.montserrat(fontWeight: FontWeight.bold)), + backgroundColor: Colors.transparent, + elevation: 0, + centerTitle: true, + ), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + children: [ + GlassCard( + child: TextField( + controller: _controller, + maxLines: 6, + decoration: InputDecoration( + hintText: "Tell us what you think...", + hintStyle: GoogleFonts.lato(color: Colors.grey), + border: InputBorder.none, + ), + style: GoogleFonts.lato(), + ), + ), + const SizedBox(height: 24), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: _isSubmitting ? null : _submit, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF00BFFF), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), + elevation: 0, + ), + child: _isSubmitting + ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2)) + : Text("Submit Feedback", style: GoogleFonts.montserrat(fontWeight: FontWeight.bold)), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart new file mode 100644 index 0000000..eb76e2d --- /dev/null +++ b/lib/screens/home_screen.dart @@ -0,0 +1,292 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; + +import '../models/drink_type.dart'; +import '../services/hydration_service.dart'; +import '../widgets/fluid_sphere_button.dart'; + +class HomeScreen extends StatelessWidget { + const HomeScreen({super.key}); + + @override + Widget build(BuildContext context) { + final hydration = context.watch(); + final dynamicColor = hydration.dynamicWaterColor; + final size = MediaQuery.of(context).size; + + return Scaffold( + body: Stack( + children: [ + Positioned( + top: -100, + left: -100, + child: ImageFiltered( + imageFilter: ImageFilter.blur(sigmaX: 100, sigmaY: 100), + child: Container( + width: 450, + height: 450, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: dynamicColor.withOpacity(0.15), + ), + ), + ), + ), + Positioned( + top: size.height * 0.3, + right: -100, + child: ImageFiltered( + imageFilter: ImageFilter.blur(sigmaX: 80, sigmaY: 80), + child: Container( + width: 300, + height: 300, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.purpleAccent.withOpacity(0.05), + ), + ), + ), + ), + + SafeArea( + child: Column( + children: [ + const SizedBox(height: 10), + _buildModernHeader(context, hydration), + + Expanded( + child: Center( + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 300, + height: 300, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: dynamicColor.withOpacity(0.05), + width: 1, + ) + ), + ), + FluidSphereButton( + percentage: hydration.percentage, + waterColor: dynamicColor, + onLongPressStart: () => HapticFeedback.heavyImpact(), + onLongPressEnd: () { + context.read().addWater(200); + HapticFeedback.mediumImpact(); + }, + ), + ], + ), + ), + ), + + Padding( + padding: const EdgeInsets.only(bottom: 25), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + child: Text( + hydration.statusText, + key: ValueKey(hydration.statusText), + style: GoogleFonts.montserrat( + color: dynamicColor.withOpacity(0.8), + fontSize: 13, + letterSpacing: 3.0, + fontWeight: FontWeight.w600 + ), + ), + ), + ), + + _buildFloatingDock(context, hydration), + const SizedBox(height: 110), + ], + ), + ), + ], + ), + ); + } + + Widget _buildModernHeader(BuildContext context, HydrationService service) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0, vertical: 20), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + DateFormat('MMM d, EEEE').format(DateTime.now()).toUpperCase(), + style: GoogleFonts.montserrat( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.grey.shade400, + letterSpacing: 1.5, + ), + ), + const SizedBox(height: 8), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: "${(service.percentage * 100).toInt()}", + style: GoogleFonts.montserrat( + color: Colors.black87, + fontSize: 64, + fontWeight: FontWeight.w200, + height: 1.0, + ), + ), + TextSpan( + text: "%", + style: GoogleFonts.montserrat( + color: Colors.black54, + fontSize: 24, + fontWeight: FontWeight.w300, + fontFeatures: [const FontFeature.superscripts()], + ), + ), + ], + ), + ), + ], + ), + + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.5), + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.03), + blurRadius: 10, + offset: const Offset(0, 4) + ) + ] + ), + child: IconButton( + icon: Icon(Icons.refresh, size: 20, color: service.dynamicWaterColor), + onPressed: () {}, + ), + ), + const SizedBox(height: 12), + Text( + "TARGET", + style: GoogleFonts.montserrat(fontSize: 10, color: Colors.grey.shade400, letterSpacing: 1), + ), + Text( + "${service.dailyGoal.toInt()} ml", + style: GoogleFonts.lato(fontSize: 14, color: Colors.black87, fontWeight: FontWeight.bold), + ), + ], + ), + ], + ), + ], + ), + ); + } + + Widget _buildFloatingDock(BuildContext context, HydrationService service) { + return Container( + height: 85, + margin: const EdgeInsets.symmetric(horizontal: 24), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.4), + borderRadius: BorderRadius.circular(24), + border: Border.all(color: Colors.white.withOpacity(0.4)), + boxShadow: [ + BoxShadow( + color: service.dynamicWaterColor.withOpacity(0.08), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(24), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: DrinkType.values.map((type) { + final isWater = type == DrinkType.Water; + return GestureDetector( + onTap: () { + context.read().addWater(250, type: type); + HapticFeedback.lightImpact(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("Recorded ${type.label}"), + backgroundColor: context.read().dynamicWaterColor, + duration: const Duration(milliseconds: 800), + behavior: SnackBarBehavior.floating, + width: 200, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + ) + ); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: isWater ? Colors.white.withOpacity(0.8) : Colors.transparent, + shape: BoxShape.circle, + boxShadow: isWater ? [ + BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 4)) + ] : null, + ), + child: Icon( + _getIconForType(type), + color: isWater ? const Color(0xFF00BFFF) : Colors.grey.shade500, + size: 22, + ), + ), + const SizedBox(height: 4), + Text( + type.label, + style: GoogleFonts.montserrat( + fontSize: 10, + fontWeight: isWater ? FontWeight.w600 : FontWeight.w400, + color: isWater ? Colors.black87 : Colors.grey.shade600 + ), + ), + ], + ), + ); + }).toList(), + ), + ), + ), + ); + } + + IconData _getIconForType(DrinkType type) { + switch(type) { + case DrinkType.Water: return Icons.water_drop; + case DrinkType.Coffee: return Icons.coffee; + case DrinkType.Tea: return Icons.emoji_food_beverage; + case DrinkType.Soda: return Icons.local_drink; + } + } +} diff --git a/lib/screens/message_screen.dart b/lib/screens/message_screen.dart new file mode 100644 index 0000000..8045fcf --- /dev/null +++ b/lib/screens/message_screen.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../widgets/glass_card.dart'; + +class MessageScreen extends StatelessWidget { + const MessageScreen({super.key}); + + @override + Widget build(BuildContext context) { + final List> messages = [ + { + "title": "Hydration Alert", + "body": "You haven't logged any water for 2 hours. Your metabolic sphere is starting to wither.", + "time": "10:30 AM", + "icon": Icons.warning_amber_rounded, + "color": Colors.orangeAccent, + }, + { + "title": "Daily Goal Achieved!", + "body": "Congratulations! You hit your 2500ml target yesterday. Keep the streak alive!", + "time": "09:00 AM", + "icon": Icons.emoji_events_rounded, + "color": Colors.amber, + }, + { + "title": "Morning Insight", + "body": "Drinking water immediately after waking up activates your internal organs.", + "time": "07:15 AM", + "icon": Icons.lightbulb_outline_rounded, + "color": Colors.blueAccent, + }, + { + "title": "System Update", + "body": "Smart Metabolism Tracking is now active. We'll adjust your goals based on your workout data.", + "time": "Yesterday", + "icon": Icons.system_update_rounded, + "color": Colors.purpleAccent, + }, + ]; + + return Scaffold( + body: SafeArea( + child: ListView( + padding: const EdgeInsets.all(24), + children: [ + Text( + "Notifications", + style: GoogleFonts.montserrat(fontSize: 28, fontWeight: FontWeight.bold) + ), + const SizedBox(height: 20), + + ...messages.map((msg) => Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: _buildMessageCard(msg), + )), + ], + ), + ), + ); + } + + Widget _buildMessageCard(Map msg) { + return GlassCard( + padding: const EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: (msg['color'] as Color).withOpacity(0.1), + shape: BoxShape.circle, + ), + child: Icon(msg['icon'] as IconData, color: msg['color'] as Color, size: 24), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + msg['title'] as String, + style: GoogleFonts.montserrat(fontWeight: FontWeight.bold, fontSize: 14), + ), + Text( + msg['time'] as String, + style: GoogleFonts.lato(fontSize: 10, color: Colors.grey), + ), + ], + ), + const SizedBox(height: 6), + Text( + msg['body'] as String, + style: GoogleFonts.lato(fontSize: 13, color: Colors.grey.shade700, height: 1.4), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart new file mode 100644 index 0000000..21bbe60 --- /dev/null +++ b/lib/screens/settings_screen.dart @@ -0,0 +1,385 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../services/hydration_service.dart'; +import '../widgets/glass_card.dart'; +import 'feedback_screen.dart'; + +class SettingsScreen extends StatelessWidget { + const SettingsScreen({super.key}); + + // 隐私政策URL + static const String privacyPolicyUrl = 'https://biofluxsmart.bitbucket.io/privacy.html'; + + @override + Widget build(BuildContext context) { + final service = context.watch(); + final color = service.dynamicWaterColor; + + return Scaffold( + body: SafeArea( + child: ListView( + padding: const EdgeInsets.all(20), + children: [ + Text( + "Settings", + style: GoogleFonts.montserrat( + fontSize: 28, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 20), + + // Personalization + _buildSectionHeader("Personalization"), + GlassCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(Icons.calculate, color: color), + const SizedBox(width: 10), + Text( + "Metabolic Goals", + style: GoogleFonts.montserrat( + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const Divider(height: 20), + + _buildSliderRow( + label: "Weight", + value: "${service.weight.toInt()} kg", + slider: Slider( + value: service.weight, + min: 30, + max: 150, + activeColor: color, + onChanged: (v) => service.updateSettings(weight: v), + ), + ), + + _buildSliderRow( + label: "Workout", + value: "${service.workoutMinutes.toInt()} min", + slider: Slider( + value: service.workoutMinutes, + min: 0, + max: 180, + activeColor: color, + onChanged: (v) => service.updateSettings(workout: v), + ), + ), + + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Center( + child: Text( + "Target: ${service.dailyGoal.toInt()} ml", + style: GoogleFonts.montserrat( + fontSize: 16, + color: color, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + const SizedBox(height: 20), + + // Preferences + _buildSectionHeader("Preferences"), + GlassCard( + padding: EdgeInsets.zero, + child: Column( + children: [ + SwitchListTile( + title: Text("Smart Reminders", style: GoogleFonts.lato()), + subtitle: Text( + "Notify when hydration drops", + style: GoogleFonts.lato(fontSize: 12, color: Colors.grey), + ), + value: service.remindersEnabled, + activeColor: color, + onChanged: (v) => service.updateSettings(reminders: v), + ), + const Divider(height: 1, indent: 16, endIndent: 16), + ], + ), + ), + const SizedBox(height: 20), + + // Support + _buildSectionHeader("Support"), + GlassCard( + padding: EdgeInsets.zero, + child: Column( + children: [ + ListTile( + leading: Icon( + Icons.share_outlined, + color: Colors.grey.shade600, + size: 20, + ), + title: Text("Share App", style: GoogleFonts.lato()), + trailing: const Icon( + Icons.chevron_right, + size: 18, + color: Colors.black26, + ), + onTap: () { + Share.share( + 'Check out BioFlux - The Metabolic Hydration Tracker! Stay fluid.', + ); + }, + ), + const Divider(height: 1, indent: 16, endIndent: 16), + ListTile( + leading: Icon( + Icons.chat_bubble_outline, + color: Colors.grey.shade600, + size: 20, + ), + title: Text("Send Feedback", style: GoogleFonts.lato()), + trailing: const Icon( + Icons.chevron_right, + size: 18, + color: Colors.black26, + ), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const FeedbackScreen(), + ), + ); + }, + ), + const Divider(height: 1, indent: 16, endIndent: 16), + ListTile( + leading: Icon( + Icons.lock_outline, + color: Colors.grey.shade600, + size: 20, + ), + title: Text("Privacy Policy", style: GoogleFonts.lato()), + trailing: const Icon( + Icons.chevron_right, + size: 18, + color: Colors.black26, + ), + onTap: () => _launchPrivacyPolicy(context), + ), + const Divider(height: 1, indent: 16, endIndent: 16), + ListTile( + leading: Icon( + Icons.info_outline, + color: Colors.grey.shade600, + size: 20, + ), + title: Text("About BioFlux", style: GoogleFonts.lato()), + trailing: const Icon( + Icons.chevron_right, + size: 18, + color: Colors.black26, + ), + onTap: () => _showAboutDialog(context), + ), + ], + ), + ), + const SizedBox(height: 100), + ], + ), + ), + ); + } + + Widget _buildSectionHeader(String title) { + return Padding( + padding: const EdgeInsets.only(left: 8, bottom: 8), + child: Text( + title.toUpperCase(), + style: GoogleFonts.montserrat( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.grey.shade500, + letterSpacing: 1.2, + ), + ), + ); + } + + Widget _buildSliderRow({ + required String label, + required String value, + required Widget slider, + }) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(label, style: GoogleFonts.lato()), + Text(value, style: GoogleFonts.lato(fontWeight: FontWeight.bold)), + ], + ), + SizedBox(height: 30, child: slider), + ], + ); + } + + // 打开隐私政策网页 + Future _launchPrivacyPolicy(BuildContext context) async { + try { + final Uri url = Uri.parse(privacyPolicyUrl); + if (await canLaunchUrl(url)) { + await launchUrl(url, mode: LaunchMode.externalApplication); + } else { + if (context.mounted) { + _showErrorDialog(context, 'Unable to open privacy policy'); + } + } + } catch (e) { + if (context.mounted) { + _showErrorDialog(context, 'Error opening privacy policy: $e'); + } + } + } + + // 显示关于应用对话框 + void _showAboutDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + title: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Colors.blue.shade400, Colors.cyan.shade300], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(10), + ), + child: const Icon( + Icons.water_drop, + color: Colors.white, + size: 24, + ), + ), + const SizedBox(width: 12), + Text( + "BioFlux", + style: GoogleFonts.montserrat( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Smart Hydration Tracker", + style: GoogleFonts.lato( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.grey.shade700, + ), + ), + const SizedBox(height: 16), + Text( + "Version 1.0.0", + style: GoogleFonts.lato( + fontSize: 14, + color: Colors.grey.shade600, + ), + ), + const SizedBox(height: 16), + Text( + "BioFlux helps you maintain optimal hydration levels based on your metabolic needs. Track your daily water intake, set personalized goals, and receive smart reminders to stay healthy and hydrated.", + style: GoogleFonts.lato(fontSize: 14, height: 1.5), + ), + const SizedBox(height: 16), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.blue.shade50, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Icon(Icons.favorite, color: Colors.red.shade400, size: 16), + const SizedBox(width: 8), + Expanded( + child: Text( + "Made with care for your health", + style: GoogleFonts.lato( + fontSize: 12, + color: Colors.grey.shade700, + ), + ), + ), + ], + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text( + "Close", + style: GoogleFonts.lato(fontWeight: FontWeight.w600), + ), + ), + ], + ); + }, + ); + } + + // 显示错误对话框 + void _showErrorDialog(BuildContext context, String message) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + "Error", + style: GoogleFonts.montserrat(fontWeight: FontWeight.bold), + ), + content: Text(message, style: GoogleFonts.lato()), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text( + "OK", + style: GoogleFonts.lato(fontWeight: FontWeight.w600), + ), + ), + ], + ); + }, + ); + } +} diff --git a/lib/screens/stats_screen.dart b/lib/screens/stats_screen.dart new file mode 100644 index 0000000..61174f2 --- /dev/null +++ b/lib/screens/stats_screen.dart @@ -0,0 +1,154 @@ +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import '../services/hydration_service.dart'; +import '../widgets/glass_card.dart'; + +class StatsScreen extends StatelessWidget { + const StatsScreen({super.key}); + + @override + Widget build(BuildContext context) { + final service = context.watch(); + final history = service.history; + final color = service.dynamicWaterColor; + + return Scaffold( + body: SafeArea( + child: ListView( + padding: const EdgeInsets.all(24.0), + children: [ + Text( + "Insights", + style: GoogleFonts.montserrat(fontSize: 28, fontWeight: FontWeight.bold) + ), + const SizedBox(height: 20), + + Row( + children: [ + _buildMetricCard("Streak", "5 Days", Icons.local_fire_department, Colors.orange), + const SizedBox(width: 12), + _buildMetricCard("Avg", "1.8 L", Icons.water, Colors.blue), + const SizedBox(width: 12), + _buildMetricCard("Goal", "90%", Icons.check_circle, Colors.green), + ], + ), + const SizedBox(height: 24), + + Text("Intake Breakdown", style: GoogleFonts.montserrat(fontSize: 16, fontWeight: FontWeight.w600)), + const SizedBox(height: 12), + GlassCard( + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: SizedBox( + height: 12, + child: Row( + children: service.breakdown.entries.map((entry) { + if (entry.value == 0) return const SizedBox.shrink(); + final flex = (entry.value * 10).toInt(); + return Expanded( + flex: flex == 0 ? 1 : flex, + child: Container(color: entry.key.color), + ); + }).toList(), + ), + ), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: service.breakdown.entries.where((e) => e.value > 0).map((entry) { + return Row( + children: [ + Container(width: 8, height: 8, decoration: BoxDecoration(color: entry.key.color, shape: BoxShape.circle)), + const SizedBox(width: 6), + Text(entry.key.label, style: GoogleFonts.lato(fontSize: 12, color: Colors.grey.shade700)), + ], + ); + }).toList(), + ) + ], + ), + ), + const SizedBox(height: 24), + + Text("Weekly Overview", style: GoogleFonts.montserrat(fontSize: 16, fontWeight: FontWeight.w600)), + const SizedBox(height: 12), + AspectRatio( + aspectRatio: 1.5, + child: GlassCard( + child: Padding( + padding: const EdgeInsets.only(right: 16, top: 16, bottom: 0), + child: BarChart( + BarChartData( + gridData: const FlGridData(show: false), + titlesData: FlTitlesData( + show: true, + rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + leftTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + const days = ['M', 'T', 'W', 'T', 'F', 'S', 'S']; + if (value.toInt() < days.length) { + return Text(days[value.toInt()], style: GoogleFonts.lato(fontSize: 10, color: Colors.grey)); + } + return const SizedBox(); + }, + ), + ), + ), + borderData: FlBorderData(show: false), + barGroups: history.asMap().entries.map((e) { + return BarChartGroupData( + x: e.key, + barRods: [ + BarChartRodData( + toY: e.value, + color: e.value >= service.dailyGoal ? color : color.withOpacity(0.5), + width: 12, + borderRadius: BorderRadius.circular(6), + backDrawRodData: BackgroundBarChartRodData( + show: true, + toY: 4000, + color: Colors.grey.withOpacity(0.1), + ), + ), + ], + ); + }).toList(), + ), + ), + ), + ), + ), + const SizedBox(height: 80), + ], + ), + ), + ); + } + + Widget _buildMetricCard(String title, String value, IconData icon, Color color) { + return Expanded( + child: GlassCard( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(icon, size: 20, color: color), + const SizedBox(height: 8), + Text(value, style: GoogleFonts.montserrat(fontSize: 18, fontWeight: FontWeight.bold)), + Text(title, style: GoogleFonts.lato(fontSize: 12, color: Colors.grey.shade600)), + ], + ), + ), + ); + } +} diff --git a/lib/services/hydration_service.dart b/lib/services/hydration_service.dart new file mode 100644 index 0000000..cd4ff2b --- /dev/null +++ b/lib/services/hydration_service.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'package:hive_flutter/hive_flutter.dart'; +import 'package:intl/intl.dart'; + +import '../models/drink_type.dart'; + +class HydrationService extends ChangeNotifier { + final Box _dataBox = Hive.box('hydrationData'); + final Box _settingsBox = Hive.box('userSettings'); + + double _currentIntake = 0.0; + double _purityScore = 1.0; + DateTime _lastDrinkTime = DateTime.now(); + + Map _breakdown = { + DrinkType.Water: 0, + DrinkType.Coffee: 0, + DrinkType.Tea: 0, + DrinkType.Soda: 0, + }; + + double _weight = 60.0; + double _workoutMinutes = 0.0; + bool _remindersEnabled = true; + double _reminderInterval = 60.0; + + List _history = []; + Timer? _decayTimer; + + HydrationService() { + _loadData(); + _startDecayCheck(); + } + + double get currentIntake => _currentIntake; + double get dailyGoal => (_weight * 35.0) + (_workoutMinutes * 8.0); + double get percentage => (_currentIntake / dailyGoal).clamp(0.0, 1.0); + List get history => _history; + double get weight => _weight; + double get workoutMinutes => _workoutMinutes; + bool get remindersEnabled => _remindersEnabled; + double get reminderInterval => _reminderInterval; + Map get breakdown => _breakdown; + + Color get dynamicWaterColor { + Color baseColor = const Color(0xFF00BFFF); + Color impurityColor = const Color(0xFFD2B48C); + Color blendedWithImpurity = Color.lerp(impurityColor, baseColor, _purityScore)!; + + final minutesSince = DateTime.now().difference(_lastDrinkTime).inMinutes; + double freshness = 1.0; + if (minutesSince > 120) { + double overdueHours = (minutesSince - 120) / 60.0; + freshness = (1.0 - (overdueHours * 0.2)).clamp(0.4, 1.0); + } + + Color dryColor = Colors.grey.shade400; + return Color.lerp(dryColor, blendedWithImpurity, freshness)!; + } + + String get statusText { + final minutesSince = DateTime.now().difference(_lastDrinkTime).inMinutes; + if (minutesSince > 120) return "Body is withering..."; + if (_purityScore < 0.7) return "Purify your system"; + return "Metabolism Optimal"; + } + + void _loadData() { + final todayKey = DateFormat('yyyyMMdd').format(DateTime.now()); + _currentIntake = _dataBox.get(todayKey, defaultValue: 0.0); + + _purityScore = _dataBox.get('purity_$todayKey', defaultValue: 1.0); + int? lastTs = _dataBox.get('last_drink_ts'); + _lastDrinkTime = lastTs != null ? DateTime.fromMillisecondsSinceEpoch(lastTs) : DateTime.now(); + + _weight = _settingsBox.get('weight', defaultValue: 60.0); + _workoutMinutes = _settingsBox.get('workout', defaultValue: 0.0); + _remindersEnabled = _settingsBox.get('reminders', defaultValue: true); + _reminderInterval = _settingsBox.get('reminderInterval', defaultValue: 60.0); + + if (_history.isEmpty) { + for (int i = 0; i < 7; i++) { + _history.add(1500 + math.Random().nextInt(1500).toDouble()); + } + } + + _breakdown = { + DrinkType.Water: _currentIntake * 0.7, + DrinkType.Coffee: _currentIntake * 0.2, + DrinkType.Tea: _currentIntake * 0.1, + DrinkType.Soda: 0, + }; + notifyListeners(); + } + + void updateSettings({double? weight, double? workout, bool? reminders, double? interval}) { + if (weight != null) _weight = weight; + if (workout != null) _workoutMinutes = workout; + if (reminders != null) _remindersEnabled = reminders; + if (interval != null) _reminderInterval = interval; + + _settingsBox.put('weight', _weight); + _settingsBox.put('workout', _workoutMinutes); + _settingsBox.put('reminders', _remindersEnabled); + _settingsBox.put('reminderInterval', _reminderInterval); + notifyListeners(); + } + + void addWater(double amount, {DrinkType type = DrinkType.Water}) { + double efficientAmount = amount * type.hydrationFactor; + _currentIntake += efficientAmount; + + _breakdown[type] = (_breakdown[type] ?? 0) + efficientAmount; + + if (type == DrinkType.Water) { + _purityScore = (_purityScore + 0.15).clamp(0.0, 1.0); + } else { + _purityScore = (_purityScore - (1.0 - type.purityFactor)).clamp(0.0, 1.0); + } + + _lastDrinkTime = DateTime.now(); + + final todayKey = DateFormat('yyyyMMdd').format(DateTime.now()); + _dataBox.put(todayKey, _currentIntake); + _dataBox.put('purity_$todayKey', _purityScore); + _dataBox.put('last_drink_ts', _lastDrinkTime.millisecondsSinceEpoch); + + notifyListeners(); + } + + void _startDecayCheck() { + _decayTimer = Timer.periodic(const Duration(minutes: 1), (timer) { + notifyListeners(); + }); + } + + @override + void dispose() { + _decayTimer?.cancel(); + super.dispose(); + } +} diff --git a/lib/widgets/fluid_sphere_button.dart b/lib/widgets/fluid_sphere_button.dart new file mode 100644 index 0000000..92ae393 --- /dev/null +++ b/lib/widgets/fluid_sphere_button.dart @@ -0,0 +1,137 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import 'wave_painter.dart'; + +class FluidSphereButton extends StatefulWidget { + final double percentage; + final Color waterColor; + final VoidCallback onLongPressStart; + final VoidCallback onLongPressEnd; + + const FluidSphereButton({ + super.key, + required this.percentage, + required this.waterColor, + required this.onLongPressStart, + required this.onLongPressEnd, + }); + + @override + State createState() => _FluidSphereButtonState(); +} + +class _FluidSphereButtonState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(seconds: 4), + )..repeat(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onLongPress: widget.onLongPressStart, + onLongPressUp: widget.onLongPressEnd, + child: AnimatedContainer( + duration: const Duration(seconds: 1), + width: 260, + height: 260, + decoration: BoxDecoration( + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: widget.waterColor.withOpacity(0.3), + blurRadius: 40, + offset: const Offset(0, 20), + ), + BoxShadow( + color: Colors.white.withOpacity(0.9), + blurRadius: 20, + offset: const Offset(-10, -10), + ), + ], + ), + child: ClipOval( + child: Stack( + children: [ + Container(color: Colors.white.withOpacity(0.2)), + + AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return CustomPaint( + painter: WavePainter( + animationValue: _controller.value, + percentage: widget.percentage, + color: widget.waterColor, + ), + size: Size.infinite, + ); + }, + ), + + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.white.withOpacity(0.6), + Colors.transparent, + Colors.transparent, + Colors.white.withOpacity(0.1), + ], + stops: const [0.0, 0.4, 0.7, 1.0], + ), + ), + ), + + Positioned( + top: 30, + right: 30, + child: Container( + width: 40, + height: 20, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.4), + borderRadius: const BorderRadius.all(Radius.elliptical(40, 20)), + ), + ), + ), + + Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "HOLD TO INFUSE", + style: GoogleFonts.montserrat( + color: widget.percentage > 0.6 ? Colors.white : Colors.black45, + fontSize: 10, + letterSpacing: 2.0, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/glass_card.dart b/lib/widgets/glass_card.dart new file mode 100644 index 0000000..d3ab71a --- /dev/null +++ b/lib/widgets/glass_card.dart @@ -0,0 +1,49 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class GlassCard extends StatelessWidget { + final Widget child; + final double? width; + final double? height; + final EdgeInsetsGeometry padding; + + const GlassCard({ + super.key, + required this.child, + this.width, + this.height, + this.padding = const EdgeInsets.all(16), + }); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular(20), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container( + width: width, + height: height, + padding: padding, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.65), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.white.withOpacity(0.6), + width: 1.0, + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.03), + blurRadius: 15, + offset: const Offset(0, 5), + ), + ], + ), + child: child, + ), + ), + ); + } +} diff --git a/lib/widgets/main_navigation_frame.dart b/lib/widgets/main_navigation_frame.dart new file mode 100644 index 0000000..422428a --- /dev/null +++ b/lib/widgets/main_navigation_frame.dart @@ -0,0 +1,78 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import '../screens/home_screen.dart'; +import '../screens/message_screen.dart'; +import '../screens/settings_screen.dart'; +import '../screens/stats_screen.dart'; +import '../services/hydration_service.dart'; + +class MainNavigationFrame extends StatefulWidget { + const MainNavigationFrame({super.key}); + + @override + State createState() => _MainNavigationFrameState(); +} + +class _MainNavigationFrameState extends State { + int _currentIndex = 0; + final List _pages = [ + const HomeScreen(), + const MessageScreen(), + const StatsScreen(), + const SettingsScreen(), + ]; + + @override + Widget build(BuildContext context) { + final service = context.watch(); + final activeColor = service.dynamicWaterColor; + + return Scaffold( + extendBody: true, + body: _pages[_currentIndex], + bottomNavigationBar: Container( + margin: const EdgeInsets.only(left: 20, right: 20, bottom: 30), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.8), + borderRadius: BorderRadius.circular(30), + boxShadow: [ + BoxShadow( + color: activeColor.withOpacity(0.15), + blurRadius: 25, + offset: const Offset(0, 8), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(30), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), + child: BottomNavigationBar( + currentIndex: _currentIndex, + onTap: (index) => setState(() => _currentIndex = index), + backgroundColor: Colors.transparent, + elevation: 0, + type: BottomNavigationBarType.fixed, + selectedItemColor: activeColor, + unselectedItemColor: Colors.grey.shade400, + selectedLabelStyle: GoogleFonts.montserrat(fontSize: 10, fontWeight: FontWeight.w600), + unselectedLabelStyle: GoogleFonts.montserrat(fontSize: 10), + showSelectedLabels: false, + showUnselectedLabels: false, + items: const [ + BottomNavigationBarItem(icon: Icon(Icons.water_drop_rounded), label: 'Home'), + BottomNavigationBarItem(icon: Icon(Icons.notifications_none_rounded), label: 'Msg'), + BottomNavigationBarItem(icon: Icon(Icons.bar_chart_rounded), label: 'Stats'), + BottomNavigationBarItem(icon: Icon(Icons.settings_outlined), label: 'Settings'), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/widgets/wave_painter.dart b/lib/widgets/wave_painter.dart new file mode 100644 index 0000000..cede03a --- /dev/null +++ b/lib/widgets/wave_painter.dart @@ -0,0 +1,63 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; + +class WavePainter extends CustomPainter { + final double animationValue; + final double percentage; + final Color color; + + WavePainter({ + required this.animationValue, + required this.percentage, + required this.color, + }); + + @override + void paint(Canvas canvas, Size size) { + final double baseHeight = size.height * (1 - percentage); + + final Paint paint1 = Paint() + ..color = color.withOpacity(0.8) + ..style = PaintingStyle.fill; + + final Paint paint2 = Paint() + ..color = color.withOpacity(0.5) + ..style = PaintingStyle.fill; + + final path1 = Path(); + final path2 = Path(); + + path1.moveTo(0, baseHeight); + path2.moveTo(0, baseHeight); + + for (double i = 0.0; i <= size.width; i++) { + path1.lineTo( + i, + baseHeight + math.sin((i / size.width * 2 * math.pi) + (animationValue * 2 * math.pi)) * 10, + ); + path2.lineTo( + i, + baseHeight + math.sin((i / size.width * 2 * math.pi) + (animationValue * 2 * math.pi) + 1.5) * 12, + ); + } + + path1.lineTo(size.width, size.height); + path1.lineTo(0, size.height); + path1.close(); + + path2.lineTo(size.width, size.height); + path2.lineTo(0, size.height); + path2.close(); + + canvas.drawPath(path2, paint2); + canvas.drawPath(path1, paint1); + } + + @override + bool shouldRepaint(covariant WavePainter oldDelegate) { + return oldDelegate.animationValue != animationValue || + oldDelegate.percentage != percentage || + oldDelegate.color != color; + } +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..3f2d6ed --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,602 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: e4a1b612fd2955908e26116075b3a4baf10c353418ca645b4deae231c82bf144 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.65" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.19.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "701dcfc06da0882883a2657c445103380e53e647060ad8d9dfb710c100996608" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.5+1" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.7" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.8" + equatable: + dependency: transitive + description: + name: equatable + sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.8" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.flutter-io.cn" + source: hosted + version: "7.0.1" + firebase_analytics: + dependency: "direct main" + description: + name: firebase_analytics + sha256: "8ca4832c7a6d145ce987fd07d6dfbb8c91d9058178342f20de6305fb77b1b40d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "12.1.0" + firebase_analytics_platform_interface: + dependency: transitive + description: + name: firebase_analytics_platform_interface + sha256: d00234716f415f89eb5c2cefb1238d7fd2f3120275d71414b84ae434dcdb7a19 + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.5" + firebase_analytics_web: + dependency: transitive + description: + name: firebase_analytics_web + sha256: e42b294e51aedb4bd4b761a886c8d6b473c44b44aa4c0b47cab06b2c66ac3fba + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.6.1+1" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "29cfa93c771d8105484acac340b5ea0835be371672c91405a300303986f4eba9" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.3.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.0.2" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: a631bbfbfa26963d68046aed949df80b228964020e9155b086eff94f462bbf1f + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.3.1" + firebase_crashlytics: + dependency: "direct main" + description: + name: firebase_crashlytics + sha256: "8d52022ee6fdd224e92c042f297d1fd0ec277195c49f39fa61b8cc500a639f00" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.6" + firebase_crashlytics_platform_interface: + dependency: transitive + description: + name: firebase_crashlytics_platform_interface + sha256: "97c6a97b35e3d3dafe38fb053a65086a1efb125022d292161405848527cc25a4" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.8.16" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + sha256: "7ca9a40f4eb85949190e54087be8b4d6ac09dc4c54238d782a34cf1f7c011de9" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + sha256: ba03d03bcaa2f6cb7bd920e3b5027181db75ab524f8891c8bc3aa603885b8055 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.3.3" + hive: + dependency: "direct main" + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.3" + hive_flutter: + dependency: "direct main" + description: + name: hive_flutter + sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.20.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.flutter-io.cn" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.22" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.8" + provider: + dependency: "direct main" + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.5+1" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840" + url: "https://pub.flutter-io.cn" + source: hosted + version: "12.0.1" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.6" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.3.28" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.3.6" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.2.2" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.2.5" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.5" + uuid: + dependency: transitive + description: + name: uuid + sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.5.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.flutter-io.cn" + source: hosted + version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.15.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" +sdks: + dart: ">=3.9.2 <4.0.0" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..037ae00 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,69 @@ +name: bio_flux +description: "BioFlux:Smart Hydration." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# 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 is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ^3.9.2 + + +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + share_plus: ^12.0.1 + provider: ^6.1.5+1 + hive: ^2.2.3 + hive_flutter: ^1.1.0 + fl_chart: ^1.1.1 + intl: ^0.20.2 + google_fonts: ^6.3.3 + firebase_core: ^4.3.0 + firebase_crashlytics: ^5.0.6 + firebase_analytics: ^12.1.0 + url_launcher: ^6.3.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^5.0.0 + +# 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 packages. +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/ + diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..742df40 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. 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:hydro_flux/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const 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); + }); +}