Unified the app's color palette & icons (#176)

* unified scaffold background color

* unified components elevation

* use filled tonal icon buttons

* aligned slider colors with guidelines

* removed unused `CloseButton`

* migrated to outlined icons

* moved timer list tile to the top of the section

* updated goldens

* migrated to the latest material utils

* reaplced `SchemeTonalSpot` with `SchemeRainbow`

* fixed ruler slider ticks color

* update goldens

* fixed tests

* fixed scheme mapping and returned to `SchemeTonalSpot`
This commit is contained in:
Vadim 2024-05-20 17:08:37 +02:00 committed by GitHub
parent 5c27f726c5
commit 8c016e548b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 176 additions and 188 deletions

View file

@ -7,6 +7,11 @@ class Dimens {
static const double borderRadiusL = 24;
static const double borderRadiusXL = 32;
static const double elevationLevel0 = 0;
static const double elevationLevel1 = 1;
static const double elevationLevel2 = 3;
static const double elevationLevel3 = 6;
static const double grid4 = 4;
static const double grid8 = 8;
static const double grid16 = 16;

View file

@ -29,14 +29,16 @@ ThemeData themeFrom(Color primaryColor, Brightness brightness) {
primaryColor: primaryColor,
colorScheme: scheme,
appBarTheme: AppBarTheme(
elevation: 4,
elevation: Dimens.elevationLevel0,
scrolledUnderElevation: Dimens.elevationLevel2,
color: scheme.surface,
foregroundColor: scheme.onBackground,
surfaceTintColor: scheme.surfaceTint,
),
cardTheme: CardTheme(
clipBehavior: Clip.antiAlias,
color: scheme.surface,
elevation: 4,
elevation: Dimens.elevationLevel1,
margin: EdgeInsets.zero,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Dimens.borderRadiusL)),
@ -46,7 +48,7 @@ ThemeData themeFrom(Color primaryColor, Brightness brightness) {
dialogTheme: DialogTheme(
backgroundColor: scheme.surface,
surfaceTintColor: scheme.surfaceTint,
elevation: 6,
elevation: Dimens.elevationLevel3,
),
dividerColor: scheme.outlineVariant,
dividerTheme: DividerThemeData(
@ -71,15 +73,19 @@ ThemeData themeFrom(Color primaryColor, Brightness brightness) {
}
ColorScheme _colorSchemeFromColor(Color primaryColor, Brightness brightness) {
final scheme = brightness == Brightness.light ? Scheme.light(primaryColor.value) : Scheme.dark(primaryColor.value);
final scheme = SchemeTonalSpot(
sourceColorHct: Hct.fromInt(primaryColor.value),
isDark: brightness == Brightness.dark,
contrastLevel: 0.0,
);
return ColorScheme(
brightness: brightness,
background: Color(scheme.background),
error: Color(scheme.error),
errorContainer: Color(scheme.errorContainer),
onBackground: Color(scheme.onBackground),
error: Color(scheme.error),
onError: Color(scheme.onError),
errorContainer: Color(scheme.errorContainer),
onErrorContainer: Color(scheme.onErrorContainer),
primary: Color(scheme.primary),
onPrimary: Color(scheme.onPrimary),
@ -87,17 +93,30 @@ ColorScheme _colorSchemeFromColor(Color primaryColor, Brightness brightness) {
onPrimaryContainer: Color(scheme.onPrimaryContainer),
secondary: Color(scheme.secondary),
onSecondary: Color(scheme.onSecondary),
surface: Color.alphaBlend(
Color(scheme.primary).withOpacity(0.05),
Color(scheme.background),
),
secondaryContainer: Color(scheme.secondaryContainer),
onSecondaryContainer: Color(scheme.onSecondaryContainer),
tertiary: Color(scheme.tertiary),
onTertiary: Color(scheme.onTertiary),
tertiaryContainer: Color(scheme.tertiaryContainer),
onTertiaryContainer: Color(scheme.onTertiaryContainer),
surface: Color(scheme.surface),
onSurface: Color(scheme.onSurface),
surfaceVariant: Color.alphaBlend(
Color(scheme.primary).withOpacity(0.5),
Color(scheme.background),
),
surfaceVariant: Color(scheme.surfaceVariant),
onSurfaceVariant: Color(scheme.onSurfaceVariant),
outline: Color(scheme.outline),
outlineVariant: Color(scheme.outlineVariant),
surfaceTint: Color(scheme.surfaceTint),
shadow: Color(scheme.shadow),
scrim: Color(scheme.scrim),
);
}
extension ElevatedSurfaceTheme on ColorScheme {
Color _surfaceWithElevation(double elevation) {
return ElevationOverlay.applySurfaceTint(surface, surfaceTint, elevation);
}
Color get surfaceElevated1 => _surfaceWithElevation(Dimens.elevationLevel1);
Color get surfaceElevated2 => _surfaceWithElevation(Dimens.elevationLevel2);
Color get surfaceElevated3 => _surfaceWithElevation(Dimens.elevationLevel3);
}

View file

@ -28,12 +28,12 @@ class MeteringBottomControls extends StatelessWidget {
Widget build(BuildContext context) {
return BottomControlsBar(
left: onSwitchEvSourceType != null
? IconButton(
? IconButton.filledTonal(
onPressed: onSwitchEvSourceType,
icon: Icon(
UserPreferencesProvider.evSourceTypeOf(context) != EvSourceType.camera
? Icons.camera_rear
: Icons.wb_incandescent,
? Icons.camera_rear_outlined
: Icons.wb_incandescent_outlined,
),
tooltip: UserPreferencesProvider.evSourceTypeOf(context) != EvSourceType.camera
? S.of(context).tooltipUseCamera
@ -46,9 +46,9 @@ class MeteringBottomControls extends StatelessWidget {
onPressed: onMeasure,
child: ev != null ? _EvValueText(ev: ev!, ev100: ev100!) : null,
),
right: IconButton(
right: IconButton.filledTonal(
onPressed: onSettings,
icon: const Icon(Icons.settings),
icon: const Icon(Icons.settings_outlined),
tooltip: S.of(context).tooltipOpenSettings,
),
);

View file

@ -21,7 +21,7 @@ class ExposureOffsetSlider extends StatelessWidget {
range: range,
value: value,
onChanged: onChanged,
icon: Icons.light_mode,
icon: Icons.light_mode_outlined,
defaultValue: 0,
rulerValueAdapter: (value) => value.toStringSignedAsFixed(0),
valueAdapter: (value) => S.of(context).evValue(value.toStringSignedAsFixed(1)),

View file

@ -32,7 +32,7 @@ class _ZoomSliderState extends State<ZoomSlider> {
range: widget.range,
value: widget.value,
onChanged: widget.onChanged,
icon: Icons.search,
icon: Icons.search_outlined,
defaultValue: EquipmentProfiles.selectedOf(context).lensZoom,
rulerValueAdapter: (value) => value.toStringAsFixed(0),
valueAdapter: (value) => value.toZoom(),

View file

@ -20,15 +20,12 @@ class CameraControlsPlaceholder extends StatelessWidget {
children: [
IconButton(
onPressed: onReset,
icon: Icon(error == CameraErrorType.permissionNotGranted ? Icons.settings : Icons.sync),
icon: Icon(error == CameraErrorType.permissionNotGranted ? Icons.settings_outlined : Icons.sync_outlined),
),
const SizedBox(height: Dimens.grid8),
Text(
error.toStringLocalized(context),
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: Theme.of(context).colorScheme.onBackground),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Theme.of(context).colorScheme.onBackground),
textAlign: TextAlign.center,
),
],

View file

@ -12,7 +12,7 @@ class CameraViewPlaceholder extends StatelessWidget {
return Card(
color: error != null ? null : Colors.black,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Dimens.borderRadiusM)),
child: Center(child: error != null ? const Icon(Icons.no_photography) : null),
child: Center(child: error != null ? const Icon(Icons.no_photography_outlined) : null),
);
}
}

View file

@ -19,7 +19,7 @@ class ExposurePairsList extends StatelessWidget {
duration: Dimens.switchDuration,
child: exposurePairs.isEmpty
? IconPlaceholder(
icon: Icons.not_interested,
icon: Icons.not_interested_outlined,
text: S.of(context).noExposurePairs,
)
: Stack(

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/res/theme.dart';
import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/shape_top_bar_metering.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/widget_container_readings.dart';
@ -19,7 +20,7 @@ class MeteringTopBar extends StatelessWidget {
Widget build(BuildContext context) {
return CustomPaint(
painter: MeteringTopBarShape(
color: Theme.of(context).colorScheme.surface,
color: Theme.of(context).colorScheme.surfaceElevated1,
appendixWidth: MediaQuery.of(context).size.width / 2 - Dimens.grid8 / 2 + Dimens.paddingM,
appendixHeight: appendixHeight,
),

View file

@ -11,7 +11,7 @@ class EquipmentProfilePicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<EquipmentProfile>(
icon: Icons.camera,
icon: Icons.camera_outlined,
title: S.of(context).equipmentProfile,
selectedValue: EquipmentProfiles.selectedOf(context),
values: EquipmentProfiles.of(context),

View file

@ -13,7 +13,7 @@ class FilmPicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<Film>(
icon: Icons.camera_roll,
icon: Icons.camera_roll_outlined,
title: S.of(context).film,
subtitle: S.of(context).filmReciprocityHint,
selectedValue: Films.selectedOf(context),
@ -23,17 +23,14 @@ class FilmPicker extends StatelessWidget {
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: _label(context),
value: Films.selectedOf(context).name.isEmpty
? S.of(context).none
: Films.selectedOf(context).name,
value: Films.selectedOf(context).name.isEmpty ? S.of(context).none : Films.selectedOf(context).name,
),
),
);
}
String _label(BuildContext context) {
if (Films.selectedOf(context) == const Film.other() ||
Films.selectedOf(context).iso == selectedIso.value) {
if (Films.selectedOf(context) == const Film.other() || Films.selectedOf(context).iso == selectedIso.value) {
return S.of(context).film;
}

View file

@ -18,16 +18,15 @@ class IsoValuePicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<IsoValue>(
icon: Icons.iso,
icon: Icons.iso_outlined,
title: S.of(context).iso,
subtitle: S.of(context).filmSpeed,
selectedValue: selectedValue,
values: values,
itemTitleBuilder: (_, value) => Text(value.value.toString()),
// using ascending order, because increase in film speed rises EV
itemTrailingBuilder: (selected, value) => value.value != selected.value
? Text(S.of(context).evValue(selected.toStringDifference(value)))
: null,
itemTrailingBuilder: (selected, value) =>
value.value != selected.value ? Text(S.of(context).evValue(selected.toStringDifference(value))) : null,
onChanged: onChanged,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(

View file

@ -18,7 +18,7 @@ class NdValuePicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<NdValue>(
icon: Icons.filter_b_and_w,
icon: Icons.filter_b_and_w_outlined,
title: S.of(context).nd,
subtitle: S.of(context).ndFilterFactor,
selectedValue: selectedValue,

View file

@ -47,31 +47,31 @@ class ReadingValueContainer extends StatelessWidget implements AnimatedDialogClo
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(Dimens.borderRadiusM),
child: ColoredBox(
return DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Dimens.borderRadiusM),
color: backgroundColor(context),
child: Padding(
padding: const EdgeInsets.all(Dimens.paddingM),
child: Stack(
children: [
if (locked)
Positioned(
top: 0,
right: 0,
child: Icon(
Icons.lock,
size: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
child: Padding(
padding: const EdgeInsets.all(Dimens.paddingM),
child: Stack(
children: [
if (locked)
Positioned(
top: 0,
right: 0,
child: Icon(
Icons.lock_outlined,
size: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: _items,
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: _items,
),
],
),
),
);

View file

@ -24,7 +24,6 @@ class MeteringScreen extends StatelessWidget {
Widget build(BuildContext context) {
return _InheritedListeners(
child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: Column(
children: [
Expanded(

View file

@ -9,7 +9,7 @@ class ReportIssueListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.bug_report),
leading: const Icon(Icons.bug_report_outlined),
title: Text(S.of(context).reportIssue),
onTap: () {
launchUrl(

View file

@ -8,7 +8,7 @@ class RestorePurchasesListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.restore),
leading: const Icon(Icons.restore_outlined),
title: Text(S.of(context).restorePurchases),
onTap: IAPProductsProvider.of(context).restorePurchases,
);

View file

@ -9,7 +9,7 @@ class SourceCodeListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.code),
leading: const Icon(Icons.code_outlined),
title: Text(S.of(context).sourceCode),
onTap: () {
launchUrl(

View file

@ -10,7 +10,7 @@ class WriteEmailListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.email),
leading: const Icon(Icons.email_outlined),
title: Text(S.of(context).writeEmail),
onTap: () {
final mailToUrl = Uri.parse('mailto:$contactEmail?subject=M3 Lightmeter');

View file

@ -12,7 +12,7 @@ class CaffeineListTile extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<CaffeineListTileBloc, bool>(
builder: (context, state) => SwitchListTile(
secondary: const Icon(Icons.screen_lock_portrait),
secondary: const Icon(Icons.screen_lock_portrait_outlined),
title: Text(S.of(context).keepScreenOn),
value: state,
onChanged: context.read<CaffeineListTileBloc>().onCaffeineChanged,

View file

@ -12,7 +12,7 @@ class HapticsListTile extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<HapticsListTileBloc, bool>(
builder: (context, state) => SwitchListTile(
secondary: const Icon(Icons.vibration),
secondary: const Icon(Icons.vibration_outlined),
title: Text(S.of(context).haptics),
value: state,
onChanged: context.read<HapticsListTileBloc>().onHapticsChanged,

View file

@ -10,14 +10,14 @@ class LanguageListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.language),
leading: const Icon(Icons.language_outlined),
title: Text(S.of(context).language),
trailing: Text(UserPreferencesProvider.localeOf(context).localizedName),
onTap: () {
showDialog<SupportedLocale>(
context: context,
builder: (_) => DialogPicker<SupportedLocale>(
icon: Icons.language,
icon: Icons.language_outlined,
title: S.of(context).chooseLanguage,
selectedValue: UserPreferencesProvider.localeOf(context),
values: SupportedLocale.values,

View file

@ -11,7 +11,7 @@ class VolumeActionsListTile extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<VolumeActionsListTileBloc, bool>(
builder: (context, state) => SwitchListTile(
secondary: const Icon(Icons.volume_up),
secondary: const Icon(Icons.volume_up_outlined),
title: Text(S.of(context).volumeKeysAction),
value: state,
onChanged: context.read<VolumeActionsListTileBloc>().onVolumeActionChanged,

View file

@ -17,9 +17,9 @@ class GeneralSettingsSection extends StatelessWidget {
return SettingsSection(
title: S.of(context).general,
children: [
const TimerListTileProvider(),
const CaffeineListTileProvider(),
const HapticsListTileProvider(),
const TimerListTileProvider(),
if (Platform.isAndroid) const VolumeActionsListTileProvider(),
const LanguageListTile(),
],

View file

@ -12,7 +12,7 @@ class BuyProListTile extends StatelessWidget {
final status = IAPProducts.productOf(context, IAPProductType.paidFeatures)?.status;
final isPending = status == IAPProductStatus.purchased || status == null;
return ListTile(
leading: const Icon(Icons.star),
leading: const Icon(Icons.star_outlined),
title: Text(S.of(context).unlockProFeatures),
onTap: !isPending
? () {

View file

@ -16,7 +16,7 @@ class CalibrationDialog extends StatelessWidget {
Widget build(BuildContext context) {
final bool hasLightSensor = ServicesProvider.of(context).environment.hasLightSensor;
return AlertDialog(
icon: const Icon(Icons.settings_brightness),
icon: const Icon(Icons.settings_brightness_outlined),
titlePadding: Dimens.dialogIconTitlePadding,
title: Text(S.of(context).calibration),
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingL),
@ -25,38 +25,27 @@ class CalibrationDialog extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Text(
hasLightSensor
? S.of(context).calibrationMessage
: S.of(context).calibrationMessageCameraOnly,
hasLightSensor ? S.of(context).calibrationMessage : S.of(context).calibrationMessageCameraOnly,
),
const SizedBox(height: Dimens.grid16),
BlocBuilder<CalibrationDialogBloc, CalibrationDialogState>(
buildWhen: (previous, current) =>
previous.cameraEvCalibration != current.cameraEvCalibration,
buildWhen: (previous, current) => previous.cameraEvCalibration != current.cameraEvCalibration,
builder: (context, state) => _CalibrationUnit(
title: S.of(context).camera,
value: state.cameraEvCalibration,
onChanged: (value) => context
.read<CalibrationDialogBloc>()
.add(CameraEvCalibrationChangedEvent(value)),
onReset: () => context
.read<CalibrationDialogBloc>()
.add(const CameraEvCalibrationResetEvent()),
onChanged: (value) => context.read<CalibrationDialogBloc>().add(CameraEvCalibrationChangedEvent(value)),
onReset: () => context.read<CalibrationDialogBloc>().add(const CameraEvCalibrationResetEvent()),
),
),
if (hasLightSensor)
BlocBuilder<CalibrationDialogBloc, CalibrationDialogState>(
buildWhen: (previous, current) =>
previous.lightSensorEvCalibration != current.lightSensorEvCalibration,
buildWhen: (previous, current) => previous.lightSensorEvCalibration != current.lightSensorEvCalibration,
builder: (context, state) => _CalibrationUnit(
title: S.of(context).lightSensor,
value: state.lightSensorEvCalibration,
onChanged: (value) => context
.read<CalibrationDialogBloc>()
.add(LightSensorEvCalibrationChangedEvent(value)),
onReset: () => context
.read<CalibrationDialogBloc>()
.add(const LightSensorEvCalibrationResetEvent()),
onChanged: (value) =>
context.read<CalibrationDialogBloc>().add(LightSensorEvCalibrationChangedEvent(value)),
onReset: () => context.read<CalibrationDialogBloc>().add(const LightSensorEvCalibrationResetEvent()),
),
),
],
@ -116,7 +105,7 @@ class _CalibrationUnit extends StatelessWidget {
),
IconButton(
onPressed: onReset,
icon: const Icon(Icons.sync),
icon: const Icon(Icons.sync_outlined),
tooltip: S.of(context).tooltipResetToZero,
),
],

View file

@ -9,7 +9,7 @@ class CalibrationListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.settings_brightness),
leading: const Icon(Icons.settings_brightness_outlined),
title: Text(S.of(context).calibration),
onTap: () {
showDialog<double>(

View file

@ -11,13 +11,13 @@ class CameraFeaturesListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IAPListTile(
leading: const Icon(Icons.camera_alt),
leading: const Icon(Icons.camera_alt_outlined),
title: Text(S.of(context).cameraFeatures),
onTap: () {
showDialog(
context: context,
builder: (_) => DialogSwitch<CameraFeature>(
icon: Icons.layers_outlined,
icon: Icons.camera_alt_outlined,
title: S.of(context).cameraFeatures,
values: UserPreferencesProvider.cameraConfigOf(context),
titleAdapter: (context, feature) {

View file

@ -171,7 +171,7 @@ class _AnimatedNameLeading extends AnimatedWidget {
return Padding(
padding: EdgeInsets.only(right: _progress.value * Dimens.grid8),
child: Icon(
Icons.edit,
Icons.edit_outlined,
size: _progress.value * Dimens.grid24,
),
);
@ -194,7 +194,7 @@ class _AnimatedArrowButton extends AnimatedWidget {
onPressed: onPressed,
icon: Transform.rotate(
angle: _progress.value * pi,
child: const Icon(Icons.keyboard_arrow_down),
child: const Icon(Icons.keyboard_arrow_down_outlined),
),
tooltip: _progress.value == 0 ? S.of(context).tooltipExpand : S.of(context).tooltipCollapse,
);
@ -239,7 +239,7 @@ class _AnimatedEquipmentListTiles extends AnimatedWidget {
child: Column(
children: [
FilterListTile<IsoValue>(
icon: Icons.iso,
icon: Icons.iso_outlined,
title: S.of(context).isoValues,
description: S.of(context).isoValuesFilterDescription,
values: IsoValue.values,
@ -247,7 +247,7 @@ class _AnimatedEquipmentListTiles extends AnimatedWidget {
onChanged: onIsoValuesSelecred,
),
FilterListTile<NdValue>(
icon: Icons.filter_b_and_w,
icon: Icons.filter_b_and_w_outlined,
title: S.of(context).ndFilters,
description: S.of(context).ndFiltersFilterDescription,
values: NdValue.values,
@ -255,7 +255,7 @@ class _AnimatedEquipmentListTiles extends AnimatedWidget {
onChanged: onNdValuesSelected,
),
RangePickerListTile<ApertureValue>(
icon: Icons.camera,
icon: Icons.camera_outlined,
title: S.of(context).apertureValues,
description: S.of(context).apertureValuesFilterDescription,
values: ApertureValue.values,
@ -263,7 +263,7 @@ class _AnimatedEquipmentListTiles extends AnimatedWidget {
onChanged: onApertureValuesSelected,
),
RangePickerListTile<ShutterSpeedValue>(
icon: Icons.shutter_speed,
icon: Icons.shutter_speed_outlined,
title: S.of(context).shutterSpeedValues,
description: S.of(context).shutterSpeedValuesFilterDescription,
values: ShutterSpeedValue.values,
@ -275,7 +275,7 @@ class _AnimatedEquipmentListTiles extends AnimatedWidget {
value.value == 1 ? S.of(context).shutterSpeedManual : value.toString(),
),
SliderPickerListTile(
icon: Icons.zoom_in,
icon: Icons.zoom_in_outlined,
title: S.of(context).lensZoom,
description: S.of(context).lensZoomDescription,
value: equipmentData.lensZoom,
@ -291,12 +291,12 @@ class _AnimatedEquipmentListTiles extends AnimatedWidget {
children: [
IconButton(
onPressed: onCopy,
icon: const Icon(Icons.copy),
icon: const Icon(Icons.copy_outlined),
tooltip: S.of(context).tooltipCopy,
),
IconButton(
onPressed: onDelete,
icon: const Icon(Icons.delete),
icon: const Icon(Icons.delete_outlined),
tooltip: S.of(context).tooltipDelete,
),
],

View file

@ -23,7 +23,7 @@ class _EquipmentProfileNameDialogState extends State<EquipmentProfileNameDialog>
@override
Widget build(BuildContext context) {
return AlertDialog(
icon: const Icon(Icons.edit),
icon: const Icon(Icons.edit_outlined),
titlePadding: Dimens.dialogIconTitlePadding,
title: Text(S.of(context).equipmentProfileName),
content: TextField(

View file

@ -32,7 +32,7 @@ class _EquipmentProfilesScreenState extends State<EquipmentProfilesScreen> {
appBarActions: [
IconButton(
onPressed: _addProfile,
icon: const Icon(Icons.add),
icon: const Icon(Icons.add_outlined),
tooltip: S.of(context).tooltipAdd,
),
],
@ -150,7 +150,7 @@ class _EquipmentProfilesListPlaceholder extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(Dimens.paddingL),
child: IconPlaceholder(
icon: Icons.add,
icon: Icons.add_outlined,
text: S.of(context).tapToAdd,
),
),

View file

@ -10,7 +10,7 @@ class EquipmentProfilesListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IAPListTile(
leading: const Icon(Icons.camera),
leading: const Icon(Icons.camera_outlined),
title: Text(S.of(context).equipmentProfiles),
onTap: () {
Navigator.of(context).push<EquipmentProfile>(

View file

@ -11,13 +11,13 @@ class FilmsListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IAPListTile(
leading: const Icon(Icons.camera_roll),
leading: const Icon(Icons.camera_roll_outlined),
title: Text(S.of(context).filmsInUse),
onTap: () {
showDialog<List<Film>>(
context: context,
builder: (_) => DialogFilter<Film>(
icon: const Icon(Icons.camera_roll),
icon: const Icon(Icons.camera_roll_outlined),
title: S.of(context).filmsInUse,
description: S.of(context).filmsInUseDescription,
values: Films.of(context).sublist(1),

View file

@ -10,14 +10,14 @@ class StopTypeListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.straighten),
leading: const Icon(Icons.straighten_outlined),
title: Text(S.of(context).fractionalStops),
trailing: Text(_typeToString(context, UserPreferencesProvider.stopTypeOf(context))),
onTap: () {
showDialog<StopType>(
context: context,
builder: (_) => DialogPicker<StopType>(
icon: Icons.straighten,
icon: Icons.straighten_outlined,
title: S.of(context).showFractionalStops,
selectedValue: UserPreferencesProvider.stopTypeOf(context),
values: StopType.values,

View file

@ -13,7 +13,7 @@ class ShowEv100ListTile extends StatelessWidget {
return Disable(
disable: !context.isPro,
child: SwitchListTile(
secondary: const Icon(Icons.adjust),
secondary: const Icon(Icons.adjust_outlined),
title: Text(S.of(context).showEv100),
value: context.isPro && UserPreferencesProvider.showEv100Of(context),
onChanged: (_) => UserPreferencesProvider.of(context).toggleShowEv100(),

View file

@ -109,7 +109,7 @@ class _DialogFilterState<T> extends State<DialogFilter<T>> {
width: 40,
child: IconButton(
padding: EdgeInsets.zero,
icon: Icon(_hasAnyUnselected ? Icons.select_all : Icons.deselect),
icon: Icon(_hasAnyUnselected ? Icons.select_all_outlined : Icons.deselect_outlined),
onPressed: _toggleAll,
tooltip: _hasAnyUnselected ? S.of(context).tooltipSelectAll : S.of(context).tooltipDesecelectAll,
),

View file

@ -10,7 +10,7 @@ class DynamicColorListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SwitchListTile(
secondary: const Icon(Icons.colorize),
secondary: const Icon(Icons.colorize_outlined),
title: Text(S.of(context).dynamicColor),
value: UserPreferencesProvider.dynamicColorStateOf(context) == DynamicColorState.enabled,
onChanged: UserPreferencesProvider.of(context).enableDynamicColor,

View file

@ -24,7 +24,7 @@ class _PrimaryColorDialogPickerState extends State<PrimaryColorDialogPicker> {
@override
Widget build(BuildContext context) {
return AlertDialog(
icon: const Icon(Icons.palette),
icon: const Icon(Icons.palette_outlined),
titlePadding: Dimens.dialogIconTitlePadding,
title: Text(S.of(context).choosePrimaryColor),
content: SizedBox(
@ -124,7 +124,7 @@ class _SelectableColorItemState extends State<_SelectableColorItem> {
duration: Dimens.durationS,
child: widget.selected
? Icon(
Icons.check,
Icons.check_outlined,
color: ThemeData.estimateBrightnessForColor(widget.color) == Brightness.light
? Colors.black
: Colors.white,

View file

@ -13,13 +13,13 @@ class PrimaryColorListTile extends StatelessWidget {
if (UserPreferencesProvider.dynamicColorStateOf(context) == DynamicColorState.enabled) {
return Disable(
child: ListTile(
leading: const Icon(Icons.palette),
leading: const Icon(Icons.palette_outlined),
title: Text(S.of(context).primaryColor),
),
);
}
return ListTile(
leading: const Icon(Icons.palette),
leading: const Icon(Icons.palette_outlined),
title: Text(S.of(context).primaryColor),
onTap: () {
showDialog<Color>(

View file

@ -10,14 +10,14 @@ class ThemeTypeListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.brightness_6),
leading: const Icon(Icons.brightness_6_outlined),
title: Text(S.of(context).theme),
trailing: Text(_typeToString(context, UserPreferencesProvider.themeTypeOf(context))),
onTap: () {
showDialog<ThemeType>(
context: context,
builder: (_) => DialogPicker<ThemeType>(
icon: Icons.brightness_6,
icon: Icons.brightness_6_outlined,
title: S.of(context).chooseTheme,
selectedValue: UserPreferencesProvider.themeTypeOf(context),
values: ThemeType.values,

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/res/theme.dart';
class BottomControlsBar extends StatelessWidget {
final Widget center;
@ -15,40 +16,27 @@ class BottomControlsBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return IconButtonTheme(
data: IconButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(scheme.surface),
elevation: const MaterialStatePropertyAll(4),
iconColor: MaterialStatePropertyAll(scheme.onSurface),
shadowColor: const MaterialStatePropertyAll(Colors.transparent),
surfaceTintColor: MaterialStatePropertyAll(scheme.surfaceTint),
fixedSize: const MaterialStatePropertyAll(Size(Dimens.grid48, Dimens.grid48)),
),
return ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(Dimens.borderRadiusL),
topRight: Radius.circular(Dimens.borderRadiusL),
),
child: ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(Dimens.borderRadiusL),
topRight: Radius.circular(Dimens.borderRadiusL),
),
child: ColoredBox(
color: Theme.of(context).colorScheme.surface,
child: SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingL),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
if (left != null) Expanded(child: Center(child: left)) else const Spacer(),
SizedBox.fromSize(
size: const Size.square(Dimens.grid72),
child: center,
),
if (right != null) Expanded(child: Center(child: right)) else const Spacer(),
],
),
child: ColoredBox(
color: Theme.of(context).colorScheme.surfaceElevated1,
child: SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingL),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
if (left != null) Expanded(child: Center(child: left)) else const Spacer(),
SizedBox.fromSize(
size: const Size.square(Dimens.grid72),
child: center,
),
if (right != null) Expanded(child: Center(child: right)) else const Spacer(),
],
),
),
),

View file

@ -1,15 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
class CloseButton extends StatelessWidget {
const CloseButton({super.key});
@override
Widget build(BuildContext context) {
return IconButton(
onPressed: Navigator.of(context).pop,
icon: const Icon(Icons.close),
tooltip: S.of(context).tooltipClose,
);
}
}

View file

@ -24,7 +24,7 @@ class ProFeaturesDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TransparentDialog(
icon: Icons.star,
icon: Icons.star_outlined,
title: S.of(context).proFeatures,
scrollableContent: false,
content: Flexible(

View file

@ -30,7 +30,7 @@ class RulerSlider extends StatelessWidget {
children: [
Text(
valueAdapter(value),
style: Theme.of(context).textTheme.labelLarge,
style: Theme.of(context).textTheme.labelLarge!.copyWith(color: Theme.of(context).colorScheme.onBackground),
),
const SizedBox(height: Dimens.grid4),
Expanded(
@ -53,7 +53,7 @@ class RulerSlider extends StatelessWidget {
),
),
IconButton(
icon: const Icon(Icons.sync),
icon: const Icon(Icons.sync_outlined),
onPressed: value != defaultValue ? () => onChanged(defaultValue) : null,
tooltip: S.of(context).tooltipResetToZero,
),
@ -78,6 +78,7 @@ class _Ruler extends StatelessWidget {
@override
Widget build(BuildContext context) {
final mainTicksFontSize = Theme.of(context).textTheme.bodySmall!.fontSize!;
final itemsColor = Theme.of(context).colorScheme.onBackground;
return LayoutBuilder(
builder: (context, constraints) {
final bool showAllMainTicks =
@ -103,11 +104,11 @@ class _Ruler extends StatelessWidget {
if (showValue)
Text(
rulerValueAdapter(index / 2 + min),
style: Theme.of(context).textTheme.bodySmall,
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: itemsColor),
),
const SizedBox(width: Dimens.grid4),
ColoredBox(
color: Theme.of(context).colorScheme.onBackground,
color: itemsColor,
child: SizedBox(
height: 1,
width: isMainTick ? Dimens.grid8 : Dimens.grid4,

View file

@ -42,7 +42,7 @@ class SliverScreen extends StatelessWidget {
if (Navigator.of(context).canPop())
IconButton(
onPressed: Navigator.of(context).pop,
icon: const Icon(Icons.close),
icon: const Icon(Icons.close_outlined),
tooltip: S.of(context).tooltipClose,
),
],

View file

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:lightmeter/data/models/exposure_pair.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/res/theme.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
@ -21,7 +22,7 @@ class TimerMeteringConfig extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
color: Theme.of(context).colorScheme.surfaceElevated1,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(Dimens.borderRadiusL),
bottomRight: Radius.circular(Dimens.borderRadiusL),

View file

@ -16,7 +16,7 @@ class TimerTimeline extends StatelessWidget {
Widget build(BuildContext context) {
return CustomPaint(
painter: _TimelinePainter(
backgroundColor: Theme.of(context).colorScheme.surface,
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
progressColor: Theme.of(context).colorScheme.primary,
progress: progress,
),

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/data/models/exposure_pair.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/shared/animated_circular_button/widget_button_circular_animated.dart';
import 'package:lightmeter/screens/shared/bottom_controls_bar/widget_bottom_controls_bar.dart';
@ -66,7 +67,6 @@ class TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
listenWhen: (previous, current) => previous.runtimeType != current.runtimeType,
listener: (context, state) => _updateAnimations(state),
child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@ -95,7 +95,7 @@ class TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
),
const Spacer(),
BottomControlsBar(
left: IconButton(
left: IconButton.filledTonal(
onPressed: () {
context.read<TimerBloc>().add(const ResetTimerEvent());
},
@ -118,7 +118,11 @@ class TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
),
),
),
right: const CloseButton(),
right: IconButton.filledTonal(
onPressed: Navigator.of(context).pop,
icon: const Icon(Icons.close),
tooltip: S.of(context).tooltipClose,
),
),
],
),

View file

@ -12,7 +12,7 @@ dependencies:
camera: 0.10.5+2
camera_android_camerax: 0.6.1+1
clipboard: 0.1.3
dynamic_color: 1.6.6
dynamic_color: 1.7.0
exif: 3.1.4
firebase_analytics: 10.6.2
firebase_core: 2.20.0
@ -58,6 +58,9 @@ dev_dependencies:
mocktail: 0.3.0
test: 1.24.3
dependency_overrides:
material_color_utilities: 0.11.1
flutter:
uses-material-design: true
assets:

View file

@ -47,7 +47,7 @@ void main() {
await pumpApplication(tester);
expectReadingValueContainerText(S.current.equipmentProfile);
await tester.openAnimatedPicker<EquipmentProfilePicker>();
expect(find.byIcon(Icons.camera), findsOneWidget);
expect(find.byIcon(Icons.camera_outlined), findsOneWidget);
expectDialogPickerText<EquipmentProfile>(S.current.equipmentProfile);
},
);

View file

@ -38,7 +38,7 @@ void main() {
await pumpApplication(tester);
expectReadingValueContainerText(S.current.iso);
await tester.openAnimatedPicker<IsoValuePicker>();
expect(find.byIcon(Icons.iso), findsOneWidget);
expect(find.byIcon(Icons.iso_outlined), findsOneWidget);
expectDialogPickerText<IsoValue>(S.current.iso);
expectDialogPickerText<IsoValue>(S.current.filmSpeed);
},

View file

@ -38,7 +38,7 @@ void main() {
await pumpApplication(tester);
expectReadingValueContainerText(S.current.nd);
await tester.openAnimatedPicker<NdValuePicker>();
expect(find.byIcon(Icons.filter_b_and_w), findsOneWidget);
expect(find.byIcon(Icons.filter_b_and_w_outlined), findsOneWidget);
expectDialogPickerText<NdValue>(S.current.nd);
expectDialogPickerText<NdValue>(S.current.ndFilterFactor);
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 KiB

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 854 KiB

After

Width:  |  Height:  |  Size: 858 KiB