mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-24 16:30:40 +00:00
implemented ProFeaturesScreen
(wip)
This commit is contained in:
parent
26a9dd05d3
commit
79885acb8d
3 changed files with 265 additions and 1 deletions
26
lib/data/models/app_feature.dart
Normal file
26
lib/data/models/app_feature.dart
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
enum AppFeature {
|
||||||
|
cameraMetering,
|
||||||
|
ndFilters,
|
||||||
|
theming,
|
||||||
|
spotMetering,
|
||||||
|
histogram,
|
||||||
|
listOfFilms,
|
||||||
|
equipmentProfiles,
|
||||||
|
timer,
|
||||||
|
mainScreenCustomization;
|
||||||
|
|
||||||
|
String get name {
|
||||||
|
return toString().replaceAll(runtimeType.toString(), '').replaceAll('.', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isFree {
|
||||||
|
switch (this) {
|
||||||
|
case AppFeature.cameraMetering:
|
||||||
|
case AppFeature.ndFilters:
|
||||||
|
case AppFeature.theming:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
231
lib/screens/pro_features/screen_pro_features.dart
Normal file
231
lib/screens/pro_features/screen_pro_features.dart
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/data/models/app_feature.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
import 'package:lightmeter/res/theme.dart';
|
||||||
|
import 'package:lightmeter/screens/settings/components/about/widget_settings_section_about.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/theme/widget_settings_section_theme.dart';
|
||||||
|
import 'package:lightmeter/screens/settings/flow_settings.dart';
|
||||||
|
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
|
import 'package:lightmeter/utils/context_utils.dart';
|
||||||
|
import 'package:lightmeter/utils/text_height.dart';
|
||||||
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
|
|
||||||
|
class ProFeaturesScreen extends StatefulWidget {
|
||||||
|
const ProFeaturesScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ProFeaturesScreen> createState() => _ProFeaturesScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProFeaturesScreenState extends State<ProFeaturesScreen> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ScaffoldMessenger(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: SliverScreen(
|
||||||
|
title: S.of(context).proFeatures,
|
||||||
|
slivers: [
|
||||||
|
const SliverToBoxAdapter(child: _FeaturesHeader()),
|
||||||
|
SliverList.separated(
|
||||||
|
itemCount: AppFeature.values.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return _FeatureItem(feature: AppFeature.values[index]);
|
||||||
|
},
|
||||||
|
separatorBuilder: (_, __) => const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
||||||
|
child: Divider(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverToBoxAdapter(child: SizedBox(height: Dimens.grid16)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceElevated1,
|
||||||
|
width: MediaQuery.sizeOf(context).width,
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
Dimens.paddingM,
|
||||||
|
Dimens.paddingM,
|
||||||
|
Dimens.paddingM,
|
||||||
|
MediaQuery.paddingOf(context).bottom,
|
||||||
|
),
|
||||||
|
child: FilledButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: Text(S.of(context).unlockFor(IAPProducts.productOf(context, IAPProductType.paidFeatures)!.price)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FeaturesHeader extends StatelessWidget {
|
||||||
|
const _FeaturesHeader();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
_FeatureHighlight(
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: Dimens.paddingM,
|
||||||
|
vertical: Dimens.paddingS,
|
||||||
|
),
|
||||||
|
child: Text('Free'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_FeatureHighlight(
|
||||||
|
roundedTop: true,
|
||||||
|
highlight: true,
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: Dimens.paddingM,
|
||||||
|
vertical: Dimens.paddingS,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Pro',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(color: Theme.of(context).colorScheme.onSecondaryContainer),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: Dimens.grid16),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FeatureItem extends StatelessWidget {
|
||||||
|
final AppFeature feature;
|
||||||
|
|
||||||
|
const _FeatureItem({
|
||||||
|
required this.feature,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minHeight: Dimens.grid48),
|
||||||
|
child: IntrinsicHeight(
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: Dimens.paddingM,
|
||||||
|
vertical: Dimens.paddingS,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
feature.name,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Opacity(
|
||||||
|
opacity: feature.isFree ? 1 : 0,
|
||||||
|
child: const _CheckBox(highlight: false),
|
||||||
|
),
|
||||||
|
_FeatureHighlight(
|
||||||
|
highlight: true,
|
||||||
|
roundedBottom: feature == AppFeature.values.last,
|
||||||
|
child: const _CheckBox(highlight: true),
|
||||||
|
),
|
||||||
|
const SizedBox(width: Dimens.grid16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FeatureHighlight extends StatelessWidget {
|
||||||
|
static double? _freeWidth;
|
||||||
|
static double? _proWidth;
|
||||||
|
|
||||||
|
final bool highlight;
|
||||||
|
final bool roundedTop;
|
||||||
|
final bool roundedBottom;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const _FeatureHighlight({
|
||||||
|
this.highlight = false,
|
||||||
|
this.roundedTop = false,
|
||||||
|
this.roundedBottom = false,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
_FeatureHighlight._freeWidth ??= textSize(
|
||||||
|
'Free',
|
||||||
|
Theme.of(context).textTheme.bodyMedium,
|
||||||
|
MediaQuery.sizeOf(context).width,
|
||||||
|
).width;
|
||||||
|
_FeatureHighlight._proWidth ??= textSize(
|
||||||
|
'Pro',
|
||||||
|
Theme.of(context).textTheme.bodyMedium,
|
||||||
|
MediaQuery.sizeOf(context).width,
|
||||||
|
).width;
|
||||||
|
return Container(
|
||||||
|
constraints:
|
||||||
|
BoxConstraints(minWidth: (highlight ? _FeatureHighlight._proWidth : _FeatureHighlight._freeWidth) ?? 0.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: highlight ? Theme.of(context).colorScheme.secondaryContainer : null,
|
||||||
|
borderRadius: roundedTop
|
||||||
|
? const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(Dimens.borderRadiusM),
|
||||||
|
topRight: Radius.circular(Dimens.borderRadiusM),
|
||||||
|
)
|
||||||
|
: roundedBottom
|
||||||
|
? const BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(Dimens.borderRadiusM),
|
||||||
|
bottomRight: Radius.circular(Dimens.borderRadiusM),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CheckBox extends StatelessWidget {
|
||||||
|
final bool highlight;
|
||||||
|
|
||||||
|
const _CheckBox({required this.highlight});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: Dimens.paddingM,
|
||||||
|
vertical: Dimens.paddingS,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.check_outlined,
|
||||||
|
color: highlight ? Theme.of(context).colorScheme.onSecondaryContainer : null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,13 @@ double textHeight(
|
||||||
String text,
|
String text,
|
||||||
TextStyle? style,
|
TextStyle? style,
|
||||||
double maxWidth,
|
double maxWidth,
|
||||||
|
) =>
|
||||||
|
textSize(text, style, maxWidth).height;
|
||||||
|
|
||||||
|
Size textSize(
|
||||||
|
String text,
|
||||||
|
TextStyle? style,
|
||||||
|
double maxWidth,
|
||||||
) {
|
) {
|
||||||
final TextPainter titlePainter = TextPainter(
|
final TextPainter titlePainter = TextPainter(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
|
@ -25,5 +32,5 @@ double textHeight(
|
||||||
),
|
),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
)..layout(maxWidth: maxWidth);
|
)..layout(maxWidth: maxWidth);
|
||||||
return titlePainter.height;
|
return titlePainter.size;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue