Apply a Material 3 seed-green palette and rounded components across the home, inventory, quick-add, quantity picker and variety-detail screens. The full-screen photo viewer can now set any photo as the cover (via attachment sort order) and delete after confirmation. Lot forms and packaging surface as choice chips. Extract About out of Settings into its own screen (app version via package_info_plus, website link). Add es/en strings for the new lot types, presentation, home tagline and About. Update Seedees v2 mockups.
159 lines
5.1 KiB
Dart
159 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import '../data/variety_repository.dart';
|
|
import '../i18n/strings.g.dart';
|
|
import '../state/quick_add_cubit.dart';
|
|
import 'photo_pick.dart';
|
|
import 'quantity_picker.dart';
|
|
|
|
Future<void> showQuickAddSheet(
|
|
BuildContext context, {
|
|
required VarietyRepository repository,
|
|
}) {
|
|
return showModalBottomSheet<void>(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
builder: (_) => BlocProvider(
|
|
create: (_) => QuickAddCubit(repository),
|
|
child: const QuickAddSheet(),
|
|
),
|
|
);
|
|
}
|
|
|
|
class QuickAddSheet extends StatelessWidget {
|
|
const QuickAddSheet({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final t = context.t;
|
|
return BlocConsumer<QuickAddCubit, QuickAddState>(
|
|
listenWhen: (prev, curr) => !prev.submitted && curr.submitted,
|
|
listener: (context, state) => Navigator.of(context).pop(),
|
|
builder: (context, state) {
|
|
final cubit = context.read<QuickAddCubit>();
|
|
return Padding(
|
|
padding: EdgeInsets.only(
|
|
left: 16,
|
|
right: 16,
|
|
top: 16,
|
|
bottom: MediaQuery.of(context).viewInsets.bottom + 16,
|
|
),
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Text(
|
|
t.quickAdd.title,
|
|
style: Theme.of(context).textTheme.titleLarge,
|
|
),
|
|
const SizedBox(height: 12),
|
|
TextField(
|
|
key: const Key('quickAdd.labelField'),
|
|
autofocus: true,
|
|
textInputAction: TextInputAction.done,
|
|
decoration: InputDecoration(
|
|
labelText: t.quickAdd.labelField,
|
|
errorText: state.showLabelError
|
|
? t.quickAdd.labelRequired
|
|
: null,
|
|
border: const OutlineInputBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
|
),
|
|
),
|
|
onChanged: cubit.labelChanged,
|
|
onSubmitted: (_) => cubit.submit(),
|
|
),
|
|
const SizedBox(height: 16),
|
|
LotTypeSelector(
|
|
value: state.lotType,
|
|
onChanged: (type) {
|
|
cubit
|
|
..setLotType(type)
|
|
..setQuantity(null);
|
|
},
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
t.quickAdd.quantity,
|
|
style: Theme.of(context).textTheme.labelLarge,
|
|
),
|
|
const SizedBox(height: 8),
|
|
QuantityPicker(
|
|
type: state.lotType,
|
|
value: state.quantity,
|
|
onChanged: cubit.setQuantity,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: TextButton.icon(
|
|
onPressed: cubit.toggleExpanded,
|
|
icon: Icon(
|
|
state.expanded ? Icons.expand_less : Icons.expand_more,
|
|
),
|
|
label: Text(t.quickAdd.more),
|
|
),
|
|
),
|
|
if (state.expanded) const _MoreSection(),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: Text(t.quickAdd.cancel),
|
|
),
|
|
const Spacer(),
|
|
FilledButton(
|
|
key: const Key('quickAdd.save'),
|
|
onPressed: state.submitting ? null : cubit.submit,
|
|
child: Text(t.quickAdd.save),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _MoreSection extends StatelessWidget {
|
|
const _MoreSection();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final t = context.t;
|
|
final state = context.watch<QuickAddCubit>().state;
|
|
final cubit = context.read<QuickAddCubit>();
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
const SizedBox(height: 8),
|
|
if (state.photoBytes != null)
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: Image.memory(
|
|
state.photoBytes!,
|
|
height: 120,
|
|
fit: BoxFit.cover,
|
|
),
|
|
),
|
|
Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: OutlinedButton.icon(
|
|
onPressed: () async {
|
|
final bytes = await pickPhoto(context);
|
|
if (bytes != null) cubit.photoPicked(bytes);
|
|
},
|
|
icon: const Icon(Icons.photo_camera_outlined),
|
|
label: Text(t.quickAdd.addPhoto),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|