fix: formatting

This commit is contained in:
2026-07-29 01:42:34 +02:00
parent 521c8f8c4e
commit 50e6fca9d1
32 changed files with 501 additions and 575 deletions
-1
View File
@@ -22,7 +22,6 @@ class BarScreenView extends StatefulWidget {
}
class _BarScreenViewState extends State<BarScreenView> {
@override
void initState() {
super.initState();
+1 -1
View File
@@ -44,4 +44,4 @@ Future<void> confirmCloseTab(BuildContext context) async {
if (confirmed != true) return;
await viewModel.closeSelectedTab();
}
}
+2 -5
View File
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:kooltab2/viewmodels/bar_screen_view_model.dart';
import 'package:provider/provider.dart';
Future<void> showNewTabDialog(BuildContext context) async {
final controller = TextEditingController();
@@ -14,9 +13,7 @@ Future<void> showNewTabDialog(BuildContext context) async {
content: TextField(
controller: controller,
autofocus: true,
decoration: const InputDecoration(
labelText: 'Customer / group name',
),
decoration: const InputDecoration(labelText: 'Customer / group name'),
onSubmitted: (value) {
Navigator.of(dialogContext).pop(value);
},
@@ -43,4 +40,4 @@ Future<void> showNewTabDialog(BuildContext context) async {
if (!context.mounted) return;
await context.read<BarScreenViewModel>().createTab(name.trim());
}
}
+14 -11
View File
@@ -52,9 +52,12 @@ class _HistoryScreenViewState extends State<HistoryScreenView> with RouteAware {
appBar: AppBar(
title: Row(
children: [
IconButton(onPressed: (){
context.go('/bar');
}, icon: const Icon(Icons.arrow_back)),
IconButton(
onPressed: () {
context.go('/bar');
},
icon: const Icon(Icons.arrow_back),
),
const SizedBox(width: 5),
const Text('Tab History'),
],
@@ -118,15 +121,15 @@ class _HistoryScreenViewState extends State<HistoryScreenView> with RouteAware {
child: viewModel.closedTabs.isEmpty
? _EmptyState()
: ListView.separated(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 20),
itemCount: viewModel.closedTabs.length,
separatorBuilder: (_, _) => const SizedBox(height: 10),
itemBuilder: (context, index) {
final closedTab = viewModel.closedTabs[index];
padding: const EdgeInsets.fromLTRB(20, 8, 20, 20),
itemCount: viewModel.closedTabs.length,
separatorBuilder: (_, _) => const SizedBox(height: 10),
itemBuilder: (context, index) {
final closedTab = viewModel.closedTabs[index];
return ClosedTabCard(closedTab: closedTab);
},
),
return ClosedTabCard(closedTab: closedTab);
},
),
),
],
);
+17 -19
View File
@@ -14,11 +14,7 @@ class PinEntryView extends StatefulWidget {
final PinEntryMode mode;
final VoidCallback? onSuccess;
const PinEntryView({
super.key,
required this.mode,
this.onSuccess,
});
const PinEntryView({super.key, required this.mode, this.onSuccess});
@override
State<PinEntryView> createState() => _PinEntryViewState();
@@ -167,9 +163,9 @@ class _PinEntryViewState extends State<PinEntryView> {
height: 20,
child: _localError != null
? Text(
_localError!,
style: TextStyle(color: scheme.error, fontSize: 13),
)
_localError!,
style: TextStyle(color: scheme.error, fontSize: 13),
)
: const SizedBox.shrink(),
),
const Spacer(flex: 2),
@@ -210,7 +206,9 @@ class _PinDots extends StatelessWidget {
shape: BoxShape.circle,
color: isFilled ? scheme.primary : Colors.transparent,
border: Border.all(
color: isFilled ? scheme.primary : scheme.onSurface.withValues(alpha: 0.3),
color: isFilled
? scheme.primary
: scheme.onSurface.withValues(alpha: 0.3),
width: 1.4,
),
),
@@ -314,18 +312,18 @@ class _KeypadButton extends StatelessWidget {
child: icon != null
? Icon(icon, color: scheme.onSurface.withValues(alpha: 0.8))
: Text(
label!,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: onTap == null
? scheme.onSurface.withValues(alpha: 0.3)
: scheme.onSurface,
),
),
label!,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: onTap == null
? scheme.onSurface.withValues(alpha: 0.3)
: scheme.onSurface,
),
),
),
),
),
);
}
}
}
+168 -183
View File
@@ -13,10 +13,7 @@ import '../viewmodels/product_list_view_model.dart';
class ProductFormView extends StatefulWidget {
final String? productId;
const ProductFormView({
super.key,
this.productId,
});
const ProductFormView({super.key, this.productId});
bool get isEditing => productId != null;
@@ -60,11 +57,9 @@ class _ProductFormViewState extends State<ProductFormView> {
if (!mounted) return;
if (product == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Product not found.'),
),
);
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Product not found.')));
context.go('/products');
return;
@@ -102,7 +97,8 @@ class _ProductFormViewState extends State<ProductFormView> {
}
final extension = path.extension(pickedFile.path);
final fileName = 'product_${DateTime.now().millisecondsSinceEpoch}$extension';
final fileName =
'product_${DateTime.now().millisecondsSinceEpoch}$extension';
final newPath = path.join(imagesDirectory.path, fileName);
final copiedFile = await File(pickedFile.path).copy(newPath);
@@ -139,19 +135,15 @@ class _ProductFormViewState extends State<ProductFormView> {
if (!_formKey.currentState!.validate()) return;
if (_imagePath == null || _imagePath!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Choose a product image.'),
),
);
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Choose a product image.')));
return;
}
if (_selectedCategory == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please select a category.'),
),
const SnackBar(content: Text('Please select a category.')),
);
return;
}
@@ -239,43 +231,38 @@ class _ProductFormViewState extends State<ProductFormView> {
height: 220,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Theme.of(context).colorScheme.outline,
),
border: Border.all(color: Theme.of(context).colorScheme.outline),
),
clipBehavior: Clip.antiAlias,
child: hasImage
? Stack(
fit: StackFit.expand,
children: [
Image.file(
File(_imagePath!),
fit: BoxFit.fitHeight,
),
Positioned(
right: 12,
bottom: 12,
child: FilledButton.icon(
onPressed: _pickImage,
icon: const Icon(Icons.image),
label: const Text('Change image'),
),
),
],
)
fit: StackFit.expand,
children: [
Image.file(File(_imagePath!), fit: BoxFit.fitHeight),
Positioned(
right: 12,
bottom: 12,
child: FilledButton.icon(
onPressed: _pickImage,
icon: const Icon(Icons.image),
label: const Text('Change image'),
),
),
],
)
: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.add_photo_alternate_outlined, size: 48),
const SizedBox(height: 12),
Text(
'Choose product image',
style: Theme.of(context).textTheme.titleMedium,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.add_photo_alternate_outlined, size: 48),
const SizedBox(height: 12),
Text(
'Choose product image',
style: Theme.of(context).textTheme.titleMedium,
),
],
),
),
],
),
),
),
);
}
@@ -301,151 +288,149 @@ class _ProductFormViewState extends State<ProductFormView> {
),
resizeToAvoidBottomInset: false,
body: _isLoading
? const Center(
child: CircularProgressIndicator(),
)
? const Center(child: CircularProgressIndicator())
: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 720),
child: Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.all(24),
children: [
_buildImagePicker(context),
const SizedBox(height: 24),
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Product name',
border: OutlineInputBorder(),
),
textInputAction: TextInputAction.next,
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Enter a product name.';
}
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 720),
child: Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.all(24),
children: [
_buildImagePicker(context),
const SizedBox(height: 24),
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Product name',
border: OutlineInputBorder(),
),
textInputAction: TextInputAction.next,
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Enter a product name.';
}
return null;
},
),
const SizedBox(height: 16),
Consumer<ProductListViewModel>(
builder: (context, viewModel, child) {
return LayoutBuilder(
builder: (context, constraints) {
return DropdownMenu<String>(
width: constraints.maxWidth,
initialSelection: _selectedCategory,
enableFilter: true,
enableSearch: true,
controller: _categoryController,
requestFocusOnTap: true,
label: const Text('Category'),
hintText: 'Select a category',
dropdownMenuEntries: viewModel.categories
.map(
(category) => DropdownMenuEntry<String>(
value: category,
label: category,
),
)
.toList(),
onSelected: (value) {
setState(() {
_selectedCategory = value;
});
},
);
},
);
},
),
const SizedBox(height: 16),
TextFormField(
controller: _priceController,
decoration: const InputDecoration(
labelText: 'Price',
prefixText: '',
border: OutlineInputBorder(),
),
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
textInputAction: TextInputAction.next,
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Enter a price.';
}
return null;
},
),
const SizedBox(height: 16),
Consumer<ProductListViewModel>(
builder: (context, viewModel, child) {
return LayoutBuilder(
builder: (context, constraints) {
return DropdownMenu<String>(
width: constraints.maxWidth,
initialSelection: _selectedCategory,
enableFilter: true,
enableSearch: true,
controller: _categoryController,
requestFocusOnTap: true,
label: const Text('Category'),
hintText: 'Select a category',
dropdownMenuEntries: viewModel.categories
.map(
(category) => DropdownMenuEntry<String>(
value: category,
label: category,
),
)
.toList(),
onSelected: (value) {
setState(() {
_selectedCategory = value;
});
},
);
},
);
},
),
const SizedBox(height: 16),
TextFormField(
controller: _priceController,
decoration: const InputDecoration(
labelText: 'Price',
prefixText: '',
border: OutlineInputBorder(),
),
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
textInputAction: TextInputAction.next,
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Enter a price.';
}
final normalized = value.replaceAll(',', '.');
final price = double.tryParse(normalized);
final normalized = value.replaceAll(',', '.');
final price = double.tryParse(normalized);
if (price == null || price < 0) {
return 'Enter a valid price.';
}
if (price == null || price < 0) {
return 'Enter a valid price.';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _stockController,
decoration: const InputDecoration(
labelText: 'Current stock',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
validator: (value) {
final number = int.tryParse(value ?? '');
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _stockController,
decoration: const InputDecoration(
labelText: 'Current stock',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
validator: (value) {
final number = int.tryParse(value ?? '');
if (number == null || number < 0) {
return 'Enter a valid stock amount.';
}
if (number == null || number < 0) {
return 'Enter a valid stock amount.';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _lowStockController,
decoration: const InputDecoration(
labelText: 'Low stock warning threshold',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
validator: (value) {
final number = int.tryParse(value ?? '');
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _lowStockController,
decoration: const InputDecoration(
labelText: 'Low stock warning threshold',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
validator: (value) {
final number = int.tryParse(value ?? '');
if (number == null || number < 0) {
return 'Enter a valid threshold.';
}
if (number == null || number < 0) {
return 'Enter a valid threshold.';
}
return null;
},
),
const SizedBox(height: 24),
FilledButton.icon(
onPressed: _isSaving ? null : _save,
icon: _isSaving
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
),
)
: const Icon(Icons.save),
label: Text(
widget.isEditing ? 'Save changes' : 'Add product',
return null;
},
),
const SizedBox(height: 24),
FilledButton.icon(
onPressed: _isSaving ? null : _save,
icon: _isSaving
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
),
)
: const Icon(Icons.save),
label: Text(
widget.isEditing ? 'Save changes' : 'Add product',
),
),
],
),
),
],
),
),
),
),
),
);
}
}
}
+41 -25
View File
@@ -46,7 +46,7 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
}
Future<void> _loadVersion() async {
final version = await currentVersion();
final version = await currentVersion();
if (!mounted) return;
@@ -87,7 +87,9 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
void _showError(String message) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(message)));
}
Future<void> _enablePin() async {
@@ -168,8 +170,10 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
),
body: Builder(
builder: (context) {
final isLoading = settingsViewModel.isLoading || pinLockViewModel.isLoading;
final hasLoaded = settingsViewModel.hasLoaded && pinLockViewModel.hasLoaded;
final isLoading =
settingsViewModel.isLoading || pinLockViewModel.isLoading;
final hasLoaded =
settingsViewModel.hasLoaded && pinLockViewModel.hasLoaded;
if (isLoading && !hasLoaded) {
return const Center(
@@ -231,7 +235,8 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
AppThemeMode.light => 'Light',
AppThemeMode.dark => 'Dark',
}),
onChanged: (value) => Navigator.pop(context, value),
onChanged: (value) =>
Navigator.pop(context, value),
);
}).toList(),
),
@@ -265,14 +270,10 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
child: Center(
child: Text(
'Version $_appVersion',
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.5),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(
context,
).colorScheme.onSurface.withValues(alpha: 0.5),
),
),
),
@@ -306,9 +307,9 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Update check failed: $e')),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Update check failed: $e')));
}
}
@@ -349,9 +350,9 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Update failed: $e")),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text("Update failed: $e")));
}
},
child: const Text("Update"),
@@ -391,7 +392,9 @@ class _SettingsSection extends StatelessWidget {
decoration: BoxDecoration(
color: scheme.onSurface.withValues(alpha: 0.04),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: scheme.onSurface.withValues(alpha: 0.06)),
border: Border.all(
color: scheme.onSurface.withValues(alpha: 0.06),
),
),
clipBehavior: Clip.antiAlias,
child: Column(children: children),
@@ -427,12 +430,19 @@ class _SettingsTile extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Icon(icon, size: 22, color: scheme.onSurface.withValues(alpha: 0.7)),
Icon(
icon,
size: 22,
color: scheme.onSurface.withValues(alpha: 0.7),
),
const SizedBox(width: 14),
Expanded(
child: Text(
title,
style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface),
style: TextStyle(
fontWeight: FontWeight.w600,
color: scheme.onSurface,
),
),
),
if (subtitle != null) ...[
@@ -440,7 +450,10 @@ class _SettingsTile extends StatelessWidget {
const SizedBox(width: 4),
],
if (onTap != null)
Icon(Icons.chevron_right_rounded, color: scheme.onSurface.withValues(alpha: 0.3)),
Icon(
Icons.chevron_right_rounded,
color: scheme.onSurface.withValues(alpha: 0.3),
),
],
),
),
@@ -475,7 +488,10 @@ class _SettingsSwitchTile extends StatelessWidget {
Expanded(
child: Text(
title,
style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface),
style: TextStyle(
fontWeight: FontWeight.w600,
color: scheme.onSurface,
),
),
),
Switch(value: value, onChanged: onChanged),
@@ -483,4 +499,4 @@ class _SettingsSwitchTile extends StatelessWidget {
),
);
}
}
}
+16 -8
View File
@@ -98,7 +98,7 @@ class _ClosedTabCardState extends State<ClosedTabCard> {
const Divider(height: 1),
const SizedBox(height: 8),
...closedTab.items.map(
(item) => _ClosedTabItemRow(item: item),
(item) => _ClosedTabItemRow(item: item),
),
],
),
@@ -120,10 +120,12 @@ class _ClosedTabItemRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
final unitPrice =
NumberFormat.simpleCurrency().format(item.unitPriceInCents / 100);
final lineTotal =
NumberFormat.simpleCurrency().format(item.lineTotalInCents / 100);
final unitPrice = NumberFormat.simpleCurrency().format(
item.unitPriceInCents / 100,
);
final lineTotal = NumberFormat.simpleCurrency().format(
item.lineTotalInCents / 100,
);
final scheme = Theme.of(context).colorScheme;
return Padding(
@@ -135,7 +137,10 @@ class _ClosedTabItemRow extends StatelessWidget {
item.productName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface),
style: TextStyle(
fontWeight: FontWeight.w600,
color: scheme.onSurface,
),
),
),
Text(
@@ -148,11 +153,14 @@ class _ClosedTabItemRow extends StatelessWidget {
child: Text(
lineTotal,
textAlign: TextAlign.end,
style: TextStyle(fontWeight: FontWeight.w700, color: scheme.onSurface),
style: TextStyle(
fontWeight: FontWeight.w700,
color: scheme.onSurface,
),
),
),
],
),
);
}
}
}
+14 -22
View File
@@ -3,7 +3,6 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:kooltab2/models/product.dart';
class ProductTile extends StatelessWidget {
final Product product;
final bool enabled;
@@ -33,7 +32,7 @@ class ProductTile extends StatelessWidget {
final outOfStock = product.stockQuantity <= 0;
final lowStock =
product.stockQuantity > 0 &&
product.stockQuantity <= product.lowStockThreshold; // adjust name
product.stockQuantity <= product.lowStockThreshold; // adjust name
final borderColor = outOfStock
? scheme.error.withValues(alpha: 0.7)
@@ -62,16 +61,16 @@ class ProductTile extends StatelessWidget {
opacity: outOfStock ? 0.35 : (enabled ? 1 : 0.4),
child: _hasImage
? Image.file(
File(product.imagePath!),
fit: BoxFit.scaleDown,
)
File(product.imagePath!),
fit: BoxFit.scaleDown,
)
: Center(
child: Icon(
Icons.image_not_supported_outlined,
size: 34,
color: scheme.onSurface.withValues(alpha: 0.3),
),
),
child: Icon(
Icons.image_not_supported_outlined,
size: 34,
color: scheme.onSurface.withValues(alpha: 0.3),
),
),
),
if (_hasImage)
@@ -95,9 +94,7 @@ class ProductTile extends StatelessWidget {
Positioned(
top: 10,
right: 10,
child: _StockBadge(
product: product,
),
child: _StockBadge(product: product),
),
// Out of stock overlay
@@ -134,9 +131,7 @@ class ProductTile extends StatelessWidget {
class _StockBadge extends StatelessWidget {
final Product product;
const _StockBadge({
required this.product,
});
const _StockBadge({required this.product});
@override
Widget build(BuildContext context) {
@@ -150,10 +145,7 @@ class _StockBadge extends StatelessWidget {
product.stockQuantity <= product.lowStockThreshold; // adjust name
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: low
? Colors.amber.withValues(alpha: 0.9)
@@ -170,4 +162,4 @@ class _StockBadge extends StatelessWidget {
),
);
}
}
}
+1 -2
View File
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
class SlideConfirm extends StatefulWidget {
final VoidCallback onConfirmed;
@@ -95,4 +94,4 @@ class _SlideConfirmState extends State<SlideConfirm> {
),
);
}
}
}