Skip to content

Commit

Permalink
feat: Allow to animate lists (in the product edition flow) (#4195)
Browse files Browse the repository at this point in the history
* Allow to animate lists

* Accessibility improvements for the + button
  • Loading branch information
g123k committed Jun 18, 2023
1 parent 29bdad3 commit d518c85
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 31 deletions.
33 changes: 33 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,15 @@
"@edit_product_label": {
"description": "Edit product button label"
},
"edit_product_form_item_add_action": "Add a new {itemType}",
"description": "Tooltip to show when the user long presses the (+) button",
"@edit_product_form_item_add_action": {
"placeholders": {
"itemType": {
"type": "String"
}
}
},
"edit_product_form_item_barcode": "Barcode",
"@edit_product_form_item_barcode": {
"description": "Product edition - Barcode"
Expand Down Expand Up @@ -1156,6 +1165,10 @@
"@edit_product_form_item_labels_hint": {
"description": "Product edition - Labels - input textfield hint"
},
"edit_product_form_item_labels_type": "label",
"@edit_product_form_item_labels_type": {
"description": "Product edition - Labels - input textfield label"
},
"edit_product_form_item_stores_title": "Stores",
"@edit_product_form_item_stores_title": {
"description": "Product edition - Stores - Title"
Expand All @@ -1164,6 +1177,10 @@
"@edit_product_form_item_stores_hint": {
"description": "Product edition - Stores - input textfield hint"
},
"edit_product_form_item_stores_type": "store",
"@edit_product_form_item_stores_type": {
"description": "Product edition - Stores - input textfield type"
},
"edit_product_form_item_origins_title": "Origins",
"@edit_product_form_item_origins_title": {
"description": "Product edition - Origins - Title"
Expand All @@ -1172,6 +1189,10 @@
"@edit_product_form_item_origins_hint": {
"description": "Product edition - Origins - input textfield hint"
},
"edit_product_form_item_origins_type": "country",
"@edit_product_form_item_origins_type": {
"description": "Product edition - Origins - input textfield type"
},
"edit_product_form_item_origins_explainer_1": "Add any indications of origins you can find on the packaging. You need not worry about origins indicated directly in the ingredient list.",
"@edit_product_form_item_origins_explainer_1": {
"description": "Product edition - Origins - input explainer, part 1"
Expand All @@ -1188,6 +1209,10 @@
"@edit_product_form_item_countries_hint": {
"description": "Product edition - Countries - input textfield hint"
},
"edit_product_form_item_countries_type": "country",
"@edit_product_form_item_countries_type": {
"description": "Product edition - Countries - input textfield type"
},
"edit_product_form_item_countries_explanations": "Countries where the product is widely available (not including stores specialising in foreign products).",
"@edit_product_form_item_countries_explanations": {
"description": "Product edition - Countries - explanations"
Expand All @@ -1200,6 +1225,10 @@
"@edit_product_form_item_emb_codes_hint": {
"description": "Product edition - Traceability Codes - input textfield hint"
},
"edit_product_form_item_emb_codes_type": "traceability code",
"@edit_product_form_item_emb_codes_type": {
"description": "Product edition - Traceability Codes - input textfield type"
},
"edit_product_form_item_emb_codes_explanations": "In Europe, code in an ellipse with the 2 country initials followed by a number and CE.\nExamples: EMB 53062, FR 62.448.034 CE, 84 R 20, 33 RECOLTANT 522",
"@edit_product_form_item_emb_codes_examples": {
"description": "Product edition - EMB Codes - explanations"
Expand All @@ -1212,6 +1241,10 @@
"@edit_product_form_item_categories_hint": {
"description": "Product edition - Categories - input textfield hint"
},
"edit_product_form_item_categories_type": "category",
"@edit_product_form_item_categories_type": {
"description": "Product edition - Categories - input textfield type"
},
"edit_product_form_item_categories_explainer_1": "Indicate only the most specific category. Parent categories will be automatically added.",
"@edit_product_form_item_categories_explainer_1": {
"description": "Product edition - Categories - input explainer, part 1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ abstract class AbstractSimpleInputPageHelper extends ChangeNotifier {
/// Returns the hint of the "add" text field.
String getAddHint(final AppLocalizations appLocalizations);

/// Returns the type of the text field (eg: label, category…).
String getTypeLabel(final AppLocalizations appLocalizations);

/// Returns additional examples about the "add" text field.
String? getAddExplanations(final AppLocalizations appLocalizations) => null;

Expand Down Expand Up @@ -166,6 +169,10 @@ class SimpleInputPageStoreHelper extends AbstractSimpleInputPageHelper {
String getAddHint(final AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_stores_hint;

@override
String getTypeLabel(AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_stores_type;

@override
TagType? getTagType() => null;

Expand Down Expand Up @@ -200,6 +207,10 @@ class SimpleInputPageOriginHelper extends AbstractSimpleInputPageHelper {
String getAddHint(final AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_origins_hint;

@override
String getTypeLabel(AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_origins_type;

@override
String? getAddExplanations(final AppLocalizations appLocalizations) =>
'${appLocalizations.edit_product_form_item_origins_explainer_1}'
Expand Down Expand Up @@ -241,6 +252,10 @@ class SimpleInputPageEmbCodeHelper extends AbstractSimpleInputPageHelper {
String getAddHint(final AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_emb_codes_hint;

@override
String getTypeLabel(AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_emb_codes_type;

@override
String getAddExplanations(final AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_emb_codes_explanations;
Expand Down Expand Up @@ -290,6 +305,10 @@ class SimpleInputPageLabelHelper extends AbstractSimpleInputPageHelper {
String getAddHint(final AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_labels_hint;

@override
String getTypeLabel(AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_labels_type;

@override
TagType? getTagType() => TagType.LABELS;

Expand Down Expand Up @@ -339,6 +358,10 @@ class SimpleInputPageCategoryHelper extends AbstractSimpleInputPageHelper {
String getAddHint(final AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_categories_hint;

@override
String getTypeLabel(AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_categories_type;

@override
TagType? getTagType() => TagType.CATEGORIES;

Expand Down Expand Up @@ -380,6 +403,10 @@ class SimpleInputPageCountryHelper extends AbstractSimpleInputPageHelper {
String getAddHint(final AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_countries_hint;

@override
String getTypeLabel(AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_countries_type;

@override
String getAddExplanations(final AppLocalizations appLocalizations) =>
appLocalizations.edit_product_form_item_countries_explanations;
Expand Down
107 changes: 76 additions & 31 deletions packages/smooth_app/lib/pages/product/simple_input_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/helpers/haptic_feedback_helper.dart';
import 'package:smooth_app/pages/product/explanation_widget.dart';
import 'package:smooth_app/pages/product/simple_input_page_helpers.dart';
import 'package:smooth_app/pages/product/simple_input_text_field.dart';
Expand All @@ -24,6 +25,8 @@ class SimpleInputWidget extends StatefulWidget {

class _SimpleInputWidgetState extends State<SimpleInputWidget> {
late final FocusNode _focusNode;

final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
final Key _autocompleteKey = UniqueKey();

@override
Expand Down Expand Up @@ -77,50 +80,55 @@ class _SimpleInputWidgetState extends State<SimpleInputWidget> {
controller: widget.controller,
),
),
IconButton(
onPressed: () {
if (widget.helper
.addItemsFromController(widget.controller)) {
setState(() {});
}
},
icon: const Icon(Icons.add_circle),
Tooltip(
message: appLocalizations.edit_product_form_item_add_action(
widget.helper.getTypeLabel(appLocalizations)),
child: IconButton(
onPressed: _onAddItem,
icon: const Icon(Icons.add_circle),
),
)
],
);
},
),
Divider(color: themeData.colorScheme.onBackground),
ListView.builder(
itemCount: widget.helper.terms.length,
itemBuilder: (BuildContext context, int position) {
AnimatedList(
key: _listKey,
initialItemCount: widget.helper.terms.length,
itemBuilder: (
BuildContext context,
int position,
Animation<double> animation,
) {
final String term = widget.helper.terms[position];
final Widget child = Text(term);

return KeyedSubtree(
key: ValueKey<String>(term),
child: ListTile(
trailing: Tooltip(
message: appLocalizations
.edit_product_form_item_remove_item_tooltip,
child: InkWell(
customBorder: const CircleBorder(),
onTap: () {
if (widget.helper.removeTerm(term)) {
setState(() {});
}
},
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: MEDIUM_SPACE,
vertical: SMALL_SPACE,
child: SizeTransition(
sizeFactor: animation,
child: ListTile(
trailing: Tooltip(
message: appLocalizations
.edit_product_form_item_remove_item_tooltip,
child: InkWell(
customBorder: const CircleBorder(),
onTap: () => _onRemoveItem(term, position, child),
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: MEDIUM_SPACE,
vertical: SMALL_SPACE,
),
child: Icon(Icons.delete),
),
child: Icon(Icons.delete),
),
),
contentPadding: const EdgeInsetsDirectional.only(
start: LARGE_SPACE,
),
title: child,
),
contentPadding: const EdgeInsetsDirectional.only(
start: LARGE_SPACE,
),
title: Text(term),
),
);
},
Expand All @@ -130,4 +138,41 @@ class _SimpleInputWidgetState extends State<SimpleInputWidget> {
],
);
}

void _onAddItem() {
final List<String> terms = widget.controller.text.split(',');

bool atLeastOneAnimatedItem = false;
if (widget.helper.addItemsFromController(widget.controller)) {
for (final String term in terms) {
final int newPosition = widget.helper.terms.indexOf(term);
if (newPosition >= 0) {
_listKey.currentState?.insertItem(newPosition);
atLeastOneAnimatedItem = true;
}
}
}

if (!atLeastOneAnimatedItem) {
setState(() {});
}

SmoothHapticFeedback.lightNotification();
}

void _onRemoveItem(String term, int position, Widget child) {
if (widget.helper.removeTerm(term)) {
_listKey.currentState?.removeItem(position,
(_, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
child: ListTile(title: child),
),
);
});
SmoothHapticFeedback.lightNotification();
}
}
}

0 comments on commit d518c85

Please sign in to comment.