fix(uiux): Major reimplementation of tiles, refactoring and tightening of UI (#525)

* fix(database): Use native extension instead of describeEnum
* new(sonarr): [Catalogue] Previous airing sorting options
* splash: Rebuild Android & iOS splash screens
* changelog: Refactor changelog sheet
* Continued WIP Polish of UI
* chore: Reintegrate pods
* calendar: Added second line for Lidarr/Radarr entries
* settings: Show quick actions information as banner block
* lidarr: Add support for monitoring options on add, posters in result list
* uiux: Fade edges of scrollable text
* drawer: Restyle header
* Remove redundant body texts in edit/add content
* macos: Set window size & alignment
* settings: Add description banner block for profiles
This commit is contained in:
Jagandeep Brar
2021-12-22 14:07:28 -05:00
committed by GitHub
parent 0ef68b20f4
commit bb725ce056
369 changed files with 5979 additions and 6542 deletions

View File

@@ -1,6 +1,5 @@
android/app/google-services.json
android/key.jks
android/key.properties
ios/ExportOptions.plist
ios/GoogleService-Info.plist
macos/GoogleService-Info.plist

1
.gitignore vendored
View File

@@ -52,7 +52,6 @@ coverage/
/android/key.properties
/android/app/google-services.json
/android/key.jks
/ios/ExportOptions.plist
/ios/GoogleService-Info.plist
/macos/GoogleService-Info.plist
/vendor/font_awesome_flutter/lib/fonts/fa-thin-100.ttf

View File

@@ -1,17 +1,41 @@
# LunaSea Changelog
## WIP
## v6.0.0 (60000004)
#### NEW
- `[Settings]` (Configuration) Show dismissable banner with module information and links
- `[Dashboard]` (Calendar) Display content poster and additional information
- `[Firebase]` (Analytics) Completely removed Analytics and Crashlytics
- `[Settings]` Show dismissable blocks with information and links
- `[Lidarr]` (Add Artist) Show artist poster in result list
- `[Lidarr]` (Add Artist) Allow user to select monitoring options
- `[Lidarr]` (Add Artist) Removed toggle to use album folders
- `[Lidarr]` (Catalogue) Show artist poster in catalogue list
- `[Lidarr]` (Missing) Display album covers
- `[Sonarr]` (Catalogue) Previous airing sorting option
- `[Sonarr]` (Season List) Display season posters
- `[Sonarr]` (Queue) Option to set queue page size
- `[UI/UX]` (Tile Blocks) Allow overflowing title or body lines to be horizontally scrolled
#### TWEAKS
- `[UI/UX]` (Drawer) Heavily reduced the size of the header area
- `[UI/UX]` (Fonts) Normalized font sizes across the UI
- `[UI/UX]` (Images) Set the default image opacity to 20% (custom set values are uneffected)
- `[UI/UX]` (Tile Blocks) Heavily improved build and memory performance of tiles
- `[UI/UX]` (Tile Blocks) Improved consistency in trailing icon/text size
- `[UI/UX]` (Tile Blocks) Improved consistency of tile height, content spacing, and padding
- `[UI/UX]` (Tile Blocks) Natively draw placeholder posters for movies, series, albums, etc.
- `[UI/UX]` (Tile Blocks) Fade-in background image loads
- `[UI/UX]` (Tooltips) Added styling to match LunaSea theme
#### FIXES
- `[Dashboard]` (Calendar) Lidarr entries would not appear
- `[Dashboard]` (Modules) Unbranded icons could use incorrect colours
- `[UI/UX]` (List Tiles) Fixed consistency of trailing icon/text size
- `[Lidarr]` (Albums) List would fail to load on nightly Lidarr builds
- `[Sonarr]` (Catalogue) Missing filter would fail to load when using Sonarr v4 in some cases
- `[Sonarr]` (Images) Images now use v3 API path
---

View File

@@ -22,9 +22,8 @@ if (flutterVersionName == null) {
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties()
@@ -34,20 +33,25 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 30
compileSdkVersion 31
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
applicationId "app.lunasea.lunasea"
minSdkVersion 24
targetSdkVersion 30
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

View File

@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.lunasea.lunasea">
<application
android:name="io.flutter.app.FlutterApplication"
android:name="${applicationName}"
android:usesCleartextTraffic="true"
android:label="LunaSea"
android:icon="@mipmap/ic_launcher">
@@ -17,6 +17,7 @@
android:value="true" />
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B

View File

@@ -1,16 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/splash_color" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
<item>
<bitmap android:gravity="center" android:src="@drawable/splash" />
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
</layer-list>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowFullscreen">false</item>
<item name="android:windowSplashScreenBackground">#32323E</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -5,6 +5,7 @@
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:windowFullscreen">false</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
@@ -15,6 +16,5 @@
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@color/splash_color</item>
<item name="android:windowFullscreen">false</item>
</style>
</resources>

View File

@@ -1,22 +1,21 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.5.32'
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.4'
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0'
classpath 'com.google.gms:google-services:4.3.8'
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}

View File

@@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -6,11 +6,13 @@
"settings.AccountHelp": "LunaSea Account",
"settings.AccountHelpHint1": "LunaSea offers a free account to backup your configuration to the cloud, with additional features coming in the future!",
"settings.AccountSettings": "Account Settings",
"settings.Add": "Add",
"settings.AddHeader": "Add Header",
"settings.AddModule": "Add Module",
"settings.AddModuleFailed": "Failed to Add Module",
"settings.AddModuleSuccess": "Module Added",
"settings.AddProfile": "Add Profile",
"settings.AddProfileDescription": "Add a New Profile",
"settings.AllFieldsAreRequired": "All fields are required",
"settings.AmoledTheme": "AMOLED Theme",
"settings.AmoledThemeBorders": "AMOLED Theme Borders",
@@ -62,6 +64,10 @@
"settings.CustomHeaders": "Custom Headers",
"settings.CustomHeadersDescription": "Add Custom Headers to Requests",
"settings.DefaultPage": "Default Page",
"settings.DefaultPages": "Default Pages",
"settings.DefaultPagesDescription": "Set Default Landing Pages",
"settings.DefaultSortingAndFiltering": "Default Sorting & Filtering",
"settings.DefaultSortingAndFilteringDescription": "Set Default Sorting & Filtering Methods",
"settings.DecryptBackup": "Decrypt Backup",
"settings.DecryptBackupHint1": "Please enter the encryption key for this backup.",
"settings.DebugMenu": "Debug Menu",
@@ -83,6 +89,7 @@
"settings.DeleteModuleHint1": "Are you sure you want to delete this external module?",
"settings.DeleteModuleSuccess": "Module Deleted",
"settings.DeleteProfile": "Delete Profile",
"settings.DeleteProfileDescription": "Delete an Existing Profile",
"settings.DismissBanners": "Dismiss Banners",
"settings.DismissBannersHint1": "Are you sure you want to dismiss all tooltip banners?",
"settings.DismissBannersHint2": "The tooltip banners will give you tips and hints for features available within LunaSea.",
@@ -158,6 +165,7 @@
"settings.RegisteredFailure": "Failed to Register",
"settings.RegisteredSuccess": "Successfully Registered",
"settings.RenameProfile": "Rename Profile",
"settings.RenameProfileDescription": "Rename an Existing Profile",
"settings.ResetPassword": "Reset Password",
"settings.Resources": "Resources",
"settings.ResourcesDescription": "Useful Resources & Links",
@@ -185,6 +193,7 @@
"settings.Username": "Username",
"settings.UsernameValidation": "Username Required",
"settings.MustBeValueBetween": "Must be a value between {} and {}",
"lunasea.Ascending": "Ascending",
"lunasea.Add": "Add",
"lunasea.AnErrorHasOccurred": "An Error Has Occurred",
"lunasea.BackUp": "Back Up",
@@ -196,12 +205,14 @@
"lunasea.ComingSoon": "Coming Soon",
"lunasea.Dashboard": "Dashboard",
"lunasea.Delete": "Delete",
"lunasea.Descending": "Descending",
"lunasea.Disable": "Disable",
"lunasea.Disabled": "Disabled",
"lunasea.Dismiss": "Dismiss",
"lunasea.ExternalModules": "External Modules",
"lunasea.GoBack": "Go Back",
"lunasea.GoToSettings": "Go to Settings",
"lunasea.Home": "Home",
"lunasea.IncorrectEncryptionKey": "Incorrect encryption key",
"lunasea.Module": "Module",
"lunasea.ModuleIsNotEnabled": "{} Is Not Enabled",
@@ -267,6 +278,7 @@
"radarr.ManualImportDescription": "Import Movies from the Filesystem",
"radarr.MinimumAvailability": "Minimum Availability",
"radarr.Missing": "Missing",
"radarr.Monitor": "Monitor",
"radarr.Monitored": "Monitored",
"radarr.MonitoredDescription": "Monitor for new releases",
"radarr.MonitorMovie": "Monitor Movie",
@@ -311,6 +323,8 @@
"radarr.SelectQuality": "Select Quality",
"radarr.Size": "Size",
"radarr.SortCatalogue": "Sort Catalogue",
"radarr.StartSearchFor": "Start Search For…",
"radarr.StartSearchForMissingMovie": "Start search for missing movie",
"radarr.Studio": "Studio",
"radarr.SystemStatus": "System Status",
"radarr.SystemStatusDescription": "System Status & Disk Space",
@@ -549,7 +563,7 @@
"sonarr.MonitorSeries": "Monitor Series",
"sonarr.Monitored": "Monitored",
"sonarr.Monitoring": "Monitoring",
"sonarr.MonitoredDescription": "Monitor series for new releases",
"sonarr.MonitoredDescription": "Download monitored episodes in this series",
"sonarr.More": "More",
"sonarr.Name": "Name",
"sonarr.NoEpisodesFound": "No Episodes Found",

Binary file not shown.

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>compileBitcode</key>
<true/>
<key>method</key>
<string>development</string>
<key>signingStyle</key>
<string>automatic</string>
<key>stripSwiftSymbols</key>
<true/>
<!--
WHAT YOU SHOULD EDIT BELOW
-->
<key>teamID</key>
<string>INSERT-YOUR-TEAMID-HERE</string>
<!--
WHAT YOU SHOULD EDIT ABOVE
-->
<key>thinning</key>
<string>&lt;none&gt;</string>
</dict>
</plist>

View File

@@ -1,5 +1,5 @@
PODS:
- cloud_firestore (3.1.0):
- cloud_firestore (3.1.5):
- Firebase/Firestore (= 8.9.0)
- firebase_core
- Flutter
@@ -37,19 +37,11 @@ PODS:
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Firebase/Analytics (8.9.0):
- Firebase/Core
- Firebase/Auth (8.9.0):
- Firebase/CoreOnly
- FirebaseAuth (~> 8.9.0)
- Firebase/Core (8.9.0):
- Firebase/CoreOnly
- FirebaseAnalytics (~> 8.9.0)
- Firebase/CoreOnly (8.9.0):
- FirebaseCore (= 8.9.0)
- Firebase/Crashlytics (8.9.0):
- Firebase/CoreOnly
- FirebaseCrashlytics (~> 8.9.0)
- Firebase/Firestore (8.9.0):
- Firebase/CoreOnly
- FirebaseFirestore (~> 8.9.0)
@@ -59,47 +51,21 @@ PODS:
- Firebase/Storage (8.9.0):
- Firebase/CoreOnly
- FirebaseStorage (~> 8.9.0)
- firebase_analytics (8.3.4):
- Firebase/Analytics (= 8.9.0)
- firebase_core
- Flutter
- firebase_auth (3.2.0):
- firebase_auth (3.3.4):
- Firebase/Auth (= 8.9.0)
- firebase_core
- Flutter
- firebase_core (1.10.0):
- firebase_core (1.10.6):
- Firebase/CoreOnly (= 8.9.0)
- Flutter
- firebase_crashlytics (2.3.0):
- Firebase/Crashlytics (= 8.9.0)
- firebase_core
- Flutter
- firebase_messaging (11.1.0):
- firebase_messaging (11.2.4):
- Firebase/Messaging (= 8.9.0)
- firebase_core
- Flutter
- firebase_storage (10.1.0):
- firebase_storage (10.2.4):
- Firebase/Storage (= 8.9.0)
- firebase_core
- Flutter
- FirebaseAnalytics (8.9.1):
- FirebaseAnalytics/AdIdSupport (= 8.9.1)
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.6)
- GoogleUtilities/MethodSwizzler (~> 7.6)
- GoogleUtilities/Network (~> 7.6)
- "GoogleUtilities/NSData+zlib (~> 7.6)"
- nanopb (~> 2.30908.0)
- FirebaseAnalytics/AdIdSupport (8.9.1):
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleAppMeasurement (= 8.9.1)
- GoogleUtilities/AppDelegateSwizzler (~> 7.6)
- GoogleUtilities/MethodSwizzler (~> 7.6)
- GoogleUtilities/Network (~> 7.6)
- "GoogleUtilities/NSData+zlib (~> 7.6)"
- nanopb (~> 2.30908.0)
- FirebaseAuth (8.9.0):
- FirebaseCore (~> 8.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.6)
@@ -109,18 +75,11 @@ PODS:
- FirebaseCoreDiagnostics (~> 8.0)
- GoogleUtilities/Environment (~> 7.6)
- GoogleUtilities/Logger (~> 7.6)
- FirebaseCoreDiagnostics (8.9.0):
- FirebaseCoreDiagnostics (8.10.0):
- GoogleDataTransport (~> 9.1)
- GoogleUtilities/Environment (~> 7.6)
- GoogleUtilities/Logger (~> 7.6)
- nanopb (~> 2.30908.0)
- FirebaseCrashlytics (8.9.0):
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleDataTransport (~> 9.1)
- GoogleUtilities/Environment (~> 7.6)
- nanopb (~> 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- FirebaseFirestore (8.9.0):
- FirebaseFirestore/AutodetectLeveldb (= 8.9.0)
- FirebaseFirestore/AutodetectLeveldb (8.9.0):
@@ -129,7 +88,7 @@ PODS:
- FirebaseFirestore/Base (8.9.0)
- FirebaseFirestore/WithLeveldb (8.9.0):
- FirebaseFirestore/Base
- FirebaseInstallations (8.9.0):
- FirebaseInstallations (8.10.0):
- FirebaseCore (~> 8.0)
- GoogleUtilities/Environment (~> 7.6)
- GoogleUtilities/UserDefaults (~> 7.6)
@@ -147,26 +106,6 @@ PODS:
- FirebaseCore (~> 8.0)
- GTMSessionFetcher/Core (~> 1.5)
- Flutter (1.0.0)
- GoogleAppMeasurement (8.9.1):
- GoogleAppMeasurement/AdIdSupport (= 8.9.1)
- GoogleUtilities/AppDelegateSwizzler (~> 7.6)
- GoogleUtilities/MethodSwizzler (~> 7.6)
- GoogleUtilities/Network (~> 7.6)
- "GoogleUtilities/NSData+zlib (~> 7.6)"
- nanopb (~> 2.30908.0)
- GoogleAppMeasurement/AdIdSupport (8.9.1):
- GoogleAppMeasurement/WithoutAdIdSupport (= 8.9.1)
- GoogleUtilities/AppDelegateSwizzler (~> 7.6)
- GoogleUtilities/MethodSwizzler (~> 7.6)
- GoogleUtilities/Network (~> 7.6)
- "GoogleUtilities/NSData+zlib (~> 7.6)"
- nanopb (~> 2.30908.0)
- GoogleAppMeasurement/WithoutAdIdSupport (8.9.1):
- GoogleUtilities/AppDelegateSwizzler (~> 7.6)
- GoogleUtilities/MethodSwizzler (~> 7.6)
- GoogleUtilities/Network (~> 7.6)
- "GoogleUtilities/NSData+zlib (~> 7.6)"
- nanopb (~> 2.30908.0)
- GoogleDataTransport (9.1.2):
- GoogleUtilities/Environment (~> 7.2)
- nanopb (~> 2.30908.0)
@@ -214,7 +153,7 @@ PODS:
- nanopb/encode (2.30908.0)
- package_info_plus (0.4.5):
- Flutter
- path_provider (0.0.1):
- path_provider_ios (0.0.1):
- Flutter
- PromisesObjC (2.0.0)
- quick_actions (0.0.1):
@@ -224,20 +163,18 @@ PODS:
- SDWebImage/Core (5.12.1)
- share_plus (0.0.1):
- Flutter
- shared_preferences (0.0.1):
- shared_preferences_ios (0.0.1):
- Flutter
- SwiftyGif (5.4.0)
- url_launcher (0.0.1):
- SwiftyGif (5.4.2)
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Firebase/Messaging
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- firebase_storage (from `.symlinks/plugins/firebase_storage/ios`)
- FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `8.9.0`)
@@ -245,26 +182,23 @@ DEPENDENCIES:
- GoogleUtilities
- in_app_purchase_ios (from `.symlinks/plugins/in_app_purchase_ios/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- quick_actions (from `.symlinks/plugins/quick_actions/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
- Firebase
- FirebaseAnalytics
- FirebaseAuth
- FirebaseCore
- FirebaseCoreDiagnostics
- FirebaseCrashlytics
- FirebaseInstallations
- FirebaseMessaging
- FirebaseStorage
- GoogleAppMeasurement
- GoogleDataTransport
- GoogleUtilities
- GTMSessionFetcher
@@ -278,14 +212,10 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/cloud_firestore/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
firebase_analytics:
:path: ".symlinks/plugins/firebase_analytics/ios"
firebase_auth:
:path: ".symlinks/plugins/firebase_auth/ios"
firebase_core:
:path: ".symlinks/plugins/firebase_core/ios"
firebase_crashlytics:
:path: ".symlinks/plugins/firebase_crashlytics/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
firebase_storage:
@@ -299,16 +229,16 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/in_app_purchase_ios/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
quick_actions:
:path: ".symlinks/plugins/quick_actions/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences:
:path: ".symlinks/plugins/shared_preferences/ios"
url_launcher:
:path: ".symlinks/plugins/url_launcher/ios"
shared_preferences_ios:
:path: ".symlinks/plugins/shared_preferences_ios/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
CHECKOUT OPTIONS:
FirebaseFirestore:
@@ -316,43 +246,38 @@ CHECKOUT OPTIONS:
:tag: 8.9.0
SPEC CHECKSUMS:
cloud_firestore: 2cff0618e560f435fc05da5c94c2d213eb7ba640
cloud_firestore: 338122a6149c14ca376ad363626b5bf3037a5056
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
Firebase: 13d8d96499e2635428d5bf0ec675df21f95d9a95
firebase_analytics: 7a7528bb2abf4ca22cff7a57bbf909dcab73e13d
firebase_auth: 0d56cc4d105ff70d1547a7fcb10ee574ad05adf5
firebase_core: f770e033e790657b3505f04be4cb24c482912f11
firebase_crashlytics: ed8c1d66dc407d617cbed798f05a5d9115da7494
firebase_messaging: 0c8d1a1732487db7f332fb65232053e93201e2fb
firebase_storage: a3bbe618ef41930ae31da8f4691d37e4ed84f14d
FirebaseAnalytics: 4ab446ce08a3fe52e8a4303dd997cf26276bf968
firebase_auth: e875a50b81aefb20f305dfede425647e9675c19e
firebase_core: c263d7daf1dc92fcd9895e6abdc04872b0ee07ff
firebase_messaging: dff5cd08781ee1de988565a83c977e435405cd7e
firebase_storage: 2ed331c6bd5bfa6d64f1a6c57f01151f4f146c16
FirebaseAuth: 2b78b2a32c07b3ecfa4970bdf1d3632b8304099b
FirebaseCore: 599ee609343eaf4941bd188f85e3aa077ffe325b
FirebaseCoreDiagnostics: 5daa63f1c1409d981a2d5007daa100b36eac6a34
FirebaseCrashlytics: 40efbd81157dae307ec95612fa1328347284d2c2
FirebaseCoreDiagnostics: 56fb7216d87e0e6ec2feddefa9d8a392fe8b2c18
FirebaseFirestore: 7c22bff6c0e1f222920cd110e9da13a117b61fea
FirebaseInstallations: caa7c8e0d3e2345b8829d2fa9ca1b4dfbf2fcc85
FirebaseInstallations: 830327b45345ffc859eaa9c17bcd5ae893fd5425
FirebaseMessaging: 82c4a48638f53f7b184f3cc9f6cd2cbe533ab316
FirebaseStorage: 452c98c31ccb40b819764bf3039426c4388d9939
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
GoogleAppMeasurement: 837649ad3987936c232f6717c5680216f6243d24
GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940
GoogleUtilities: 684ee790a24f73ebb2d1d966e9711c203f2a4237
GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
in_app_purchase_ios: 04ed55fdd1012db54095d6801335b5f5de0a475f
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider: d1e9807085df1f9cc9318206cd649dc0b76be3de
path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5
PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58
quick_actions: cd83314083fa994182e6cd9e7516167a215a4f83
SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
shared_preferences: 5033afbb22d372e15aff8ff766df9021b845f273
SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7
url_launcher: b6e016d912f04be9f5bf6e8e82dc599b7ba59649
shared_preferences_ios: aef470a42dc4675a1cdd50e3158b42e3d1232b32
SwiftyGif: dec758a9dd3d278e5a855dbf279bf062c923c387
url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af
PODFILE CHECKSUM: 6140814a61b9571bc8e1daa6ec9af34598f6e610
COCOAPODS: 1.10.2
COCOAPODS: 1.11.2

View File

@@ -3,15 +3,14 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
1EA81D57F09988D75AC476F5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AE878F72B0C2DCB5A4603ABD /* Pods_Runner.framework */; };
22ABA27907009C6D67E17F02 /* Pods_ImageNotification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DDBAA25D93558DC2ACE8794 /* Pods_ImageNotification.framework */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
7585ADE8DCB47DC8753EF72A /* Pods_ImageNotification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 989B86C0CD70FC0AB24F07D6 /* Pods_ImageNotification.framework */; };
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 */; };
@@ -19,6 +18,7 @@
9C5DBBF2257EB50400836D8D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9C5DBBF1257EB50400836D8D /* GoogleService-Info.plist */; };
9C7092B325E77E470056AE0B /* NotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C7092B225E77E470056AE0B /* NotificationService.m */; };
9C7092B725E77E470056AE0B /* ImageNotification.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 9C7092AF25E77E470056AE0B /* ImageNotification.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
E81187C204448290256D1292 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 525B84ABAED8CC10DD278134 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -58,14 +58,13 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
1F2EFFABEACE166F53F7025F /* Pods-ImageNotification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.debug.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.debug.xcconfig"; sourceTree = "<group>"; };
2FC45D8C03585F29FFEEBAD5 /* 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 = "<group>"; };
1AEC3393F733CF63CC471C78 /* 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 = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
50F2C55D9F5DC33EBDADB9C4 /* Pods-ImageNotification.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.profile.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.profile.xcconfig"; sourceTree = "<group>"; };
525B84ABAED8CC10DD278134 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
76B7822037A027527BE9DFD3 /* 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 = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7DDBAA25D93558DC2ACE8794 /* Pods_ImageNotification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageNotification.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -73,7 +72,6 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
989B86C0CD70FC0AB24F07D6 /* Pods_ImageNotification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageNotification.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9C0A396F23E0F00900EC8127 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
9C2991232453FE3100B93AB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
9C5DBBF1257EB50400836D8D /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
@@ -83,9 +81,11 @@
9C7092B125E77E470056AE0B /* NotificationService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationService.h; sourceTree = "<group>"; };
9C7092B225E77E470056AE0B /* NotificationService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationService.m; sourceTree = "<group>"; };
9C7092B425E77E470056AE0B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AE878F72B0C2DCB5A4603ABD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D94DB394AEFBDD2422D8B950 /* 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 = "<group>"; };
EC88C989B98F6E7BDEBE5FE5 /* Pods-ImageNotification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.release.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.release.xcconfig"; sourceTree = "<group>"; };
AB50C11FF4EFC5D9961D1031 /* 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 = "<group>"; };
ACE755908B540FD8855E4D89 /* 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 = "<group>"; };
C041C2ED656F3E1D27D9E228 /* Pods-ImageNotification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.debug.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.debug.xcconfig"; sourceTree = "<group>"; };
C9A446482226EB46FF708884 /* Pods-ImageNotification.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.profile.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.profile.xcconfig"; sourceTree = "<group>"; };
FE066D193740042A640E4E50 /* Pods-ImageNotification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageNotification.release.xcconfig"; path = "Target Support Files/Pods-ImageNotification/Pods-ImageNotification.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -94,7 +94,7 @@
buildActionMask = 2147483647;
files = (
9C2991242453FE3100B93AB2 /* StoreKit.framework in Frameworks */,
1EA81D57F09988D75AC476F5 /* Pods_Runner.framework in Frameworks */,
E81187C204448290256D1292 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -102,7 +102,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7585ADE8DCB47DC8753EF72A /* Pods_ImageNotification.framework in Frameworks */,
22ABA27907009C6D67E17F02 /* Pods_ImageNotification.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -182,8 +182,8 @@
isa = PBXGroup;
children = (
9C2991232453FE3100B93AB2 /* StoreKit.framework */,
989B86C0CD70FC0AB24F07D6 /* Pods_ImageNotification.framework */,
AE878F72B0C2DCB5A4603ABD /* Pods_Runner.framework */,
7DDBAA25D93558DC2ACE8794 /* Pods_ImageNotification.framework */,
525B84ABAED8CC10DD278134 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -191,12 +191,12 @@
F5D05D221249C73674C5DB0D /* Pods */ = {
isa = PBXGroup;
children = (
1F2EFFABEACE166F53F7025F /* Pods-ImageNotification.debug.xcconfig */,
EC88C989B98F6E7BDEBE5FE5 /* Pods-ImageNotification.release.xcconfig */,
50F2C55D9F5DC33EBDADB9C4 /* Pods-ImageNotification.profile.xcconfig */,
2FC45D8C03585F29FFEEBAD5 /* Pods-Runner.debug.xcconfig */,
76B7822037A027527BE9DFD3 /* Pods-Runner.release.xcconfig */,
D94DB394AEFBDD2422D8B950 /* Pods-Runner.profile.xcconfig */,
C041C2ED656F3E1D27D9E228 /* Pods-ImageNotification.debug.xcconfig */,
FE066D193740042A640E4E50 /* Pods-ImageNotification.release.xcconfig */,
C9A446482226EB46FF708884 /* Pods-ImageNotification.profile.xcconfig */,
AB50C11FF4EFC5D9961D1031 /* Pods-Runner.debug.xcconfig */,
ACE755908B540FD8855E4D89 /* Pods-Runner.release.xcconfig */,
1AEC3393F733CF63CC471C78 /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@@ -208,7 +208,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
B7F9B35AFB424DACC3F5FE98 /* [CP] Check Pods Manifest.lock */,
3485969F7F1D5F22AA9F93DD /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
@@ -216,9 +216,8 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
9C7092B825E77E470056AE0B /* Embed App Extensions */,
9CF1F8FD25F2A02100D93ED1 /* Firebase Crashlytics */,
6D0F2A4EFEF187992207F8A9 /* [CP] Embed Pods Frameworks */,
002D86FC0B93B0FCE4CC4735 /* [CP] Copy Pods Resources */,
FEF0F5B439D2594CBDB93362 /* [CP] Embed Pods Frameworks */,
71292F5AA25ED2C228C6EFFB /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -234,7 +233,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 9C7092BC25E77E470056AE0B /* Build configuration list for PBXNativeTarget "ImageNotification" */;
buildPhases = (
9C183B3B1872F82EEDC35FDC /* [CP] Check Pods Manifest.lock */,
5FBBFCF3DA2647B52FD6E1AE /* [CP] Check Pods Manifest.lock */,
9C7092AB25E77E470056AE0B /* Sources */,
9C7092AC25E77E470056AE0B /* Frameworks */,
9C7092AD25E77E470056AE0B /* Resources */,
@@ -260,11 +259,13 @@
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = VPH33JQH4R;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
};
9C7092AE25E77E470056AE0B = {
CreatedOnToolsVersion = 12.4;
DevelopmentTeam = VPH33JQH4R;
ProvisioningStyle = Automatic;
};
};
@@ -311,22 +312,26 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
002D86FC0B93B0FCE4CC4735 /* [CP] Copy Pods Resources */ = {
3485969F7F1D5F22AA9F93DD /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
"${PODS_ROOT}/FirebaseFirestore/FirebaseFirestore/Resources/gRPCCertificates-Cpp.bundle",
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle",
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
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;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
@@ -343,85 +348,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
6D0F2A4EFEF187992207F8A9 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseStorage/FirebaseStorage.framework",
"${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
"${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework",
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
"${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
"${BUILT_PRODUCTS_DIR}/in_app_purchase_ios/in_app_purchase_ios.framework",
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
"${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework",
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
"${BUILT_PRODUCTS_DIR}/quick_actions/quick_actions.framework",
"${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework",
"${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework",
"${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCrashlytics.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseStorage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/quick_actions.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
9C183B3B1872F82EEDC35FDC /* [CP] Check Pods Manifest.lock */ = {
5FBBFCF3DA2647B52FD6E1AE /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -443,46 +370,98 @@
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;
};
9CF1F8FD25F2A02100D93ED1 /* Firebase Crashlytics */ = {
71292F5AA25ED2C228C6EFFB /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
);
name = "Firebase Crashlytics";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
"${PODS_ROOT}/FirebaseFirestore/FirebaseFirestore/Resources/gRPCCertificates-Cpp.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "${PODS_ROOT}/FirebaseCrashlytics/run\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
B7F9B35AFB424DACC3F5FE98 /* [CP] Check Pods Manifest.lock */ = {
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
name = "Run Script";
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";
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
FEF0F5B439D2594CBDB93362 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseStorage/FirebaseStorage.framework",
"${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
"${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework",
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
"${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
"${BUILT_PRODUCTS_DIR}/in_app_purchase_ios/in_app_purchase_ios.framework",
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
"${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework",
"${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework",
"${BUILT_PRODUCTS_DIR}/quick_actions/quick_actions.framework",
"${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework",
"${BUILT_PRODUCTS_DIR}/shared_preferences_ios/shared_preferences_ios.framework",
"${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseStorage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/quick_actions.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@@ -720,8 +699,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};

View File

@@ -3,14 +3,11 @@ import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
var flutter_native_splash = 1
UIApplication.shared.isStatusBarHidden = false
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "background.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B

View File

@@ -1,23 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -16,13 +16,19 @@
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
</subviews>
<color key="backgroundColor" red="0.19607843137254902" green="0.19607843137254902" blue="0.24313725490196078" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
</constraints>
</view>
</viewController>
@@ -32,6 +38,7 @@
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="512" height="373"/>
<image name="LaunchImage" width="685" height="500"/>
<image name="LaunchBackground" width="1" height="1"/>
</resources>
</document>

View File

@@ -60,7 +60,7 @@
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarHidden</key>
<true/>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>

View File

@@ -1,6 +1,7 @@
// Packages
export 'package:easy_localization/easy_localization.dart';
export 'package:expandable/expandable.dart';
export 'package:fading_edge_scrollview/fading_edge_scrollview.dart';
export 'package:flash/flash.dart';
export 'package:fluro/fluro.dart';
export 'package:hive/hive.dart';
@@ -27,4 +28,3 @@ export 'core/state.dart';
export 'core/system.dart';
export 'core/ui.dart';
export 'core/utilities.dart';
export 'core/widgets.dart';

View File

@@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
@@ -6,8 +5,6 @@ enum LunaDatabaseValue {
DRAWER_AUTOMATIC_MANAGE,
DRAWER_MANUAL_ORDER,
ENABLED_PROFILE,
ENABLE_FIREBASE_ANALYTICS,
ENABLE_FIREBASE_CRASHLYTICS,
THEME_AMOLED,
THEME_AMOLED_BORDER,
THEME_IMAGE_BACKGROUND_OPACITY,
@@ -22,6 +19,7 @@ enum LunaDatabaseValue {
QUICK_ACTIONS_TAUTULLI,
QUICK_ACTIONS_SEARCH,
USE_24_HOUR_TIME,
DEFAULT_LAUNCH_MODULE,
}
class LunaDatabase extends LunaModuleDatabase {
@@ -47,6 +45,10 @@ class LunaDatabase extends LunaModuleDatabase {
data[value.key] =
(LunaDatabaseValue.SELECTED_BROWSER.data as LunaBrowser).key;
break;
case LunaDatabaseValue.DEFAULT_LAUNCH_MODULE:
data[value.key] =
(LunaDatabaseValue.DEFAULT_LAUNCH_MODULE.data as LunaModule).key;
break;
case LunaDatabaseValue.DRAWER_MANUAL_ORDER:
data[value.key] = LunaDrawer.moduleOrderedList()
?.map<String>((module) => module.key)
@@ -71,6 +73,9 @@ class LunaDatabase extends LunaModuleDatabase {
case LunaDatabaseValue.SELECTED_BROWSER:
value.put(LunaBrowser.APPLE_SAFARI.fromKey(config[key]));
break;
case LunaDatabaseValue.DEFAULT_LAUNCH_MODULE:
value.put(LunaModule.DASHBOARD.fromKey(config[key]));
break;
case LunaDatabaseValue.DRAWER_MANUAL_ORDER:
value.put(
(config[key] as List)
@@ -98,7 +103,7 @@ class LunaDatabase extends LunaModuleDatabase {
extension LunaDatabaseValueExtension on LunaDatabaseValue {
String get key {
if (this == LunaDatabaseValue.ENABLED_PROFILE) return 'profile';
return 'LUNASEA_${describeEnum(this)}';
return 'LUNASEA_${this.name}';
}
dynamic get data {
@@ -136,10 +141,6 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue {
return value is bool;
case LunaDatabaseValue.DRAWER_MANUAL_ORDER:
return value is List;
case LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS:
return value is bool;
case LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS:
return value is bool;
case LunaDatabaseValue.THEME_AMOLED:
return value is bool;
case LunaDatabaseValue.THEME_AMOLED_BORDER:
@@ -168,6 +169,8 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue {
return value is bool;
case LunaDatabaseValue.USE_24_HOUR_TIME:
return value is bool;
case LunaDatabaseValue.DEFAULT_LAUNCH_MODULE:
return value is LunaModule;
}
throw Exception('Invalid LunaDatabaseValue');
}
@@ -187,11 +190,7 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue {
case LunaDatabaseValue.THEME_AMOLED_BORDER:
return false;
case LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY:
return 10;
case LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS:
return true;
case LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS:
return true;
return 20;
case LunaDatabaseValue.QUICK_ACTIONS_LIDARR:
return false;
case LunaDatabaseValue.QUICK_ACTIONS_RADARR:
@@ -212,6 +211,8 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue {
return false;
case LunaDatabaseValue.USE_24_HOUR_TIME:
return false;
case LunaDatabaseValue.DEFAULT_LAUNCH_MODULE:
return LunaModule.DASHBOARD;
}
throw Exception('Invalid LunaDatabaseValue');
}

View File

@@ -8,7 +8,7 @@ extension ScrollControllerExtension on ScrollController {
this.animateTo(
0.00,
duration: const Duration(milliseconds: LunaUI.ANIMATION_SPEED * 2),
curve: Curves.decelerate,
curve: Curves.easeInOutQuart,
);
}
}

View File

@@ -1,3 +1,4 @@
import 'package:flutter/services.dart';
import 'package:lunasea/core.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -103,4 +104,15 @@ extension StringLinksExtension on String {
/// Attach this string as a series ID to TVMaze and attempt to launch it as a URL.
Future<void> lunaOpenTVMaze() async =>
await _openLink('https://www.tvmaze.com/shows/$this');
Future<void> copyToClipboard({
bool showSnackBar = true,
}) async {
await Clipboard.setData(ClipboardData(text: this));
if (showSnackBar)
showLunaSuccessSnackBar(
title: 'Copied',
message: 'Copied content to the clipboard',
);
}
}

View File

@@ -1,7 +1,5 @@
export 'firebase/analytics.dart';
export 'firebase/auth.dart';
export 'firebase/core.dart';
export 'firebase/crashlytics.dart';
export 'firebase/firestore.dart';
export 'firebase/messaging.dart';
export 'firebase/storage.dart';

View File

@@ -1,41 +0,0 @@
import 'dart:io';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
import 'package:lunasea/core.dart';
class LunaFirebaseAnalytics {
/// Returns true if Firebase Analytics is compatible with this build system/platform.
static bool get isPlatformCompatible {
return LunaFirebase.isPlatformCompatible &&
!Platform.isLinux &&
!Platform.isMacOS &&
!Platform.isWindows;
}
/// Returns an instance of [FirebaseAnalytics].
///
/// Throws an error if [LunaFirebase.initialize] has not been called.
static FirebaseAnalytics get instance => FirebaseAnalytics();
/// Returns an instance of [FirebaseAnalyticsObserver].
///
/// Throws an error if [LunaFirebase.initialize] has not been called.
static FirebaseAnalyticsObserver get observer =>
FirebaseAnalyticsObserver(analytics: instance);
/// Set the enabled state of Firebase Analytics.
///
/// If `enabled` is set to false, force-disables Analytics.
void setEnabledState() {
if (isPlatformCompatible) {
bool state = LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS.data;
instance.setAnalyticsCollectionEnabled(state);
}
}
/// Log an "app_open" event.
Future<void> appOpened() async {
if (isPlatformCompatible) instance.logAppOpen();
}
}

View File

@@ -1,13 +1,7 @@
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
class LunaFirebase {
/// Return an instance of [FirebaseApp].
///
/// Throws an error if [LunaFirebase.initialize] has not been called.
static FirebaseApp get instance => Firebase.app();
/// Returns true if Firebase in its entirety is compatible with this build system/platform.
static bool get isPlatformCompatible =>
Platform.isIOS || Platform.isAndroid || Platform.isMacOS;
@@ -15,6 +9,5 @@ class LunaFirebase {
/// Initialize Firebase and configuration.
///
/// This must be called before anything accesses Firebase services, or an exception will be thrown.
Future<void> initialize({String name}) async =>
await Firebase.initializeApp();
Future<void> initialize() async => await Firebase.initializeApp();
}

View File

@@ -1,30 +0,0 @@
import 'dart:io';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
import 'package:lunasea/core.dart';
class LunaFirebaseCrashlytics {
/// Returns true if Firebase Analytics is compatible with this build system/platform.
static bool get isPlatformCompatible {
return LunaFirebase.isPlatformCompatible &&
!Platform.isLinux &&
!Platform.isMacOS &&
!Platform.isWindows;
}
/// Returns an instance of [FirebaseCrashlytics].
///
/// Throws an error if [LunaFirebase.initialize] has not been called.
static FirebaseCrashlytics get instance => FirebaseCrashlytics.instance;
/// Set the enabled state of Firebase Crashlytics.
///
/// If `enabled` is set to false, force-disables Analytics.
void setEnabledState() {
if (isPlatformCompatible) {
bool state =
kReleaseMode && LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data;
instance.setCrashlyticsCollectionEnabled(state);
}
}
}

Binary file not shown.

View File

@@ -67,7 +67,7 @@ class LunaFirebaseMessaging {
/// Return a [StreamSubscription] that will show a notification banner on a newly received notification.
///
/// This listens on [FirebaseMessaging.onMessage], where the application must be open and in the foreground.
StreamSubscription<RemoteMessage> onMessageListener() {
StreamSubscription<RemoteMessage> registerOnMessageListener() {
return onMessage.listen((message) {
if (message == null) return;
LunaModule module = (message.data ?? {}).isNotEmpty
@@ -88,7 +88,7 @@ class LunaFirebaseMessaging {
/// Returns a [StreamSubscription] that will handle messages/notifications that are opened while LunaSea is running in the background.
///
/// This listens on [FirebaseMessaging.onMessageOpenedApp], where the application must be open but in the background.
StreamSubscription<RemoteMessage> onMessageOpenedAppListener() =>
StreamSubscription<RemoteMessage> registerOnMessageOpenedAppListener() =>
onMessageOpenedApp.listen(_handleWebhook);
/// Check to see if there was an initial [RemoteMessage] available to be accessed.

View File

@@ -533,7 +533,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.WAKE_ON_LAN:
return 'Wake on LAN is an industry standard protocol for waking computers up from a very low power mode remotely by sending a specially constructed packet to the machine.';
case LunaModule.EXTERNAL_MODULES:
return 'LunaSea allows you to add additional modules that are not currently supported directly within the application, allowing you to open the module\'s web GUI without having to leave LunaSea!';
return 'LunaSea allows you to add links to additional modules that are not currently supported allowing you to open the module\'s web GUI without having to leave LunaSea!';
}
throw Exception('Invalid LunaModule');
}

View File

@@ -1,6 +1,7 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/main.dart';
import 'package:lunasea/modules.dart';
class LunaRouter {
@@ -16,6 +17,12 @@ class LunaRouter {
router.notFoundHandler = Handler(
handlerFunc: (context, params) => LunaInvalidRoute(),
);
router.define(
'/',
handler: Handler(handlerFunc: (context, params) => const LunaOS()),
transitionType: transitionType,
);
// Module routers
DashboardRouter().defineAllRoutes(router);
ExternalModulesRouter().defineAllRoutes(router);
OverseerrRouter().defineAllRoutes(router);

View File

@@ -1,19 +1,27 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:window_size/window_size.dart' as window_size;
import 'package:bitsdojo_window/bitsdojo_window.dart';
class LunaDesktopWindow {
static const double _MINIMUM_WINDOW_SIZE = 400;
static const double _INITIAL_WINDOW_SIZE = 700;
static bool get isPlatformCompatible =>
Platform.isWindows || Platform.isMacOS || Platform.isLinux;
/// Initialize the Desktop window:
/// - Sets the minimum window size
void initialize() {
if (isPlatformCompatible) {
window_size.setWindowMinSize(
const Size(_MINIMUM_WINDOW_SIZE, _MINIMUM_WINDOW_SIZE),
);
doWhenWindowReady(() {
const Size minSize = Size(_MINIMUM_WINDOW_SIZE, _MINIMUM_WINDOW_SIZE);
const Size size = Size(_INITIAL_WINDOW_SIZE, _INITIAL_WINDOW_SIZE);
final win = appWindow;
win.minSize = minSize;
win.size = size;
win.alignment = Alignment.center;
win.title = "LunaSea";
win.show();
});
}
}
}

View File

@@ -6,7 +6,7 @@ class LunaProfile {
List<String> profilesList() => Database.profilesBox.keys
.map<String>((profile) => profile as String)
.toList()
..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
/// Safely change profiles.
///

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
export 'ui/appbar.dart';
export 'ui/banner.dart';
export 'ui/block.dart';
export 'ui/bottom_bar.dart';
export 'ui/bottom_modal_sheet.dart';
export 'ui/button.dart';
@@ -18,6 +19,7 @@ export 'ui/header.dart';
export 'ui/highlighted_node.dart';
export 'ui/icons.dart';
export 'ui/input_bar.dart';
export 'ui/linear_indicator.dart';
export 'ui/list_tile.dart';
export 'ui/list_view.dart';
export 'ui/loader.dart';
@@ -30,6 +32,7 @@ export 'ui/shape.dart';
export 'ui/snackbar.dart';
export 'ui/switch.dart';
export 'ui/table.dart';
export 'ui/text_span.dart';
export 'ui/text.dart';
export 'ui/theme.dart';
export 'ui/will_pop_scope.dart';
@@ -40,37 +43,69 @@ class LunaUI {
static const String TEXT_ARROW_LEFT = '';
static const String TEXT_ARROW_RIGHT = '';
static const String TEXT_BULLET = '';
static const String TEXT_OBFUSCATED_PASSWORD = '••••••••••';
static const String TEXT_OBFUSCATED_PASSWORD = '••••••••••••';
static const String TEXT_ELLIPSIS = '';
static const String TEXT_EMDASH = '';
// Font-Related
static const double FONT_SIZE_APP_BAR = 18.0;
static const double FONT_SIZE_BUTTON = 14.0;
static const double FONT_SIZE_GRAPH_LEGEND = 10.0;
static const double FONT_SIZE_HEADER = 18.0;
static const double FONT_SIZE_HIGHLIGHTED_NODE = 12.0;
static const double FONT_SIZE_MESSAGES = 16.0;
static const double FONT_SIZE_NAVIGATION_BAR = 13.0;
static const double FONT_SIZE_SUBHEADER = 12.0;
static const double FONT_SIZE_SUBTITLE = 13.0;
static const double FONT_SIZE_TITLE = 16.0;
static const FontWeight FONT_WEIGHT_BOLD = FontWeight.w600;
// UI-Related
// <--> Font Sizes
static const double FONT_SIZE_H1 = 18.0;
static const double FONT_SIZE_H2 = 16.0;
static const double FONT_SIZE_H3 = 14.0;
static const double FONT_SIZE_H4 = 12.0;
static const double FONT_SIZE_H5 = 10.0;
// <--> Font Size Mappings
static const double FONT_SIZE_BUTTON = FONT_SIZE_H3;
static const double FONT_SIZE_GRAPH_LEGEND = FONT_SIZE_H5;
static const double FONT_SIZE_HEADER = FONT_SIZE_H2;
static const double FONT_SIZE_MESSAGES = FONT_SIZE_H2;
static const double FONT_SIZE_SUBHEADER = FONT_SIZE_H3;
static const double FONT_SIZE_SUBTITLE = FONT_SIZE_H3;
static const double FONT_SIZE_TITLE = FONT_SIZE_H2;
// <--> Icons
static const double ICON_SIZE = 24.0;
// <--> Other
static const int ANIMATION_SPEED = 250;
static const int ANIMATION_IMAGE_FADE_IN_SPEED = 125;
static const int ANIMATION_SPEED_IMAGES = 175;
static const double BORDER_RADIUS = 10.0;
static const double BUTTON_BACKGROUND_OPACITY = 0.80;
static const double OPACITY_DIMMED = 0.75;
static const double OPACITY_DISABLED = 0.50;
static const double OPACITY_SPLASH = 0.25;
static const double ELEVATION = 0.0;
static const FontWeight FONT_WEIGHT_BOLD = FontWeight.w600;
// <--> Margins
static const double DEFAULT_MARGIN_SIZE = 12.0;
static const double MARGIN_SIZE_HALF = DEFAULT_MARGIN_SIZE / 2;
static const EdgeInsets MARGIN_DEFAULT = EdgeInsets.all(DEFAULT_MARGIN_SIZE);
static const EdgeInsets MARGIN_CARD = EdgeInsets.symmetric(
horizontal: DEFAULT_MARGIN_SIZE,
vertical: DEFAULT_MARGIN_SIZE / 2,
);
// Border-Related
static bool get shouldUseBorder =>
LunaTheme.isAMOLEDTheme && LunaTheme.useAMOLEDBorders;
static ShapeBorder get shapeBorder => shouldUseBorder
? LunaShapeBorder.roundedWithBorder()
: LunaShapeBorder.rounded();
static const EdgeInsets MARGIN_HALF = EdgeInsets.all(MARGIN_SIZE_HALF);
static const EdgeInsets MARGIN_DEFAULT_HORIZONTAL =
EdgeInsets.symmetric(horizontal: DEFAULT_MARGIN_SIZE);
static const EdgeInsets MARGIN_DEFAULT_VERTICAL =
EdgeInsets.symmetric(vertical: DEFAULT_MARGIN_SIZE);
static const EdgeInsets MARGIN_HALF_HORIZONTAL =
EdgeInsets.symmetric(horizontal: MARGIN_SIZE_HALF);
static const EdgeInsets MARGIN_HALF_VERTICAL =
EdgeInsets.symmetric(vertical: MARGIN_SIZE_HALF);
static const EdgeInsets MARGIN_H_DEFAULT_V_HALF = EdgeInsets.symmetric(
horizontal: DEFAULT_MARGIN_SIZE, vertical: MARGIN_SIZE_HALF);
static const EdgeInsets MARGIN_H_HALF_V_DEFAULT = EdgeInsets.symmetric(
horizontal: MARGIN_SIZE_HALF, vertical: DEFAULT_MARGIN_SIZE);
// <--> Borders
static bool get shouldUseBorder {
return LunaTheme.isAMOLEDTheme && LunaTheme.useAMOLEDBorders;
}
static ShapeBorder get shapeBorder {
return shouldUseBorder
? LunaShapeBorder.roundedWithBorder()
: LunaShapeBorder.rounded();
}
}

View File

@@ -22,11 +22,12 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget {
final List<String> profiles;
final PageController pageController;
final List<ScrollController> scrollControllers;
final Color backgroundColor;
@override
Size get preferredSize {
double _size = (height ?? APPBAR_HEIGHT);
if (bottom != null) _size += bottom.preferredSize.height;
if (bottom != null) _size += bottom.preferredSize.height - 0.0;
return Size.fromHeight(_size);
}
@@ -42,6 +43,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget {
this.pageController,
this.scrollControllers,
this.hideLeading = false,
this.backgroundColor,
});
/// Create a new [AppBar] widget pre-styled for LunaSea.
@@ -58,6 +60,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget {
bool useDrawer = false,
bool hideLeading = false,
PageController pageController,
Color backgroundColor,
List<ScrollController> scrollControllers,
}) {
assert(title != null);
@@ -72,6 +75,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget {
pageController: pageController,
scrollControllers: scrollControllers,
hideLeading: hideLeading,
backgroundColor: backgroundColor,
type: _APPBAR_TYPE.DEFAULT,
);
}
@@ -86,6 +90,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget {
@required double height,
EdgeInsets padding = LunaTextInputBar.appBarMargin,
Alignment alignment = Alignment.topCenter,
Color backgroundColor,
}) {
assert(child != null);
assert(height != null);
@@ -98,6 +103,7 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget {
),
height: height,
useDrawer: false,
backgroundColor: backgroundColor,
type: _APPBAR_TYPE.EMPTY,
);
}
@@ -117,6 +123,8 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget {
List<Widget> actions,
PageController pageController,
List<ScrollController> scrollControllers,
PreferredSizeWidget bottom,
Color backgroundColor,
}) {
assert(title != null);
assert(profiles != null);
@@ -129,18 +137,22 @@ class LunaAppBar extends StatefulWidget implements PreferredSizeWidget {
actions: actions,
useDrawer: useDrawer,
hideLeading: hideLeading,
bottom: bottom,
pageController: pageController,
scrollControllers: scrollControllers,
backgroundColor: backgroundColor,
type: _APPBAR_TYPE.DEFAULT,
);
return LunaAppBar._internal(
title: title,
profiles: profiles,
actions: actions,
bottom: bottom,
useDrawer: useDrawer,
hideLeading: hideLeading,
pageController: pageController,
scrollControllers: scrollControllers,
backgroundColor: backgroundColor,
type: _APPBAR_TYPE.DROPDOWN,
);
}
@@ -210,38 +222,36 @@ class _State extends State<LunaAppBar> {
Widget _sharedLeading(BuildContext context) {
if (widget.hideLeading ?? false) return null;
if (widget.useDrawer)
return IconButton(
icon: const Icon(Icons.menu_rounded),
onPressed: () async {
HapticFeedback.lightImpact();
if (Scaffold.of(context).hasDrawer) {
Scaffold.of(context).openDrawer();
FocusManager.instance.primaryFocus?.unfocus();
}
},
return SizedBox(
child: LunaIconButton.appBar(
icon: Icons.menu_rounded,
onPressed: () async {
HapticFeedback.lightImpact();
if (Scaffold.of(context).hasDrawer) {
Scaffold.of(context).openDrawer();
FocusManager.instance.primaryFocus?.unfocus();
}
},
),
height: kToolbarHeight,
);
return InkWell(
child: const Center(
child: Icon(Icons.arrow_back_ios_new_rounded),
return SizedBox(
child: LunaIconButton.appBar(
icon: Icons.arrow_back_ios_new_rounded,
onPressed: () async => Navigator.of(context).lunaSafetyPop(),
onLongPress: () async => Navigator.of(context).lunaPopToFirst(),
),
onTap: () async {
HapticFeedback.lightImpact();
Navigator.of(context).lunaSafetyPop();
},
onLongPress: () async {
HapticFeedback.heavyImpact();
Navigator.of(context).lunaPopToFirst();
},
borderRadius: BorderRadius.circular(28.0),
height: kToolbarHeight,
);
}
Widget _default(BuildContext context) {
return AppBar(
backgroundColor: widget.backgroundColor,
title: Text(
widget.title ?? '',
overflow: TextOverflow.fade,
style: const TextStyle(fontSize: LunaUI.FONT_SIZE_APP_BAR),
style: const TextStyle(fontSize: LunaUI.FONT_SIZE_H1),
),
leading: _sharedLeading(context),
automaticallyImplyLeading: !(widget.hideLeading ?? false),
@@ -254,6 +264,7 @@ class _State extends State<LunaAppBar> {
Widget _empty(BuildContext context) {
return AppBar(
backgroundColor: widget.backgroundColor,
automaticallyImplyLeading: false,
toolbarHeight: widget.height,
leadingWidth: 0.0,
@@ -265,15 +276,24 @@ class _State extends State<LunaAppBar> {
Widget _dropdown(BuildContext context) {
return AppBar(
backgroundColor: widget.backgroundColor,
automaticallyImplyLeading: !(widget.hideLeading ?? false),
title: LunaPopupMenuButton<String>(
tooltip: 'Change Profiles',
child: Wrap(
direction: Axis.horizontal,
crossAxisAlignment: WrapCrossAlignment.center,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
widget.title,
style: const TextStyle(fontSize: LunaUI.FONT_SIZE_HEADER),
Flexible(
child: FadingEdgeScrollView.fromSingleChildScrollView(
child: SingleChildScrollView(
controller: ScrollController(),
scrollDirection: Axis.horizontal,
child: Text(
widget.title,
style: const TextStyle(fontSize: LunaUI.FONT_SIZE_H1),
),
),
),
),
const Icon(Icons.arrow_drop_down_rounded),
],
@@ -290,7 +310,7 @@ class _State extends State<LunaAppBar> {
child: Text(
profile,
style: TextStyle(
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
fontSize: LunaUI.FONT_SIZE_H3,
color:
(LunaDatabaseValue.ENABLED_PROFILE.data ?? 'default') ==
profile
@@ -306,6 +326,7 @@ class _State extends State<LunaAppBar> {
centerTitle: false,
elevation: 0,
actions: widget.actions,
bottom: widget.bottom,
);
}
}

View File

@@ -23,7 +23,7 @@ class LunaBanner extends StatelessWidget {
this.iconColor = LunaColours.accent,
this.backgroundColor,
this.headerColor = Colors.white,
this.bodyColor = Colors.white70,
this.bodyColor = LunaColours.grey,
this.buttons,
}) : super(key: key);
@@ -32,12 +32,13 @@ class LunaBanner extends StatelessWidget {
return LunaCard(
context: context,
child: Container(
padding: EdgeInsets.symmetric(vertical: LunaUI.MARGIN_CARD.top),
padding:
EdgeInsets.symmetric(vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.top),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: LunaUI.MARGIN_CARD,
padding: LunaUI.MARGIN_H_DEFAULT_V_HALF,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@@ -73,7 +74,7 @@ class LunaBanner extends StatelessWidget {
),
if (bodyText?.isNotEmpty ?? false)
Padding(
padding: LunaUI.MARGIN_CARD.copyWith(top: 0),
padding: LunaUI.MARGIN_H_DEFAULT_V_HALF.copyWith(top: 0),
child: LunaText.subtitle(
text: bodyText.toString(),
color: bodyColor,
@@ -84,7 +85,7 @@ class LunaBanner extends StatelessWidget {
if (buttons?.isNotEmpty ?? false)
LunaButtonContainer(
padding: EdgeInsets.symmetric(
horizontal: LunaUI.MARGIN_CARD.left / 2),
horizontal: LunaUI.MARGIN_H_DEFAULT_V_HALF.left / 2),
children: buttons,
),
],

302
lib/core/ui/block.dart Normal file
View File

@@ -0,0 +1,302 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:transparent_image/transparent_image.dart';
class LunaBlock extends StatelessWidget {
static const TITLE_HEIGHT = LunaUI.FONT_SIZE_H2 + 4.0;
static const SUBTITLE_HEIGHT = LunaUI.FONT_SIZE_H3 + 4.0;
final bool disabled;
final String title;
final Color titleColor;
/// If defined, only takes the first body subtitle from the [body] array
/// And allows that single [TextSpan] to overflow to this many lines
final int customBodyMaxLines;
final List<TextSpan> body;
/// Icons that lead the body lines. If defined, then the length of this list
/// must match the length of [body]. If a specific line does not need a
/// leading icon, pass in null.
final List<IconData> bodyLeadingIcons;
final Color bodyLeadingIconsColor;
final Widget leading;
final Widget trailing;
final Widget bottom;
final double bottomHeight;
final Function onTap;
final Function onLongPress;
final IconData posterPlaceholderIcon;
final String posterUrl;
final Map posterHeaders;
final bool posterIsSquare;
final String backgroundUrl;
final Map backgroundHeaders;
const LunaBlock({
Key key,
this.disabled = false,
@required this.title,
this.titleColor = Colors.white,
this.body,
this.bodyLeadingIcons,
this.bodyLeadingIconsColor = LunaColours.accent,
this.bottom,
this.bottomHeight = SUBTITLE_HEIGHT,
this.customBodyMaxLines,
this.posterPlaceholderIcon,
this.posterUrl,
this.posterHeaders = const {},
this.posterIsSquare = false,
this.backgroundUrl,
this.backgroundHeaders = const {},
this.onTap,
this.onLongPress,
this.leading,
this.trailing,
}) : super(key: key);
static double calculateItemExtent(
int subtitleLines, {
bool hasBottom = false,
double bottomHeight = SUBTITLE_HEIGHT,
}) {
double height = calculateItemHeight(
subtitleLines,
hasBottom: hasBottom,
bottomHeight: bottomHeight,
);
return height + LunaUI.MARGIN_H_DEFAULT_V_HALF.vertical;
}
static double calculateItemHeight(
int subtitleLines, {
bool hasBottom = false,
double bottomHeight = SUBTITLE_HEIGHT,
}) {
double height = (LunaUI.DEFAULT_MARGIN_SIZE * 2) + TITLE_HEIGHT;
height += subtitleLines * SUBTITLE_HEIGHT;
if (hasBottom) height += bottomHeight;
return height;
}
double _calculateHeight() {
int _scalar = customBodyMaxLines;
_scalar ??= body?.length ?? 0;
return calculateItemHeight(
_scalar,
hasBottom: bottom != null,
bottomHeight: bottomHeight,
);
}
@override
Widget build(BuildContext context) {
double _height = _calculateHeight();
return LunaCard(
context: context,
child: InkWell(
child: Stack(
children: [
if (backgroundUrl?.isNotEmpty ?? false)
_fadeInBackground(context, _height),
Opacity(
opacity: disabled ? LunaUI.OPACITY_DISABLED : 1.0,
child: Row(
children: [
_poster(context, _height),
_tile(context, _height),
],
),
),
],
),
mouseCursor: onTap != null || onLongPress != null
? SystemMouseCursors.click
: MouseCursor.defer,
onTap: onTap,
onLongPress: onLongPress,
),
height: _height,
);
}
Widget _fadeInBackground(BuildContext context, double _height) {
int _percent = LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data as int;
if (_percent == 0) return const SizedBox(height: 0, width: 0);
double _opacity = _percent / 100;
if (disabled) _opacity *= LunaUI.OPACITY_DISABLED;
return Opacity(
opacity: _opacity,
child: FadeInImage(
placeholder: MemoryImage(kTransparentImage),
height: _height,
width: MediaQuery.of(context).size.width,
fadeInDuration: const Duration(
milliseconds: LunaUI.ANIMATION_SPEED_IMAGES,
),
fit: BoxFit.cover,
image: NetworkImage(
backgroundUrl,
headers: backgroundHeaders?.cast<String, String>(),
),
imageErrorBuilder: (context, error, stack) => SizedBox(
height: _height,
width: MediaQuery.of(context).size.width,
),
),
);
}
Widget _poster(BuildContext context, double height) {
if (posterUrl == null && posterPlaceholderIcon == null) {
return const SizedBox(width: 0.0, height: 0.0);
}
double _dimension = height - LunaUI.DEFAULT_MARGIN_SIZE;
return Padding(
padding: const EdgeInsets.only(left: LunaUI.DEFAULT_MARGIN_SIZE / 2),
child: LunaNetworkImage(
context: context,
url: posterUrl ?? '',
headers: posterHeaders,
placeholderIcon: posterPlaceholderIcon,
height: _dimension,
width: _dimension / (posterIsSquare ? 1.0 : 1.5),
),
);
}
Widget _tile(BuildContext context, double height) {
return Expanded(
// ignore: deprecated_member_use_from_same_package
child: LunaListTile(
context: context,
title: _scrollableText(
child: LunaText.title(
text: title,
color: titleColor,
overflow: TextOverflow.visible,
maxLines: 1,
),
),
subtitle: _subtitle(),
color: Colors.transparent,
margin: EdgeInsets.zero,
drawBorder: false,
height: height,
trailing: trailing,
leading: leading,
),
);
}
Widget _subtitle() {
int maxLines = customBodyMaxLines ?? 1;
double height = SUBTITLE_HEIGHT * maxLines;
if (bodyLeadingIcons != null) {
assert(
bodyLeadingIcons.length == body?.length,
'bodyLeadingIcons and body should be the same size',
);
}
Widget _wrapper(List<Widget> children) {
return Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
),
);
}
Widget _entry(TextSpan textSpan, IconData icon) {
return SizedBox(
height: height,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (icon != null)
Padding(
child: Container(
child: Icon(
icon,
color: bodyLeadingIconsColor,
size: LunaUI.FONT_SIZE_H2,
),
height: SUBTITLE_HEIGHT * maxLines,
width: SUBTITLE_HEIGHT,
alignment: Alignment.centerLeft,
),
padding: const EdgeInsets.only(
right: LunaUI.DEFAULT_MARGIN_SIZE / 4,
),
),
Expanded(
child: _scrollableText(
scrollDirection: maxLines > 1 ? Axis.vertical : Axis.horizontal,
child: Container(
child: RichText(
text: TextSpan(
style: const TextStyle(
fontSize: LunaUI.FONT_SIZE_H3,
color: LunaColours.grey,
),
children: [textSpan],
),
overflow: TextOverflow.ellipsis,
softWrap: maxLines == 1 ? false : true,
maxLines: maxLines,
),
alignment: Alignment.centerLeft,
),
),
),
],
),
);
}
List<Widget> _children = [];
if (body?.isNotEmpty ?? false) {
if (customBodyMaxLines != null) {
_children.add(_entry(body[0], bodyLeadingIcons?.elementAtOrNull(0)));
} else {
for (int i = 0; i < body.length; i++) {
_children.add(_entry(body[i], bodyLeadingIcons?.elementAtOrNull(i)));
}
}
}
if (bottom != null) {
_children.add(SizedBox(
height: bottomHeight,
child: bottom,
));
}
return _children.isEmpty ? null : _wrapper(_children);
}
Widget _scrollableText({
@required Widget child,
Axis scrollDirection = Axis.horizontal,
}) {
return FadingEdgeScrollView.fromSingleChildScrollView(
child: SingleChildScrollView(
controller: ScrollController(),
scrollDirection: scrollDirection,
child: child,
),
);
}
}

View File

@@ -1,3 +1,4 @@
export 'bottom_bar/action_bar_card.dart';
export 'bottom_bar/action_bar.dart';
export 'bottom_bar/badge.dart';
export 'bottom_bar/navigation_bar.dart';

View File

@@ -21,7 +21,7 @@ class LunaActionBarCard extends StatelessWidget {
this.onLongPress,
this.backgroundColor,
this.color = LunaColours.accent,
this.icon = Icons.arrow_forward_ios_rounded,
this.icon = LunaIcons.ARROW_RIGHT,
this.checkboxState,
this.checkboxOnChanged,
}) : super(key: key);
@@ -59,7 +59,7 @@ class LunaActionBarCard extends StatelessWidget {
overflow: TextOverflow.fade,
style: const TextStyle(
fontSize: LunaUI.FONT_SIZE_SUBHEADER,
color: Colors.white70,
color: LunaColours.grey,
),
),
],
@@ -77,7 +77,7 @@ class LunaActionBarCard extends StatelessWidget {
),
),
),
if (icon != null)
if (checkboxState == null && icon != null)
Container(
width: 30.0,
alignment: Alignment.centerRight,
@@ -96,26 +96,38 @@ class LunaActionBarCard extends StatelessWidget {
height: LunaButton.DEFAULT_HEIGHT,
),
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
onTap: onTap == null
? null
: () async {
HapticFeedback.lightImpact();
onTap();
},
onLongPress: onLongPress == null
? null
: () async {
HapticFeedback.heavyImpact();
onLongPress();
},
onTap: _onTapHandler(),
onLongPress: _onLongPressHandler(),
),
margin: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
margin: LunaUI.MARGIN_HALF,
color: backgroundColor != null
? backgroundColor.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY)
? backgroundColor.withOpacity(LunaUI.OPACITY_DIMMED)
: LunaTheme.isAMOLEDTheme
? Colors.black.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY)
: LunaColours.primary
.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY),
? Colors.black.withOpacity(LunaUI.OPACITY_DIMMED)
: LunaColours.primary.withOpacity(LunaUI.OPACITY_DIMMED),
);
}
Function _onTapHandler() {
if (onTap != null) {
return () async {
HapticFeedback.lightImpact();
onTap();
};
}
if (checkboxState != null && checkboxOnChanged != null) {
return () async => checkboxOnChanged(!checkboxState);
}
return null;
}
Function _onLongPressHandler() {
if (onLongPress != null) {
return () async {
HapticFeedback.heavyImpact();
onLongPress();
};
}
return null;
}
}

View File

@@ -11,8 +11,7 @@ class LunaNavigationBarBadge extends Badge {
@required bool isActive,
}) : super(
key: key,
badgeColor:
LunaColours.accent.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY),
badgeColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_DIMMED),
elevation: 0,
animationDuration:
const Duration(milliseconds: LunaUI.ANIMATION_SPEED),
@@ -29,8 +28,7 @@ class LunaNavigationBarBadge extends Badge {
child: Icon(
icon,
color: isActive
? LunaColours.accent
.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY)
? LunaColours.accent.withOpacity(LunaUI.OPACITY_DIMMED)
: Colors.white,
),
showBadge: showBadge,

View File

@@ -79,11 +79,10 @@ class _State extends State<LunaBottomNavigationBar> {
child: Padding(
child: GNav(
gap: 8.0,
iconSize: 24.0,
duration: const Duration(milliseconds: LunaUI.ANIMATION_SPEED),
tabBackgroundColor: Theme.of(context)
.canvasColor
.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY),
.withOpacity(LunaUI.OPACITY_DIMMED),
activeColor: LunaColours.accent,
tabs: List.generate(
widget.icons.length,
@@ -91,7 +90,7 @@ class _State extends State<LunaBottomNavigationBar> {
icon: widget.icons[index],
text: widget.titles[index],
active: _index == index,
iconSize: 22.0,
iconSize: LunaUI.ICON_SIZE,
haptic: true,
padding: _index == index
? const EdgeInsets.all(10.0)
@@ -100,7 +99,7 @@ class _State extends State<LunaBottomNavigationBar> {
iconColor: Colors.white,
textStyle: const TextStyle(
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
fontSize: LunaUI.FONT_SIZE_NAVIGATION_BAR,
fontSize: LunaUI.FONT_SIZE_H3,
color: Colors.white,
),
iconActiveColor: LunaColours.accent,
@@ -109,7 +108,7 @@ class _State extends State<LunaBottomNavigationBar> {
: widget.leadingOnTab[index],
)).toList(),
tabActiveBorder: LunaUI.shouldUseBorder
? Border.all(color: Colors.white12)
? Border.all(color: LunaColours.white10)
: null,
tabBorder: LunaUI.shouldUseBorder
? Border.all(color: Colors.transparent)

View File

@@ -3,6 +3,7 @@ import 'package:lunasea/core.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class LunaBottomModalSheet {
@protected
Future<dynamic> showModal({
@required BuildContext context,
Widget Function(BuildContext context) builder,
@@ -20,4 +21,13 @@ class LunaBottomModalSheet {
}
Widget builder(BuildContext context) => null;
Future<dynamic> show({
@required BuildContext context,
Widget Function(BuildContext context) builder,
}) async =>
showModal(
context: context,
builder: builder,
);
}

View File

@@ -10,13 +10,12 @@ enum LunaButtonType {
/// A Luna-styled button.
class LunaButton extends Card {
static const DEFAULT_HEIGHT = 50.0;
static const DEFAULT_HEIGHT = 46.0;
LunaButton._({
Key key,
@required Widget child,
EdgeInsets margin =
const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
EdgeInsets margin = LunaUI.MARGIN_HALF,
Color backgroundColor,
double height = DEFAULT_HEIGHT,
Alignment alignment = Alignment.center,
@@ -47,11 +46,10 @@ class LunaButton extends Card {
),
margin: margin,
color: backgroundColor != null
? backgroundColor.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY)
? backgroundColor.withOpacity(LunaUI.OPACITY_DIMMED)
: LunaTheme.isAMOLEDTheme
? Colors.black.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY)
: LunaColours.primary
.withOpacity(LunaUI.BUTTON_BACKGROUND_OPACITY),
? Colors.black.withOpacity(LunaUI.OPACITY_DIMMED)
: LunaColours.primary.withOpacity(LunaUI.OPACITY_DIMMED),
shape: backgroundColor != null
? LunaShapeBorder.rounded()
: LunaUI.shapeBorder,
@@ -70,10 +68,9 @@ class LunaButton extends Card {
Color backgroundColor,
String text,
IconData icon,
double iconSize = 22.0,
double iconSize = LunaUI.ICON_SIZE,
LunaLoadingState loadingState,
EdgeInsets margin =
const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
EdgeInsets margin = LunaUI.MARGIN_HALF,
double height = DEFAULT_HEIGHT,
Alignment alignment = Alignment.center,
Decoration decoration,
@@ -163,11 +160,10 @@ class LunaButton extends Card {
factory LunaButton.text({
@required String text,
@required IconData icon,
double iconSize = 22.0,
double iconSize = LunaUI.ICON_SIZE,
Color color = LunaColours.accent,
Color backgroundColor,
EdgeInsets margin =
const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
EdgeInsets margin = LunaUI.MARGIN_HALF,
double height = DEFAULT_HEIGHT,
Alignment alignment = Alignment.center,
Decoration decoration,
@@ -188,7 +184,8 @@ class LunaButton extends Card {
color: color,
size: iconSize,
),
padding: const EdgeInsets.only(right: 8.0),
padding: const EdgeInsets.only(
right: LunaUI.DEFAULT_MARGIN_SIZE / 2),
),
Flexible(
child: Text(
@@ -196,7 +193,7 @@ class LunaButton extends Card {
style: const TextStyle(
color: Colors.white,
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
fontSize: LunaUI.FONT_SIZE_BUTTON,
fontSize: LunaUI.FONT_SIZE_H3,
),
overflow: TextOverflow.fade,
softWrap: false,
@@ -205,7 +202,8 @@ class LunaButton extends Card {
),
],
),
padding: const EdgeInsets.symmetric(horizontal: 12.0),
padding:
const EdgeInsets.symmetric(horizontal: LunaUI.DEFAULT_MARGIN_SIZE),
),
margin: margin,
height: height,
@@ -220,8 +218,7 @@ class LunaButton extends Card {
/// Build a button that contains a [LunaLoader].
factory LunaButton.loader({
EdgeInsets margin =
const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
EdgeInsets margin = LunaUI.MARGIN_HALF,
Color color = LunaColours.accent,
Color backgroundColor,
double height = DEFAULT_HEIGHT,
@@ -235,7 +232,7 @@ class LunaButton extends Card {
child: LunaLoader(
useSafeArea: false,
color: color,
size: 16.0,
size: LunaUI.FONT_SIZE_H3,
),
margin: margin,
height: height,
@@ -253,10 +250,9 @@ class LunaButton extends Card {
@required IconData icon,
Color color = LunaColours.accent,
Color backgroundColor,
EdgeInsets margin =
const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0),
EdgeInsets margin = LunaUI.MARGIN_HALF,
double height = DEFAULT_HEIGHT,
double iconSize = 22.0,
double iconSize = LunaUI.ICON_SIZE,
Alignment alignment = Alignment.center,
Decoration decoration,
Function onTap,

View File

@@ -6,7 +6,7 @@ class LunaCard extends Card {
Key key,
@required BuildContext context,
@required Widget child,
EdgeInsets margin = LunaUI.MARGIN_CARD,
EdgeInsets margin = LunaUI.MARGIN_H_DEFAULT_V_HALF,
Color color,
Decoration decoration,
double height,

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaColours {
/// List of LunaSea colours in order that the should appear in a list.
@@ -25,20 +26,17 @@ class LunaColours {
/// Core secondary colour (appbar, bottom bar, etc.),
static const Color secondary = Color(0xFF282834);
/// Blue
static const Color blue = Color(0xFF00A8E8);
/// Orange
static const Color blueGrey = Color(0xFF848FA5);
static const Color grey = Color(0xFFBBBBBB);
static const Color orange = Color(0xFFFF9000);
/// Red
static const Color purple = Color(0xFF9649CB);
static const Color red = Color(0xFFF71735);
/// Purple
static const Color purple = Color(0xFF9649CB);
/// Blue Grey
static const Color blueGrey = Color(0xFF848FA5);
/// Shades of White
static const Color white = Color(0xFFFFFFFF);
static const Color white70 = Color(0xB3FFFFFF);
static const Color white10 = Color(0x1AFFFFFF);
/// Returns the correct colour for a graph by what layer it is on the graph canvas.
Color byGraphLayer(int index) {
@@ -60,3 +58,15 @@ class LunaColours {
return _LIST_COLOR_ICONS[index % _LIST_COLOR_ICONS.length];
}
}
extension LunaColor on Color {
Color disabled([bool condition = true]) {
if (condition) return this.withOpacity(LunaUI.OPACITY_DISABLED);
return this;
}
Color enabled([bool condition = true]) {
if (condition) return this;
return this.withOpacity(LunaUI.OPACITY_DISABLED);
}
}

View File

@@ -4,9 +4,14 @@ import 'package:lunasea/core.dart';
// ignore: non_constant_identifier_names
Decoration LunaCardDecoration({@required String uri, @required Map headers}) {
if (LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data == 0) return null;
if (uri == null || uri.isEmpty)
if (uri == null || uri.isEmpty) {
return BoxDecoration(
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS));
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
);
}
double _opacity =
(LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data as int) / 100;
return BoxDecoration(
image: DecorationImage(
image: NetworkImage(
@@ -15,12 +20,7 @@ Decoration LunaCardDecoration({@required String uri, @required Map headers}) {
),
// To prevent excessive logs, just handle an error loading the image with an empty callback
onError: (error, stack) {},
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(
(LunaDatabaseValue.THEME_IMAGE_BACKGROUND_OPACITY.data as int) /
100),
BlendMode.dstATop,
),
opacity: _opacity,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),

View File

@@ -4,10 +4,9 @@ import 'package:lunasea/core.dart';
// ignore: avoid_classes_with_only_static_members
abstract class LunaDialog {
static const HEADER_SIZE = LunaUI.FONT_SIZE_HEADER;
static const BODY_SIZE = LunaUI.FONT_SIZE_SUBTITLE;
static const SUBBODY_SIZE = LunaUI.FONT_SIZE_SUBHEADER;
static const BUTTON_SIZE = LunaUI.FONT_SIZE_SUBHEADER;
static const HEADER_SIZE = LunaUI.FONT_SIZE_H1;
static const BODY_SIZE = LunaUI.FONT_SIZE_H3;
static const BUTTON_SIZE = LunaUI.FONT_SIZE_H4;
static Widget title({
@required String text,
@@ -135,7 +134,7 @@ abstract class LunaDialog {
decoration: InputDecoration(
labelText: title,
labelStyle: const TextStyle(
color: Colors.white54,
color: LunaColours.grey,
decoration: TextDecoration.none,
fontSize: LunaDialog.BODY_SIZE,
),
@@ -143,7 +142,8 @@ abstract class LunaDialog {
borderSide: BorderSide(color: LunaColours.accent),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: LunaColours.accent.withOpacity(0.3)),
borderSide: BorderSide(
color: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH)),
),
),
style: const TextStyle(
@@ -172,7 +172,7 @@ abstract class LunaDialog {
decoration: InputDecoration(
labelText: title,
labelStyle: const TextStyle(
color: Colors.white54,
color: LunaColours.grey,
decoration: TextDecoration.none,
fontSize: LunaDialog.BODY_SIZE,
),
@@ -180,7 +180,8 @@ abstract class LunaDialog {
borderSide: BorderSide(color: LunaColours.accent),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: LunaColours.accent.withOpacity(0.3)),
borderSide: BorderSide(
color: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH)),
),
),
style: const TextStyle(

View File

@@ -31,12 +31,22 @@ class LunaDrawer extends StatelessWidget {
valueListenable: Database.indexersBox.listenable(),
builder: (context, indexerBox, widget) => Drawer(
child: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.listen(
builder: (context, _, __) => ListView(
children: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data
? _getAlphabeticalOrder(context)
: _getManualOrder(context),
padding: const EdgeInsets.only(bottom: 8.0),
physics: const ClampingScrollPhysics(),
builder: (context, _, __) => Column(
children: [
LunaDrawerHeader(page: page),
Expanded(
child: LunaListView(
controller: PrimaryScrollController.of(context),
children: LunaDatabaseValue.DRAWER_AUTOMATIC_MANAGE.data
? _getAlphabeticalOrder(context)
: _getManualOrder(context),
physics: const ClampingScrollPhysics(),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom,
),
),
),
],
),
),
),
@@ -46,12 +56,9 @@ class LunaDrawer extends StatelessWidget {
List<Widget> _sharedHeader(BuildContext context) {
return [
LunaDrawerHeader(),
_buildEntry(context: context, module: LunaModule.DASHBOARD),
_buildEntry(context: context, module: LunaModule.SETTINGS),
const Divider(
thickness: 1.0,
color: Colors.white12,
_buildEntry(
context: context,
module: LunaModule.DASHBOARD,
),
];
}
@@ -65,10 +72,10 @@ class LunaDrawer extends StatelessWidget {
..._sharedHeader(context),
..._modules.map((module) {
if (module.isEnabled) {
if (module == LunaModule.WAKE_ON_LAN) return _buildWakeOnLAN(context);
return _buildEntry(
context: context,
module: module,
onTap: module == LunaModule.WAKE_ON_LAN ? _wakeOnLAN : null,
);
}
return const SizedBox(height: 0.0);
@@ -82,10 +89,10 @@ class LunaDrawer extends StatelessWidget {
..._sharedHeader(context),
..._modules.map((module) {
if (module.isEnabled) {
if (module == LunaModule.WAKE_ON_LAN) return _buildWakeOnLAN(context);
return _buildEntry(
context: context,
module: module,
onTap: module == LunaModule.WAKE_ON_LAN ? _wakeOnLAN : null,
);
}
return const SizedBox(height: 0.0);
@@ -96,55 +103,54 @@ class LunaDrawer extends StatelessWidget {
Widget _buildEntry({
@required BuildContext context,
@required LunaModule module,
Function onTap,
}) {
bool currentPage = page == module.key.toLowerCase();
return ListTile(
leading: Icon(
module.icon,
color: currentPage ? module.color : Colors.white,
),
title: Text(
module.name,
style: TextStyle(
color: currentPage ? module.color : Colors.white,
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
return SizedBox(
height: 56.0,
child: InkWell(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
child: Icon(
module.icon,
color: currentPage ? module.color : LunaColours.white,
),
padding: LunaUI.MARGIN_DEFAULT_HORIZONTAL,
),
Text(
module.name,
style: TextStyle(
color: currentPage ? module.color : LunaColours.white,
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
),
),
],
),
onTap: onTap ??
() async {
Navigator.of(context).pop();
if (!currentPage) module.launch();
},
),
onTap: () async {
Navigator.of(context).pop();
if (!currentPage) module.launch();
},
contentPadding: const EdgeInsets.fromLTRB(16.0, 0.0, 0.0, 0.0),
);
}
Widget _buildWakeOnLAN(BuildContext context) {
return ListTile(
leading: Icon(LunaModule.WAKE_ON_LAN.icon),
title: Text(
LunaModule.WAKE_ON_LAN.name,
style: const TextStyle(
color: Colors.white,
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
),
),
onTap: () async {
WakeOnLANAPI api = WakeOnLANAPI.fromProfile();
await api
.wake()
.then((_) => showLunaSuccessSnackBar(
title: 'Machine is Waking Up...',
message: 'Magic packet successfully sent',
))
.catchError((error, stack) {
LunaLogger().error('Failed to wake machine', error, stack);
return showLunaErrorSnackBar(
title: 'Failed to Wake Machine',
error: error,
);
});
},
contentPadding: const EdgeInsets.fromLTRB(16.0, 0.0, 0.0, 0.0),
);
Future<void> _wakeOnLAN() async {
WakeOnLANAPI api = WakeOnLANAPI.fromProfile();
await api
.wake()
.then((_) => showLunaSuccessSnackBar(
title: 'Machine is Waking Up...',
message: 'Magic packet successfully sent',
))
.catchError((error, stack) {
LunaLogger().error('Failed to wake machine', error, stack);
return showLunaErrorSnackBar(
title: 'Failed to Wake Machine',
error: error,
);
});
}
}

View File

@@ -1,72 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lunasea/core.dart';
class LunaDrawerHeader extends UserAccountsDrawerHeader {
LunaDrawerHeader({
class LunaDrawerHeader extends StatelessWidget {
final String page;
const LunaDrawerHeader({
Key key,
}) : super(
key: key,
accountName: StreamBuilder(
stream: LunaFirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) =>
LunaText.title(text: LunaFirebaseAuth().email ?? 'LunaSea'),
),
accountEmail: LunaDatabaseValue.ENABLED_PROFILE.listen(
builder: (context, lunaBox, widget) => ValueListenableBuilder(
valueListenable: Database.profilesBox.listenable(),
builder: (context, profilesBox, widget) => Padding(
child: LunaPopupMenuButton<String>(
tooltip: 'Change Profiles',
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
LunaText.subtitle(
text: LunaDatabaseValue.ENABLED_PROFILE.data,
),
const Icon(
Icons.arrow_drop_down_rounded,
color: Colors.white70,
size: LunaUI.FONT_SIZE_HEADER,
),
],
),
onSelected: (result) {
HapticFeedback.selectionClick();
LunaProfile().safelyChangeProfiles(result);
},
itemBuilder: (context) {
return <PopupMenuEntry<String>>[
for (String profile in (profilesBox as Box).keys)
PopupMenuItem<String>(
value: profile,
child: Text(
profile,
style: TextStyle(
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
color: (LunaDatabaseValue.ENABLED_PROFILE.data ??
'default') ==
profile
? LunaColours.accent
: Colors.white,
),
),
)
];
},
),
padding: const EdgeInsets.only(right: 12.0),
),
@required this.page,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return LunaDatabaseValue.ENABLED_PROFILE.listen(
builder: (context, box, _) => Container(
child: LunaAppBar.dropdown(
backgroundColor: Colors.transparent,
hideLeading: true,
useDrawer: false,
title: Database.profilesBox.keys.length == 1
? 'LunaSea'
: Database.currentProfile ?? 'LunaSea',
profiles: Database.profilesBox.keys.cast<String>().toList(),
actions: [
LunaIconButton(
icon: LunaIcons.SETTINGS,
onPressed: page == LunaModule.SETTINGS.key
? Navigator.of(context).pop
: LunaModule.SETTINGS.launch,
)
],
),
decoration: BoxDecoration(
color: LunaColours.accent,
image: DecorationImage(
image: const AssetImage(LunaAssets.brandingLogo),
colorFilter: ColorFilter.mode(
LunaColours.primary.withOpacity(0.15),
BlendMode.dstATop,
),
fit: BoxFit.cover,
),
decoration: BoxDecoration(
color: LunaColours.accent,
image: DecorationImage(
image: const AssetImage(LunaAssets.brandingLogo),
colorFilter: ColorFilter.mode(
LunaColours.primary.withOpacity(0.15), BlendMode.dstATop),
fit: BoxFit.cover,
),
),
);
),
),
);
}
}

View File

@@ -29,7 +29,7 @@ class LunaFloatingActionButtonExtended extends StatelessWidget {
style: TextStyle(
color: color,
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
fontSize: LunaUI.FONT_SIZE_H3,
letterSpacing: 0.35,
),
),

View File

@@ -4,13 +4,11 @@ import 'package:lunasea/core.dart';
class LunaHeader extends StatelessWidget {
final String text;
final String subtitle;
final EdgeInsets padding;
const LunaHeader({
Key key,
@required this.text,
this.subtitle,
this.padding,
}) : super(key: key);
Widget _headerText() {
@@ -18,7 +16,7 @@ class LunaHeader extends StatelessWidget {
text.toUpperCase(),
style: const TextStyle(
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
fontSize: LunaUI.FONT_SIZE_HEADER,
fontSize: LunaUI.FONT_SIZE_H2,
color: Colors.white,
),
);
@@ -28,28 +26,31 @@ class LunaHeader extends StatelessWidget {
return Padding(
child: Container(
height: 2.0,
width: 48.0,
width: LunaUI.DEFAULT_MARGIN_SIZE * 3,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
color: LunaColours.accent,
),
),
padding: EdgeInsets.only(
top: 4.0,
left: 1.0,
bottom: subtitle != null ? 6.0 : 0.0,
padding: const EdgeInsets.only(
top: LunaUI.DEFAULT_MARGIN_SIZE / 2,
left: 0,
bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
);
}
Widget _subtitle() {
return Text(
subtitle,
style: const TextStyle(
fontSize: LunaUI.FONT_SIZE_SUBHEADER,
color: Colors.white70,
fontWeight: FontWeight.w300,
return Padding(
child: Text(
subtitle,
style: const TextStyle(
fontSize: LunaUI.FONT_SIZE_H4,
color: LunaColours.grey,
fontWeight: FontWeight.w300,
),
),
padding: const EdgeInsets.only(bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2),
);
}
@@ -64,13 +65,11 @@ class LunaHeader extends StatelessWidget {
if (subtitle != null) _subtitle(),
],
),
padding: padding ??
const EdgeInsets.fromLTRB(
LunaUI.DEFAULT_MARGIN_SIZE,
LunaUI.DEFAULT_MARGIN_SIZE / 2,
LunaUI.DEFAULT_MARGIN_SIZE,
LunaUI.DEFAULT_MARGIN_SIZE / 1.5,
),
padding: const EdgeInsets.only(
left: LunaUI.DEFAULT_MARGIN_SIZE,
right: LunaUI.DEFAULT_MARGIN_SIZE,
top: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
);
}
}

View File

@@ -23,7 +23,7 @@ class LunaHighlightedNode extends StatelessWidget {
text,
maxLines: 1,
style: TextStyle(
fontSize: LunaUI.FONT_SIZE_HIGHLIGHTED_NODE,
fontSize: LunaUI.FONT_SIZE_H4,
color: textColor,
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
),

View File

@@ -1,2 +1,3 @@
export 'icons/icon_brands.dart';
export 'icons/icon_button.dart';
export 'icons/icon.dart';

View File

@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
class LunaIcons {
static const IconData ADD = Icons.add_rounded;
static const IconData ARROW_RIGHT = Icons.arrow_forward_ios_rounded;
static const IconData ARROW_DROPDOWN = Icons.arrow_drop_down_rounded;
static const IconData CLOUD_DELETE = Icons.cloud_off_rounded;
static const IconData CLOUD_DOWNLOAD = Icons.cloud_download_rounded;
static const IconData CLOUD_UPLOAD = Icons.cloud_upload_rounded;
static const IconData CONNECTION_TEST = Icons.wifi_tethering_rounded;
static const IconData DELETE = Icons.delete_rounded;
static const IconData DEVICES = Icons.devices_rounded;
static const IconData DOWNLOAD = Icons.file_download_rounded;
static const IconData ERROR = Icons.error_rounded;
static const IconData HOME = Icons.home_rounded;
static const IconData MONITOR_OFF = Icons.turned_in_not_rounded;
static const IconData MONITOR_ON = Icons.turned_in_rounded;
static const IconData MUSIC = Icons.music_note_rounded;
static const IconData PLAY = Icons.play_arrow_rounded;
static const IconData PROFILES = Icons.switch_account_rounded;
static const IconData REFRESH = Icons.refresh_rounded;
static const IconData RENAME = Icons.text_format_rounded;
static const IconData SEARCH = Icons.search_rounded;
static const IconData SETTINGS = Icons.settings_rounded;
static const IconData SHUFFLE = Icons.shuffle_rounded;
static const IconData TRANSLATE = Icons.translate_rounded;
static const IconData USER = Icons.person_rounded;
static const IconData VIDEO_CAM = Icons.videocam_rounded;
static const IconData WATCHED = Icons.visibility_rounded;
}

View File

@@ -12,23 +12,49 @@ class LunaIconButton extends StatelessWidget {
final Function onLongPress;
final LunaLoadingState loadingState;
final AlignmentGeometry alignment;
final EdgeInsetsGeometry padding;
final MouseCursor mouseCursor;
LunaIconButton({
const LunaIconButton.arrow({
Key key,
this.text,
this.textSize = 10.0,
this.icon,
this.iconSize = 24.0,
this.textSize = LunaUI.FONT_SIZE_H5,
this.icon = LunaIcons.ARROW_RIGHT,
this.iconSize = LunaUI.ICON_SIZE,
this.alignment = Alignment.center,
this.padding = const EdgeInsets.all(8.0),
this.color = Colors.white,
this.onPressed,
this.onLongPress,
this.loadingState,
}) : super(key: key) {
assert((text != null || icon != null), 'both text and icon cannot be null');
}
this.mouseCursor,
}) : super(key: key);
const LunaIconButton.appBar({
Key key,
this.text,
this.textSize = LunaUI.FONT_SIZE_H5,
this.icon,
this.iconSize = LunaUI.ICON_SIZE,
this.alignment = Alignment.center,
this.color = Colors.white,
this.onPressed,
this.onLongPress,
this.loadingState,
this.mouseCursor,
}) : super(key: key);
const LunaIconButton({
Key key,
this.text,
this.textSize = LunaUI.FONT_SIZE_H5,
this.icon,
this.iconSize = LunaUI.ICON_SIZE,
this.alignment = Alignment.center,
this.color = Colors.white,
this.onPressed,
this.onLongPress,
this.loadingState,
this.mouseCursor,
}) : super(key: key);
@override
Widget build(BuildContext context) {
@@ -37,9 +63,14 @@ class LunaIconButton extends StatelessWidget {
icon: loadingState == LunaLoadingState.ACTIVE ? _loader() : _icon(),
iconSize: iconSize,
alignment: alignment,
padding: padding,
padding: EdgeInsets.zero,
onPressed: _onPressed(),
mouseCursor: mouseCursor ??
(onPressed != null ? SystemMouseCursors.click : MouseCursor.defer),
),
hoverColor: Colors.transparent,
mouseCursor: mouseCursor ??
(onLongPress != null ? SystemMouseCursors.click : MouseCursor.defer),
onLongPress: _onLongPress(),
);
}
@@ -64,13 +95,14 @@ class LunaIconButton extends StatelessWidget {
Widget _loader() {
return LunaLoader(
size: 12.0,
size: LunaUI.FONT_SIZE_H4,
color: color,
useSafeArea: false,
);
}
Widget _icon() {
assert((text != null || icon != null), 'both text and icon cannot be null');
if (loadingState == LunaLoadingState.ERROR) {
return Icon(
Icons.error_rounded,

View File

@@ -2,13 +2,14 @@ import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaTextInputBar extends StatefulWidget {
static const double appBarHeight = 62.0;
static const double appBarInnerHeight = 62.0 - 13.0;
static const double defaultHeight = 50.0;
static const double defaultAppBarHeight =
defaultHeight + LunaUI.DEFAULT_MARGIN_SIZE;
static const EdgeInsets appBarMargin = EdgeInsets.fromLTRB(
12.0,
1.0,
12.0,
12.0,
LunaUI.DEFAULT_MARGIN_SIZE,
0,
LunaUI.DEFAULT_MARGIN_SIZE,
LunaUI.DEFAULT_MARGIN_SIZE,
);
final TextEditingController controller;
@@ -38,7 +39,7 @@ class LunaTextInputBar extends StatefulWidget {
this.keyboardType = TextInputType.text,
this.labelText,
this.labelIcon = Icons.search_rounded,
this.margin = LunaUI.MARGIN_CARD,
this.margin = LunaUI.MARGIN_H_DEFAULT_V_HALF,
this.autofocus = false,
this.obscureText = false,
this.isFormField = false,
@@ -79,21 +80,21 @@ class _State extends State<LunaTextInputBar> {
child: widget.isFormField ? _isForm : _isNotForm,
),
),
height: LunaTextInputBar.appBarInnerHeight,
height: LunaTextInputBar.defaultHeight,
color: Theme.of(context).canvasColor,
);
TextStyle get _sharedTextStyle => const TextStyle(
color: Colors.white,
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
color: LunaColours.white,
fontSize: LunaUI.FONT_SIZE_H3,
);
InputDecoration get _sharedInputDecoration => InputDecoration(
labelText: widget.labelText ?? 'lunasea.SearchTextBar'.tr(),
labelStyle: const TextStyle(
color: Colors.white54,
color: LunaColours.grey,
decoration: TextDecoration.none,
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
fontSize: LunaUI.FONT_SIZE_H3,
),
suffixIcon: AnimatedOpacity(
child: InkWell(

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
class LunaLinearPercentIndicator extends StatelessWidget {
static const _LINE_HEIGHT = 4.0;
static const double height = _LINE_HEIGHT + LunaUI.DEFAULT_MARGIN_SIZE / 2;
final double percent;
final Color progressColor;
final Color backgroundColor;
const LunaLinearPercentIndicator({
Key key,
this.percent,
this.progressColor = LunaColours.accent,
this.backgroundColor,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: height,
alignment: Alignment.bottomCenter,
child: LinearPercentIndicator(
percent: percent,
padding: const EdgeInsets.symmetric(horizontal: 2.0),
lineHeight: 4.0,
progressColor: progressColor,
backgroundColor:
backgroundColor ?? progressColor.withOpacity(LunaUI.OPACITY_SPLASH),
),
);
}
}

View File

@@ -1 +1,2 @@
export 'list_tile/expandable_list_tile.dart';
export 'list_tile/list_tile.dart';

View File

@@ -0,0 +1,179 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaExpandableListTile extends StatefulWidget {
final String title;
final List<TextSpan> collapsedSubtitles;
final Widget collapsedTrailing;
final Widget collapsedLeading;
final Color backgroundColor;
final Function onLongPress;
final List<LunaHighlightedNode> expandedHighlightedNodes;
final List<LunaTableContent> expandedTableContent;
final List<LunaButton> expandedTableButtons;
final bool initialExpanded;
/// Create a [LunaExpandableListTile] which is a list tile that expands into a table-style card.
///
/// If [expandedWidget] is supplied, that widget is used as the body within the expanded card.
/// Any
LunaExpandableListTile({
Key key,
@required this.title,
@required this.collapsedSubtitles,
@required this.expandedTableContent,
this.collapsedTrailing,
this.collapsedLeading,
this.onLongPress,
this.expandedHighlightedNodes,
this.expandedTableButtons,
this.backgroundColor,
this.initialExpanded = false,
}) : super(key: key) {
assert(title != null);
assert(collapsedSubtitles != null);
assert(expandedTableContent != null);
}
@override
State<StatefulWidget> createState() => _State();
}
class _State extends State<LunaExpandableListTile> {
ExpandableController controller;
@override
void initState() {
super.initState();
controller = ExpandableController(initialExpanded: widget.initialExpanded);
}
@override
void dispose() {
controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ExpandableNotifier(
controller: controller,
child: Expandable(
collapsed: collapsed(),
expanded: expanded(),
),
);
}
List<TextSpan> _parseSubtitles() {
if (widget.collapsedSubtitles.isEmpty) return [];
List<TextSpan> _result = [];
for (int i = 0; i < widget.collapsedSubtitles.length; i++) {
_result.add(widget.collapsedSubtitles[i]);
}
return _result;
}
Widget collapsed() {
return LunaBlock(
title: widget.title ?? LunaUI.TEXT_EMDASH,
body: _parseSubtitles(),
onTap: controller.toggle,
onLongPress: widget.onLongPress,
trailing: widget.collapsedTrailing,
leading: widget.collapsedLeading,
// color: widget.backgroundColor,
);
}
Widget expanded() {
return LunaCard(
context: context,
child: InkWell(
child: Padding(
padding: EdgeInsets.only(
top: LunaUI.DEFAULT_MARGIN_SIZE,
bottom: widget.expandedTableButtons?.isEmpty ?? true
? (LunaUI.DEFAULT_MARGIN_SIZE / 4 * 3)
: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
child: LunaText.title(
text: widget.title ?? LunaUI.TEXT_EMDASH,
softWrap: true,
maxLines: 8,
),
padding: const EdgeInsets.only(
left: LunaUI.DEFAULT_MARGIN_SIZE,
right: LunaUI.DEFAULT_MARGIN_SIZE,
bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
),
if (widget.expandedHighlightedNodes != null)
Padding(
child: Wrap(
direction: Axis.horizontal,
spacing: LunaUI.DEFAULT_MARGIN_SIZE / 2,
runSpacing: LunaUI.DEFAULT_MARGIN_SIZE / 2,
children: widget.expandedHighlightedNodes,
),
padding: const EdgeInsets.only(
left: LunaUI.DEFAULT_MARGIN_SIZE,
right: LunaUI.DEFAULT_MARGIN_SIZE,
bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
),
...widget.expandedTableContent
.map((child) => Padding(
child: child,
padding: const EdgeInsets.symmetric(
horizontal: LunaUI.DEFAULT_MARGIN_SIZE,
),
))
.toList(),
if (widget.expandedTableButtons != null)
Padding(
child: Wrap(
children: [
...List.generate(
widget.expandedTableButtons.length,
(index) {
int bCount = widget.expandedTableButtons.length;
double widthFactor = 0.5;
if (index == (bCount - 1) && bCount.isOdd) {
widthFactor = 1;
}
return FractionallySizedBox(
child: widget.expandedTableButtons[index],
widthFactor: widthFactor,
);
},
),
],
),
padding: const EdgeInsets.symmetric(
horizontal: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
),
],
),
),
],
),
),
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
onTap: controller.toggle,
onLongPress: widget.onLongPress,
),
color: widget.backgroundColor,
);
}
}

View File

@@ -1,12 +1,13 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
@Deprecated("Use LunaBlock instead")
class LunaListTile extends Card {
LunaListTile({
Key key,
EdgeInsets margin = LunaUI.MARGIN_CARD,
@required BuildContext context,
@required Widget title,
@required double height,
Widget subtitle,
Widget trailing,
Widget leading,
@@ -14,57 +15,65 @@ class LunaListTile extends Card {
Decoration decoration,
Function onTap,
Function onLongPress,
bool contentPadding = false,
EdgeInsets customContentPadding,
bool drawBorder = true,
EdgeInsets margin = LunaUI.MARGIN_H_DEFAULT_V_HALF,
}) : super(
key: key,
child: Container(
height: height,
child: InkWell(
child: Row(
children: [
if (leading != null)
SizedBox(
width: LunaUI.DEFAULT_MARGIN_SIZE * 4 +
LunaUI.DEFAULT_MARGIN_SIZE / 2,
child: leading,
),
Expanded(
child: ListTile(
title: title,
subtitle: subtitle,
// trailing: trailing,
leading: leading,
mouseCursor: onTap != null || onLongPress != null
? SystemMouseCursors.click
: null,
contentPadding: contentPadding
? customContentPadding?.copyWith(
right: trailing != null ? 0 : null,
) ??
LunaUI.MARGIN_CARD.copyWith(
right: trailing != null ? 0 : null,
)
: LunaUI.MARGIN_CARD.copyWith(
top: 0,
bottom: 0,
right: trailing != null ? 0 : null,
),
child: Padding(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
child: title,
height: LunaBlock.TITLE_HEIGHT,
),
if (subtitle != null) subtitle,
],
),
padding: EdgeInsets.only(
top: LunaUI.DEFAULT_MARGIN_SIZE,
bottom: LunaUI.DEFAULT_MARGIN_SIZE,
left: leading != null ? 0 : LunaUI.DEFAULT_MARGIN_SIZE,
right:
trailing != null ? 0 : LunaUI.DEFAULT_MARGIN_SIZE,
),
),
),
if (trailing != null)
Padding(
padding: EdgeInsets.only(right: LunaUI.MARGIN_CARD.right),
padding: const EdgeInsets.only(
right: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
child: SizedBox(
width: 48.0,
width: LunaUI.DEFAULT_MARGIN_SIZE * 4,
child: trailing,
),
),
],
),
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
mouseCursor: SystemMouseCursors.click,
onTap: onTap,
onLongPress: onLongPress,
mouseCursor: MouseCursor.defer,
),
decoration: decoration,
),
margin: margin,
elevation: LunaUI.ELEVATION,
shape: LunaUI.shapeBorder,
shape: drawBorder ? LunaUI.shapeBorder : LunaShapeBorder.rounded(),
color: color ?? Theme.of(context).primaryColor,
);
}

View File

@@ -5,3 +5,4 @@ export 'list_view/list_view_modal_builder.dart';
export 'list_view/paged_list_view.dart';
export 'list_view/reorderable_list_view.dart';
export 'list_view/reorderable_list_view_builder.dart';
export 'list_view/reorderable_list_view_dragger.dart';

View File

@@ -28,7 +28,7 @@ class LunaListView extends StatelessWidget {
itemExtent: itemExtent,
padding: padding ??
MediaQuery.of(context).padding.add(EdgeInsets.symmetric(
vertical: LunaUI.MARGIN_CARD.bottom,
vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom,
)),
physics: physics,
),

View File

@@ -31,7 +31,7 @@ class LunaListViewBuilder extends StatelessWidget {
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
padding: padding ??
MediaQuery.of(context).padding.add(EdgeInsets.symmetric(
vertical: LunaUI.MARGIN_CARD.bottom,
vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom,
)),
physics: physics,
itemCount: itemCount,

View File

@@ -53,12 +53,14 @@ class LunaListViewModal extends StatelessWidget {
return EdgeInsets.fromLTRB(
_padding.left + _viewInsets.left,
appBar != null
? LunaUI.MARGIN_CARD.top
: _padding.top + _viewInsets.top + LunaUI.MARGIN_CARD.top,
? LunaUI.MARGIN_H_DEFAULT_V_HALF.top
: _padding.top + _viewInsets.top + LunaUI.MARGIN_H_DEFAULT_V_HALF.top,
_padding.right + _viewInsets.right,
actionBar != null
? 0
: _padding.bottom + _viewInsets.bottom + LunaUI.MARGIN_CARD.bottom,
: _padding.bottom +
_viewInsets.bottom +
LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom,
);
}
}

View File

@@ -66,12 +66,14 @@ class LunaListViewModalBuilder extends StatelessWidget {
return EdgeInsets.fromLTRB(
_padding.left + _viewInsets.left,
appBar != null
? LunaUI.MARGIN_CARD.top
: _padding.top + _viewInsets.top + LunaUI.MARGIN_CARD.top,
? LunaUI.MARGIN_H_DEFAULT_V_HALF.top
: _padding.top + _viewInsets.top + LunaUI.MARGIN_H_DEFAULT_V_HALF.top,
_padding.right + _viewInsets.right,
actionBar != null
? 0
: _padding.bottom + _viewInsets.bottom + LunaUI.MARGIN_CARD.bottom,
: _padding.bottom +
_viewInsets.bottom +
LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom,
);
}
}

View File

@@ -68,11 +68,11 @@ class _State<T> extends State<LunaPagedListView<T>> {
),
padding: const EdgeInsets.only(bottom: 0.0),
),
newPageErrorIndicatorBuilder: (context) => LunaIconButton(
newPageErrorIndicatorBuilder: (context) => const LunaIconButton(
icon: Icons.error_rounded,
color: LunaColours.red,
),
noMoreItemsIndicatorBuilder: (context) => LunaIconButton(
noMoreItemsIndicatorBuilder: (context) => const LunaIconButton(
icon: Icons.check_rounded,
color: LunaColours.accent,
),
@@ -85,8 +85,9 @@ class _State<T> extends State<LunaPagedListView<T>> {
padding: widget.padding ??
MediaQuery.of(context)
.padding
.copyWith(bottom: LunaUI.MARGIN_CARD.bottom)
.add(EdgeInsets.only(top: LunaUI.MARGIN_CARD.top)),
.copyWith(bottom: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom)
.add(
EdgeInsets.only(top: LunaUI.MARGIN_H_DEFAULT_V_HALF.top)),
physics: const AlwaysScrollableScrollPhysics(),
),
),

View File

@@ -30,7 +30,7 @@ class LunaReorderableListView extends StatelessWidget {
children: children,
padding: padding ??
MediaQuery.of(context).padding.add(EdgeInsets.symmetric(
vertical: LunaUI.MARGIN_CARD.bottom,
vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom,
)),
physics: physics,
onReorder: onReorder,

View File

@@ -34,7 +34,7 @@ class LunaReorderableListViewBuilder extends StatelessWidget {
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
padding: padding ??
MediaQuery.of(context).padding.add(EdgeInsets.symmetric(
vertical: LunaUI.MARGIN_CARD.bottom,
vertical: LunaUI.MARGIN_H_DEFAULT_V_HALF.bottom,
)),
physics: physics,
itemCount: itemCount,

View File

@@ -1,9 +1,10 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaReorderableListDragger extends StatelessWidget {
class LunaReorderableListViewDragger extends StatelessWidget {
final int index;
const LunaReorderableListDragger({
const LunaReorderableListViewDragger({
Key key,
@required this.index,
}) : super(key: key);
@@ -15,7 +16,10 @@ class LunaReorderableListDragger extends StatelessWidget {
children: [
ReorderableDragStartListener(
index: index,
child: const Icon(Icons.menu_rounded),
child: const LunaIconButton(
icon: Icons.menu_rounded,
mouseCursor: SystemMouseCursors.click,
),
),
],
);

View File

@@ -98,7 +98,7 @@ class LunaMessage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Card(
margin: LunaUI.MARGIN_CARD,
margin: LunaUI.MARGIN_H_DEFAULT_V_HALF,
elevation: LunaUI.ELEVATION,
shape: LunaUI.shapeBorder,
child: Row(

View File

@@ -1,37 +1,57 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:transparent_image/transparent_image.dart';
class LunaNetworkImage extends ClipRRect {
LunaNetworkImage({
Key key,
double height,
double width,
@required BuildContext context,
@required double height,
@required double width,
String url,
@required String placeholderAsset,
IconData placeholderIcon,
@Deprecated('Use placeholderIcon') String placeholderAsset,
Map headers,
bool roundCorners = true,
}) : super(
key: key,
child: SizedBox(
child: Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: [
Image.asset(
placeholderAsset,
Container(
height: height,
width: width,
child: Center(
child: placeholderIcon != null
? Icon(
placeholderIcon,
color: LunaColours.accent,
size: height / 3,
)
: null,
),
decoration: BoxDecoration(
color: Theme.of(context).canvasColor,
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
border: LunaUI.shouldUseBorder
? Border.all(color: LunaColours.white10)
: null,
),
),
if (url != null && url.isNotEmpty)
if (url?.isNotEmpty ?? false)
FadeInImage(
height: height,
width: width,
fadeInDuration: const Duration(
milliseconds: LunaUI.ANIMATION_IMAGE_FADE_IN_SPEED),
milliseconds: LunaUI.ANIMATION_SPEED_IMAGES,
),
fadeOutDuration: const Duration(milliseconds: 1),
placeholder: AssetImage(placeholderAsset),
placeholder: MemoryImage(kTransparentImage),
fit: BoxFit.cover,
image: NetworkImage(url,
headers: headers?.cast<String, String>()),
image: NetworkImage(
url,
headers: headers?.cast<String, String>(),
),
imageErrorBuilder: (context, error, stack) => SizedBox(
height: height,
width: width,
@@ -42,11 +62,7 @@ class LunaNetworkImage extends ClipRRect {
height: height,
width: width,
),
borderRadius:
roundCorners ? BorderRadius.circular(LunaUI.BORDER_RADIUS) : null,
) {
// assert(height != null);
// assert(width != null);
assert(placeholderAsset != null);
}
clipBehavior: Clip.antiAlias,
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
);
}

View File

@@ -6,7 +6,9 @@ class LunaShapeBorder extends RoundedRectangleBorder {
bool useBorder = false,
}) : super(
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
side: useBorder ? const BorderSide(color: Colors.white12) : BorderSide.none,
side: useBorder
? const BorderSide(color: LunaColours.white10)
: BorderSide.none,
);
factory LunaShapeBorder.rounded() => LunaShapeBorder._internal();

View File

@@ -36,8 +36,6 @@ extension LunaSnackbarTypeExtension on LunaSnackbarType {
}
}
const _PADDING = 8.0;
Future<void> showLunaSnackBar({
@required String title,
@required LunaSnackbarType type,
@@ -56,31 +54,28 @@ Future<void> showLunaSnackBar({
controller: controller,
boxShadows: [
BoxShadow(
blurRadius: 6.0,
spreadRadius: 4.0,
color: Colors.black.withOpacity(0.10))
blurRadius: LunaUI.DEFAULT_MARGIN_SIZE / 2,
spreadRadius: LunaUI.DEFAULT_MARGIN_SIZE / 4,
color: Colors.black.withOpacity(LunaUI.OPACITY_SPLASH / 2),
),
],
horizontalDismissDirection: HorizontalDismissDirection.horizontal,
margin: const EdgeInsets.all(_PADDING),
margin: LunaUI.MARGIN_DEFAULT,
behavior: FlashBehavior.floating,
position: position,
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
borderColor:
LunaUI.shouldUseBorder ? Colors.white12 : Colors.transparent,
LunaUI.shouldUseBorder ? LunaColours.white10 : Colors.transparent,
child: FlashBar(
title: Text(
title,
style: const TextStyle(
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
fontSize: LunaUI.FONT_SIZE_TITLE,
),
title: LunaText.title(
text: title,
maxLines: 4,
overflow: TextOverflow.ellipsis,
),
content: Text(
message,
style: const TextStyle(
color: Colors.white70,
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
),
content: LunaText.subtitle(
text: message,
maxLines: 8,
overflow: TextOverflow.ellipsis,
),
shouldIconPulse: false,
icon: Padding(
@@ -88,7 +83,9 @@ Future<void> showLunaSnackBar({
icon: type.icon,
color: type.color,
),
padding: const EdgeInsets.only(left: _PADDING),
padding: const EdgeInsets.only(
left: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
),
primaryAction: showButton
? TextButton(

View File

@@ -19,9 +19,14 @@ class LunaTableCard extends StatelessWidget {
context: context,
child: Padding(
child: _body(),
padding: buttons == null
? const EdgeInsets.symmetric(vertical: 8.0)
: const EdgeInsets.only(top: 8.0, bottom: 6.0),
padding: EdgeInsets.only(
left: LunaUI.DEFAULT_MARGIN_SIZE / 2,
right: LunaUI.DEFAULT_MARGIN_SIZE / 2,
top: LunaUI.DEFAULT_MARGIN_SIZE - LunaUI.DEFAULT_MARGIN_SIZE / 4,
bottom: buttons?.isEmpty ?? true
? LunaUI.DEFAULT_MARGIN_SIZE - LunaUI.DEFAULT_MARGIN_SIZE / 4
: 0,
),
),
);
}
@@ -40,7 +45,8 @@ class LunaTableCard extends StatelessWidget {
return Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
padding: const EdgeInsets.symmetric(
horizontal: LunaUI.DEFAULT_MARGIN_SIZE),
child: LunaText.title(text: title),
),
],
@@ -49,12 +55,13 @@ class LunaTableCard extends StatelessWidget {
List<Widget> _content() {
return content
?.map<Widget>((content) => Padding(
child: content,
padding: const EdgeInsets.symmetric(horizontal: 12.0),
))
?.toList() ??
[];
.map((child) => Padding(
child: child,
padding: const EdgeInsets.symmetric(
horizontal: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
))
.toList();
}
Widget _buttons() {
@@ -64,7 +71,10 @@ class LunaTableCard extends StatelessWidget {
children:
buttons.map<Widget>((button) => Expanded(child: button)).toList(),
),
padding: const EdgeInsets.symmetric(horizontal: 6.0),
padding: const EdgeInsets.only(
top: LunaUI.DEFAULT_MARGIN_SIZE / 2 - LunaUI.DEFAULT_MARGIN_SIZE / 4,
bottom: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
);
}
}

View File

@@ -1,56 +1,94 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lunasea/core.dart';
enum _Type {
CONTENT,
SPACER,
}
class LunaTableContent extends StatelessWidget {
final String title;
final String body;
final bool bodyIsUrl;
final int titleFlex;
final int bodyFlex;
final double spacerSize;
final TextAlign titleAlign;
final TextAlign bodyAlign;
final Color titleColor;
final Color bodyColor;
final EdgeInsets padding;
final _Type type;
const LunaTableContent({
const LunaTableContent._({
Key key,
@required this.title,
@required this.body,
this.title,
this.body,
this.bodyIsUrl = false,
this.titleAlign = TextAlign.end,
this.bodyAlign = TextAlign.start,
this.titleFlex = 5,
this.bodyFlex = 10,
this.titleColor = Colors.white70,
this.bodyColor = Colors.white,
this.padding = const EdgeInsets.symmetric(vertical: 4.0),
}) : super(key: key);
this.spacerSize = LunaUI.DEFAULT_MARGIN_SIZE,
@required this.type,
});
factory LunaTableContent.spacer({
Key key,
double spacerSize = LunaUI.DEFAULT_MARGIN_SIZE,
}) =>
LunaTableContent._(
key: key,
type: _Type.SPACER,
spacerSize: spacerSize,
);
factory LunaTableContent({
Key key,
@required String title,
@required String body,
bool bodyIsUrl = false,
TextAlign titleAlign = TextAlign.end,
TextAlign bodyAlign = TextAlign.start,
int titleFlex = 1,
int bodyFlex = 2,
}) =>
LunaTableContent._(
key: key,
title: title,
body: body,
bodyIsUrl: bodyIsUrl,
titleAlign: titleAlign,
bodyAlign: bodyAlign,
titleFlex: titleFlex,
bodyFlex: bodyFlex,
type: _Type.CONTENT,
);
@override
Widget build(BuildContext context) {
return Padding(
child: Row(
children: [
_title(),
SizedBox(width: LunaUI.MARGIN_DEFAULT.left, height: 0.0),
_subtitle(),
],
crossAxisAlignment: CrossAxisAlignment.start,
),
padding: padding,
if (type == _Type.SPACER) return SizedBox(height: spacerSize);
return Row(
children: [
_title(),
_subtitle(),
],
crossAxisAlignment: CrossAxisAlignment.start,
);
}
Widget _title() {
return Expanded(
child: Text(
title?.toUpperCase() ?? LunaUI.TEXT_EMDASH,
textAlign: titleAlign,
style: TextStyle(
color: titleColor,
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
child: Padding(
child: Text(
title?.toUpperCase() ?? LunaUI.TEXT_EMDASH,
textAlign: titleAlign,
style: const TextStyle(
color: LunaColours.grey,
fontSize: LunaUI.FONT_SIZE_H3,
),
),
padding: const EdgeInsets.only(
top: LunaUI.DEFAULT_MARGIN_SIZE / 4,
bottom: LunaUI.DEFAULT_MARGIN_SIZE / 4,
right: LunaUI.DEFAULT_MARGIN_SIZE / 4,
),
),
flex: titleFlex,
@@ -60,24 +98,24 @@ class LunaTableContent extends StatelessWidget {
Widget _subtitle() {
return Expanded(
child: InkWell(
child: Text(
body ?? LunaUI.TEXT_EMDASH,
textAlign: bodyAlign,
style: TextStyle(
color: bodyColor,
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
child: Padding(
child: Text(
body ?? LunaUI.TEXT_EMDASH,
textAlign: bodyAlign,
style: const TextStyle(
color: LunaColours.white,
fontSize: LunaUI.FONT_SIZE_H3,
),
),
padding: const EdgeInsets.only(
top: LunaUI.DEFAULT_MARGIN_SIZE / 4,
bottom: LunaUI.DEFAULT_MARGIN_SIZE / 4,
left: LunaUI.DEFAULT_MARGIN_SIZE / 2,
),
),
onTap: !bodyIsUrl ? null : body.lunaOpenGenericLink,
onLongPress: !bodyIsUrl
? null
: () async {
await Clipboard.setData(ClipboardData(text: body));
showLunaSuccessSnackBar(
title: 'Copied Content',
message: 'Copied link to the clipboard');
},
borderRadius: BorderRadius.circular(5.0),
onLongPress: !bodyIsUrl ? null : () => body.copyToClipboard(),
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
),
flex: bodyFlex,
);

View File

@@ -27,21 +27,22 @@ class LunaText extends Text {
@required String text,
int maxLines = 1,
bool softWrap = false,
@Deprecated('Dead property') bool darken,
TextAlign textAlign = TextAlign.start,
TextOverflow overflow = TextOverflow.fade,
Color color = Colors.white,
bool darken = false,
}) =>
LunaText(
text: text,
key: key,
maxLines: maxLines,
overflow: TextOverflow.fade,
overflow: overflow,
softWrap: softWrap,
textAlign: textAlign,
style: TextStyle(
color: darken ? color.withOpacity(0.30) : color,
color: color,
fontWeight: LunaUI.FONT_WEIGHT_BOLD,
fontSize: LunaUI.FONT_SIZE_TITLE,
fontSize: LunaUI.FONT_SIZE_H2,
),
);
@@ -51,9 +52,10 @@ class LunaText extends Text {
@required String text,
int maxLines = 1,
bool softWrap = false,
@Deprecated('Dead property') bool darken,
TextAlign textAlign = TextAlign.start,
Color color = Colors.white70,
bool darken = false,
TextOverflow overflow = TextOverflow.fade,
Color color = LunaColours.grey,
FontStyle fontStyle = FontStyle.normal,
}) =>
LunaText(
@@ -62,10 +64,10 @@ class LunaText extends Text {
softWrap: softWrap,
maxLines: maxLines,
textAlign: textAlign,
overflow: TextOverflow.fade,
overflow: overflow,
style: TextStyle(
color: darken ? color.withOpacity(0.30) : color,
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
color: color,
fontSize: LunaUI.FONT_SIZE_H3,
fontStyle: fontStyle,
),
);

View File

@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaTextSpan extends TextSpan {
const LunaTextSpan.extended({
@required String text,
}) : super(
text: text,
style: const TextStyle(
height: LunaBlock.SUBTITLE_HEIGHT / LunaUI.FONT_SIZE_H3,
),
);
}

View File

@@ -35,10 +35,10 @@ class LunaTheme {
brightness: Brightness.dark,
canvasColor: LunaColours.primary,
primaryColor: LunaColours.secondary,
highlightColor: LunaColours.splash.withOpacity(0.15),
highlightColor: LunaColours.splash.withOpacity(LunaUI.OPACITY_SPLASH / 2),
cardColor: LunaColours.secondary,
hoverColor: LunaColours.accent.withOpacity(0.15),
splashColor: LunaColours.accent.withOpacity(0.30),
hoverColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH / 2),
splashColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH),
dialogBackgroundColor: LunaColours.secondary,
toggleableActiveColor: LunaColours.accent,
iconTheme: const IconThemeData(
@@ -50,7 +50,7 @@ class LunaTheme {
borderRadius: BorderRadius.all(Radius.circular(LunaUI.BORDER_RADIUS)),
),
textStyle: TextStyle(
color: Colors.white70,
color: LunaColours.grey,
fontSize: LunaUI.FONT_SIZE_SUBHEADER,
),
preferBelow: true,
@@ -68,10 +68,10 @@ class LunaTheme {
brightness: Brightness.dark,
canvasColor: Colors.black,
primaryColor: Colors.black,
highlightColor: LunaColours.splash.withOpacity(0.15),
highlightColor: LunaColours.splash.withOpacity(LunaUI.OPACITY_SPLASH / 2),
cardColor: Colors.black,
hoverColor: LunaColours.accent.withOpacity(0.15),
splashColor: LunaColours.accent.withOpacity(0.30),
hoverColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH / 2),
splashColor: LunaColours.accent.withOpacity(LunaUI.OPACITY_SPLASH),
dialogBackgroundColor: Colors.black,
toggleableActiveColor: LunaColours.accent,
iconTheme: const IconThemeData(
@@ -83,10 +83,11 @@ class LunaTheme {
borderRadius: const BorderRadius.all(
Radius.circular(LunaUI.BORDER_RADIUS),
),
border: useAMOLEDBorders ? Border.all(color: Colors.white12) : null,
border:
useAMOLEDBorders ? Border.all(color: LunaColours.white10) : null,
),
textStyle: const TextStyle(
color: Colors.white70,
color: LunaColours.grey,
fontSize: LunaUI.FONT_SIZE_SUBHEADER,
),
preferBelow: true,
@@ -121,7 +122,7 @@ class LunaTheme {
return TextButtonThemeData(
style: ButtonStyle(
overlayColor: MaterialStateProperty.all<Color>(
LunaColours.splash.withOpacity(0.30),
LunaColours.splash.withOpacity(LunaUI.OPACITY_SPLASH),
),
),
);

View File

@@ -4,123 +4,141 @@ import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:package_info_plus/package_info_plus.dart';
class LunaChangelog {
/// Check if the current build number is not equal to the stored build number.
///
/// If they are not the same, stores the current build number and shows the changelog.
Future<void> checkAndShowChangelog() async {
PackageInfo.fromPlatform().then((package) {
if (package != null &&
AlertsDatabaseValue.CHANGELOG.data != package.buildNumber) {
AlertsDatabaseValue.CHANGELOG.put(package.buildNumber);
showChangelog(package.version, package.buildNumber);
}
}).catchError((error, stack) {
LunaLogger().error('Failed to fetch platform information', error, stack);
});
class LunaChangelogSheet extends LunaBottomModalSheet {
_Changelog _changelog;
String _version;
String _buildNumber;
static Future<dynamic> delayedShow([
Duration duration = const Duration(seconds: 1),
bool checkBuildNumber = true,
]) async {
Future.delayed(
duration,
() async => LunaChangelogSheet().show(
context: LunaState.navigatorKey.currentContext,
checkBuildNumber: checkBuildNumber,
),
);
}
/// Load the changelog asset and show a bottom modal sheet with the changelog.
Future<void> showChangelog(String version, String buildNumber) async {
await DefaultAssetBundle.of(LunaState.navigatorKey.currentContext)
.loadString('assets/changelog.json')
.then((data) {
_Changelog changelog = _Changelog.fromJson(json.decode(data));
// TODO: Abstract this
LunaBottomModalSheet().showModal(
context: LunaState.navigatorKey.currentContext,
builder: (context) => LunaListViewModal(
children: [
if (version != null && buildNumber != null)
LunaHeader(
text: '$version ($buildNumber)', subtitle: changelog.motd),
if (version == null || buildNumber == null)
LunaHeader(text: 'LunaSea', subtitle: changelog.motd),
if ((changelog.changesNew?.length ?? 0) != 0)
const LunaHeader(text: 'New'),
if ((changelog.changesNew?.length ?? 0) != 0)
LunaTableCard(
content: List<LunaTableContent>.generate(
changelog.changesNew.length,
(index) => LunaTableContent(
title: changelog.changesNew[index].module,
body: changelog.changesNew[index].changes
.fold(
'',
(data, object) =>
data += '${LunaUI.TEXT_BULLET}\t$object\n')
.trim(),
),
),
),
if ((changelog.changesTweaks?.length ?? 0) != 0)
const LunaHeader(text: 'Tweaks'),
if ((changelog.changesTweaks?.length ?? 0) != 0)
LunaTableCard(
content: List<LunaTableContent>.generate(
changelog.changesTweaks.length,
(index) => LunaTableContent(
title: changelog.changesTweaks[index].module,
body: changelog.changesTweaks[index].changes
.fold(
'',
(data, object) =>
data += '${LunaUI.TEXT_BULLET}\t$object\n')
.trim(),
),
),
),
if ((changelog.changesFixes?.length ?? 0) != 0)
const LunaHeader(text: 'Fixes'),
if ((changelog.changesFixes?.length ?? 0) != 0)
LunaTableCard(
content: List<LunaTableContent>.generate(
changelog.changesFixes.length,
(index) => LunaTableContent(
title: changelog.changesFixes[index].module,
body: changelog.changesFixes[index].changes
.fold(
'',
(data, object) =>
data += '${LunaUI.TEXT_BULLET}\t$object\n')
.trim(),
),
),
),
if ((changelog.changesPlatform?.length ?? 0) != 0)
const LunaHeader(text: 'Platform-Specific'),
if ((changelog.changesPlatform?.length ?? 0) != 0)
LunaTableCard(
content: List<LunaTableContent>.generate(
changelog.changesPlatform.length,
(index) => LunaTableContent(
title: changelog.changesPlatform[index].module,
body: changelog.changesPlatform[index].changes
.fold(
'',
(data, object) =>
data += '${LunaUI.TEXT_BULLET}\t$object\n')
.trim(),
),
),
),
const SizedBox(height: LunaUI.DEFAULT_MARGIN_SIZE / 2),
],
actionBar: LunaBottomActionBar(
actions: [
LunaButton.text(
text: 'Full Changelog',
icon: Icons.track_changes_rounded,
onTap: LunaLinks.CHANGELOG.launch,
),
],
@override
Future<dynamic> show({
@required BuildContext context,
Widget Function(BuildContext context) builder,
bool checkBuildNumber = false,
}) async {
try {
_changelog =
await DefaultAssetBundle.of(LunaState.navigatorKey.currentContext)
.loadString('assets/changelog.json')
.then((data) => _Changelog.fromJson(json.decode(data)));
PackageInfo _package = await PackageInfo.fromPlatform();
if (_package == null) return;
_version = _package.version;
_buildNumber = _package.buildNumber;
if (checkBuildNumber) {
AlertsDatabaseValue _db = AlertsDatabaseValue.CHANGELOG;
if (_db.data == _package.buildNumber) return;
_db.put(_package.buildNumber);
}
return this.showModal(context: context, builder: builder);
} catch (error, stack) {
LunaLogger().error('Failed to show changelog sheet', error, stack);
}
}
@override
Widget builder(BuildContext context) {
return LunaListViewModal(
children: [
if (_version != null && _buildNumber != null)
LunaHeader(
text: '$_version ($_buildNumber)', subtitle: _changelog.motd),
if (_version == null || _buildNumber == null)
LunaHeader(text: 'LunaSea', subtitle: _changelog.motd),
if ((_changelog.changesNew?.length ?? 0) != 0)
const LunaHeader(text: 'New'),
if ((_changelog.changesNew?.length ?? 0) != 0)
LunaTableCard(
content: List<LunaTableContent>.generate(
_changelog.changesNew.length,
(index) {
String _body = _changelog.changesNew[index].changes
.fold('', (d, o) => d += '${LunaUI.TEXT_BULLET}\t$o\n')
.trim();
return LunaTableContent(
title: _changelog.changesNew[index].module,
body: _body,
);
},
),
),
),
);
}).catchError((error, stack) {
LunaLogger().error('Failed to fetched changelog asset', error, stack);
showLunaErrorSnackBar(title: 'Failed to Load Changelog', error: error);
});
if ((_changelog.changesTweaks?.length ?? 0) != 0)
const LunaHeader(text: 'Tweaks'),
if ((_changelog.changesTweaks?.length ?? 0) != 0)
LunaTableCard(
content: List<LunaTableContent>.generate(
_changelog.changesTweaks.length,
(index) {
String _body = _changelog.changesTweaks[index].changes
.fold('', (d, o) => d += '${LunaUI.TEXT_BULLET}\t$o\n')
.trim();
return LunaTableContent(
title: _changelog.changesTweaks[index].module,
body: _body,
);
},
),
),
if ((_changelog.changesFixes?.length ?? 0) != 0)
const LunaHeader(text: 'Fixes'),
if ((_changelog.changesFixes?.length ?? 0) != 0)
LunaTableCard(
content: List<LunaTableContent>.generate(
_changelog.changesFixes.length, (index) {
String _body = _changelog.changesFixes[index].changes
.fold('', (d, o) => d += '${LunaUI.TEXT_BULLET}\t$o\n')
.trim();
return LunaTableContent(
title: _changelog.changesFixes[index].module,
body: _body,
);
}),
),
if ((_changelog.changesPlatform?.length ?? 0) != 0)
const LunaHeader(text: 'Platform-Specific'),
if ((_changelog.changesPlatform?.length ?? 0) != 0)
LunaTableCard(
content: List<LunaTableContent>.generate(
_changelog.changesPlatform.length,
(index) {
String _body = _changelog.changesPlatform[index].changes
.fold('', (d, o) => d += '${LunaUI.TEXT_BULLET}\t$o\n')
.trim();
return LunaTableContent(
title: _changelog.changesPlatform[index].module,
body: _body,
);
},
),
),
const SizedBox(height: LunaUI.DEFAULT_MARGIN_SIZE / 2),
],
actionBar: LunaBottomActionBar(
actions: [
LunaButton.text(
text: 'Full Changelog',
icon: Icons.track_changes_rounded,
onTap: LunaLinks.CHANGELOG.launch,
),
],
),
);
}
}

View File

@@ -1,8 +1,8 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaLogger {
@@ -10,13 +10,10 @@ class LunaLogger {
/// Initialize the logger by setting the timestamp format and capturing errors on [FlutterError.onError].
void initialize() {
LunaFirebaseAnalytics().setEnabledState();
LunaFirebaseCrashlytics().setEnabledState();
FlutterError.onError =
(FlutterErrorDetails details, {bool forceReport = false}) async {
if (kDebugMode)
FlutterError.dumpErrorToConsole(details, forceReport: forceReport);
recordCrashlyticsFlutterError(details);
Zone.current.handleUncaughtError(details.exception, details.stack);
};
_compactDatabase();
@@ -41,7 +38,7 @@ class LunaLogger {
if (log != null) logs.add(log.toMap());
});
// Create a string
JsonEncoder encoder = const JsonEncoder.withIndent(' ');
JsonEncoder encoder = JsonEncoder.withIndent(' '.repeat(4));
String data = encoder.convert(logs);
return data;
}
@@ -49,36 +46,6 @@ class LunaLogger {
/// Clear all logs currently saved to the database.
Future<void> clearLogs() async => Database().clearLogsBox();
/// Records a Crashlytics error if it passes the following checks:
/// - In release mode
/// - Crashlytics is enabled in the instance
/// - Crashlytics is enabled in Hive
/// - The error is not a DioError
Future<void> recordCrashlyticsError(
dynamic error, StackTrace stackTrace) async {
if (kReleaseMode &&
LunaFirebaseCrashlytics.isPlatformCompatible &&
LunaFirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled &&
LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data &&
error is! DioError)
LunaFirebaseCrashlytics.instance.recordError(error, stackTrace);
}
/// Records a Crashlytics flutter error if it passes the following checks:
/// - In release mode
/// - Crashlytics is enabled in the instance
/// - Crashlytics is enabled in Hive
/// - The error is not a DioError
Future<void> recordCrashlyticsFlutterError(
FlutterErrorDetails details) async {
if (kReleaseMode &&
LunaFirebaseCrashlytics.isPlatformCompatible &&
LunaFirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled &&
LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data &&
error is! DioError)
LunaFirebaseCrashlytics.instance.recordFlutterError(details);
}
/// Log a new debug-level log.
void debug(String message) {
LunaLogHiveObject log = LunaLogHiveObject.withMessage(
@@ -101,26 +68,35 @@ class LunaLogger {
/// Log a new error-level log.
void error(String message, dynamic error, StackTrace stackTrace) {
recordCrashlyticsError(error, stackTrace);
LunaLogHiveObject log = LunaLogHiveObject.withError(
type: LunaLogType.ERROR,
message: message,
error: error,
stackTrace: stackTrace,
);
Database.logsBox.add(log);
if (error is! NetworkImageLoadException) {
LunaLogHiveObject log = LunaLogHiveObject.withError(
type: LunaLogType.ERROR,
message: message,
error: error,
stackTrace: stackTrace,
);
if (kDebugMode) {
print(message);
print(error);
print(stackTrace);
}
Database.logsBox.add(log);
}
}
/// Log a new critical-level log.
void critical(dynamic error, StackTrace stackTrace) {
if (error is! DioError) {
recordCrashlyticsError(error, stackTrace);
if (error is! NetworkImageLoadException) {
LunaLogHiveObject log = LunaLogHiveObject.withError(
type: LunaLogType.CRITICAL,
message: error?.toString() ?? 'Unknown critical error',
error: error,
stackTrace: stackTrace,
);
if (kDebugMode) {
print(error);
print(stackTrace);
}
Database.logsBox.add(log);
}
}

View File

@@ -1,6 +0,0 @@
export 'widgets/action_bar_card.dart';
export 'widgets/expandable_list_tile.dart';
export 'widgets/five_line_card_with_poster.dart';
export 'widgets/four_line_card_with_poster.dart';
export 'widgets/reorderable_list_dragger.dart';
export 'widgets/two_line_card_with_poster.dart';

View File

@@ -1,189 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaExpandableListTile extends StatefulWidget {
final String title;
final List<TextSpan> collapsedSubtitles;
final Widget collapsedTrailing;
final Widget collapsedLeading;
final Color backgroundColor;
final Function onLongPress;
final List<LunaHighlightedNode> expandedHighlightedNodes;
final List<LunaTableContent> expandedTableContent;
final List<LunaButton> expandedTableButtons;
final Widget expandedCustomWidget;
final bool initialExpanded;
/// Create a [LunaExpandableListTile] which is a list tile that expands into a table-style card.
///
/// If [expandedWidget] is supplied, that widget is used as the body within the expanded card.
/// Any
LunaExpandableListTile({
Key key,
@required this.title,
@required this.collapsedSubtitles,
this.collapsedTrailing,
this.collapsedLeading,
this.onLongPress,
this.expandedHighlightedNodes,
this.expandedTableContent,
this.expandedTableButtons,
this.expandedCustomWidget,
this.backgroundColor,
this.initialExpanded = false,
}) : super(key: key) {
assert(collapsedSubtitles != null);
if (expandedCustomWidget == null) {
assert(expandedTableContent != null);
}
}
@override
State<StatefulWidget> createState() => _State();
}
class _State extends State<LunaExpandableListTile> {
ExpandableController controller;
@override
void initState() {
super.initState();
controller = ExpandableController(initialExpanded: widget.initialExpanded);
}
@override
void dispose() {
controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ExpandableNotifier(
controller: controller,
child: Expandable(
collapsed: collapsed(),
expanded: expanded(),
),
);
}
List<TextSpan> _parseSubtitles() {
if (widget.collapsedSubtitles.isEmpty) return [];
List<TextSpan> _result = [];
for (int i = 0; i < widget.collapsedSubtitles.length; i++) {
_result.add(widget.collapsedSubtitles[i]);
if (i != widget.collapsedSubtitles.length - 1) {
_result.add(const TextSpan(text: '\n'));
}
}
return _result;
}
Widget collapsed() {
return LunaListTile(
context: context,
title: LunaText.title(text: widget.title ?? LunaUI.TEXT_EMDASH),
subtitle: RichText(
text: TextSpan(
style: const TextStyle(
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
color: Colors.white70,
),
children: _parseSubtitles(),
),
overflow: TextOverflow.fade,
softWrap: false,
maxLines: widget.collapsedSubtitles.length,
),
onTap: controller.toggle,
onLongPress: widget.onLongPress,
contentPadding: widget.collapsedSubtitles.length > 1,
trailing: widget.collapsedTrailing,
leading: widget.collapsedLeading,
color: widget.backgroundColor,
);
}
Widget expanded() {
Widget child = widget.expandedCustomWidget ??
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
child: LunaText.title(
text: widget.title ?? LunaUI.TEXT_EMDASH,
softWrap: true,
maxLines: 8,
),
padding: const EdgeInsets.only(
bottom: 8.0, left: 12.0, right: 12.0, top: 10.0),
),
if (widget.expandedHighlightedNodes != null)
Padding(
child: Wrap(
direction: Axis.horizontal,
spacing: 6.0,
runSpacing: 6.0,
children: widget.expandedHighlightedNodes,
),
padding: const EdgeInsets.only(
bottom: 8.0, left: 12.0, right: 12.0, top: 0.0),
),
Padding(
child: Column(
children: [
...widget.expandedTableContent
.map<Widget>((content) => Padding(
child: content,
padding: const EdgeInsets.symmetric(
horizontal: 12.0),
))
.toList(),
if (widget.expandedTableButtons != null)
Padding(
child: Wrap(
children: [
...List.generate(
widget.expandedTableButtons.length,
(index) {
double widthFactor = 0.5;
if (index ==
(widget.expandedTableButtons.length -
1) &&
widget.expandedTableButtons.length.isOdd)
widthFactor = 1;
return FractionallySizedBox(
child: widget.expandedTableButtons[index],
widthFactor: widthFactor,
);
}),
],
),
padding:
const EdgeInsets.symmetric(horizontal: 6.0),
),
],
),
padding: const EdgeInsets.only(bottom: 6.0),
),
],
),
),
],
);
return LunaCard(
context: context,
child: InkWell(
child: child,
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
onTap: controller.toggle,
onLongPress: widget.onLongPress,
),
color: widget.backgroundColor,
);
}
}

View File

@@ -1,156 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaFiveLineCardWithPoster extends StatelessWidget {
final Function onTap;
final Function onLongPress;
final String backgroundUrl;
final String posterUrl;
final String posterPlaceholder;
final String title;
final TextSpan subtitle1;
final TextSpan subtitle2;
final TextSpan subtitle3;
final TextSpan subtitle4;
final int subtitle1MaxLines;
final int subtitle2MaxLines;
final int subtitle3MaxLines;
final int subtitle4MaxLines;
final Widget customSubtitle1;
final Widget customSubtitle2;
final Widget customSubtitle3;
final Widget customSubtitle4;
final bool darken;
final Color titleColor;
final Map posterHeaders;
final LunaIconButton trailing;
static const double itemExtent = 105.0;
static const double _padding = 8.0;
const LunaFiveLineCardWithPoster({
Key key,
@required this.posterPlaceholder,
@required this.posterUrl,
@required this.posterHeaders,
@required this.title,
this.backgroundUrl,
this.titleColor = Colors.white,
this.subtitle1,
this.subtitle2,
this.subtitle3,
this.subtitle4,
this.subtitle1MaxLines = 1,
this.subtitle2MaxLines = 1,
this.subtitle3MaxLines = 1,
this.subtitle4MaxLines = 1,
this.customSubtitle1,
this.customSubtitle2,
this.customSubtitle3,
this.customSubtitle4,
this.darken = false,
this.onTap,
this.onLongPress,
this.trailing,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return LunaCard(
context: context,
child: InkWell(
child: Row(
children: [
_poster(context),
_body(context),
if (trailing != null) _trailing(),
],
),
onTap: onTap,
onLongPress: onLongPress,
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
),
decoration: backgroundUrl == null
? null
: LunaCardDecoration(
uri: backgroundUrl,
headers: posterHeaders,
),
);
}
Widget _poster(BuildContext context) {
return LunaNetworkImage(
url: posterUrl,
placeholderAsset: posterPlaceholder,
height: itemExtent,
width: itemExtent / 1.5,
headers: posterHeaders,
);
}
Widget _body(BuildContext context) {
return Expanded(
child: Padding(
child: SizedBox(
child: Column(
children: [
_title(),
if (customSubtitle1 != null) customSubtitle1,
if (customSubtitle1 == null && subtitle1 != null)
_subtitle(subtitle1, subtitle1MaxLines),
if (customSubtitle2 != null) customSubtitle2,
if (customSubtitle2 == null && subtitle2 != null)
_subtitle(subtitle2, subtitle2MaxLines),
if (customSubtitle3 != null) customSubtitle3,
if (customSubtitle3 == null && subtitle3 != null)
_subtitle(subtitle3, subtitle3MaxLines),
if (customSubtitle4 != null) customSubtitle4,
if (customSubtitle4 == null && subtitle4 != null)
_subtitle(subtitle4, subtitle4MaxLines),
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
),
height: (itemExtent - (_padding * 2)),
),
padding: const EdgeInsets.all(_padding),
),
);
}
Widget _title() {
return LunaText.title(
text: title,
color: titleColor,
darken: darken,
maxLines: 1,
);
}
Widget _subtitle(TextSpan text, int maxLines) {
return RichText(
text: TextSpan(
style: TextStyle(
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
color: darken ? Colors.white30 : Colors.white70,
),
children: [text],
),
overflow: TextOverflow.fade,
softWrap: maxLines == 1 ? false : true,
maxLines: maxLines,
);
}
Widget _trailing() {
return Padding(
padding: EdgeInsets.only(right: LunaUI.MARGIN_CARD.right),
child: SizedBox(
width: 48.0,
height: itemExtent,
child: trailing,
),
);
}
}

View File

@@ -1,148 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaFourLineCardWithPoster extends StatelessWidget {
final Function onTap;
final Function onLongPress;
final String backgroundUrl;
final String posterUrl;
final String posterPlaceholder;
final String title;
final TextSpan subtitle1;
final TextSpan subtitle2;
final TextSpan subtitle3;
final int subtitle1MaxLines;
final int subtitle2MaxLines;
final int subtitle3MaxLines;
final Widget customSubtitle1;
final Widget customSubtitle2;
final Widget customSubtitle3;
final bool darken;
final Color titleColor;
final Map posterHeaders;
final LunaIconButton trailing;
static const double itemExtent = 90.0;
static const double padding = 8.0;
const LunaFourLineCardWithPoster({
Key key,
@required this.posterPlaceholder,
@required this.posterUrl,
@required this.posterHeaders,
@required this.title,
this.backgroundUrl,
this.titleColor = Colors.white,
this.subtitle1,
this.subtitle2,
this.subtitle3,
this.subtitle1MaxLines = 1,
this.subtitle2MaxLines = 1,
this.subtitle3MaxLines = 1,
this.customSubtitle1,
this.customSubtitle2,
this.customSubtitle3,
this.darken = false,
this.onTap,
this.onLongPress,
this.trailing,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return LunaCard(
context: context,
child: InkWell(
child: Row(
children: [
_poster(context),
_body(context),
if (trailing != null) _trailing(),
],
),
onTap: onTap,
onLongPress: onLongPress,
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
),
decoration: backgroundUrl == null
? null
: LunaCardDecoration(
uri: backgroundUrl,
headers: posterHeaders,
),
height: itemExtent,
);
}
Widget _poster(BuildContext context) {
return LunaNetworkImage(
url: posterUrl,
placeholderAsset: posterPlaceholder,
height: itemExtent,
width: itemExtent / 1.5,
headers: posterHeaders,
);
}
Widget _body(BuildContext context) {
return Expanded(
child: Padding(
child: SizedBox(
child: Column(
children: [
_title(),
if (customSubtitle1 != null) customSubtitle1,
if (customSubtitle1 == null && subtitle1 != null)
_subtitle(subtitle1, subtitle1MaxLines),
if (customSubtitle2 != null) customSubtitle2,
if (customSubtitle2 == null && subtitle2 != null)
_subtitle(subtitle2, subtitle2MaxLines),
if (customSubtitle3 != null) customSubtitle3,
if (customSubtitle3 == null && subtitle3 != null)
_subtitle(subtitle3, subtitle3MaxLines),
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
),
height: (itemExtent - (padding * 2)),
),
padding: const EdgeInsets.all(padding),
),
);
}
Widget _title() {
return LunaText.title(
text: title,
color: titleColor,
darken: darken,
maxLines: 1,
);
}
Widget _subtitle(TextSpan text, int maxLines) {
return RichText(
text: TextSpan(
style: TextStyle(
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
color: darken ? Colors.white30 : Colors.white70,
),
children: [text],
),
overflow: TextOverflow.fade,
softWrap: maxLines == 1 ? false : true,
maxLines: maxLines,
);
}
Widget _trailing() {
return Padding(
padding: EdgeInsets.only(right: LunaUI.MARGIN_CARD.right),
child: SizedBox(
width: 48.0,
height: itemExtent,
child: trailing,
),
);
}
}

View File

@@ -1,134 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaTwoLineCardWithPoster extends StatelessWidget {
final Function onTap;
final Function onLongPress;
final String backgroundUrl;
final String posterUrl;
final String posterPlaceholder;
final String title;
final TextSpan subtitle1;
final TextSpan subtitle2;
final Widget customSubtitle1;
final Widget customSubtitle2;
final bool darken;
final Color titleColor;
final Map posterHeaders;
final LunaIconButton trailing;
static const double itemExtent = 67.0;
static const double _padding = 8.0;
const LunaTwoLineCardWithPoster({
Key key,
@required this.posterPlaceholder,
@required this.posterUrl,
@required this.posterHeaders,
@required this.title,
this.backgroundUrl,
this.titleColor = Colors.white,
this.subtitle1,
this.subtitle2,
this.customSubtitle1,
this.customSubtitle2,
this.darken = false,
this.onTap,
this.onLongPress,
this.trailing,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return LunaCard(
context: context,
child: InkWell(
child: Row(
children: [
_poster(context),
_body(context),
if (trailing != null) _trailing(),
],
),
onTap: onTap,
onLongPress: onLongPress,
borderRadius: BorderRadius.circular(LunaUI.BORDER_RADIUS),
),
decoration: backgroundUrl == null
? null
: LunaCardDecoration(
uri: backgroundUrl,
headers: posterHeaders,
),
);
}
Widget _poster(BuildContext context) {
return LunaNetworkImage(
url: posterUrl,
placeholderAsset: posterPlaceholder,
height: itemExtent,
width: itemExtent,
headers: posterHeaders,
);
}
Widget _body(BuildContext context) {
return Expanded(
child: Padding(
child: SizedBox(
child: Column(
children: [
_title(),
if (customSubtitle1 != null) customSubtitle1,
if (customSubtitle1 == null && subtitle1 != null)
_subtitle(subtitle1),
if (customSubtitle2 != null) customSubtitle2,
if (customSubtitle2 == null && subtitle2 != null)
_subtitle(subtitle2),
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
),
height: (itemExtent - (_padding * 2)),
),
padding: const EdgeInsets.all(_padding),
),
);
}
Widget _title() {
return LunaText.title(
text: title,
color: titleColor,
darken: darken,
maxLines: 1,
);
}
Widget _subtitle(TextSpan text) {
return RichText(
text: TextSpan(
style: TextStyle(
fontSize: LunaUI.FONT_SIZE_SUBTITLE,
color: darken ? Colors.white30 : Colors.white70,
),
children: [text],
),
overflow: TextOverflow.fade,
softWrap: false,
maxLines: 1,
);
}
Widget _trailing() {
return Padding(
padding: EdgeInsets.only(right: LunaUI.MARGIN_CARD.right),
child: SizedBox(
width: 48.0,
height: itemExtent,
child: trailing,
),
);
}
}

Some files were not shown because too many files have changed in this diff Show More