mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-24 16:30:40 +00:00
[FilmEditScreen
] added validation
This commit is contained in:
parent
b7f0305055
commit
1eebc7008e
8 changed files with 229 additions and 41 deletions
|
@ -150,5 +150,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"close": "Close"
|
"close": "Close",
|
||||||
|
"filmFormula": "Formula",
|
||||||
|
"filmFormulaExponential": "T=t^Rf",
|
||||||
|
"filmFormulaExponentialRf": "Rf",
|
||||||
|
"name": "Name"
|
||||||
}
|
}
|
|
@ -67,7 +67,8 @@ ThemeData themeFrom(Color primaryColor, Brightness brightness) {
|
||||||
style: ListTileStyle.list,
|
style: ListTileStyle.list,
|
||||||
iconColor: scheme.onSurface,
|
iconColor: scheme.onSurface,
|
||||||
textColor: scheme.onSurface,
|
textColor: scheme.onSurface,
|
||||||
subtitleTextStyle: theme.textTheme.bodyMedium!.copyWith(color: scheme.onSurfaceVariant),
|
subtitleTextStyle: theme.textTheme.bodyMedium,
|
||||||
|
leadingAndTrailingTextStyle: theme.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,18 @@ import 'package:lightmeter/screens/film_edit/state_film_edit.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class FilmEditBloc extends Bloc<FilmEditEvent, FilmEditState> {
|
class FilmEditBloc extends Bloc<FilmEditEvent, FilmEditState> {
|
||||||
|
final FilmExponential _originalFilm;
|
||||||
|
FilmExponential _newFilm;
|
||||||
|
|
||||||
FilmEditBloc(FilmExponential film)
|
FilmEditBloc(FilmExponential film)
|
||||||
: super(
|
: _originalFilm = film,
|
||||||
|
_newFilm = film,
|
||||||
|
super(
|
||||||
FilmEditState(
|
FilmEditState(
|
||||||
film,
|
name: film.name,
|
||||||
IsoValue.values.firstWhere((element) => element.value == film.iso),
|
isoValue: IsoValue.values.firstWhere((element) => element.value == film.iso),
|
||||||
|
exponent: film.exponent,
|
||||||
|
canSave: false,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
on<FilmEditEvent>(
|
on<FilmEditEvent>(
|
||||||
|
@ -27,18 +34,47 @@ class FilmEditBloc extends Bloc<FilmEditEvent, FilmEditState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onNameChanged(FilmEditNameChangedEvent event, Emitter emit) async {}
|
Future<void> _onNameChanged(FilmEditNameChangedEvent event, Emitter emit) async {
|
||||||
|
_newFilm = _newFilm.copyWith(name: event.name);
|
||||||
Future<void> _onIsoChanged(FilmEditIsoChangedEvent event, Emitter emit) async {
|
|
||||||
emit(
|
emit(
|
||||||
FilmEditState(
|
FilmEditState(
|
||||||
state.film.copyWith(iso: event.iso.value),
|
name: event.name,
|
||||||
event.iso,
|
isoValue: state.isoValue,
|
||||||
|
exponent: state.exponent,
|
||||||
|
canSave: _canSave(event.name, state.exponent),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onExpChanged(FilmEditExpChangedEvent event, Emitter emit) async {}
|
Future<void> _onIsoChanged(FilmEditIsoChangedEvent event, Emitter emit) async {
|
||||||
|
_newFilm = _newFilm.copyWith(iso: event.iso.value);
|
||||||
|
emit(
|
||||||
|
FilmEditState(
|
||||||
|
name: state.name,
|
||||||
|
isoValue: event.iso,
|
||||||
|
exponent: state.exponent,
|
||||||
|
canSave: _canSave(state.name, state.exponent),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onExpChanged(FilmEditExpChangedEvent event, Emitter emit) async {
|
||||||
|
if (event.exponent != null) {
|
||||||
|
_newFilm = _newFilm.copyWith(exponent: event.exponent);
|
||||||
|
}
|
||||||
|
emit(
|
||||||
|
FilmEditState(
|
||||||
|
name: state.name,
|
||||||
|
isoValue: state.isoValue,
|
||||||
|
exponent: event.exponent,
|
||||||
|
canSave: _canSave(state.name, event.exponent),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _onSave(FilmEditSaveEvent _, Emitter emit) async {}
|
Future<void> _onSave(FilmEditSaveEvent _, Emitter emit) async {}
|
||||||
|
|
||||||
|
bool _canSave(String name, double? exponent) {
|
||||||
|
return name.isNotEmpty && exponent != null && _newFilm != _originalFilm;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
import 'package:lightmeter/screens/shared/text_field/widget_text_field.dart';
|
||||||
|
|
||||||
|
class ExponentialFormulaInput extends StatefulWidget {
|
||||||
|
final double? value;
|
||||||
|
final ValueChanged<double?> onChanged;
|
||||||
|
|
||||||
|
const ExponentialFormulaInput({
|
||||||
|
super.key,
|
||||||
|
required this.value,
|
||||||
|
required this.onChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ExponentialFormulaInput> createState() => _ExponentialFormulaInputState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ExponentialFormulaInputState extends State<ExponentialFormulaInput> {
|
||||||
|
TextStyle get style =>
|
||||||
|
Theme.of(context).textTheme.bodyMedium!.copyWith(color: Theme.of(context).listTileTheme.textColor);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.show_chart),
|
||||||
|
title: Text(S.of(context).filmFormula),
|
||||||
|
trailing: Text(S.of(context).filmFormulaExponential),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const SizedBox(),
|
||||||
|
title: Text(
|
||||||
|
S.of(context).filmFormulaExponentialRf,
|
||||||
|
style: Theme.of(context).listTileTheme.titleTextStyle,
|
||||||
|
),
|
||||||
|
trailing: SizedBox(
|
||||||
|
width: _textInputWidth(context),
|
||||||
|
child: LightmeterTextField(
|
||||||
|
initialValue: widget.value?.toString() ?? '',
|
||||||
|
inputFormatters: [FilteringTextInputFormatter.allow(RegExp("[0-9.]"))],
|
||||||
|
onChanged: (value) {
|
||||||
|
widget.onChanged(double.tryParse(value));
|
||||||
|
},
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: style,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _textInputWidth(BuildContext context) {
|
||||||
|
final textPainter = TextPainter(
|
||||||
|
text: TextSpan(text: widget.value.toString(), style: style),
|
||||||
|
maxLines: 1,
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
)..layout();
|
||||||
|
return textPainter.maxIntrinsicWidth + Dimens.grid4;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,9 +17,9 @@ class FilmEditIsoChangedEvent extends FilmEditEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
class FilmEditExpChangedEvent extends FilmEditEvent {
|
class FilmEditExpChangedEvent extends FilmEditEvent {
|
||||||
final double exp;
|
final double? exponent;
|
||||||
|
|
||||||
const FilmEditExpChangedEvent(this.exp);
|
const FilmEditExpChangedEvent(this.exponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
class FilmEditSaveEvent extends FilmEditEvent {
|
class FilmEditSaveEvent extends FilmEditEvent {
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/film_edit/bloc_film_edit.dart';
|
import 'package:lightmeter/screens/film_edit/bloc_film_edit.dart';
|
||||||
|
import 'package:lightmeter/screens/film_edit/components/exponential_formula_input/widget_input_exponential_formula.dart';
|
||||||
import 'package:lightmeter/screens/film_edit/event_film_edit.dart';
|
import 'package:lightmeter/screens/film_edit/event_film_edit.dart';
|
||||||
import 'package:lightmeter/screens/film_edit/state_film_edit.dart';
|
import 'package:lightmeter/screens/film_edit/state_film_edit.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart';
|
||||||
import 'package:lightmeter/screens/settings/components/shared/dialog_filter/widget_dialog_filter.dart';
|
|
||||||
import 'package:lightmeter/screens/settings/components/shared/expandable_section_list/widget_expandable_section_list.dart';
|
|
||||||
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
|
import 'package:lightmeter/screens/shared/text_field/widget_text_field.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class FilmEditScreen extends StatefulWidget {
|
class FilmEditScreen extends StatefulWidget {
|
||||||
|
@ -22,37 +24,54 @@ class _FilmEditScreenState extends State<FilmEditScreen> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SliverScreen(
|
return SliverScreen(
|
||||||
title: BlocBuilder<FilmEditBloc, FilmEditState>(
|
title: BlocBuilder<FilmEditBloc, FilmEditState>(
|
||||||
buildWhen: (previous, current) => previous.film.name != current.film.name,
|
buildWhen: (previous, current) => previous.name != current.name,
|
||||||
builder: (context, state) => TextFormField(
|
builder: (_, state) => Text(state.name),
|
||||||
initialValue: state.film.name,
|
|
||||||
onChanged: (value) {},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
appBarActions: [
|
appBarActions: [
|
||||||
IconButton(
|
BlocBuilder<FilmEditBloc, FilmEditState>(
|
||||||
onPressed: () {},
|
buildWhen: (previous, current) => previous.canSave != current.canSave,
|
||||||
icon: const Icon(Icons.save),
|
builder: (context, state) => IconButton(
|
||||||
|
onPressed: state.canSave ? () {} : null,
|
||||||
|
icon: const Icon(Icons.save),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: BlocBuilder<FilmEditBloc, FilmEditState>(
|
child: Padding(
|
||||||
buildWhen: (previous, current) => previous.isoValue != current.isoValue,
|
padding: const EdgeInsets.fromLTRB(
|
||||||
builder: (context, state) => _IsoPickerListTile(
|
Dimens.paddingM,
|
||||||
selected: state.isoValue,
|
0,
|
||||||
onChanged: (value) {
|
Dimens.paddingM,
|
||||||
context.read<FilmEditBloc>().add(FilmEditIsoChangedEvent(value));
|
Dimens.paddingM,
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
child: Card(
|
||||||
),
|
child: Padding(
|
||||||
SliverToBoxAdapter(
|
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
||||||
child: BlocBuilder<FilmEditBloc, FilmEditState>(
|
child: Column(
|
||||||
buildWhen: (previous, current) => previous.film.exponent != current.film.exponent,
|
children: [
|
||||||
builder: (context, state) => ListTile(
|
_NameFieldBuilder(),
|
||||||
leading: const Icon(Icons.equalizer),
|
BlocBuilder<FilmEditBloc, FilmEditState>(
|
||||||
title: Text('Formula'),
|
buildWhen: (previous, current) => previous.isoValue != current.isoValue,
|
||||||
trailing: Text(state.film.exponent.toString()),
|
builder: (context, state) => _IsoPickerListTile(
|
||||||
|
selected: state.isoValue,
|
||||||
|
onChanged: (value) {
|
||||||
|
context.read<FilmEditBloc>().add(FilmEditIsoChangedEvent(value));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
BlocBuilder<FilmEditBloc, FilmEditState>(
|
||||||
|
buildWhen: (previous, current) => previous.exponent != current.exponent,
|
||||||
|
builder: (context, state) => ExponentialFormulaInput(
|
||||||
|
value: state.exponent,
|
||||||
|
onChanged: (value) {
|
||||||
|
context.read<FilmEditBloc>().add(FilmEditExpChangedEvent(value));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -62,6 +81,33 @@ class _FilmEditScreenState extends State<FilmEditScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _NameFieldBuilder extends StatelessWidget {
|
||||||
|
const _NameFieldBuilder();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: Dimens.paddingM,
|
||||||
|
top: Dimens.paddingS / 2,
|
||||||
|
right: Dimens.paddingL,
|
||||||
|
bottom: Dimens.paddingS / 2,
|
||||||
|
),
|
||||||
|
child: BlocBuilder<FilmEditBloc, FilmEditState>(
|
||||||
|
buildWhen: (previous, current) => previous.name != current.name,
|
||||||
|
builder: (context, state) => LightmeterTextField(
|
||||||
|
initialValue: state.name,
|
||||||
|
onChanged: (value) {
|
||||||
|
context.read<FilmEditBloc>().add(FilmEditNameChangedEvent(value));
|
||||||
|
},
|
||||||
|
style: Theme.of(context).listTileTheme.titleTextStyle,
|
||||||
|
leading: const Icon(Icons.edit_outlined),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _IsoPickerListTile extends StatelessWidget {
|
class _IsoPickerListTile extends StatelessWidget {
|
||||||
final IsoValue selected;
|
final IsoValue selected;
|
||||||
final ValueChanged<IsoValue> onChanged;
|
final ValueChanged<IsoValue> onChanged;
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class FilmEditState {
|
class FilmEditState {
|
||||||
final FilmExponential film;
|
final String name;
|
||||||
final IsoValue isoValue;
|
final IsoValue isoValue;
|
||||||
|
final double? exponent;
|
||||||
|
final bool canSave;
|
||||||
|
|
||||||
const FilmEditState(this.film, this.isoValue);
|
const FilmEditState({
|
||||||
|
required this.name,
|
||||||
|
required this.isoValue,
|
||||||
|
required this.exponent,
|
||||||
|
required this.canSave,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
29
lib/screens/shared/text_field/widget_text_field.dart
Normal file
29
lib/screens/shared/text_field/widget_text_field.dart
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LightmeterTextField extends TextFormField {
|
||||||
|
LightmeterTextField({
|
||||||
|
super.controller,
|
||||||
|
super.focusNode,
|
||||||
|
super.initialValue,
|
||||||
|
super.inputFormatters,
|
||||||
|
super.onChanged,
|
||||||
|
super.style,
|
||||||
|
super.textAlign,
|
||||||
|
Widget? leading,
|
||||||
|
}) : super(
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
maxLines: 1,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
errorStyle: const TextStyle(fontSize: 0),
|
||||||
|
icon: leading,
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue