Upgrade project to the latest stable Flutter version (#201)

* fixed fvm path typo

* Update pubspec.yaml

* version control pubspec.lock

* fixed ios build

* deleted `ExpandableSectionList`

* removed redundant default cases

* avoided async gaps

* replaced deprecated color value getter

* `WillPopScope` -> `PopScope`

* removed theme deprecations

* replaced text scale deprecation

* updated goldens

* updated flutter version across workflows

* [android] migrated to the new gradle

* upgraded dependencies

* [android] fixed build

* [ios] fixed build

* updated config

* allow release notes to fail

* updated stub pubspec

* [android] use java 17

* [ios] enable flutterfire

* added firebase.json to secrets

* typo

* update color utils

* use exact versions

* reverted color utils

* updated goldens
This commit is contained in:
Vadim 2025-01-20 19:32:57 +01:00 committed by GitHub
parent fd97fc7fef
commit fb58b6cd9f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
56 changed files with 1857 additions and 528 deletions

3
.fvmrc Normal file
View file

@ -0,0 +1,3 @@
{
"flutter": "3.27.1"
}

View file

@ -90,7 +90,7 @@ jobs:
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "11"
java-version: "17"
- name: Restore Android keystore .jsk and .properties files
run: |
@ -100,8 +100,10 @@ jobs:
- name: Restore google-services.json
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.GOOGLE_SERVICES_JSON_ANDROID }}" "android/app/google-services.json"
- name: Restore firebase_options.dart
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
- name: Setup Firebase
run: |
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_JSON }}" "firebase.json"
- name: Restore constants.dart
env:
@ -112,6 +114,7 @@ jobs:
run: bash ./.github/scripts/increment_build_number.sh ${{ github.event.inputs.version }}
- name: Download release notes
continue-on-error: true
uses: actions/download-artifact@v3
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
@ -121,7 +124,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.13.9"
flutter-version: "3.27.1"
- name: Prepare flutter project
run: |

View file

@ -45,7 +45,7 @@ env:
jobs:
build:
name: Build .ipa
runs-on: macos-13
runs-on: macos-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v3
@ -92,11 +92,11 @@ jobs:
- name: Restore ExportOptions.plist
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.APP_STORE_EXPORT_OPTIONS }}" "ios/Runner/ExportOptions.plist"
- name: Restore firebase_app_id_file.json
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_APP_ID_FILE }}" "ios/firebase_app_id_file.json"
- name: Restore firebase_options.dart
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
- name: Setup Firebase
run: |
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_APP_ID_FILE }}" "ios/firebase_app_id_file.json"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_JSON }}" "firebase.json"
- name: Restore constants.dart
env:
@ -107,6 +107,7 @@ jobs:
run: bash ./.github/scripts/increment_build_number.sh ${{ github.event.inputs.version }}
- name: Download release notes
continue-on-error: true
uses: actions/download-artifact@v3
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
@ -116,13 +117,14 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.13.9"
flutter-version: "3.27.1"
- name: Prepare flutter project
run: |
flutter --version
flutter pub get
flutter pub run intl_utils:generate
dart pub global activate flutterfire_cli
- name: Build .ipa
run: |

View file

@ -42,7 +42,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.13.9"
flutter-version: "3.27.1"
- name: Prepare flutter project
run: |

View file

@ -27,11 +27,12 @@ jobs:
bash .github/scripts/restore_from_base64.sh "${{ secrets.CONSTANTS }}" "lib/constants.dart"
bash .github/scripts/restore_from_base64.sh "${{ secrets.GOOGLE_SERVICES_JSON_IOS }}" "ios/Runner/GoogleService-Info.plist"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_APP_ID_FILE }}" "ios/firebase_app_id_file.json"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_JSON }}" "firebase.json"
- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.13.9"
flutter-version: "3.27.1"
- name: Prepare app
run: |
@ -39,6 +40,7 @@ jobs:
flutter pub get
flutter pub run intl_utils:generate
flutter analyze lib --fatal-infos
dart pub global activate flutterfire_cli
- name: Launch iOS simulator
uses: futureware-tech/simulator-action@v3

7
.gitignore vendored
View file

@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
@ -48,7 +50,6 @@ app.*.map.json
/ios/build/
/ios/Runner.xcodeproj/project.pbxproj
pubspec.lock
/ios/Podfile.lock
.fvm/
@ -63,4 +64,6 @@ ios/Runner/GoogleService-Info.plist
coverage/
test/coverage_helper_test.dart
**/failures/*.png
screenshots/generated/raw/
screenshots/generated/raw/
firebase.json

44
.vscode/settings.json vendored
View file

@ -1,25 +1,25 @@
{
"task.slowProviderWarning": true,
"dart.flutterSdkPath": "fvm",
"search.exclude": {
"**/.fvm": true
},
"files.watcherExclude": {
"**/.fvm": true
},
"dart.lineLength": 120,
"[dart]": {
"editor.rulers": [
120,
],
"editor.selectionHighlight": true,
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.suggestSelection": "first",
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": "off"
},
"dart.doNotFormat": [
"**/generated/**",
"lib/data/**"
"task.slowProviderWarning": true,
"dart.flutterSdkPath": ".fvm/flutter_sdk",
"search.exclude": {
"**/.fvm": true
},
"files.watcherExclude": {
"**/.fvm": true
},
"dart.lineLength": 120,
"[dart]": {
"editor.rulers": [
120
],
"editor.selectionHighlight": true,
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.suggestSelection": "first",
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": "off"
},
"dart.doNotFormat": [
"**/generated/**",
"lib/data/**"
]
}

View file

