This commit is contained in:
Vadim 2023-05-17 10:23:32 +02:00
parent a0eb641cf3
commit 6f094d6432
5 changed files with 158 additions and 40 deletions

View file

@ -1,11 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lightmeter/utils/inherited_generics.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:provider/provider.dart';
class StopTypeProvider extends StatefulWidget { class StopTypeProvider extends StatefulWidget {
final Widget? child; final Widget child;
const StopTypeProvider({this.child, super.key}); const StopTypeProvider({required this.child, super.key});
static StopTypeProviderState of(BuildContext context) { static StopTypeProviderState of(BuildContext context) {
return context.findAncestorStateOfType<StopTypeProviderState>()!; return context.findAncestorStateOfType<StopTypeProviderState>()!;
@ -28,8 +28,8 @@ class StopTypeProviderState extends State<StopTypeProvider> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Provider.value( return InheritedWidgetBase<StopType>(
value: _stopType, data: _stopType,
child: widget.child, child: widget.child,
); );
} }

View file

@ -10,6 +10,7 @@ import 'package:lightmeter/providers/equipment_profile_provider.dart';
import 'package:lightmeter/screens/metering/bloc_metering.dart'; import 'package:lightmeter/screens/metering/bloc_metering.dart';
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
import 'package:lightmeter/screens/metering/screen_metering.dart'; import 'package:lightmeter/screens/metering/screen_metering.dart';
import 'package:lightmeter/utils/inherited_generics.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -39,7 +40,7 @@ class _MeteringFlowState extends State<MeteringFlow> {
context.read<MeteringCommunicationBloc>(), context.read<MeteringCommunicationBloc>(),
context.read<MeteringInteractor>(), context.read<MeteringInteractor>(),
EquipmentProfile.of(context, listen: false), EquipmentProfile.of(context, listen: false),
context.read<StopType>(), context.get<StopType>(),
), ),
), ),
], ],

View file

@ -14,6 +14,7 @@ import 'package:lightmeter/screens/metering/components/camera_container/provider
import 'package:lightmeter/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart'; import 'package:lightmeter/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart';
import 'package:lightmeter/screens/metering/event_metering.dart'; import 'package:lightmeter/screens/metering/event_metering.dart';
import 'package:lightmeter/screens/metering/state_metering.dart'; import 'package:lightmeter/screens/metering/state_metering.dart';
import 'package:lightmeter/utils/inherited_generics.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class MeteringScreen extends StatefulWidget { class MeteringScreen extends StatefulWidget {
@ -30,7 +31,6 @@ 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)));
_bloc.add(StopTypeChangedEvent(context.watch<StopType>()));
if (!MeteringScreenLayout.featureStatusOf(context, MeteringScreenLayoutFeature.filmPicker)) { if (!MeteringScreenLayout.featureStatusOf(context, MeteringScreenLayoutFeature.filmPicker)) {
_bloc.add(const FilmChangedEvent(Film.other())); _bloc.add(const FilmChangedEvent(Film.other()));
} }
@ -38,39 +38,44 @@ class _MeteringScreenState extends State<MeteringScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return InheritedWidgetListener<StopType>(
backgroundColor: Theme.of(context).colorScheme.background, onDidChangeDependencies: (value) {
body: Column( context.read<MeteringBloc>().add(StopTypeChangedEvent(value));
children: [ },
Expanded( child: Scaffold(
child: BlocBuilder<MeteringBloc, MeteringState>( backgroundColor: Theme.of(context).colorScheme.background,
builder: (_, state) => _MeteringContainerBuidler( body: Column(
fastest: state is MeteringDataState ? state.fastest : null, children: [
slowest: state is MeteringDataState ? state.slowest : null, Expanded(
exposurePairs: state is MeteringDataState ? state.exposurePairs : [], child: BlocBuilder<MeteringBloc, MeteringState>(
film: state.film, builder: (_, state) => _MeteringContainerBuidler(
iso: state.iso, fastest: state is MeteringDataState ? state.fastest : null,
nd: state.nd, slowest: state is MeteringDataState ? state.slowest : null,
onFilmChanged: (value) => _bloc.add(FilmChangedEvent(value)), exposurePairs: state is MeteringDataState ? state.exposurePairs : [],
onIsoChanged: (value) => _bloc.add(IsoChangedEvent(value)), film: state.film,
onNdChanged: (value) => _bloc.add(NdChangedEvent(value)), iso: state.iso,
nd: state.nd,
onFilmChanged: (value) => _bloc.add(FilmChangedEvent(value)),
onIsoChanged: (value) => _bloc.add(IsoChangedEvent(value)),
onNdChanged: (value) => _bloc.add(NdChangedEvent(value)),
),
), ),
), ),
), 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 MeteringDataState && state.continuousMetering,
state is LoadingState || 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 : null,
: null, onMeasure: () => _bloc.add(const MeasureEvent()),
onMeasure: () => _bloc.add(const MeasureEvent()), onSettings: () => Navigator.pushNamed(context, 'settings'),
onSettings: () => Navigator.pushNamed(context, 'settings'), ),
), ),
), ],
], ),
), ),
); );
} }

