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:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||
import 'package:lightmeter/utils/inherited_generics.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
typedef MeteringScreenLayout = InheritedModelBase<MeteringScreenLayoutFeature, bool>;
|
||||
|
||||
class MeteringScreenLayoutProvider extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
|
@ -22,8 +25,8 @@ class MeteringScreenLayoutProviderState extends State<MeteringScreenLayoutProvid
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MeteringScreenLayout(
|
||||
config: MeteringScreenLayoutConfig.from(_config),
|
||||
return InheritedModelBase<MeteringScreenLayoutFeature, bool>(
|
||||
data: MeteringScreenLayoutConfig.from(_config),
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
|
@ -41,42 +44,3 @@ class MeteringScreenLayoutProviderState extends State<MeteringScreenLayoutProvid
|
|||
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.paddingS;
|
||||
}
|
||||
if (MeteringScreenLayout.featureStatusOf(
|
||||
if (MeteringScreenLayout.featureOf(
|
||||
context,
|
||||
MeteringScreenLayoutFeature.extremeExposurePairs,
|
||||
)) {
|
||||
topBarOverflow += Dimens.readingContainerDoubleValueHeight;
|
||||
topBarOverflow += Dimens.paddingS;
|
||||
}
|
||||
if (MeteringScreenLayout.featureStatusOf(
|
||||
if (MeteringScreenLayout.featureOf(
|
||||
context,
|
||||
MeteringScreenLayoutFeature.filmPicker,
|
||||
)) {
|
||||
|
|
|
@ -42,7 +42,7 @@ class ReadingsContainer extends StatelessWidget {
|
|||
const _EquipmentProfilePicker(),
|
||||
const _InnerPadding(),
|
||||
],
|
||||
if (MeteringScreenLayout.featureStatusOf(
|
||||
if (MeteringScreenLayout.featureOf(
|
||||
context,
|
||||
MeteringScreenLayoutFeature.extremeExposurePairs,
|
||||
)) ...[
|
||||
|
@ -60,7 +60,7 @@ class ReadingsContainer extends StatelessWidget {
|
|||
),
|
||||
const _InnerPadding(),
|
||||
],
|
||||
if (MeteringScreenLayout.featureStatusOf(
|
||||
if (MeteringScreenLayout.featureOf(
|
||||
context,
|
||||
MeteringScreenLayoutFeature.filmPicker,
|
||||
)) ...[
|
||||
|
|
|
@ -7,7 +7,6 @@ import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
|||
import 'package:lightmeter/environment.dart';
|
||||
import 'package:lightmeter/providers/equipment_profile_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/components/bottom_controls/provider_bottom_controls.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/provider_container_camera.dart';
|
||||
|
@ -31,17 +30,11 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
|||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_bloc.add(EquipmentProfileChangedEvent(EquipmentProfile.of(context)));
|
||||
if (!MeteringScreenLayout.featureStatusOf(context, MeteringScreenLayoutFeature.filmPicker)) {
|
||||
_bloc.add(const FilmChangedEvent(Film.other()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InheritedWidgetListener<StopType>(
|
||||
onDidChangeDependencies: (value) {
|
||||
context.read<MeteringBloc>().add(StopTypeChangedEvent(value));
|
||||
},
|
||||
return _InheritedListeners(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
body: Column(
|
||||
|
@ -64,8 +57,8 @@ class _MeteringScreenState extends State<MeteringScreen> {
|
|||
BlocBuilder<MeteringBloc, MeteringState>(
|
||||
builder: (context, state) => MeteringBottomControlsProvider(
|
||||
ev: state is MeteringDataState ? state.ev : null,
|
||||
isMetering:
|
||||
state is LoadingState || state is MeteringDataState && state.continuousMetering,
|
||||
isMetering: state is LoadingState ||
|
||||
state is MeteringDataState && state.continuousMetering,
|
||||
hasError: state is MeteringDataState && state.hasError,
|
||||
onSwitchEvSourceType: context.read<Environment>().hasLightSensor
|
||||
? 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 {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
|
|
|
@ -62,45 +62,6 @@ class InheritedWidgetBase<T> extends InheritedWidget {
|
|||
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 {
|
||||
T get<T>() {
|
||||
return InheritedWidgetBase.of<T>(this, listen: false);
|
||||
|
@ -110,3 +71,101 @@ extension InheritedWidgetBaseContext on BuildContext {
|
|||
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