@ -34,7 +34,7 @@ Without further delay behold my new Lightmeter app inspired by Material You (a.k
### 1. Install Flutter
To build this app you need to install Flutter 3.13.9 stable. [How to install](https://docs.flutter.dev/get-started/install).
To build this app you need to install Flutter 3.27.1 stable. [How to install](https://docs.flutter.dev/get-started/install).
### 2. Project setup

View file

@ -1,3 +1,11 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id "dev.flutter.flutter-gradle-plugin"
id "com.google.gms.google-services"
id "com.google.firebase.crashlytics"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@ -6,11 +14,6 @@ if (localPropertiesFile.exists()) {
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@ -27,15 +30,9 @@ if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
apply plugin: 'com.android.application'
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"
android {
compileSdkVersion 34
ndkVersion flutter.ndkVersion
ndkVersion "27.0.12077973"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@ -43,7 +40,7 @@ android {
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = JavaVersion.VERSION_1_8
}
sourceSets {
@ -51,7 +48,7 @@ android {
}
defaultConfig {
minSdkVersion 21
minSdkVersion 23
targetSdkVersion 34
ndk {
debugSymbolLevel 'FULL'
@ -101,6 +98,7 @@ android {
}
}
}
namespace 'com.vodemn.lightmeter'
}
flutter {
@ -108,7 +106,6 @@ flutter {
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.android.billingclient:billing-ktx:6.0.0"
implementation "com.google.firebase:firebase-analytics:17.4.1"
}

View file

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.vodemn.lightmeter">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.

View file

@ -1,6 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.vodemn.lightmeter">
xmlns:tools="http://schemas.android.com/tools">
<application
android:label="@string/app_name"

View file

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.vodemn.lightmeter">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.

View file

@ -1,18 +1,3 @@
buildscript {
ext.kotlin_version = '1.8.21'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()

View file

@ -1,3 +1,6 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

View file

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

View file

@ -1,11 +1,27 @@
include ':app'
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.7.3' apply false
id "org.jetbrains.kotlin.android" version "1.8.21" apply false
id "com.google.gms.google-services" version "4.4.0" apply false
id "com.google.firebase.crashlytics" version "2.9.9" apply false
}
include ":app"

View file

@ -13,7 +13,7 @@ dependencies:
git:
url: "https://github.com/vodemn/m3_lightmeter_resources"
ref: v2.1.0
shared_preferences: 2.2.0
shared_preferences:
dev_dependencies:
flutter_test:

View file

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>

View file

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '12.0'
platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@ -36,6 +36,16 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == 'BoringSSL-GRPC'
target.source_build_phase.files.each do |file|
if file.settings && file.settings['COMPILER_FLAGS']
flags = file.settings['COMPILER_FLAGS'].split
flags.reject! { |flag| flag == '-GCC_WARN_INHIBIT_ALL_WARNINGS' }
file.settings['COMPILER_FLAGS'] = flags.join(' ')
end
end
end
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|

View file

@ -155,6 +155,8 @@
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
45F53C083F2EA48EF231DA16 /* [CP] Embed Pods Frameworks */,
FF00F85CE432774850A0EDB7 /* [firebase_crashlytics] Crashlytics Upload Symbols */,
08127035D2CDEEEBA66FCDBB /* [CP] Copy Pods Resources */,
6F6C086A620B15784F8BE312 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
);
buildRules = (
);
@ -171,7 +173,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@ -214,6 +216,23 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
08127035D2CDEEEBA66FCDBB /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
385E047940E442D45ED68E6E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -269,6 +288,24 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
6F6C086A620B15784F8BE312 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\"";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\n#!/bin/bash\nPATH=\"${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\"\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --platform=ios --apple-project-path=\"${SRCROOT}\" --env-platform-name=\"${PLATFORM_NAME}\" --env-configuration=\"${CONFIGURATION}\" --env-project-dir=\"${PROJECT_DIR}\" --env-built-products-dir=\"${BUILT_PRODUCTS_DIR}\" --env-dwarf-dsym-folder-path=\"${DWARF_DSYM_FOLDER_PATH}\" --env-dwarf-dsym-file-name=\"${DWARF_DSYM_FILE_NAME}\" --env-infoplist-path=\"${INFOPLIST_PATH}\" --default-config=default\n";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@ -292,11 +329,11 @@
inputFileListPaths = (
);
inputPaths = (
"\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\"",
"\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/\"",
"\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"",
"\"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)\"",
"\"$(PROJECT_DIR)/firebase_app_id_file.json\"",
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}",
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${PRODUCT_NAME}",
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist",
"$(BUILT_PRODUCTS_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/GoogleService-Info.plist",
"$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_PATH)",
);
name = "[firebase_crashlytics] Crashlytics Upload Symbols";
outputFileListPaths = (
@ -305,7 +342,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --flutter-project \"$PROJECT_DIR/firebase_app_id_file.json\" ";
shellScript = "\"${PODS_ROOT}/FirebaseCrashlytics/upload-symbols\" -gsp \"${PROJECT_DIR}/Runner/GoogleService-Info.plist\" -p ios \"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\"\n";
};
/* End PBXShellScriptBuildPhase section */
@ -382,7 +419,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -397,6 +434,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-prod";
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@ -466,7 +504,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -515,7 +553,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -532,6 +570,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-prod";
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@ -561,6 +600,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-prod";
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
@ -633,7 +673,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -647,6 +687,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-dev";
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@ -711,7 +752,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -728,6 +769,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-dev";
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@ -791,7 +833,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -806,6 +848,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-dev";
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;

View file

@ -1,7 +1,7 @@
import UIKit
import Flutter
@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,

View file

@ -44,7 +44,7 @@ class Application extends StatelessWidget {
],
supportedLocales: S.delegate.supportedLocales,
builder: (context, child) => MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling),
child: child!,
),
initialRoute: NavigationRoutes.meteringScreen.name,

View file

@ -22,7 +22,7 @@ class LightmeterAnalytics {
Future<void> logEvent(
String eventName, {
Map<String, dynamic>? parameters,
Map<String, Object>? parameters,
}) async {
if (!kReleaseMode) {
log('<LightmeterAnalytics> logEvent: $eventName / $parameters');

View file

@ -1,7 +1,7 @@
abstract class ILightmeterAnalyticsApi {
Future<void> logEvent(
String eventName, {
Map<String, dynamic>? parameters,
Map<String, Object>? parameters,
});
Future<void> logCrash(

View file

@ -10,7 +10,7 @@ class LightmeterAnalyticsFirebase implements ILightmeterAnalyticsApi {
@override
Future<void> logEvent(
String eventName, {
Map<String, dynamic>? parameters,
Map<String, Object>? parameters,
}) async {
try {
await FirebaseAnalytics.instance.logEvent(

View file

@ -8,7 +8,7 @@ typedef MeteringScreenLayoutConfig = Map<MeteringScreenLayoutFeature, bool>;
extension MeteringScreenLayoutConfigJson on MeteringScreenLayoutConfig {
static MeteringScreenLayoutConfig fromJson(Map<String, dynamic> data) {
int? migratedIndex(MeteringScreenLayoutFeature feature) {
int migratedIndex(MeteringScreenLayoutFeature feature) {
switch (feature) {
case MeteringScreenLayoutFeature.extremeExposurePairs:
return 0;
@ -16,8 +16,6 @@ extension MeteringScreenLayoutConfigJson on MeteringScreenLayoutConfig {
return 1;
case MeteringScreenLayoutFeature.equipmentProfiles:
return 3;
default:
return null;
}
}

View file

@ -7,6 +7,7 @@ import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
import 'package:lightmeter/data/models/supported_locale.dart';
import 'package:lightmeter/data/models/theme_type.dart';
import 'package:lightmeter/data/models/volume_action.dart';
import 'package:lightmeter/utils/color_to_int.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -155,7 +156,7 @@ class UserPreferencesService {
set themeType(ThemeType value) => _sharedPreferences.setInt(themeTypeKey, value.index);
Color get primaryColor => Color(_sharedPreferences.getInt(primaryColorKey) ?? 0xff2196f3);
set primaryColor(Color value) => _sharedPreferences.setInt(primaryColorKey, value.value);
set primaryColor(Color value) => _sharedPreferences.setInt(primaryColorKey, value.toInt());
bool get dynamicColor => _sharedPreferences.getBool(dynamicColorKey) ?? false;
set dynamicColor(bool value) => _sharedPreferences.setBool(dynamicColorKey, value);

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/utils/color_to_int.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
const primaryColorsList = [
@ -32,7 +33,7 @@ ThemeData themeFrom(Color primaryColor, Brightness brightness) {
elevation: Dimens.elevationLevel0,
scrolledUnderElevation: Dimens.elevationLevel2,
color: scheme.surface,
foregroundColor: scheme.onBackground,
foregroundColor: scheme.onSurface,
surfaceTintColor: scheme.surfaceTint,
),
cardTheme: CardTheme(
@ -75,15 +76,13 @@ ThemeData themeFrom(Color primaryColor, Brightness brightness) {
ColorScheme _colorSchemeFromColor(Color primaryColor, Brightness brightness) {
final scheme = SchemeTonalSpot(
sourceColorHct: Hct.fromInt(primaryColor.value),
sourceColorHct: Hct.fromInt(primaryColor.toInt()),
isDark: brightness == Brightness.dark,
contrastLevel: 0.0,
);
return ColorScheme(
brightness: brightness,
background: Color(scheme.background),
onBackground: Color(scheme.onBackground),
error: Color(scheme.error),
onError: Color(scheme.onError),
errorContainer: Color(scheme.errorContainer),
@ -102,7 +101,7 @@ ColorScheme _colorSchemeFromColor(Color primaryColor, Brightness brightness) {
onTertiaryContainer: Color(scheme.onTertiaryContainer),
surface: Color(scheme.surface),
onSurface: Color(scheme.onSurface),
surfaceVariant: Color(scheme.surfaceVariant),
surfaceContainerHighest: Color(scheme.surfaceContainerHighest),
onSurfaceVariant: Color(scheme.onSurfaceVariant),
outline: Color(scheme.outline),
outlineVariant: Color(scheme.outlineVariant),

View file

@ -91,7 +91,6 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
case communication_states.SettingsClosedState():
_settingsOpened = false;
add(const InitializeEvent());
default:
}
}

View file

@ -25,7 +25,7 @@ class CameraControlsPlaceholder extends StatelessWidget {
const SizedBox(height: Dimens.grid8),
Text(
error.toStringLocalized(context),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Theme.of(context).colorScheme.onBackground),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Theme.of(context).colorScheme.onSurface),
textAlign: TextAlign.center,
),
],

View file

@ -62,14 +62,14 @@ class _Title<T extends PhotographyStopValue> extends StatelessWidget {
value.toString(),
stepGranularity: 0.5,
minFontSize: 10,
style: labelTextStyle(context).copyWith(color: Theme.of(context).colorScheme.onBackground),
style: labelTextStyle(context).copyWith(color: Theme.of(context).colorScheme.onSurface),
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,
)
: Text(
value.toString(),
style: labelTextStyle(context).copyWith(color: Theme.of(context).colorScheme.onBackground),
style: labelTextStyle(context).copyWith(color: Theme.of(context).colorScheme.onSurface),
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,
@ -97,7 +97,7 @@ class _Tick extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ColoredBox(
color: Theme.of(context).colorScheme.onBackground,
color: Theme.of(context).colorScheme.onSurface,
child: SizedBox(
height: 1,
width: _length,

View file

@ -78,7 +78,7 @@ class ExposurePairsList extends StatelessWidget {
? constraints.maxHeight / 2
: constraints.maxHeight,
child: ColoredBox(
color: Theme.of(context).colorScheme.onBackground,
color: Theme.of(context).colorScheme.onSurface,
child: const SizedBox(width: 1),
),
),

View file

@ -178,8 +178,8 @@ class AnimatedDialogState extends State<AnimatedDialog> with SingleTickerProvide
opaque: false,
transitionDuration: Duration.zero,
reverseTransitionDuration: Duration.zero,
pageBuilder: (_, __, ___) => WillPopScope(
onWillPop: () => _animateReverse().then((value) => true),
pageBuilder: (_, __, ___) => PopScope(
onPopInvokedWithResult: (_, __) => _animateReverse().then((value) => true),
child: _AnimatedOverlay(
controller: _animationController,
barrierColorAnimation: _barrierColorAnimation,
@ -221,7 +221,13 @@ class AnimatedDialogState extends State<AnimatedDialog> with SingleTickerProvide
});
}
Future<void> close() => _animateReverse().then((_) => Navigator.of(context).pop());
Future<void> close() {
return _animateReverse().then((_) {
if (mounted) {
Navigator.of(context).pop();
}
});
}
}
class _AnimatedOverlay extends StatelessWidget {

View file

@ -69,9 +69,14 @@ class MeteringScreen extends StatelessWidget {
}
void pushNamed(BuildContext context, String routeName, {Object? arguments}) {
context.read<MeteringBloc>().add(const ScreenOnTopOpenedEvent());
Navigator.pushNamed(context, routeName, arguments: arguments).then((_) {
context.read<MeteringBloc>().add(const ScreenOnTopClosedEvent());
final bloc = context.read<MeteringBloc>();
bloc.add(const ScreenOnTopOpenedEvent());
Navigator.pushNamed(
context,
routeName,
arguments: arguments,
).then((_) {
bloc.add(const ScreenOnTopClosedEvent());
});
}
}

View file

@ -4,9 +4,14 @@ import 'package:lightmeter/constants.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:url_launcher/url_launcher.dart';
class WriteEmailListTile extends StatelessWidget {
class WriteEmailListTile extends StatefulWidget {
const WriteEmailListTile({super.key});
@override
State<WriteEmailListTile> createState() => _WriteEmailListTileState();
}
class _WriteEmailListTileState extends State<WriteEmailListTile> {
@override
Widget build(BuildContext context) {
return ListTile(
@ -20,24 +25,30 @@ class WriteEmailListTile extends StatelessWidget {
mailToUrl,
mode: LaunchMode.externalApplication,
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(S.of(context).youDontHaveMailApp),
behavior: SnackBarBehavior.floating,
action: SnackBarAction(
label: S.of(context).copyEmail,
onPressed: () {
FlutterClipboard.copy(contactEmail).then((_) {
ScaffoldMessenger.of(context).clearSnackBars();
});
},
),
),
);
} else if (mounted) {
_showSnackBar();
}
});
},
);
}
Future<void> _showSnackBar() async {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(S.of(context).youDontHaveMailApp),
behavior: SnackBarBehavior.floating,
action: SnackBarAction(
label: S.of(context).copyEmail,
onPressed: () {
FlutterClipboard.copy(contactEmail).then((_) {
if (mounted) {
ScaffoldMessenger.of(context).clearSnackBars();
}
});
},
),
),
);
}
}

View file

@ -14,6 +14,7 @@ class LanguageListTile extends StatelessWidget {
title: Text(S.of(context).language),
trailing: Text(UserPreferencesProvider.localeOf(context).localizedName),
onTap: () {
final prefs = UserPreferencesProvider.of(context);
showDialog<SupportedLocale>(
context: context,
builder: (_) => DialogPicker<SupportedLocale>(
@ -21,11 +22,11 @@ class LanguageListTile extends StatelessWidget {
title: S.of(context).chooseLanguage,
selectedValue: UserPreferencesProvider.localeOf(context),
values: SupportedLocale.values,
titleAdapter: (context, value) => value.localizedName,
titleAdapter: (_, value) => value.localizedName,
),
).then((value) {
if (value != null) {
UserPreferencesProvider.of(context).setLocale(value);
prefs.setLocale(value);
}
});
},

View file

@ -14,6 +14,7 @@ class StopTypeListTile extends StatelessWidget {
title: Text(S.of(context).fractionalStops),
trailing: Text(_typeToString(context, UserPreferencesProvider.stopTypeOf(context))),
onTap: () {
final prefs = UserPreferencesProvider.of(context);
showDialog<StopType>(
context: context,
builder: (_) => DialogPicker<StopType>(
@ -25,7 +26,7 @@ class StopTypeListTile extends StatelessWidget {
),
).then((value) {
if (value != null) {
UserPreferencesProvider.of(context).setStopType(value);
prefs.setStopType(value);
}
});
},

View file

@ -1,56 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart';
class ExpandableSectionNameDialog extends StatefulWidget {
final String title;
final String hint;
final String initialValue;
const ExpandableSectionNameDialog({
this.initialValue = '',
required this.title,
required this.hint,
super.key,
});
@override
State<ExpandableSectionNameDialog> createState() => _ExpandableSectionNameDialogState();
}
class _ExpandableSectionNameDialogState extends State<ExpandableSectionNameDialog> {
late final _nameController = TextEditingController(text: widget.initialValue);
@override
void dispose() {
_nameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
icon: const Icon(Icons.edit_outlined),
titlePadding: Dimens.dialogIconTitlePadding,
title: Text(widget.title),
content: TextField(
autofocus: true,
controller: _nameController,
decoration: InputDecoration(hintText: widget.hint),
),
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: Text(S.of(context).cancel),
),
ValueListenableBuilder(
valueListenable: _nameController,
builder: (_, value, __) => TextButton(
onPressed: value.text.isNotEmpty ? () => Navigator.of(context).pop(value.text) : null,
child: Text(S.of(context).save),
),
),
],
);
}
}

View file

@ -1,184 +0,0 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart';
class ExpandableSectionListItem extends StatefulWidget {
final String title;
final VoidCallback onTitleTap;
final VoidCallback onExpand;
final List<IconButton> actions;
final List<Widget> children;
const ExpandableSectionListItem({
required this.title,
required this.onTitleTap,
required this.onExpand,
required this.actions,
required this.children,
super.key,
});
static ExpandableSectionListItemState of(BuildContext context) {
return context.findAncestorStateOfType<ExpandableSectionListItemState>()!;
}
@override
State<ExpandableSectionListItem> createState() => ExpandableSectionListItemState();
}
class ExpandableSectionListItemState extends State<ExpandableSectionListItem> with TickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
duration: Dimens.durationM,
vsync: this,
);
bool get _expanded => _controller.isCompleted;
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
title: Row(
children: [
_AnimatedNameLeading(controller: _controller),
const SizedBox(width: Dimens.grid8),
Flexible(
child: Text(
widget.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
trailing: _AnimatedArrowButton(
controller: _controller,
onPressed: () => _expanded ? collapse() : expand(),
),
onTap: () => _expanded ? widget.onTitleTap() : expand(),
),
_AnimatedContent(
controller: _controller,
actions: widget.actions,
children: widget.children,
),
],
),
),
);
}
void expand() {
widget.onExpand();
_controller.forward();
SchedulerBinding.instance.addPostFrameCallback((_) {
Future.delayed(_controller.duration!).then((_) {
Scrollable.ensureVisible(
context,
alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd,
duration: _controller.duration!,
);
});
});
}
void collapse() {
_controller.reverse();
}
}
class _AnimatedNameLeading extends AnimatedWidget {
const _AnimatedNameLeading({required AnimationController controller}) : super(listenable: controller);
Animation<double> get _progress => listenable as Animation<double>;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(right: _progress.value * Dimens.grid8),
child: Icon(
Icons.edit_outlined,
size: _progress.value * Dimens.grid24,
),
);
}
}
class _AnimatedArrowButton extends AnimatedWidget {
final VoidCallback onPressed;
const _AnimatedArrowButton({
required AnimationController controller,
required this.onPressed,
}) : super(listenable: controller);
Animation<double> get _progress => listenable as Animation<double>;
@override
Widget build(BuildContext context) {
return IconButton(
onPressed: onPressed,
icon: Transform.rotate(
angle: _progress.value * pi,
child: const Icon(Icons.keyboard_arrow_down_outlined),
),
tooltip: _progress.value == 0 ? S.of(context).tooltipExpand : S.of(context).tooltipCollapse,
);
}
}
class _AnimatedContent extends AnimatedWidget {
final List<IconButton> actions;
final List<Widget> children;
const _AnimatedContent({
required AnimationController controller,
required this.actions,
required this.children,
}) : super(listenable: controller);
Animation<double> get _progress => listenable as Animation<double>;
@override
Widget build(BuildContext context) {
return SizedOverflowBox(
alignment: Alignment.topCenter,
size: Size(
double.maxFinite,
_progress.value * Dimens.grid56 * (children.length + 1),
),
// https://github.com/gskinnerTeam/flutter-folio/pull/62
child: Opacity(
opacity: _progress.value,
child: Column(
children: [
...children,
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
trailing: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: actions,
),
),
],
),
),
);
}
}

View file

@ -1,95 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/settings/components/shared/expandable_section_list/components/expandable_section_list_item/widget_expandable_section_list_item.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
typedef _WidgetBuilder<W, T extends Identifiable> = W Function(BuildContext context, T value);
class ExpandableSectionList<T extends Identifiable> extends StatefulWidget {
final List<T> values;
final VoidCallback onSectionTitleTap;
final _WidgetBuilder<List<Widget>, T> contentBuilder;
final _WidgetBuilder<List<IconButton>, T> actionsBuilder;
const ExpandableSectionList({
required this.values,
required this.onSectionTitleTap,
required this.contentBuilder,
required this.actionsBuilder,
super.key,
});
@override
State<ExpandableSectionList> createState() => _ExpandableSectionListState<T>();
}
class _ExpandableSectionListState<T extends Identifiable> extends State<ExpandableSectionList<T>> {
final Map<String, GlobalKey<ExpandableSectionListItemState>> keysMap = {};
@override
void didChangeDependencies() {
super.didChangeDependencies();
_updateProfilesKeys();
}
@override
Widget build(BuildContext context) {
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final item = widget.values[index];
return Padding(
padding: EdgeInsets.fromLTRB(
Dimens.paddingM,
index == 0 ? Dimens.paddingM : 0,
Dimens.paddingM,
Dimens.paddingM,
),
child: ExpandableSectionListItem(
key: keysMap[item.id],
title: item.name,
onTitleTap: widget.onSectionTitleTap,
onExpand: () => _keepExpandedAt(index),
actions: widget.actionsBuilder(context, item),
children: widget.contentBuilder(context, item),
),
);
},
childCount: widget.values.length,
),
);
}
void _keepExpandedAt(int index) {
keysMap.values.toList().getRange(0, index).forEach((element) {
element.currentState?.collapse();
});
keysMap.values.toList().getRange(index + 1, keysMap.length).forEach((element) {
element.currentState?.collapse();
});
}
void _updateProfilesKeys() {
if (widget.values.length > keysMap.length) {
// item added
final List<String> idsToAdd = [];
for (final item in widget.values) {
if (!keysMap.keys.contains(item.id)) idsToAdd.add(item.id);
}
for (final id in idsToAdd) {
keysMap[id] = GlobalKey<ExpandableSectionListItemState>(debugLabel: id);
}
idsToAdd.clear();
} else if (widget.values.length < keysMap.length) {
// item deleted
final List<String> idsToDelete = [];
for (final id in keysMap.keys) {
if (!widget.values.any((p) => p.id == id)) idsToDelete.add(id);
}
idsToDelete.forEach(keysMap.remove);
idsToDelete.clear();
} else {
// item updated, no need to updated keys
}
}
}

View file

@ -3,6 +3,7 @@ import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/res/theme.dart';
import 'package:lightmeter/screens/shared/filled_circle/widget_circle_filled.dart';
import 'package:lightmeter/utils/color_to_int.dart';
class PrimaryColorDialogPicker extends StatefulWidget {
const PrimaryColorDialogPicker({super.key});
@ -45,7 +46,7 @@ class _PrimaryColorDialogPickerState extends State<PrimaryColorDialogPicker> {
padding: EdgeInsets.only(left: index == 0 ? 0 : Dimens.paddingS),
child: _SelectableColorItem(
color: color,
selected: color.value == _selected.value,
selected: color.toInt() == _selected.toInt(),
onTap: () {
setState(() {
_selected = color;

View file

@ -22,12 +22,13 @@ class PrimaryColorListTile extends StatelessWidget {
leading: const Icon(Icons.palette_outlined),
title: Text(S.of(context).primaryColor),
onTap: () {
final prefs = UserPreferencesProvider.of(context);
showDialog<Color>(
context: context,
builder: (_) => const PrimaryColorDialogPicker(),
).then((value) {
if (value != null) {
UserPreferencesProvider.of(context).setPrimaryColor(value);
prefs.setPrimaryColor(value);
}
});
},

View file

@ -14,6 +14,7 @@ class ThemeTypeListTile extends StatelessWidget {
title: Text(S.of(context).theme),
trailing: Text(_typeToString(context, UserPreferencesProvider.themeTypeOf(context))),
onTap: () {
final prefs = UserPreferencesProvider.of(context);
showDialog<ThemeType>(
context: context,
builder: (_) => DialogPicker<ThemeType>(
@ -25,7 +26,7 @@ class ThemeTypeListTile extends StatelessWidget {
),
).then((value) {
if (value != null) {
UserPreferencesProvider.of(context).setThemeType(value);
prefs.setThemeType(value);
}
});
},

View file

@ -120,7 +120,7 @@ class _Slider extends StatelessWidget {
width: handleDistance + trackThickness,
child: ClipRRect(
borderRadius: BorderRadius.circular(trackThickness / 2),
child: ColoredBox(color: Theme.of(context).colorScheme.surfaceVariant),
child: ColoredBox(color: Theme.of(context).colorScheme.surfaceContainerHighest),
),
),
AnimatedPositioned.fromRect(

View file

@ -20,15 +20,12 @@ class IconPlaceholder extends StatelessWidget {
children: [
Icon(
icon,
color: Theme.of(context).colorScheme.onBackground,
color: Theme.of(context).colorScheme.onSurface,
),
const SizedBox(height: Dimens.grid8),
Text(
text,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: Theme.of(context).colorScheme.onBackground),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Theme.of(context).colorScheme.onSurface),
textAlign: TextAlign.center,
),
],

View file

@ -21,10 +21,11 @@ class ReleaseNotesFlow extends StatelessWidget {
child: BlocListener<ReleaseNotesBloc, ReleaseNotesState>(
listener: (context, state) {
if (state is ShowReleaseNotesDialogState) {
final bloc = context.read<ReleaseNotesBloc>();
showDialog(
context: context,
builder: (_) => ReleaseNotesDialog(version: state.version),
).then((_) => context.read<ReleaseNotesBloc>().setChangelogVersion());
).then((_) => bloc.setChangelogVersion());
}
},
child: child,

View file

@ -30,7 +30,7 @@ class RulerSlider extends StatelessWidget {
children: [
Text(
valueAdapter(value),
style: Theme.of(context).textTheme.labelLarge!.copyWith(color: Theme.of(context).colorScheme.onBackground),
style: Theme.of(context).textTheme.labelLarge!.copyWith(color: Theme.of(context).colorScheme.onSurface),
),
const SizedBox(height: Dimens.grid4),
Expanded(
@ -78,7 +78,7 @@ class _Ruler extends StatelessWidget {
@override
Widget build(BuildContext context) {
final mainTicksFontSize = Theme.of(context).textTheme.bodySmall!.fontSize!;
final itemsColor = Theme.of(context).colorScheme.onBackground;
final itemsColor = Theme.of(context).colorScheme.onSurface;
return LayoutBuilder(
builder: (context, constraints) {
final bool showAllMainTicks =

View file

@ -0,0 +1,12 @@
import 'dart:ui';
extension ColorToInt on Color {
int toInt() {
final a = (this.a * 255).round();
final r = (this.r * 255).round();
final g = (this.g * 255).round();
final b = (this.b * 255).round();
return (a << 24) | (r << 16) | (g << 8) | b;
}
}

1565
pubspec.lock Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,61 +7,61 @@ environment:
sdk: ">=3.0.0 <4.0.0"
dependencies:
app_settings: 4.2.0
app_settings: 5.1.1
auto_size_text: 3.0.0
bloc_concurrency: 0.2.2
camera: 0.10.5+2
camera_android_camerax: 0.6.1+1
bloc_concurrency: 0.3.0
camera: 0.11.0+2
clipboard: 0.1.3
collection: any
dynamic_color: 1.7.0
exif: 3.1.4
firebase_analytics: 10.6.2
firebase_core: 2.20.0
firebase_crashlytics: 3.4.2
firebase_remote_config: 4.3.2
exif: 3.3.0
firebase_analytics: 11.4.0
firebase_core: 3.10.0
firebase_crashlytics: 4.3.0
firebase_remote_config: 5.3.0
flutter:
sdk: flutter
flutter_bloc: 8.1.3
flutter_bloc: 9.0.0
flutter_localizations:
sdk: flutter
flutter_native_splash: 2.3.5
intl: 0.18.1
intl_utils: 2.8.2
light_sensor: 3.0.0
flutter_native_splash: 2.4.4
intl: 0.19.0
intl_utils: 2.8.7
light_sensor: 3.0.1
m3_lightmeter_iap:
git:
url: "https://github.com/vodemn/m3_lightmeter_iap"
ref: v2.1.0
ref: v2.1.1
m3_lightmeter_resources:
git:
url: "https://github.com/vodemn/m3_lightmeter_resources"
ref: v2.1.0
material_color_utilities: 0.5.0
package_info_plus: 4.2.0
permission_handler: 10.4.3
platform: 3.1.0
shared_preferences: 2.2.0
url_launcher: 6.1.12
uuid: 3.0.7
vibration: 1.8.1
material_color_utilities: 0.12.0
package_info_plus: 8.1.3
permission_handler: 11.3.1
platform: 3.1.5
shared_preferences: 2.3.5
url_launcher:
url_launcher_ios: 6.3.2
uuid: 4.5.1
vibration: 2.0.1
dev_dependencies:
args: 2.5.0
bloc_test: 9.1.3
build_runner: 2.4.6
args: 2.6.0
bloc_test: 10.0.0
build_runner: 2.4.14
flutter_test:
sdk: flutter
golden_toolkit: 0.15.0
google_fonts: 3.0.1
image: 4.1.7
google_fonts: 6.2.1
image: 4.3.0
integration_test:
sdk: flutter
lint: 2.1.2
logging: 1.2.0
meta: 1.9.1
mocktail: 0.3.0
test: 1.24.3
lint: 2.3.0
logging: 1.3.0
meta: 1.15.0
mocktail: 1.0.4
test: 1.25.8
dependency_overrides:
material_color_utilities: 0.11.1

View file

@ -22,6 +22,7 @@ import 'package:lightmeter/screens/metering/screen_metering.dart';
import 'package:lightmeter/screens/settings/screen_settings.dart';
import 'package:lightmeter/screens/shared/animated_circular_button/widget_button_circular_animated.dart';
import 'package:lightmeter/screens/timer/screen_timer.dart';
import 'package:lightmeter/utils/color_to_int.dart';
import 'package:lightmeter/utils/platform_utils.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -77,7 +78,7 @@ void main() {
/// Theme settings
UserPreferencesService.themeTypeKey: theme.index,
UserPreferencesService.primaryColorKey: color.value,
UserPreferencesService.primaryColorKey: color.toInt(),
UserPreferencesService.dynamicColorKey: false,
UserPreferencesService.seenChangelogVersionKey: await const PlatformUtils().version,
@ -190,7 +191,7 @@ extension on WidgetTester {
name: name,
deviceName: const String.fromEnvironment('deviceName'),
platformFolder: _platformFolder,
backgroundColor: backgroundColor.value.toRadixString(16),
backgroundColor: backgroundColor.toInt().toRadixString(16),
isDark: theme.brightness == Brightness.dark,
).toString(),
);

View file

@ -47,7 +47,7 @@ class WidgetTestApplicationMock extends StatelessWidget {
],
supportedLocales: S.delegate.supportedLocales,
builder: (context, child) => MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling),
child: child!,
),
home: Scaffold(body: child),
@ -124,7 +124,7 @@ class _GoldenTestApplicationMockState extends State<GoldenTestApplicationMock> {
],
supportedLocales: S.delegate.supportedLocales,
builder: (context, child) => MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling),
child: child!,
),
home: widget.child,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 858 KiB

After

Width:  |  Height:  |  Size: 863 KiB