111 lines
3.5 KiB
Dart
111 lines
3.5 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import '../viewmodels/product_list_view_model.dart';
|
|
import '../l10n/app_localizations.dart';
|
|
import '../l10n/app_localizations_helpers.dart';
|
|
import '../utils/navigation.dart';
|
|
|
|
class ProductListView extends StatefulWidget {
|
|
const ProductListView({super.key});
|
|
|
|
@override
|
|
State<ProductListView> createState() => _ProductListViewState();
|
|
}
|
|
|
|
class _ProductListViewState extends State<ProductListView> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
context.read<ProductListViewModel>().loadProducts();
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final viewModel = context.watch<ProductListViewModel>();
|
|
final l10n = AppLocalizations.of(context);
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
leading: IconButton(
|
|
onPressed: () => context.popOrGo('/bar'),
|
|
icon: const Icon(Icons.arrow_back),
|
|
),
|
|
title: Text(l10n.products),
|
|
),
|
|
resizeToAvoidBottomInset: false,
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
onPressed: () => context.push('/products/new'),
|
|
icon: const Icon(Icons.add),
|
|
label: Text(l10n.addProduct),
|
|
),
|
|
body: Builder(
|
|
builder: (context) {
|
|
if (viewModel.isLoading) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
|
|
if (viewModel.errorMessage != null) {
|
|
return Center(
|
|
child: Text(l10n.localizedError(viewModel.errorMessage)),
|
|
);
|
|
}
|
|
|
|
if (viewModel.products.isEmpty) {
|
|
return Center(child: Text(l10n.noProductsYet));
|
|
}
|
|
|
|
return ListView.separated(
|
|
padding: const EdgeInsets.all(16),
|
|
itemCount: viewModel.products.length,
|
|
separatorBuilder: (_, _) => const SizedBox(height: 8),
|
|
itemBuilder: (context, index) {
|
|
final product = viewModel.products[index];
|
|
|
|
return Card(
|
|
child: ListTile(
|
|
leading: SizedBox(
|
|
width: 56,
|
|
height: 56,
|
|
child: Center(
|
|
child:
|
|
product.imagePath != null &&
|
|
product.imagePath!.isNotEmpty
|
|
? Image.file(
|
|
File(product.imagePath!),
|
|
fit: BoxFit.contain,
|
|
cacheWidth: 112,
|
|
errorBuilder: (context, error, stackTrace) =>
|
|
const Icon(
|
|
Icons.image_not_supported_outlined,
|
|
),
|
|
)
|
|
: const Icon(Icons.image_not_supported_outlined),
|
|
),
|
|
),
|
|
title: Text(product.name),
|
|
subtitle: Text(
|
|
l10n.productStockSummary(
|
|
l10n.categoryLabel(product.category),
|
|
product.formattedPrice,
|
|
product.stockQuantity,
|
|
),
|
|
),
|
|
trailing: const Icon(Icons.chevron_right),
|
|
onTap: () => context.push('/products/${product.id}/edit'),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|