mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-24 00:10:47 +00:00
MeteringScreenLayout = InheritedModelBase<MeteringScreenLayoutFeature, bool>
This commit is contained in:
parent
6f094d6432
commit
2c85a3fddc
5 changed files with 132 additions and 94 deletions
|
@ -1,8 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||||
|
import 'package:lightmeter/utils/inherited_generics.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
typedef MeteringScreenLayout = InheritedModelBase<MeteringScreenLayoutFeature, bool>;
|
||||||
|
|
||||||
class MeteringScreenLayoutProvider extends StatefulWidget {
|
class MeteringScreenLayoutProvider extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
|
@ -22,8 +25,8 @@ class MeteringScreenLayoutProviderState extends State<MeteringScreenLayoutProvid
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MeteringScreenLayout(
|
return InheritedModelBase<MeteringScreenLayoutFeature, bool>(
|
||||||
config: MeteringScreenLayoutConfig.from(_config),
|
data: MeteringScreenLayoutConfig.from(_config),
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -41,42 +44,3 @@ class MeteringScreenLayoutProviderState extends State<MeteringScreenLayoutProvid
|
||||||
context.read<UserPreferencesService>().meteringScreenLayout = _config;
|
context.read<UserPreferencesService>().meteringScreenLayout = _config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MeteringScreenLayout extends InheritedModel<MeteringScreenLayoutFeature> {
|
|
||||||
final MeteringScreenLayoutConfig config;
|
|
||||||
|
|
||||||
const MeteringScreenLayout({
|
|
||||||
required this.config,
|
|
||||||
required super.child,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
static MeteringScreenLayoutConfig of(BuildContext context, {bool listen = true}) {
|
|
||||||
if (listen) {
|
|
||||||
return context.dependOnInheritedWidgetOfExactType<MeteringScreenLayout>()!.config;
|
|
||||||
} else {
|
|
||||||
return context.findAncestorWidgetOfExactType<MeteringScreenLayout>()!.config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool featureStatusOf(BuildContext context, MeteringScreenLayoutFeature feature) {
|
|
||||||
return InheritedModel.inheritFrom<MeteringScreenLayout>(context, aspect: feature)!
|
|
||||||
.config[feature]!;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotify(MeteringScreenLayout oldWidget) => true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotifyDependent(
|
|
||||||
MeteringScreenLayout oldWidget,
|
|
||||||
Set<MeteringScreenLayoutFeature> dependencies,
|
|
||||||
) {
|
|
||||||
for (final dependecy in dependencies) {
|
|
||||||
if (oldWidget.config[dependecy] != config[dependecy]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -56,14 +56,14 @@ class CameraContainer extends StatelessWidget {
|
||||||
topBarOverflow += Dimens.readingContainerSingleValueHeight;
|
topBarOverflow += Dimens.readingContainerSingleValueHeight;
|
||||||
topBarOverflow += Dimens.paddingS;
|
topBarOverflow += Dimens.paddingS;
|
||||||
}
|
}
|
||||||
if (MeteringScreenLayout.featureStatusOf(
|
if (MeteringScreenLayout.featureOf(
|
||||||
context,
|
context,
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs,
|
MeteringScreenLayoutFeature.extremeExposurePairs,
|
||||||
)) {
|
)) {
|
||||||
topBarOverflow += Dimens.readingContainerDoubleValueHeight;
|
topBarOverflow += Dimens.readingContainerDoubleValueHeight;
|
||||||
topBarOverflow += Dimens.paddingS;
|
topBarOverflow += Dimens.paddingS;
|
||||||
}
|
}
|
||||||
if (MeteringScreenLayout.featureStatusOf(
|
if (MeteringScreenLayout.featureOf(
|
||||||
context,
|
context,
|
||||||
MeteringScreenLayoutFeature.filmPicker,
|
MeteringScreenLayoutFeature.filmPicker,
|
||||||
)) {
|
)) {
|
||||||
|
|
|
@ -42,7 +42,7 @@ class ReadingsContainer extends StatelessWidget {
|
||||||
const _EquipmentProfilePicker(),
|
const _EquipmentProfilePicker(),
|
||||||
const _InnerPadding(),
|
const _InnerPadding(),
|
||||||
],
|
],
|
||||||
if (MeteringScreenLayout.featureStatusOf(
|
if (MeteringScreenLayout.featureOf(
|
||||||
context,
|
context,
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs,
|
MeteringScreenLayoutFeature.extremeExposurePairs,
|
||||||
)) ...[
|
)) ...[
|
||||||
|
@ -60,7 +60,7 @@ class ReadingsContainer extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const _InnerPadding(),
|
const _InnerPadding(),
|
||||||
],
|
],
|
||||||
if (MeteringScreenLayout.featureStatusOf(
|
if (MeteringScreenLayout.featureOf(
|
||||||
context,
|
context,
|
||||||
MeteringScreenLayoutFeature.filmPicker,
|
MeteringScreenLayoutFeature.filmPicker,
|
||||||
)) ...[
|
)) ...[
|
||||||
|
|
|
@ -7,7 +7,6 @@ import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||||
import 'package:lightmeter/environment.dart';
|
import 'package:lightmeter/environment.dart';
|
||||||
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||||
import 'package:lightmeter/providers/ev_source_type_provider.dart';
|
import 'package:lightmeter/providers/ev_source_type_provider.dart';
|
||||||
import 'package:lightmeter/providers/metering_screen_layout_provider.dart';
|
|
||||||
import 'package:lightmeter/screens/metering/bloc_metering.dart';
|
import 'package:lightmeter/screens/metering/bloc_metering.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/bottom_controls/provider_bottom_controls.dart';
|
import 'package:lightmeter/screens/metering/components/bottom_controls/provider_bottom_controls.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/camera_container/provider_container_camera.dart';
|
import 'package:lightmeter/screens/metering/components/camera_container/provider_container_camera.dart';
|
||||||
|
@ -31,17 +30,11 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
_bloc.add(EquipmentProfileChangedEvent(EquipmentProfile.of(context)));
|
_bloc.add(EquipmentProfileChangedEvent(EquipmentProfile.of(context)));
|
||||||
if (!MeteringScreenLayout.featureStatusOf(context, MeteringScreenLayoutFeature.filmPicker)) {
|
|
||||||
_bloc.add(const FilmChangedEvent(Film.other()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InheritedWidgetListener<StopType>(
|
return _InheritedListeners(
|
||||||
onDidChangeDependencies: (value) {
|
|
||||||
context.read<MeteringBloc>().add(StopTypeChangedEvent(value));
|
|
||||||
},
|
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Theme.of(context).colorScheme.background,
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
body: Column(
|
body: Column(
|
||||||
|
@ -64,8 +57,8 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
BlocBuilder<MeteringBloc, MeteringState>(
|
BlocBuilder<MeteringBloc, MeteringState>(
|
||||||
builder: (context, state) => MeteringBottomControlsProvider(
|
builder: (context, state) => MeteringBottomControlsProvider(
|
||||||
ev: state is MeteringDataState ? state.ev : null,
|
ev: state is MeteringDataState ? state.ev : null,
|
||||||
isMetering:
|
isMetering: state is LoadingState ||
|
||||||
state is LoadingState || state is MeteringDataState && state.continuousMetering,
|
state is MeteringDataState && state.continuousMetering,
|
||||||
hasError: state is MeteringDataState && state.hasError,
|
hasError: state is MeteringDataState && state.hasError,
|
||||||
onSwitchEvSourceType: context.read<Environment>().hasLightSensor
|
onSwitchEvSourceType: context.read<Environment>().hasLightSensor
|
||||||
? EvSourceTypeProvider.of(context).toggleType
|
? EvSourceTypeProvider.of(context).toggleType
|
||||||
|
@ -81,6 +74,28 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _InheritedListeners extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const _InheritedListeners({required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InheritedWidgetListener<StopType>(
|
||||||
|
onDidChangeDependencies: (value) {
|
||||||
|
context.read<MeteringBloc>().add(StopTypeChangedEvent(value));
|
||||||
|
},
|
||||||
|
child: InheritedModelAspectListener<MeteringScreenLayoutFeature, bool>(
|
||||||
|
aspect: MeteringScreenLayoutFeature.filmPicker,
|
||||||
|
onDidChangeDependencies: (value) {
|
||||||
|
if (!value) context.read<MeteringBloc>().add(const FilmChangedEvent(Film.other()));
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _MeteringContainerBuidler extends StatelessWidget {
|
class _MeteringContainerBuidler extends StatelessWidget {
|
||||||
final ExposurePair? fastest;
|
final ExposurePair? fastest;
|
||||||
final ExposurePair? slowest;
|
final ExposurePair? slowest;
|
||||||
|
|
|
@ -62,45 +62,6 @@ class InheritedWidgetBase<T> extends InheritedWidget {
|
||||||
bool updateShouldNotify(InheritedWidgetBase<T> oldWidget) => true;
|
bool updateShouldNotify(InheritedWidgetBase<T> oldWidget) => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// class InheritedModelBase<A, R> extends InheritedModel<A> {
|
|
||||||
// final Map<A, R> data;
|
|
||||||
|
|
||||||
// const InheritedModelBase({
|
|
||||||
// required this.data,
|
|
||||||
// required super.child,
|
|
||||||
// super.key,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// static Map<A, R> of<R, A>(BuildContext context, {bool listen = true}) {
|
|
||||||
// if (listen) {
|
|
||||||
// return context.dependOnInheritedWidgetOfExactType<InheritedModelBase<A, R>>()!.data;
|
|
||||||
// } else {
|
|
||||||
// return context.findAncestorWidgetOfExactType<InheritedModelBase<A, R>>()!.data;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static R featureStatusOf<A, R>(BuildContext context, A aspect) {
|
|
||||||
// return InheritedModel.inheritFrom<InheritedModelBase<A, R>>(context, aspect: aspect)!
|
|
||||||
// .data[aspect]!;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// bool updateShouldNotify(InheritedModelBase oldWidget) => true;
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// bool updateShouldNotifyDependent(
|
|
||||||
// InheritedModelBase<A, R> oldWidget,
|
|
||||||
// Set<A> dependencies,
|
|
||||||
// ) {
|
|
||||||
// for (final dependecy in dependencies) {
|
|
||||||
// if (oldWidget.data[dependecy] != data[dependecy]) {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
extension InheritedWidgetBaseContext on BuildContext {
|
extension InheritedWidgetBaseContext on BuildContext {
|
||||||
T get<T>() {
|
T get<T>() {
|
||||||
return InheritedWidgetBase.of<T>(this, listen: false);
|
return InheritedWidgetBase.of<T>(this, listen: false);
|
||||||
|
@ -110,3 +71,101 @@ extension InheritedWidgetBaseContext on BuildContext {
|
||||||
return InheritedWidgetBase.of<T>(this);
|
return InheritedWidgetBase.of<T>(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Listening to multiple dependencies at the same time causes firing an event for all dependencies
|
||||||
|
/// even though some of them didn't change:
|
||||||
|
/// ```dart
|
||||||
|
/// @override
|
||||||
|
/// void didChangeDependencies() {
|
||||||
|
/// super.didChangeDependencies();
|
||||||
|
/// _bloc.add(EquipmentProfileChangedEvent(EquipmentProfile.of(context)));
|
||||||
|
/// if (!MeteringScreenLayout.featureStatusOf(context, MeteringScreenLayoutFeature.filmPicker)) {
|
||||||
|
/// _bloc.add(const FilmChangedEvent(Film.other()));
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// To overcome this issue I've decided to create a generic listener,
|
||||||
|
/// that will listen to each dependency separately.
|
||||||
|
class InheritedModelAspectListener<A extends Object, T> extends StatefulWidget {
|
||||||
|
final A aspect;
|
||||||
|
final ValueChanged<T> onDidChangeDependencies;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const InheritedModelAspectListener({
|
||||||
|
required this.aspect,
|
||||||
|
required this.onDidChangeDependencies,
|
||||||
|
required this.child,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<InheritedModelAspectListener<A, T>> createState() =>
|
||||||
|
_InheritedModelAspectListenerState<A, T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InheritedModelAspectListenerState<A extends Object, T>
|
||||||
|
extends State<InheritedModelAspectListener<A, T>> {
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
widget.onDidChangeDependencies(context.listenModelFeature<A, T>(widget.aspect));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return widget.child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InheritedModelBase<A, T> extends InheritedModel<A> {
|
||||||
|
final Map<A, T> data;
|
||||||
|
|
||||||
|
const InheritedModelBase({
|
||||||
|
required this.data,
|
||||||
|
required super.child,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
static Map<A, T> of<A, T>(BuildContext context, {bool listen = true}) {
|
||||||
|
if (listen) {
|
||||||
|
return context.dependOnInheritedWidgetOfExactType<InheritedModelBase<A, T>>()!.data;
|
||||||
|
} else {
|
||||||
|
return context.findAncestorWidgetOfExactType<InheritedModelBase<A, T>>()!.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static T featureOf<A extends Object, T>(BuildContext context, A aspect) {
|
||||||
|
return InheritedModel.inheritFrom<InheritedModelBase<A, T>>(context, aspect: aspect)!
|
||||||
|
.data[aspect]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(InheritedModelBase oldWidget) => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotifyDependent(
|
||||||
|
InheritedModelBase<A, T> oldWidget,
|
||||||
|
Set<A> dependencies,
|
||||||
|
) {
|
||||||
|
for (final dependecy in dependencies) {
|
||||||
|
if (oldWidget.data[dependecy] != data[dependecy]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension InheritedModelBaseContext on BuildContext {
|
||||||
|
Map<A, T> getModel<A, T>() {
|
||||||
|
return InheritedModelBase.of<A, T>(this, listen: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<A, T> listenModel<A, T>() {
|
||||||
|
return InheritedModelBase.of<A, T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
T listenModelFeature<A extends Object, T>(A aspect) {
|
||||||
|
return InheritedModelBase.featureOf<A, T>(this, aspect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue