Skip to content

Commit e744b72

Browse files
author
Freek van de Ven
committed
feat: add catalog modification screen
1 parent 3d35e2b commit e744b72

15 files changed

Lines changed: 825 additions & 33 deletions

packages/flutter_catalog/lib/l10n/app_en.arb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"filterButton": "Filters",
77
"noItemsFound": "No items found.",
88
"itemLoadingError": "Failed to load items.",
9+
"itemCreatePageMandatorySection": "Mandatory",
910
"detailDescriptionTitle": "Description",
1011
"applyFiltersButton": "Apply filters",
1112
"sendMessageButton": "Send message",

packages/flutter_catalog/lib/l10n/app_localizations.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ abstract class FlutterCatalogLocalizations {
129129
/// **'Failed to load items.'**
130130
String get itemLoadingError;
131131

132+
/// No description provided for @itemCreatePageMandatorySection.
133+
///
134+
/// In en, this message translates to:
135+
/// **'Mandatory'**
136+
String get itemCreatePageMandatorySection;
137+
132138
/// No description provided for @detailDescriptionTitle.
133139
///
134140
/// In en, this message translates to:

packages/flutter_catalog/lib/l10n/app_localizations_en.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class FlutterCatalogLocalizationsEn extends FlutterCatalogLocalizations {
2222
@override
2323
String get itemLoadingError => 'Failed to load items.';
2424

25+
@override
26+
String get itemCreatePageMandatorySection => 'Mandatory';
27+
2528
@override
2629
String get detailDescriptionTitle => 'Description';
2730

packages/flutter_catalog/lib/src/flutter_catalog_userstory.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import "package:flutter_catalog/src/routes.dart";
99
import "package:flutter_catalog/src/services/catalog_service.dart";
1010
import "package:flutter_catalog/src/services/pop_handler.dart";
1111
import "package:flutter_catalog/src/utils/scope.dart";
12+
import "package:flutter_catalog/src/views/catalog_modify_view.dart";
1213
import "package:flutter_catalog_interface/flutter_catalog_interface.dart";
1314
import "package:flutter_hooks/flutter_hooks.dart";
1415

@@ -56,6 +57,30 @@ class FlutterCatalogDetailUserstory extends _BaseCatalogNavigatorUserstory {
5657
);
5758
}
5859

60+
/// A self-contained user story for creating or editing a catalog item.
61+
class FlutterCatalogModifyUserstory extends _BaseCatalogNavigatorUserstory {
62+
/// Constructs a [FlutterCatalogModifyUserstory].
63+
const FlutterCatalogModifyUserstory({
64+
required super.userId,
65+
required super.options,
66+
this.initialItem,
67+
super.onExit,
68+
super.key,
69+
});
70+
71+
/// The initial catalog item to edit, or `null` for creating a new item.
72+
final CatalogItem? initialItem;
73+
74+
@override
75+
MaterialPageRoute buildInitialRoute(BuildContext context) =>
76+
MaterialPageRoute(
77+
builder: (context) => CatalogModifyView(
78+
initialItem: initialItem,
79+
onExit: onExit,
80+
),
81+
);
82+
}
83+
5984
/// Base hook widget for catalog navigator user stories.
6085
abstract class _BaseCatalogNavigatorUserstory extends HookWidget {
6186
/// Constructs a [_BaseCatalogNavigatorUserstory].
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import "package:image_picker/image_picker.dart";
2+
3+
/// A data class to manage the state of the item modification form.
4+
class ItemModificationData {
5+
///
6+
const ItemModificationData({
7+
this.title,
8+
this.description,
9+
this.newImages = const [],
10+
this.existingImageUrls = const [],
11+
this.customFieldValues = const {},
12+
});
13+
14+
///
15+
final String? title;
16+
17+
///
18+
final String? description;
19+
20+
///
21+
final List<XFile> newImages;
22+
23+
///
24+
final List<String> existingImageUrls;
25+
26+
/// Custom field values for the item.
27+
final Map<String, dynamic> customFieldValues;
28+
29+
///
30+
ItemModificationData copyWith({
31+
String? title,
32+
String? description,
33+
List<XFile>? newImages,
34+
List<String>? existingImageUrls,
35+
Map<String, dynamic>? customFieldValues,
36+
}) =>
37+
ItemModificationData(
38+
title: title ?? this.title,
39+
description: description ?? this.description,
40+
newImages: newImages ?? this.newImages,
41+
existingImageUrls: existingImageUrls ?? this.existingImageUrls,
42+
customFieldValues: customFieldValues ?? this.customFieldValues,
43+
);
44+
}

packages/flutter_catalog/lib/src/routes.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import "package:flutter/material.dart";
22
import "package:flutter_catalog/src/views/catalog_detail_view.dart";
33
import "package:flutter_catalog/src/views/catalog_filter_view.dart";
4+
import "package:flutter_catalog/src/views/catalog_modify_view.dart";
45
import "package:flutter_catalog/src/views/catalog_overview_view.dart";
56
import "package:flutter_catalog_interface/flutter_catalog_interface.dart";
67

@@ -32,6 +33,25 @@ MaterialPageRoute catalogDetailRoute({
3233
builder: (context) => CatalogDetailView(
3334
item: item,
3435
onExit: () => Navigator.of(context).pop(),
36+
onEditItem: (itemToEdit) async => _routeToScreen(
37+
context,
38+
catalogModifyRoute(
39+
initialItem: itemToEdit,
40+
onExit: () => Navigator.of(context).pop(),
41+
).builder(context),
42+
),
43+
),
44+
);
45+
46+
/// Returns a [MaterialPageRoute] for the create/edit screen.
47+
MaterialPageRoute<void> catalogModifyRoute({
48+
required VoidCallback onExit,
49+
CatalogItem? initialItem,
50+
}) =>
51+
MaterialPageRoute(
52+
builder: (context) => CatalogModifyView(
53+
initialItem: initialItem,
54+
onExit: onExit,
3555
),
3656
);
3757

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
1+
import "package:cross_file/cross_file.dart";
12
import "package:flutter_catalog_interface/flutter_catalog_interface.dart";
23

34
/// A service class to manage catalog-related operations.
45
///
5-
/// This class uses a [CatalogRepository] to fetch and manipulate catalog data.
6-
class CatalogService {
6+
/// This class is generic and uses a [CatalogRepository] to fetch and
7+
/// manipulate catalog data of type T.
8+
class CatalogService<T extends CatalogItem> {
79
/// Creates a [CatalogService] with the given repository.
810
CatalogService({
9-
required CatalogRepository repository,
11+
required CatalogRepository<T> repository,
1012
required this.userId,
1113
}) : _repository = repository;
1214

13-
final CatalogRepository _repository;
15+
final CatalogRepository<T> _repository;
1416

1517
/// The ID of the user for whom catalog operations are performed.
1618
final String userId;
1719

18-
/// Retrieves catalog items, optionally applying filters, user location,
19-
/// pagination, and filtered by user.
20-
///
21-
/// [userId] is required for personalized results or distance calculations.
22-
/// [userLocation] provides the GPS location for distance calculation.
23-
/// [filters] allow for filtering the items (e.g., by category).
24-
/// [limit] specifies the maximum number of items to return.
25-
/// [offset] specifies the starting index for pagination.
26-
Future<List<CatalogItem>> fetchCatalogItems({
20+
/// Retrieves catalog items, optionally applying filters and user location.
21+
Future<List<T>> fetchCatalogItems({
2722
LatLng? userLocation,
2823
Map<String, dynamic>? filters,
2924
int? limit,
@@ -37,15 +32,30 @@ class CatalogService {
3732
offset: offset,
3833
);
3934

40-
/// Toggles the favorite status of an item for a specific user.
41-
///
42-
/// [itemId] is the ID of the catalog item.
43-
Future<void> toggleFavorite(
44-
String itemId,
45-
) async =>
35+
/// Toggles the favorite status of an item for the current user.
36+
Future<void> toggleFavorite(String itemId) async =>
4637
_repository.toggleFavorite(itemId, userId);
4738

4839
/// Fetches a single catalog item by its ID.
49-
Future<CatalogItem?> fetchCatalogItemById(String id) async =>
40+
Future<T?> fetchCatalogItemById(String id) async =>
5041
_repository.fetchCatalogItemById(id, userId);
42+
43+
/// Creates a new catalog item.
44+
Future<void> createCatalogItem(Map<String, dynamic> item) async =>
45+
_repository.createCatalogItem(item);
46+
47+
/// Updates an existing catalog item by its ID.
48+
Future<void> updateCatalogItem(
49+
String itemId,
50+
Map<String, dynamic> item,
51+
) async =>
52+
_repository.updateCatalogItem(itemId, item);
53+
54+
/// Deletes a catalog item by its ID.
55+
Future<void> deleteCatalogItem(String itemId) async =>
56+
_repository.deleteCatalogItem(itemId);
57+
58+
/// Uploads an image file.
59+
Future<String> uploadImage(XFile imageFile) async =>
60+
_repository.uploadImage(imageFile);
5161
}

packages/flutter_catalog/lib/src/views/catalog_detail_view.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class CatalogDetailView extends HookWidget {
1111
/// Creates a [CatalogDetailView].
1212
const CatalogDetailView({
1313
required this.item,
14+
required this.onEditItem,
1415
super.key,
1516
this.onExit,
1617
});
@@ -21,11 +22,14 @@ class CatalogDetailView extends HookWidget {
2122
/// A callback to execute when the user wants to navigate back.
2223
final VoidCallback? onExit;
2324

25+
/// A callback to execute when the user wants to edit the item.
26+
final void Function(CatalogItem item) onEditItem;
27+
2428
@override
2529
Widget build(BuildContext context) {
2630
var scope = CatalogScope.of(context);
2731
var service = scope.catalogService;
28-
32+
var isAuthor = scope.userId == item.authorId;
2933
var isFavorite = useState(item.isFavorited ?? false);
3034

3135
useEffect(
@@ -67,6 +71,12 @@ class CatalogDetailView extends HookWidget {
6771
),
6872
onPressed: toggleFavorite,
6973
),
74+
if (isAuthor)
75+
IconButton(
76+
icon: const Icon(Icons.edit),
77+
// Call the new callback when pressed
78+
onPressed: () => onEditItem(item),
79+
),
7080
],
7181
),
7282
body: SingleChildScrollView(

0 commit comments

Comments
 (0)