diff --git a/lib/providers/stop_type_provider.dart b/lib/providers/stop_type_provider.dart index a6bbe1a..690f65f 100644 --- a/lib/providers/stop_type_provider.dart +++ b/lib/providers/stop_type_provider.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:lightmeter/utils/inherited_generics.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; -import 'package:provider/provider.dart'; 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) { return context.findAncestorStateOfType()!; @@ -28,8 +28,8 @@ class StopTypeProviderState extends State { @override Widget build(BuildContext context) { - return Provider.value( - value: _stopType, + return InheritedWidgetBase( + data: _stopType, child: widget.child, ); } diff --git a/lib/screens/metering/flow_metering.dart b/lib/screens/metering/flow_metering.dart index 3f830c2..ff34afa 100644 --- a/lib/screens/metering/flow_metering.dart +++ b/lib/screens/metering/flow_metering.dart @@ -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/communication/bloc_communication_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:provider/provider.dart'; @@ -39,7 +40,7 @@ class _MeteringFlowState extends State { context.read(), context.read(), EquipmentProfile.of(context, listen: false), - context.read(), + context.get(), ), ), ], diff --git a/lib/screens/metering/screen_metering.dart b/lib/screens/metering/screen_metering.dart index f3c6b22..99ca16c 100644 --- a/lib/screens/metering/screen_metering.dart +++ b/lib/screens/metering/screen_metering.dart @@ -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/event_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'; class MeteringScreen extends StatefulWidget { @@ -30,7 +31,6 @@ class _MeteringScreenState extends State { void didChangeDependencies() { super.didChangeDependencies(); _bloc.add(EquipmentProfileChangedEvent(EquipmentProfile.of(context))); - _bloc.add(StopTypeChangedEvent(context.watch())); if (!MeteringScreenLayout.featureStatusOf(context, MeteringScreenLayoutFeature.filmPicker)) { _bloc.add(const FilmChangedEvent(Film.other())); } @@ -38,39 +38,44 @@ class _MeteringScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Theme.of(context).colorScheme.background, - body: Column( - children: [ - Expanded( - child: BlocBuilder( - builder: (_, state) => _MeteringContainerBuidler( - fastest: state is MeteringDataState ? state.fastest : null, - slowest: state is MeteringDataState ? state.slowest : null, - exposurePairs: state is MeteringDataState ? state.exposurePairs : [], - film: state.film, - iso: state.iso, - nd: state.nd, - onFilmChanged: (value) => _bloc.add(FilmChangedEvent(value)), - onIsoChanged: (value) => _bloc.add(IsoChangedEvent(value)), - onNdChanged: (value) => _bloc.add(NdChangedEvent(value)), + return InheritedWidgetListener( + onDidChangeDependencies: (value) { + context.read().add(StopTypeChangedEvent(value)); + }, + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.background, + body: Column( + children: [ + Expanded( + child: BlocBuilder( + builder: (_, state) => _MeteringContainerBuidler( + fastest: state is MeteringDataState ? state.fastest : null, + slowest: state is MeteringDataState ? state.slowest : null, + exposurePairs: state is MeteringDataState ? state.exposurePairs : [], + film: state.film, + 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( - builder: (context, state) => MeteringBottomControlsProvider( - ev: state is MeteringDataState ? state.ev : null, - isMetering: - state is LoadingState || state is MeteringDataState && state.continuousMetering, - hasError: state is MeteringDataState && state.hasError, - onSwitchEvSourceType: context.read().hasLightSensor - ? EvSourceTypeProvider.of(context).toggleType - : null, - onMeasure: () => _bloc.add(const MeasureEvent()), - onSettings: () => Navigator.pushNamed(context, 'settings'), + BlocBuilder( + builder: (context, state) => MeteringBottomControlsProvider( + ev: state is MeteringDataState ? state.ev : null, + isMetering: + state is LoadingState || state is MeteringDataState && state.continuousMetering, + hasError: state is MeteringDataState && state.hasError, + onSwitchEvSourceType: context.read().hasLightSensor + ? EvSourceTypeProvider.of(context).toggleType + : null, + onMeasure: () => _bloc.add(const MeasureEvent()), + onSettings: () => Navigator.pushNamed(context, 'settings'), + ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart b/lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart index a21ec94..2a27a56 100644 --- a/lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart +++ b/lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.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/utils/inherited_generics.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; -import 'package:provider/provider.dart'; class StopTypeListTile extends StatelessWidget { const StopTypeListTile({super.key}); @@ -13,14 +13,14 @@ class StopTypeListTile extends StatelessWidget { return ListTile( leading: const Icon(Icons.straighten), title: Text(S.of(context).fractionalStops), - trailing: Text(_typeToString(context, context.watch())), + trailing: Text(_typeToString(context, context.listen())), onTap: () { showDialog( context: context, builder: (_) => DialogPicker( icon: Icons.straighten, title: S.of(context).showFractionalStops, - selectedValue: context.read(), + selectedValue: context.get(), values: StopType.values, titleAdapter: _typeToString, ), diff --git a/lib/utils/inherited_generics.dart b/lib/utils/inherited_generics.dart new file mode 100644 index 0000000..026f564 --- /dev/null +++ b/lib/utils/inherited_generics.dart @@ -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 extends StatefulWidget { + final ValueChanged onDidChangeDependencies; + final Widget child; + + const InheritedWidgetListener({ + required this.onDidChangeDependencies, + required this.child, + super.key, + }); + + @override + State> createState() => _InheritedWidgetListenerState(); +} + +class _InheritedWidgetListenerState extends State> { + @override + void didChangeDependencies() { + super.didChangeDependencies(); + widget.onDidChangeDependencies(context.listen()); + } + + @override + Widget build(BuildContext context) { + return widget.child; + } +} + +class InheritedWidgetBase extends InheritedWidget { + final T data; + + const InheritedWidgetBase({ + required this.data, + required super.child, + super.key, + }); + + static T of(BuildContext context, {bool listen = true}) { + if (listen) { + return context.dependOnInheritedWidgetOfExactType>()!.data; + } else { + return context.findAncestorWidgetOfExactType>()!.data; + } + } + + @override + bool updateShouldNotify(InheritedWidgetBase oldWidget) => true; +} + +// class InheritedModelBase extends InheritedModel { +// final Map data; + +// const InheritedModelBase({ +// required this.data, +// required super.child, +// super.key, +// }); + +// static Map of(BuildContext context, {bool listen = true}) { +// if (listen) { +// return context.dependOnInheritedWidgetOfExactType>()!.data; +// } else { +// return context.findAncestorWidgetOfExactType>()!.data; +// } +// } + +// static R featureStatusOf(BuildContext context, A aspect) { +// return InheritedModel.inheritFrom>(context, aspect: aspect)! +// .data[aspect]!; +// } + +// @override +// bool updateShouldNotify(InheritedModelBase oldWidget) => true; + +// @override +// bool updateShouldNotifyDependent( +// InheritedModelBase oldWidget, +// Set dependencies, +// ) { +// for (final dependecy in dependencies) { +// if (oldWidget.data[dependecy] != data[dependecy]) { +// return true; +// } +// } +// return false; +// } +// } + +extension InheritedWidgetBaseContext on BuildContext { + T get() { + return InheritedWidgetBase.of(this, listen: false); + } + + T listen() { + return InheritedWidgetBase.of(this); + } +}