mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-24 00:10:47 +00:00
ML-25 Revise permission handling (#26)
* fixed permission handling * translations cleanup * [Android] removed unused permissions
This commit is contained in:
parent
f75afbb6c2
commit
a183a5433e
19 changed files with 200 additions and 191 deletions
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
|
@ -8,6 +8,7 @@
|
|||
"name": "dev (android)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
//"flutterMode": "profile",
|
||||
"args": [
|
||||
"--flavor",
|
||||
"dev",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.vodemn.lightmeter">
|
||||
<application
|
||||
|
||||
<application
|
||||
android:label="Lightmeter"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
|
@ -18,12 +20,12 @@
|
|||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
|
@ -35,4 +37,8 @@
|
|||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
||||
</manifest>
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove" />
|
||||
<uses-permission android:name="android.permission.MICROPHONE" tools:node="remove" />
|
||||
<uses-feature android:name="android.hardware.microphone" android:required="false" />
|
||||
</manifest>
|
|
@ -1,17 +1,22 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:app_settings/app_settings.dart';
|
||||
import 'package:lightmeter/data/haptics_service.dart';
|
||||
import 'package:lightmeter/data/light_sensor_service.dart';
|
||||
import 'package:lightmeter/data/permissions_service.dart';
|
||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class MeteringInteractor {
|
||||
final UserPreferencesService _userPreferencesService;
|
||||
final HapticsService _hapticsService;
|
||||
final PermissionsService _permissionsService;
|
||||
final LightSensorService _lightSensorService;
|
||||
|
||||
const MeteringInteractor(
|
||||
this._userPreferencesService,
|
||||
this._hapticsService,
|
||||
this._permissionsService,
|
||||
this._lightSensorService,
|
||||
);
|
||||
|
||||
|
@ -30,6 +35,22 @@ class MeteringInteractor {
|
|||
if (_userPreferencesService.haptics) _hapticsService.responseVibration();
|
||||
}
|
||||
|
||||
Future<bool> checkCameraPermission() async {
|
||||
return _permissionsService
|
||||
.checkCameraPermission()
|
||||
.then((value) => value == PermissionStatus.granted);
|
||||
}
|
||||
|
||||
Future<bool> requestPermission() async {
|
||||
return _permissionsService
|
||||
.requestCameraPermission()
|
||||
.then((value) => value == PermissionStatus.granted);
|
||||
}
|
||||
|
||||
void openAppSettings() {
|
||||
AppSettings.openAppSettings();
|
||||
}
|
||||
|
||||
void enableHaptics(bool enable) => _userPreferencesService.haptics = enable;
|
||||
|
||||
Future<bool> hasAmbientLightSensor() async {
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
{
|
||||
"@@locale": "en",
|
||||
"permissionNeeded": "Permission needed",
|
||||
"permissionNeededMessage": "To use Lightmeter, turn on Camera permissions.",
|
||||
"openSettings": "Open settings",
|
||||
"fastestExposurePair": "Fastest",
|
||||
"slowestExposurePair": "Slowest",
|
||||
"ev": "{value} EV",
|
||||
|
@ -18,6 +15,9 @@
|
|||
"nd": "ND",
|
||||
"ndFilterFactor": "Neutral density filter factor",
|
||||
"noExposurePairs": "There are no exposure pairs for the selected settings.",
|
||||
"noCamerasDetected": "Seems like your device doesn't have any cameras connected",
|
||||
"noCameraPermission": "Camera permission is not granted.",
|
||||
"otherCameraError": "An error occurred when connecting to the camera.",
|
||||
"none": "None",
|
||||
"cancel": "Cancel",
|
||||
"select": "Select",
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:exif/exif.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
|
||||
|
@ -43,12 +44,14 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
|||
_observer = _WidgetsBindingObserver(_appLifecycleStateObserver);
|
||||
WidgetsBinding.instance.addObserver(_observer);
|
||||
|
||||
on<RequestPermissionEvent>(_onRequestPermission);
|
||||
on<OpenAppSettingsEvent>(_onOpenAppSettings);
|
||||
on<InitializeEvent>(_onInitialize);
|
||||
on<ZoomChangedEvent>(_onZoomChanged);
|
||||
on<ExposureOffsetChangedEvent>(_onExposureOffsetChanged);
|
||||
on<ExposureOffsetResetEvent>(_onExposureOffsetResetEvent);
|
||||
|
||||
add(const InitializeEvent());
|
||||
add(const RequestPermissionEvent());
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -71,10 +74,33 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _onRequestPermission(_, Emitter emit) async {
|
||||
final hasPermission = await _meteringInteractor.requestPermission();
|
||||
if (!hasPermission) {
|
||||
emit(const CameraErrorState(CameraErrorType.permissionNotGranted));
|
||||
} else {
|
||||
add(const InitializeEvent());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onOpenAppSettings(_, Emitter emit) async {
|
||||
_meteringInteractor.openAppSettings();
|
||||
}
|
||||
|
||||
Future<void> _onInitialize(_, Emitter emit) async {
|
||||
emit(const CameraLoadingState());
|
||||
final hasPermission = await _meteringInteractor.checkCameraPermission();
|
||||
if (!hasPermission) {
|
||||
emit(const CameraErrorState(CameraErrorType.permissionNotGranted));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final cameras = await availableCameras();
|
||||
if (cameras.isEmpty) {
|
||||
emit(const CameraErrorState(CameraErrorType.noCamerasDetected));
|
||||
return;
|
||||
}
|
||||
_cameraController = CameraController(
|
||||
cameras.firstWhere(
|
||||
(camera) => camera.lensDirection == CameraLensDirection.back,
|
||||
|
@ -111,7 +137,7 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
|||
|
||||
_emitActiveState(emit);
|
||||
} catch (e) {
|
||||
emit(const CameraErrorState());
|
||||
emit(const CameraErrorState(CameraErrorType.other));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +200,7 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
|||
add(const InitializeEvent());
|
||||
break;
|
||||
case AppLifecycleState.paused:
|
||||
case AppLifecycleState.inactive:
|
||||
case AppLifecycleState.detached:
|
||||
_cameraController?.dispose();
|
||||
_cameraController = null;
|
||||
break;
|
||||
|
@ -186,11 +212,16 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
|||
/// This is needed only because we cannot use `with` with mixins
|
||||
class _WidgetsBindingObserver with WidgetsBindingObserver {
|
||||
final ValueChanged<AppLifecycleState> onLifecycleStateChanged;
|
||||
AppLifecycleState? _prevState;
|
||||
|
||||
_WidgetsBindingObserver(this.onLifecycleStateChanged);
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (_prevState == AppLifecycleState.inactive && state == AppLifecycleState.resumed) {
|
||||
return;
|
||||
}
|
||||
_prevState = state;
|
||||
onLifecycleStateChanged(state);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||
|
||||
class CameraControlsPlaceholder extends StatelessWidget {
|
||||
final CameraErrorType error;
|
||||
final VoidCallback onReset;
|
||||
|
||||
const CameraControlsPlaceholder({
|
||||
required this.error,
|
||||
required this.onReset,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: onReset,
|
||||
icon: Icon(error == CameraErrorType.permissionNotGranted ? Icons.settings : Icons.sync),
|
||||
),
|
||||
const SizedBox(height: Dimens.grid8),
|
||||
Text(
|
||||
error.toStringLocalized(context),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(color: Theme.of(context).colorScheme.onBackground),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,19 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||
|
||||
class CameraViewPlaceholder extends StatelessWidget {
|
||||
const CameraViewPlaceholder({super.key});
|
||||
final CameraErrorType? error;
|
||||
|
||||
const CameraViewPlaceholder({required this.error, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Dimens.borderRadiusM)),
|
||||
child: const Center(child: Icon(Icons.no_photography)),
|
||||
child: Center(
|
||||
child: error != null ? const Icon(Icons.no_photography) : const CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,22 @@ abstract class CameraContainerEvent {
|
|||
const CameraContainerEvent();
|
||||
}
|
||||
|
||||
class RequestPermissionEvent extends CameraContainerEvent {
|
||||
const RequestPermissionEvent();
|
||||
}
|
||||
|
||||
class OpenAppSettingsEvent extends CameraContainerEvent {
|
||||
const OpenAppSettingsEvent();
|
||||
}
|
||||
|
||||
class InitializeEvent extends CameraContainerEvent {
|
||||
const InitializeEvent();
|
||||
}
|
||||
|
||||
class ReinitializeEvent extends CameraContainerEvent {
|
||||
const ReinitializeEvent();
|
||||
}
|
||||
|
||||
class ZoomChangedEvent extends CameraContainerEvent {
|
||||
final double value;
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
|
||||
enum CameraErrorType { noCamerasDetected, permissionNotGranted, other }
|
||||
|
||||
extension CameraErrorTypeString on CameraErrorType {
|
||||
String toStringLocalized(BuildContext context) {
|
||||
switch (this) {
|
||||
case CameraErrorType.noCamerasDetected:
|
||||
return S.of(context).noCamerasDetected;
|
||||
case CameraErrorType.permissionNotGranted:
|
||||
return S.of(context).noCameraPermission;
|
||||
default:
|
||||
return S.of(context).otherCameraError;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ class CameraContainerProvider extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
lazy: false,
|
||||
create: (context) => CameraContainerBloc(
|
||||
context.read<MeteringInteractor>(),
|
||||
context.read<MeteringCommunicationBloc>(),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:camera/camera.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||
|
||||
abstract class CameraContainerState {
|
||||
const CameraContainerState();
|
||||
|
@ -36,5 +37,7 @@ class CameraActiveState extends CameraContainerState {
|
|||
}
|
||||
|
||||
class CameraErrorState extends CameraContainerState {
|
||||
const CameraErrorState();
|
||||
final CameraErrorType error;
|
||||
|
||||
const CameraErrorState(this.error);
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@ import 'package:lightmeter/data/models/photography_values/nd_value.dart';
|
|||
import 'package:lightmeter/platform_config.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_view/widget_camera_view.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/widget_container_readings.dart';
|
||||
|
||||
import 'bloc_container_camera.dart';
|
||||
import 'components/camera_controls/widget_camera_controls.dart';
|
||||
import 'components/camera_controls_placeholder/widget_placeholder_camera_controls.dart';
|
||||
import 'components/camera_view_placeholder/widget_placeholder_camera_view.dart';
|
||||
import 'event_container_camera.dart';
|
||||
import 'state_container_camera.dart';
|
||||
|
@ -78,10 +80,17 @@ class _CameraViewBuilder extends StatelessWidget {
|
|||
return AspectRatio(
|
||||
aspectRatio: PlatformConfig.cameraPreviewAspectRatio,
|
||||
child: BlocBuilder<CameraContainerBloc, CameraContainerState>(
|
||||
buildWhen: (previous, current) => current is CameraInitializedState,
|
||||
builder: (context, state) => state is CameraInitializedState
|
||||
? Center(child: CameraView(controller: state.controller))
|
||||
: const CameraViewPlaceholder(),
|
||||
buildWhen: (previous, current) =>
|
||||
current is CameraLoadingState ||
|
||||
current is CameraInitializedState ||
|
||||
current is CameraErrorState,
|
||||
builder: (context, state) {
|
||||
if (state is CameraInitializedState) {
|
||||
return Center(child: CameraView(controller: state.controller));
|
||||
} else {
|
||||
return CameraViewPlaceholder(error: state is CameraErrorState ? state.error : null);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -95,23 +104,40 @@ class _CameraControlsBuilder extends StatelessWidget {
|
|||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
||||
child: BlocBuilder<CameraContainerBloc, CameraContainerState>(
|
||||
builder: (context, state) => AnimatedSwitcher(
|
||||
duration: Dimens.durationS,
|
||||
child: state is CameraActiveState
|
||||
? CameraControls(
|
||||
exposureOffsetRange: state.exposureOffsetRange,
|
||||
exposureOffsetValue: state.currentExposureOffset,
|
||||
onExposureOffsetChanged: (value) {
|
||||
context.read<CameraContainerBloc>().add(ExposureOffsetChangedEvent(value));
|
||||
},
|
||||
zoomRange: state.zoomRange,
|
||||
zoomValue: state.currentZoom,
|
||||
onZoomChanged: (value) {
|
||||
context.read<CameraContainerBloc>().add(ZoomChangedEvent(value));
|
||||
},
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
builder: (context, state) {
|
||||
late final Widget child;
|
||||
if (state is CameraActiveState) {
|
||||
child = CameraControls(
|
||||
exposureOffsetRange: state.exposureOffsetRange,
|
||||
exposureOffsetValue: state.currentExposureOffset,
|
||||
onExposureOffsetChanged: (value) {
|
||||
context.read<CameraContainerBloc>().add(ExposureOffsetChangedEvent(value));
|
||||
},
|
||||
zoomRange: state.zoomRange,
|
||||
zoomValue: state.currentZoom,
|
||||
onZoomChanged: (value) {
|
||||
context.read<CameraContainerBloc>().add(ZoomChangedEvent(value));
|
||||
},
|
||||
);
|
||||
} else if (state is CameraErrorState) {
|
||||
child = CameraControlsPlaceholder(
|
||||
error: state.error,
|
||||
onReset: () {
|
||||
context.read<CameraContainerBloc>().add(
|
||||
state.error == CameraErrorType.permissionNotGranted
|
||||
? const OpenAppSettingsEvent()
|
||||
: const InitializeEvent());
|
||||
},
|
||||
);
|
||||
} else {
|
||||
child = const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return AnimatedSwitcher(
|
||||
duration: Dimens.durationS,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||
import 'package:lightmeter/data/haptics_service.dart';
|
||||
import 'package:lightmeter/data/light_sensor_service.dart';
|
||||
import 'package:lightmeter/data/models/photography_values/photography_value.dart';
|
||||
import 'package:lightmeter/data/permissions_service.dart';
|
||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
@ -25,6 +26,7 @@ class _MeteringFlowState extends State<MeteringFlow> {
|
|||
create: (context) => MeteringInteractor(
|
||||
context.read<UserPreferencesService>(),
|
||||
context.read<HapticsService>(),
|
||||
context.read<PermissionsService>(),
|
||||
context.read<LightSensorService>(),
|
||||
),
|
||||
child: MultiBlocProvider(
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/permissions_service.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import 'event_permissions_check.dart';
|
||||
import 'state_permissions_check.dart';
|
||||
|
||||
class PermissionsCheckBloc extends Bloc<PermissionsCheckEvent, PermissionsCheckState> {
|
||||
final PermissionsService _permissionsService;
|
||||
|
||||
PermissionsCheckBloc(this._permissionsService) : super(const LoadingState()) {
|
||||
on<PermissionsGrantedEvent>((event, emit) => emit(const PermissionsGrantedState()));
|
||||
on<PermissionsDeniedEvent>((event, emit) => emit(const PermissionsDeniedState()));
|
||||
_checkAndRequestPermissions();
|
||||
}
|
||||
|
||||
Future<void> _checkAndRequestPermissions() async {
|
||||
_permissionsService.checkCameraPermission().then((value) {
|
||||
switch (value) {
|
||||
case PermissionStatus.permanentlyDenied:
|
||||
case PermissionStatus.restricted:
|
||||
add(const PermissionsDeniedEvent());
|
||||
break;
|
||||
case PermissionStatus.denied:
|
||||
_permissionsService.requestCameraPermission().then((value) {
|
||||
switch (value) {
|
||||
case PermissionStatus.permanentlyDenied:
|
||||
case PermissionStatus.restricted:
|
||||
case PermissionStatus.denied:
|
||||
add(const PermissionsDeniedEvent());
|
||||
break;
|
||||
case PermissionStatus.limited:
|
||||
case PermissionStatus.granted:
|
||||
add(const PermissionsGrantedEvent());
|
||||
break;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case PermissionStatus.limited:
|
||||
case PermissionStatus.granted:
|
||||
add(const PermissionsGrantedEvent());
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
abstract class PermissionsCheckEvent {
|
||||
const PermissionsCheckEvent();
|
||||
}
|
||||
|
||||
class PermissionsDeniedEvent extends PermissionsCheckEvent {
|
||||
const PermissionsDeniedEvent();
|
||||
}
|
||||
|
||||
class PermissionsGrantedEvent extends PermissionsCheckEvent {
|
||||
const PermissionsGrantedEvent();
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/permissions_service.dart';
|
||||
import 'package:lightmeter/screens/permissions_check/screen_permissions_check.dart';
|
||||
|
||||
import 'bloc_permissions_check.dart';
|
||||
|
||||
class PermissionsCheckFlow extends StatelessWidget {
|
||||
const PermissionsCheckFlow({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => PermissionsCheckBloc(context.read<PermissionsService>()),
|
||||
child: const PermissionsCheckScreen(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
|
||||
import 'bloc_permissions_check.dart';
|
||||
import 'state_permissions_check.dart';
|
||||
|
||||
class PermissionsCheckScreen extends StatelessWidget {
|
||||
const PermissionsCheckScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(Dimens.paddingM * 2),
|
||||
child: Center(
|
||||
child: BlocConsumer<PermissionsCheckBloc, PermissionsCheckState>(
|
||||
listener: (context, state) {
|
||||
if (state is PermissionsGrantedState) {
|
||||
Navigator.of(context).pushReplacementNamed("metering");
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return AnimatedSwitcher(
|
||||
duration: Dimens.durationS,
|
||||
child: state is LoadingState
|
||||
? const CircularProgressIndicator()
|
||||
: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).permissionNeeded,
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: Dimens.grid16),
|
||||
Text(
|
||||
S.of(context).permissionNeededMessage,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: Dimens.grid24),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Text(S.of(context).openSettings),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
abstract class PermissionsCheckState {
|
||||
const PermissionsCheckState();
|
||||
}
|
||||
|
||||
class LoadingState extends PermissionsCheckState {
|
||||
const LoadingState();
|
||||
}
|
||||
|
||||
class PermissionsGrantedState extends PermissionsCheckState {
|
||||
const PermissionsGrantedState();
|
||||
}
|
||||
|
||||
class PermissionsDeniedState extends PermissionsCheckState {
|
||||
const PermissionsDeniedState();
|
||||
}
|
|
@ -7,6 +7,7 @@ environment:
|
|||
sdk: ">=2.18.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
app_settings: 4.2.0
|
||||
camera: 0.10.0+4
|
||||
exif: 3.1.2
|
||||
dynamic_color: 1.5.4
|
||||
|
|
Loading…
Reference in a new issue