mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2025-08-17 10:36:41 +00:00
Merge 82669bc5e5
into 056cf4f44b
This commit is contained in:
commit
bce487a788
30 changed files with 266 additions and 277 deletions
|
@ -10,7 +10,7 @@ import 'package:lightmeter/screens/metering/components/shared/readings_container
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/lightmeter_pro/widget_lightmeter_pro.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/lightmeter_pro_badge/widget_badge_lightmeter_pro.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/disable/widget_disable.dart';
|
import 'package:lightmeter/screens/settings/components/shared/disable/widget_disable.dart';
|
||||||
import 'package:lightmeter/screens/settings/screen_settings.dart';
|
import 'package:lightmeter/screens/settings/screen_settings.dart';
|
||||||
|
@ -77,7 +77,7 @@ void testPurchases(String description) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _expectProMeteringScreen({required bool enabled}) {
|
void _expectProMeteringScreen({required bool enabled}) {
|
||||||
expect(find.byType(LightmeterProAnimatedDialog), !enabled ? findsOneWidget : findsNothing);
|
expect(find.byType(LightmeterProBadge), !enabled ? findsOneWidget : findsNothing);
|
||||||
expect(find.byType(EquipmentProfilePicker), enabled ? findsOneWidget : findsNothing);
|
expect(find.byType(EquipmentProfilePicker), enabled ? findsOneWidget : findsNothing);
|
||||||
expect(find.byType(ExtremeExposurePairsContainer), findsOneWidget);
|
expect(find.byType(ExtremeExposurePairsContainer), findsOneWidget);
|
||||||
expect(find.byType(FilmPicker), enabled ? findsOneWidget : findsNothing);
|
expect(find.byType(FilmPicker), enabled ? findsOneWidget : findsNothing);
|
||||||
|
|
|
@ -26,7 +26,7 @@ Future<void> runLightmeterApp(Environment env) async {
|
||||||
runApp(
|
runApp(
|
||||||
env.buildType == BuildType.dev
|
env.buildType == BuildType.dev
|
||||||
? IAPProducts(
|
? IAPProducts(
|
||||||
isPro: true,
|
isPro: false,
|
||||||
child: application,
|
child: application,
|
||||||
)
|
)
|
||||||
: IAPProductsProvider(child: application),
|
: IAPProductsProvider(child: application),
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/equipment_profile_edit/flow_equipment_profile_edit.dart';
|
import 'package:lightmeter/screens/equipment_profile_edit/flow_equipment_profile_edit.dart';
|
||||||
import 'package:lightmeter/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart';
|
import 'package:lightmeter/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart';
|
||||||
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
|
import 'package:lightmeter/utils/guard_pro_tap.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class EquipmentProfilesScreen extends StatefulWidget {
|
class EquipmentProfilesScreen extends StatefulWidget {
|
||||||
|
@ -41,9 +42,14 @@ class _EquipmentProfilesScreenState extends State<EquipmentProfilesScreen> with
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addProfile() {
|
void _addProfile() {
|
||||||
Navigator.of(context).pushNamed(
|
guardProTap(
|
||||||
NavigationRoutes.equipmentProfileEditScreen.name,
|
context,
|
||||||
arguments: const EquipmentProfileEditArgs(editType: EquipmentProfileEditType.add),
|
() {
|
||||||
|
Navigator.of(context).pushNamed(
|
||||||
|
NavigationRoutes.equipmentProfileEditScreen.name,
|
||||||
|
arguments: const EquipmentProfileEditArgs(editType: EquipmentProfileEditType.add),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,24 +107,18 @@ class _Products extends StatelessWidget {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (monthly case final monthly?)
|
if (monthly case final monthly?)
|
||||||
Padding(
|
_ProductItem(
|
||||||
padding: const EdgeInsets.only(bottom: Dimens.paddingS),
|
title: S.of(context).monthly,
|
||||||
child: _ProductItem(
|
price: S.of(context).pricePerMonth(monthly.price),
|
||||||
title: S.of(context).monthly,
|
isSelected: selected == monthly,
|
||||||
price: S.of(context).pricePerMonth(monthly.price),
|
onPressed: () => onProductSelected(monthly),
|
||||||
isSelected: selected == monthly,
|
|
||||||
onPressed: () => onProductSelected(monthly),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (yearly case final yearly?)
|
if (yearly case final yearly?)
|
||||||
Padding(
|
_ProductItem(
|
||||||
padding: const EdgeInsets.only(bottom: Dimens.paddingS),
|
title: S.of(context).yearly,
|
||||||
child: _ProductItem(
|
price: S.of(context).pricePerYear(yearly.price),
|
||||||
title: S.of(context).yearly,
|
isSelected: selected == yearly,
|
||||||
price: S.of(context).pricePerYear(yearly.price),
|
onPressed: () => onProductSelected(yearly),
|
||||||
isSelected: selected == yearly,
|
|
||||||
onPressed: () => onProductSelected(yearly),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (lifetime case final lifetime?)
|
if (lifetime case final lifetime?)
|
||||||
_ProductItem(
|
_ProductItem(
|
||||||
|
@ -133,7 +127,7 @@ class _Products extends StatelessWidget {
|
||||||
isSelected: selected == lifetime,
|
isSelected: selected == lifetime,
|
||||||
onPressed: () => onProductSelected(lifetime),
|
onPressed: () => onProductSelected(lifetime),
|
||||||
),
|
),
|
||||||
],
|
].intersperse(const SizedBox(height: Dimens.grid8)).toList(growable: false),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,3 +214,16 @@ class _ProductAnimatedText extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension on List<Widget> {
|
||||||
|
Iterable<Widget> intersperse(Widget element) sync* {
|
||||||
|
final iterator = this.iterator;
|
||||||
|
if (iterator.moveNext()) {
|
||||||
|
yield iterator.current;
|
||||||
|
while (iterator.moveNext()) {
|
||||||
|
yield element;
|
||||||
|
yield iterator.current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -256,10 +256,7 @@ class _FeatureHighlight extends StatelessWidget {
|
||||||
).width +
|
).width +
|
||||||
Dimens.paddingM * 2,
|
Dimens.paddingM * 2,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingS),
|
||||||
horizontal: Dimens.paddingM,
|
|
||||||
vertical: Dimens.paddingS,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: highlight ? Theme.of(context).colorScheme.secondaryContainer : null,
|
color: highlight ? Theme.of(context).colorScheme.secondaryContainer : null,
|
||||||
borderRadius: roundedTop
|
borderRadius: roundedTop
|
||||||
|
@ -274,7 +271,7 @@ class _FeatureHighlight extends StatelessWidget {
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
child: child,
|
child: Center(child: child),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/logbook_photos/components/grid_tile/widget_grid_tile_logbook_photo.dart';
|
import 'package:lightmeter/screens/logbook_photos/components/grid_tile/widget_grid_tile_logbook_photo.dart';
|
||||||
import 'package:lightmeter/screens/shared/icon_placeholder/widget_icon_placeholder.dart';
|
import 'package:lightmeter/screens/shared/icon_placeholder/widget_icon_placeholder.dart';
|
||||||
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
|
import 'package:lightmeter/utils/context_utils.dart';
|
||||||
|
import 'package:lightmeter/utils/guard_pro_tap.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class LogbookPhotosScreen extends StatefulWidget {
|
class LogbookPhotosScreen extends StatefulWidget {
|
||||||
|
@ -30,8 +32,15 @@ class _LogbookPhotosScreenState extends State<LogbookPhotosScreen> with SingleTi
|
||||||
child: SwitchListTile(
|
child: SwitchListTile(
|
||||||
secondary: const Icon(Icons.book_outlined),
|
secondary: const Icon(Icons.book_outlined),
|
||||||
title: Text(S.of(context).saveNewPhotos),
|
title: Text(S.of(context).saveNewPhotos),
|
||||||
value: LogbookPhotos.isEnabledOf(context),
|
value: LogbookPhotos.isEnabledOf(context) && context.isPro,
|
||||||
onChanged: LogbookPhotosProvider.of(context).saveLogbookPhotos,
|
onChanged: (value) {
|
||||||
|
guardProTap(
|
||||||
|
context,
|
||||||
|
() {
|
||||||
|
Navigator.of(context).pushNamed(NavigationRoutes.proFeaturesScreen.name);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||||
import 'package:lightmeter/screens/shared/animated_circular_button/widget_button_circular_animated.dart';
|
import 'package:lightmeter/screens/shared/animated_circular_button/widget_button_circular_animated.dart';
|
||||||
import 'package:lightmeter/screens/shared/bottom_controls_bar/widget_bottom_controls_bar.dart';
|
import 'package:lightmeter/screens/shared/bottom_controls_bar/widget_bottom_controls_bar.dart';
|
||||||
import 'package:lightmeter/utils/context_utils.dart';
|
|
||||||
|
|
||||||
class MeteringBottomControls extends StatelessWidget {
|
class MeteringBottomControls extends StatelessWidget {
|
||||||
final double? ev;
|
final double? ev;
|
||||||
|
@ -76,7 +75,7 @@ class _EvValueText extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
String _text(BuildContext context) {
|
String _text(BuildContext context) {
|
||||||
final bool showEv100 = context.isPro && UserPreferencesProvider.showEv100Of(context);
|
final bool showEv100 = UserPreferencesProvider.showEv100Of(context);
|
||||||
final StringBuffer buffer = StringBuffer()
|
final StringBuffer buffer = StringBuffer()
|
||||||
..writeAll([
|
..writeAll([
|
||||||
(showEv100 ? ev100 : ev).toStringAsFixed(1),
|
(showEv100 ? ev100 : ev).toStringAsFixed(1),
|
||||||
|
|
|
@ -17,6 +17,7 @@ import 'package:lightmeter/screens/metering/components/camera_container/models/c
|
||||||
import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart';
|
import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
|
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart';
|
import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/lightmeter_pro_badge/widget_badge_lightmeter_pro.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/widget_container_readings.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/widget_container_readings.dart';
|
||||||
import 'package:lightmeter/utils/context_utils.dart';
|
import 'package:lightmeter/utils/context_utils.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
@ -112,20 +113,17 @@ class CameraContainer extends StatelessWidget {
|
||||||
|
|
||||||
double _meteringContainerHeight(BuildContext context) {
|
double _meteringContainerHeight(BuildContext context) {
|
||||||
double enabledFeaturesHeight = 0;
|
double enabledFeaturesHeight = 0;
|
||||||
if (!context.isPro) {
|
if (!context.isPro && RemoteConfig.isEnabled(context, Feature.showUnlockProOnMainScreen)) {
|
||||||
if (RemoteConfig.isEnabled(context, Feature.showUnlockProOnMainScreen)) {
|
enabledFeaturesHeight += LightmeterProBadge.height(context);
|
||||||
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
|
enabledFeaturesHeight += Dimens.paddingS;
|
||||||
enabledFeaturesHeight += Dimens.paddingS;
|
}
|
||||||
}
|
if (context.meteringFeature(MeteringScreenLayoutFeature.equipmentProfiles)) {
|
||||||
} else {
|
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
|
||||||
if (context.meteringFeature(MeteringScreenLayoutFeature.equipmentProfiles)) {
|
enabledFeaturesHeight += Dimens.paddingS;
|
||||||
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
|
}
|
||||||
enabledFeaturesHeight += Dimens.paddingS;
|
if (context.meteringFeature(MeteringScreenLayoutFeature.filmPicker)) {
|
||||||
}
|
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
|
||||||
if (context.meteringFeature(MeteringScreenLayoutFeature.filmPicker)) {
|
enabledFeaturesHeight += Dimens.paddingS;
|
||||||
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
|
|
||||||
enabledFeaturesHeight += Dimens.paddingS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (context.meteringFeature(MeteringScreenLayoutFeature.extremeExposurePairs)) {
|
if (context.meteringFeature(MeteringScreenLayoutFeature.extremeExposurePairs)) {
|
||||||
enabledFeaturesHeight += Dimens.readingContainerDoubleValueHeight;
|
enabledFeaturesHeight += Dimens.readingContainerDoubleValueHeight;
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
|
||||||
import 'package:lightmeter/navigation/routes.dart';
|
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
|
|
||||||
|
|
||||||
class LightmeterProAnimatedDialog extends StatelessWidget {
|
|
||||||
const LightmeterProAnimatedDialog({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).pushNamed(NavigationRoutes.proFeaturesScreen.name);
|
|
||||||
},
|
|
||||||
child: ReadingValueContainer(
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
textColor: Theme.of(context).colorScheme.onSecondary,
|
|
||||||
values: [
|
|
||||||
ReadingValue(
|
|
||||||
label: S.of(context).proFeaturesTitle,
|
|
||||||
value: S.of(context).getPro,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/navigation/routes.dart';
|
||||||
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
import 'package:lightmeter/utils/text_height.dart';
|
||||||
|
|
||||||
|
class LightmeterProBadge extends StatelessWidget {
|
||||||
|
const LightmeterProBadge({super.key});
|
||||||
|
|
||||||
|
static double height(BuildContext context) {
|
||||||
|
if (Theme.of(context).textTheme.titleMedium?.lineHeight case final lineHeight?) {
|
||||||
|
return Dimens.paddingS * 2 + lineHeight;
|
||||||
|
} else {
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pushNamed(NavigationRoutes.proFeaturesScreen.name);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(Dimens.borderRadiusM),
|
||||||
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: Dimens.paddingM,
|
||||||
|
vertical: Dimens.paddingS,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
S.of(context).getPro,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium
|
||||||
|
?.copyWith(color: Theme.of(context).colorScheme.onSecondaryContainer),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ import 'package:lightmeter/screens/metering/components/shared/readings_container
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/lightmeter_pro/widget_lightmeter_pro.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/lightmeter_pro_badge/widget_badge_lightmeter_pro.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart';
|
||||||
import 'package:lightmeter/utils/context_utils.dart';
|
import 'package:lightmeter/utils/context_utils.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
@ -38,10 +38,10 @@ class ReadingsContainer extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
if (!context.isPro && RemoteConfig.isEnabled(context, Feature.showUnlockProOnMainScreen)) ...[
|
if (!context.isPro && RemoteConfig.isEnabled(context, Feature.showUnlockProOnMainScreen)) ...[
|
||||||
const LightmeterProAnimatedDialog(),
|
const LightmeterProBadge(),
|
||||||
const _InnerPadding(),
|
const _InnerPadding(),
|
||||||
],
|
],
|
||||||
if (context.isPro && context.meteringFeature(MeteringScreenLayoutFeature.equipmentProfiles)) ...[
|
if (context.meteringFeature(MeteringScreenLayoutFeature.equipmentProfiles)) ...[
|
||||||
const EquipmentProfilePicker(),
|
const EquipmentProfilePicker(),
|
||||||
const _InnerPadding(),
|
const _InnerPadding(),
|
||||||
],
|
],
|
||||||
|
@ -52,7 +52,7 @@ class ReadingsContainer extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const _InnerPadding(),
|
const _InnerPadding(),
|
||||||
],
|
],
|
||||||
if (context.isPro && context.meteringFeature(MeteringScreenLayoutFeature.filmPicker)) ...[
|
if (context.meteringFeature(MeteringScreenLayoutFeature.filmPicker)) ...[
|
||||||
FilmPicker(selectedIso: iso),
|
FilmPicker(selectedIso: iso),
|
||||||
const _InnerPadding(),
|
const _InnerPadding(),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/about/components/report_issue/widget_list_tile_report_issue.dart';
|
import 'package:lightmeter/screens/settings/components/about/components/report_issue/widget_list_tile_report_issue.dart';
|
||||||
|
import 'package:lightmeter/screens/settings/components/about/components/restore_purchases/widget_list_tile_restore_purchases.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/about/components/source_code/widget_list_tile_source_code.dart';
|
import 'package:lightmeter/screens/settings/components/about/components/source_code/widget_list_tile_source_code.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/about/components/version/widget_list_tile_version.dart';
|
import 'package:lightmeter/screens/settings/components/about/components/version/widget_list_tile_version.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/about/components/write_email/widget_list_tile_write_email.dart';
|
import 'package:lightmeter/screens/settings/components/about/components/write_email/widget_list_tile_write_email.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
||||||
|
import 'package:lightmeter/utils/context_utils.dart';
|
||||||
|
|
||||||
class AboutSettingsSection extends StatelessWidget {
|
class AboutSettingsSection extends StatelessWidget {
|
||||||
const AboutSettingsSection({super.key});
|
const AboutSettingsSection({super.key});
|
||||||
|
@ -13,11 +15,12 @@ class AboutSettingsSection extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SettingsSection(
|
return SettingsSection(
|
||||||
title: S.of(context).about,
|
title: S.of(context).about,
|
||||||
children: const [
|
children: [
|
||||||
SourceCodeListTile(),
|
const SourceCodeListTile(),
|
||||||
ReportIssueListTile(),
|
const ReportIssueListTile(),
|
||||||
WriteEmailListTile(),
|
const WriteEmailListTile(),
|
||||||
VersionListTile(),
|
const VersionListTile(),
|
||||||
|
if (context.isPro) const RestorePurchasesListTile(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,39 +4,53 @@ import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/providers/services_provider.dart';
|
import 'package:lightmeter/providers/services_provider.dart';
|
||||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/dialog_switch/widget_dialog_switch.dart';
|
import 'package:lightmeter/screens/settings/components/shared/dialog_switch/widget_dialog_switch.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart';
|
|
||||||
|
|
||||||
class CameraFeaturesListTile extends StatelessWidget {
|
class CameraFeaturesListTile extends StatelessWidget {
|
||||||
const CameraFeaturesListTile({super.key});
|
const CameraFeaturesListTile({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return IAPListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.camera_alt_outlined),
|
leading: const Icon(Icons.camera_alt_outlined),
|
||||||
title: Text(S.of(context).cameraFeatures),
|
title: Text(S.of(context).cameraFeatures),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
UserPreferencesProvider.cameraConfigOf(context).entries.map(
|
||||||
|
(entry) => DialogSwitchListItem(
|
||||||
|
value: CameraFeature.spotMetering,
|
||||||
|
title: S.of(context).cameraFeatureSpotMetering,
|
||||||
|
subtitle: S.of(context).cameraFeatureSpotMeteringHint,
|
||||||
|
initialValue: UserPreferencesProvider.cameraFeatureOf(context, CameraFeature.spotMetering),
|
||||||
|
isProRequired: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => DialogSwitch<CameraFeature>(
|
builder: (_) => DialogSwitch<CameraFeature>(
|
||||||
icon: Icons.camera_alt_outlined,
|
icon: Icons.camera_alt_outlined,
|
||||||
title: S.of(context).cameraFeatures,
|
title: S.of(context).cameraFeatures,
|
||||||
values: UserPreferencesProvider.cameraConfigOf(context),
|
items: [
|
||||||
enabledAdapter: (feature) => switch (feature) {
|
DialogSwitchListItem(
|
||||||
CameraFeature.spotMetering => true,
|
value: CameraFeature.showFocalLength,
|
||||||
CameraFeature.histogram => true,
|
title: S.of(context).cameraFeaturesShowFocalLength,
|
||||||
CameraFeature.showFocalLength =>
|
subtitle: S.of(context).cameraFeaturesShowFocalLengthHint,
|
||||||
ServicesProvider.of(context).userPreferencesService.cameraFocalLength != null,
|
initialValue: UserPreferencesProvider.cameraFeatureOf(context, CameraFeature.showFocalLength),
|
||||||
},
|
isEnabled: ServicesProvider.of(context).userPreferencesService.cameraFocalLength != null,
|
||||||
titleAdapter: (context, feature) => switch (feature) {
|
),
|
||||||
CameraFeature.spotMetering => S.of(context).cameraFeatureSpotMetering,
|
DialogSwitchListItem(
|
||||||
CameraFeature.histogram => S.of(context).cameraFeatureHistogram,
|
value: CameraFeature.spotMetering,
|
||||||
CameraFeature.showFocalLength => S.of(context).cameraFeaturesShowFocalLength,
|
title: S.of(context).cameraFeatureSpotMetering,
|
||||||
},
|
subtitle: S.of(context).cameraFeatureSpotMeteringHint,
|
||||||
subtitleAdapter: (context, feature) => switch (feature) {
|
initialValue: UserPreferencesProvider.cameraFeatureOf(context, CameraFeature.spotMetering),
|
||||||
CameraFeature.spotMetering => S.of(context).cameraFeatureSpotMeteringHint,
|
isProRequired: true,
|
||||||
CameraFeature.histogram => S.of(context).cameraFeatureHistogramHint,
|
),
|
||||||
CameraFeature.showFocalLength => S.of(context).cameraFeaturesShowFocalLengthHint,
|
DialogSwitchListItem(
|
||||||
},
|
value: CameraFeature.histogram,
|
||||||
|
title: S.of(context).cameraFeatureHistogram,
|
||||||
|
subtitle: S.of(context).cameraFeatureHistogramHint,
|
||||||
|
initialValue: UserPreferencesProvider.cameraFeatureOf(context, CameraFeature.histogram),
|
||||||
|
isProRequired: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
onSave: UserPreferencesProvider.of(context).setCameraFeature,
|
onSave: UserPreferencesProvider.of(context).setCameraFeature,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/navigation/routes.dart';
|
import 'package:lightmeter/navigation/routes.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart';
|
|
||||||
|
|
||||||
class LogbookListTile extends StatelessWidget {
|
class LogbookListTile extends StatelessWidget {
|
||||||
const LogbookListTile({super.key});
|
const LogbookListTile({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return IAPListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.book_outlined),
|
leading: const Icon(Icons.book_outlined),
|
||||||
title: Text(S.of(context).logbook),
|
title: Text(S.of(context).logbook),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|
|
@ -3,24 +3,26 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/general/components/timer/bloc_list_tile_timer.dart';
|
import 'package:lightmeter/screens/settings/components/general/components/timer/bloc_list_tile_timer.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/disable/widget_disable.dart';
|
|
||||||
import 'package:lightmeter/utils/context_utils.dart';
|
import 'package:lightmeter/utils/context_utils.dart';
|
||||||
|
import 'package:lightmeter/utils/guard_pro_tap.dart';
|
||||||
|
|
||||||
class TimerListTile extends StatelessWidget {
|
class TimerListTile extends StatelessWidget {
|
||||||
const TimerListTile({super.key});
|
const TimerListTile({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Disable(
|
return BlocBuilder<TimerListTileBloc, bool>(
|
||||||
disable: !context.isPro,
|
builder: (context, state) => SwitchListTile(
|
||||||
child: BlocBuilder<TimerListTileBloc, bool>(
|
secondary: const Icon(Icons.timer_outlined),
|
||||||
builder: (context, state) => SwitchListTile(
|
title: Text(S.of(context).autostartTimer),
|
||||||
secondary: const Icon(Icons.timer_outlined),
|
value: context.isPro && state,
|
||||||
title: Text(S.of(context).autostartTimer),
|
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
||||||
value: state && context.isPro,
|
onChanged: (value) {
|
||||||
onChanged: context.read<TimerListTileBloc>().onChanged,
|
guardProTap(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
context,
|
||||||
),
|
() => context.read<TimerListTileBloc>().onChanged(value),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
|
||||||
import 'package:lightmeter/navigation/routes.dart';
|
|
||||||
|
|
||||||
class BuyProListTile extends StatelessWidget {
|
|
||||||
const BuyProListTile({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// TODO: implement pending handling via REvenueCat
|
|
||||||
return ListTile(
|
|
||||||
leading: const Icon(Icons.bolt),
|
|
||||||
title: Text(S.of(context).getPro),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).pushNamed(NavigationRoutes.proFeaturesScreen.name);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
|
||||||
import 'package:lightmeter/screens/settings/components/lightmeter_pro/components/buy_pro/widget_list_tile_buy_pro.dart';
|
|
||||||
import 'package:lightmeter/screens/settings/components/lightmeter_pro/components/restore_purchases/widget_list_tile_restore_purchases.dart';
|
|
||||||
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
|
||||||
|
|
||||||
class LightmeterProSettingsSection extends StatelessWidget {
|
|
||||||
const LightmeterProSettingsSection({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SettingsSection(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
|
||||||
foregroundColor: Theme.of(context).colorScheme.onSecondary,
|
|
||||||
title: S.of(context).proFeaturesTitle,
|
|
||||||
children: const [
|
|
||||||
BuyProListTile(),
|
|
||||||
RestorePurchasesListTile(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/navigation/routes.dart';
|
import 'package:lightmeter/navigation/routes.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart';
|
|
||||||
|
|
||||||
class EquipmentProfilesListTile extends StatelessWidget {
|
class EquipmentProfilesListTile extends StatelessWidget {
|
||||||
const EquipmentProfilesListTile({super.key});
|
const EquipmentProfilesListTile({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return IAPListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.camera_outlined),
|
leading: const Icon(Icons.camera_outlined),
|
||||||
title: Text(S.of(context).equipmentProfiles),
|
title: Text(S.of(context).equipmentProfiles),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/navigation/routes.dart';
|
import 'package:lightmeter/navigation/routes.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart';
|
import 'package:lightmeter/utils/guard_pro_tap.dart';
|
||||||
|
|
||||||
class FilmsListTile extends StatelessWidget {
|
class FilmsListTile extends StatelessWidget {
|
||||||
const FilmsListTile({super.key});
|
const FilmsListTile({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return IAPListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.camera_roll_outlined),
|
leading: const Icon(Icons.camera_roll_outlined),
|
||||||
title: Text(S.of(context).films),
|
title: Text(S.of(context).films),
|
||||||
onTap: () => Navigator.of(context).pushNamed(NavigationRoutes.filmsListScreen.name),
|
onTap: () {
|
||||||
|
guardProTap(
|
||||||
|
context,
|
||||||
|
() {
|
||||||
|
Navigator.of(context).pushNamed(NavigationRoutes.filmsListScreen.name);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||||
import 'package:lightmeter/providers/films_provider.dart';
|
import 'package:lightmeter/providers/films_provider.dart';
|
||||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/dialog_switch/widget_dialog_switch.dart';
|
import 'package:lightmeter/screens/settings/components/shared/dialog_switch/widget_dialog_switch.dart';
|
||||||
import 'package:lightmeter/utils/context_utils.dart';
|
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class MeteringScreenLayoutListTile extends StatelessWidget {
|
class MeteringScreenLayoutListTile extends StatelessWidget {
|
||||||
|
@ -23,17 +22,16 @@ class MeteringScreenLayoutListTile extends StatelessWidget {
|
||||||
icon: Icons.layers_outlined,
|
icon: Icons.layers_outlined,
|
||||||
title: S.of(context).meteringScreenLayout,
|
title: S.of(context).meteringScreenLayout,
|
||||||
description: S.of(context).meteringScreenLayoutHint,
|
description: S.of(context).meteringScreenLayoutHint,
|
||||||
values: UserPreferencesProvider.meteringScreenConfigOf(context),
|
items: UserPreferencesProvider.meteringScreenConfigOf(context)
|
||||||
titleAdapter: _toStringLocalized,
|
.entries
|
||||||
enabledAdapter: (value) {
|
.map(
|
||||||
switch (value) {
|
(entry) => DialogSwitchListItem(
|
||||||
case MeteringScreenLayoutFeature.equipmentProfiles:
|
value: entry.key,
|
||||||
case MeteringScreenLayoutFeature.filmPicker:
|
title: _toStringLocalized(context, entry.key),
|
||||||
return context.isPro;
|
initialValue: UserPreferencesProvider.meteringScreenFeatureOf(context, entry.key),
|
||||||
default:
|
),
|
||||||
return true;
|
)
|
||||||
}
|
.toList(growable: false),
|
||||||
},
|
|
||||||
onSave: (value) {
|
onSave: (value) {
|
||||||
if (!value[MeteringScreenLayoutFeature.equipmentProfiles]!) {
|
if (!value[MeteringScreenLayoutFeature.equipmentProfiles]!) {
|
||||||
EquipmentProfilesProvider.of(context).selectProfile(EquipmentProfiles.of(context).first);
|
EquipmentProfilesProvider.of(context).selectProfile(EquipmentProfiles.of(context).first);
|
||||||
|
|
|
@ -2,23 +2,18 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/disable/widget_disable.dart';
|
|
||||||
import 'package:lightmeter/utils/context_utils.dart';
|
|
||||||
|
|
||||||
class ShowEv100ListTile extends StatelessWidget {
|
class ShowEv100ListTile extends StatelessWidget {
|
||||||
const ShowEv100ListTile({super.key});
|
const ShowEv100ListTile({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Disable(
|
return SwitchListTile(
|
||||||
disable: !context.isPro,
|
secondary: const Icon(Icons.adjust_outlined),
|
||||||
child: SwitchListTile(
|
title: Text(S.of(context).showEv100),
|
||||||
secondary: const Icon(Icons.adjust_outlined),
|
value: UserPreferencesProvider.showEv100Of(context),
|
||||||
title: Text(S.of(context).showEv100),
|
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
||||||
value: context.isPro && UserPreferencesProvider.showEv100Of(context),
|
onChanged: (_) => UserPreferencesProvider.of(context).toggleShowEv100(),
|
||||||
onChanged: (_) => UserPreferencesProvider.of(context).toggleShowEv100(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,41 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/disable/widget_disable.dart';
|
import 'package:lightmeter/utils/context_utils.dart';
|
||||||
|
import 'package:lightmeter/utils/guard_pro_tap.dart';
|
||||||
|
|
||||||
typedef StringAdapter<T> = String Function(BuildContext context, T value);
|
typedef StringAdapter<T> = String Function(BuildContext context, T value);
|
||||||
|
|
||||||
|
class DialogSwitchListItem<T> {
|
||||||
|
final T value;
|
||||||
|
final String title;
|
||||||
|
final String? subtitle;
|
||||||
|
final bool initialValue;
|
||||||
|
final bool isEnabled;
|
||||||
|
final bool isProRequired;
|
||||||
|
|
||||||
|
const DialogSwitchListItem({
|
||||||
|
required this.value,
|
||||||
|
required this.title,
|
||||||
|
this.subtitle,
|
||||||
|
required this.initialValue,
|
||||||
|
this.isEnabled = true,
|
||||||
|
this.isProRequired = false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class DialogSwitch<T> extends StatefulWidget {
|
class DialogSwitch<T> extends StatefulWidget {
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final String title;
|
final String title;
|
||||||
final String? description;
|
final String? description;
|
||||||
final Map<T, bool> values;
|
final List<DialogSwitchListItem<T>> items;
|
||||||
final StringAdapter<T> titleAdapter;
|
|
||||||
final StringAdapter<T>? subtitleAdapter;
|
|
||||||
final bool Function(T value)? enabledAdapter;
|
|
||||||
final ValueChanged<Map<T, bool>> onSave;
|
final ValueChanged<Map<T, bool>> onSave;
|
||||||
|
|
||||||
const DialogSwitch({
|
const DialogSwitch({
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.description,
|
this.description,
|
||||||
required this.values,
|
required this.items,
|
||||||
required this.titleAdapter,
|
|
||||||
this.subtitleAdapter,
|
|
||||||
this.enabledAdapter,
|
|
||||||
required this.onSave,
|
required this.onSave,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
@ -32,7 +45,11 @@ class DialogSwitch<T> extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DialogSwitchState<T> extends State<DialogSwitch<T>> {
|
class _DialogSwitchState<T> extends State<DialogSwitch<T>> {
|
||||||
late final Map<T, bool> _features = Map.from(widget.values);
|
late final Map<T, bool> _features = Map.fromEntries(
|
||||||
|
widget.items.map(
|
||||||
|
(item) => MapEntry(item.value, item.initialValue),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -55,27 +72,15 @@ class _DialogSwitchState<T> extends State<DialogSwitch<T>> {
|
||||||
],
|
],
|
||||||
ListView(
|
ListView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
children: _features.entries.map(
|
children: widget.items.map(
|
||||||
(entry) {
|
(item) {
|
||||||
final isEnabled = widget.enabledAdapter?.call(entry.key) ?? true;
|
final value = _features[item.value]!;
|
||||||
return Disable(
|
return SwitchListTile(
|
||||||
disable: !isEnabled,
|
contentPadding: EdgeInsets.symmetric(horizontal: Dimens.dialogTitlePadding.left),
|
||||||
child: SwitchListTile(
|
title: Text(item.title),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: Dimens.dialogTitlePadding.left),
|
subtitle: item.subtitle != null ? Text(item.subtitle!) : null,
|
||||||
title: Text(widget.titleAdapter(context, entry.key)),
|
value: item.isProRequired ? context.isPro && value : value,
|
||||||
subtitle: widget.subtitleAdapter != null
|
onChanged: item.isEnabled ? (value) => _setItem(item, value) : null,
|
||||||
? Text(
|
|
||||||
widget.subtitleAdapter!.call(context, entry.key),
|
|
||||||
style: Theme.of(context).listTileTheme.subtitleTextStyle,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
value: isEnabled && _features[entry.key]!,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_features.update(entry.key, (_) => value);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).toList(),
|
).toList(),
|
||||||
|
@ -99,4 +104,18 @@ class _DialogSwitchState<T> extends State<DialogSwitch<T>> {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _setItem(DialogSwitchListItem<T> item, bool value) {
|
||||||
|
void setItemState() {
|
||||||
|
setState(() {
|
||||||
|
_features.update(item.value, (_) => value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.isProRequired) {
|
||||||
|
guardProTap(context, setItemState);
|
||||||
|
} else {
|
||||||
|
setItemState();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:lightmeter/screens/settings/components/shared/disable/widget_disable.dart';
|
|
||||||
import 'package:lightmeter/utils/context_utils.dart';
|
|
||||||
|
|
||||||
/// Depends on the product status and replaces [onTap] with purchase callback
|
|
||||||
/// if the product is purchasable.
|
|
||||||
class IAPListTile extends StatelessWidget {
|
|
||||||
final Icon leading;
|
|
||||||
final Text title;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final bool showPendingTrailing;
|
|
||||||
|
|
||||||
const IAPListTile({
|
|
||||||
required this.leading,
|
|
||||||
required this.title,
|
|
||||||
required this.onTap,
|
|
||||||
this.showPendingTrailing = false,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Disable(
|
|
||||||
disable: !context.isPro,
|
|
||||||
child: ListTile(
|
|
||||||
leading: leading,
|
|
||||||
title: title,
|
|
||||||
onTap: onTap,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,14 +4,10 @@ import 'package:lightmeter/res/dimens.dart';
|
||||||
class SettingsSection extends StatelessWidget {
|
class SettingsSection extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
final Color? backgroundColor;
|
|
||||||
final Color? foregroundColor;
|
|
||||||
|
|
||||||
const SettingsSection({
|
const SettingsSection({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.children,
|
required this.children,
|
||||||
this.backgroundColor,
|
|
||||||
this.foregroundColor,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,33 +21,22 @@ class SettingsSection extends StatelessWidget {
|
||||||
Dimens.paddingM,
|
Dimens.paddingM,
|
||||||
),
|
),
|
||||||
child: Card(
|
child: Card(
|
||||||
color: backgroundColor,
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
||||||
child: Theme(
|
child: Column(
|
||||||
data: Theme.of(context).copyWith(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
listTileTheme: Theme.of(context).listTileTheme.copyWith(
|
mainAxisSize: MainAxisSize.min,
|
||||||
iconColor: foregroundColor,
|
children: [
|
||||||
textColor: foregroundColor,
|
Padding(
|
||||||
),
|
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
||||||
),
|
child: Text(
|
||||||
child: Column(
|
title,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
style:
|
||||||
mainAxisSize: MainAxisSize.min,
|
Theme.of(context).textTheme.labelLarge?.copyWith(color: Theme.of(context).colorScheme.onSurface),
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
|
||||||
child: Text(
|
|
||||||
title,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelLarge
|
|
||||||
?.copyWith(color: foregroundColor ?? Theme.of(context).colorScheme.onSurface),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
...children,
|
),
|
||||||
],
|
...children,
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,12 +3,10 @@ import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/about/widget_settings_section_about.dart';
|
import 'package:lightmeter/screens/settings/components/about/widget_settings_section_about.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/camera/widget_settings_section_camera.dart';
|
import 'package:lightmeter/screens/settings/components/camera/widget_settings_section_camera.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/general/widget_settings_section_general.dart';
|
import 'package:lightmeter/screens/settings/components/general/widget_settings_section_general.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/lightmeter_pro/widget_settings_section_lightmeter_pro.dart';
|
|
||||||
import 'package:lightmeter/screens/settings/components/metering/widget_settings_section_metering.dart';
|
import 'package:lightmeter/screens/settings/components/metering/widget_settings_section_metering.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/theme/widget_settings_section_theme.dart';
|
import 'package:lightmeter/screens/settings/components/theme/widget_settings_section_theme.dart';
|
||||||
import 'package:lightmeter/screens/settings/flow_settings.dart';
|
import 'package:lightmeter/screens/settings/flow_settings.dart';
|
||||||
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
import 'package:lightmeter/utils/context_utils.dart';
|
|
||||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
|
|
||||||
class SettingsScreen extends StatefulWidget {
|
class SettingsScreen extends StatefulWidget {
|
||||||
|
@ -41,7 +39,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildListDelegate(
|
delegate: SliverChildListDelegate(
|
||||||
<Widget>[
|
<Widget>[
|
||||||
if (!context.isPro) const LightmeterProSettingsSection(),
|
|
||||||
const MeteringSettingsSection(),
|
const MeteringSettingsSection(),
|
||||||
const CameraSettingsSection(),
|
const CameraSettingsSection(),
|
||||||
const GeneralSettingsSection(),
|
const GeneralSettingsSection(),
|
||||||
|
|
11
lib/utils/guard_pro_tap.dart
Normal file
11
lib/utils/guard_pro_tap.dart
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/navigation/routes.dart';
|
||||||
|
import 'package:lightmeter/utils/context_utils.dart';
|
||||||
|
|
||||||
|
void guardProTap(BuildContext context, VoidCallback callback) {
|
||||||
|
if (context.isPro) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
Navigator.of(context).pushNamed(NavigationRoutes.proFeaturesScreen.name);
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 298 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: 473 KiB After Width: | Height: | Size: 474 KiB |
Loading…
Reference in a new issue