View file

@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/providers/stop_type_provider.dart'; import 'package:lightmeter/providers/stop_type_provider.dart';
import 'package:lightmeter/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart'; import 'package:lightmeter/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart';
import 'package:lightmeter/utils/inherited_generics.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:provider/provider.dart';
class StopTypeListTile extends StatelessWidget { class StopTypeListTile extends StatelessWidget {
const StopTypeListTile({super.key}); const StopTypeListTile({super.key});
@ -13,14 +13,14 @@ class StopTypeListTile extends StatelessWidget {
return ListTile( return ListTile(
leading: const Icon(Icons.straighten), leading: const Icon(Icons.straighten),
title: Text(S.of(context).fractionalStops), title: Text(S.of(context).fractionalStops),
trailing: Text(_typeToString(context, context.watch<StopType>())), trailing: Text(_typeToString(context, context.listen<StopType>())),
onTap: () { onTap: () {
showDialog<StopType>( showDialog<StopType>(
context: context, context: context,
builder: (_) => DialogPicker<StopType>( builder: (_) => DialogPicker<StopType>(
icon: Icons.straighten, icon: Icons.straighten,
title: S.of(context).showFractionalStops, title: S.of(context).showFractionalStops,
selectedValue: context.read<StopType>(), selectedValue: context.get<StopType>(),
values: StopType.values, values: StopType.values,
titleAdapter: _typeToString, titleAdapter: _typeToString,
), ),

View file

@ -0,0 +1,112 @@
import 'package:flutter/widgets.dart';
/// 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 InheritedWidgetListener<T> extends StatefulWidget {
final ValueChanged<T> onDidChangeDependencies;
final Widget child;
const InheritedWidgetListener({
required this.onDidChangeDependencies,
required this.child,
super.key,
});
@override
State<InheritedWidgetListener<T>> createState() => _InheritedWidgetListenerState<T>();
}
class _InheritedWidgetListenerState<T> extends State<InheritedWidgetListener<T>> {
@override
void didChangeDependencies() {
super.didChangeDependencies();
widget.onDidChangeDependencies(context.listen<T>());
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
class InheritedWidgetBase<T> extends InheritedWidget {
final T data;
const InheritedWidgetBase({
required this.data,
required super.child,
super.key,
});
static T of<T>(BuildContext context, {bool listen = true}) {
if (listen) {
return context.dependOnInheritedWidgetOfExactType<InheritedWidgetBase<T>>()!.data;
} else {
return context.findAncestorWidgetOfExactType<InheritedWidgetBase<T>>()!.data;
}
}
@override
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);
}
T listen<T>() {
return InheritedWidgetBase.of<T>(this);
}
}