Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Product page redesign #6262

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 29 additions & 29 deletions packages/smooth_app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -250,53 +250,53 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"

SPEC CHECKSUMS:
app_settings: 3507c575c2b18a462c99948f61d5de21d4420999
audioplayers_darwin: ccf9c770ee768abb07e26d90af093f7bab1c12ab
camera_avfoundation: 04b44aeb14070126c6529e5ab82cc7c9fca107cf
connectivity_plus: 2256d3e20624a7749ed21653aafe291a46446fee
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
app_settings: 017320c6a680cdc94c799949d95b84cb69389ebc
audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40
camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4
connectivity_plus: 18382e7311ba19efcaee94442b23b32507b20695
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_custom_tabs_ios: dd647919edd75e82ba6b00009eb3460a28c011b8
flutter_email_sender: 2397f5e84aaacfb61af569637a963e7c687858d8
flutter_icmp_ping: 47c1df3440c18ddd39fc457e607bb3b42d4a339f
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
flutter_native_splash: 6cad9122ea0fad137d23137dd14b937f3e90b145
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
flutter_custom_tabs_ios: a651b18786388923b62de8c0537607de87c2eccf
flutter_email_sender: 10a22605f92809a11ef52b2f412db806c6082d40
flutter_icmp_ping: 2b159955eee0c487c766ad83fec224ae35e7c935
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
flutter_native_splash: f71420956eb811e6d310720fee915f1d42852e7a
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318
GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
in_app_review: 5596fe56fab799e8edb3561c03d053363ab13457
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
iso_countries: 7ca741b02ae533b86f1f18a8bb52961d776ac77e
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
iso_countries: eb09d40f388e4c65e291e0bb36a701dfe7de6c74
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
MLImage: 0ad1c5f50edd027672d8b26b0fee78a8b4a0fc56
MLKitBarcodeScanning: 0a3064da0a7f49ac24ceb3cb46a5bc67496facd2
MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d
MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e
mobile_scanner: af8f71879eaba2bbcb4d86c6a462c3c0e7f23036
mobile_scanner: fd0054c52ede661e80bf5a4dea477a2467356bee
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
qr_code_scanner: d77f94ecc9abf96d9b9b8fc04ef13f611e5a147a
rive_common: dd421daaf9ae69f0125aa761dd96abd278399952
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
rive_common: 4743dbfd2911c99066547a3c6454681e0fa907df
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
Sentry: 38ed8bf38eab5812787274bf591e528074c19e02
sentry_flutter: a72ca0eb6e78335db7c4ddcddd1b9f6c8ed5b764
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
torch_light: d093d579a221a59ef8a6b8c0eca20d52f7178087
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
webview_flutter_wkwebview: 44d4dee7d7056d5ad185d25b38404436d56c547c
sentry_flutter: 7d1f1df30f3768c411603ed449519bbb90a7d87b
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
torch_light: 682062fa12102172fa38b6b14c106d93b060f83e
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4

PODFILE CHECKSUM: 6ac49c02151268e5844d0422787205b7cbdd62d2

COCOAPODS: 1.16.2
COCOAPODS: 1.15.2
16 changes: 16 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3727,5 +3727,21 @@
"type": "String"
}
}
},
"product_page_tab_for_me": "For me",
"@product_page_tab_for_me": {
"description": "Label of the for me tab on the product page"
},
"product_page_tab_website": "Website",
"@product_page_tab_website": {
"description": "Label of the website tab on the product page"
},
"product_page_tab_prices": "Prices",
"@product_page_tab_prices": {
"description": "Label of the prices tab on the product page"
},
"product_page_tab_folksonomy": "Folksonomy",
"@product_page_tab_folksonomy": {
"description": "Label of the folksonomy tab on the product page"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/generic_lib/bottom_sheets/smooth_bottom_sheet.dart';
import 'package:smooth_app/generic_lib/buttons/smooth_simple_button.dart';
import 'package:smooth_app/knowledge_panel/knowledge_panels_builder.dart';
import 'package:smooth_app/pages/folksonomy/folksonomy_card.dart';
import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart';
import 'package:smooth_app/pages/prices/prices_card.dart';
import 'package:smooth_app/pages/product/product_page/header/reorder_bottom_sheet.dart';
import 'package:smooth_app/pages/product/website_card.dart';
import 'package:smooth_app/widgets/smooth_tabbar.dart';

class ProductPageTab {
const ProductPageTab({
required this.labelBuilder,
required this.builder,
});

final String Function(BuildContext) labelBuilder;
final Widget Function(Product) builder;
}

class ProductPageTabBar extends StatelessWidget {
const ProductPageTabBar({
required this.tabController,
required this.tabs,
});

final TabController tabController;
final List<ProductPageTab> tabs;

@override
Widget build(BuildContext context) {
return SliverPersistentHeader(
delegate: _TabBarDelegate(
PreferredSize(
preferredSize: const Size.fromHeight(SmoothTabBar.TAB_BAR_HEIGHT),
child: SmoothTabBar<ProductPageTab>(
tabController: tabController,
items: tabs.map((ProductPageTab tab) {
return SmoothTabBarItem<ProductPageTab>(
label: tab.labelBuilder(context),
value: tab,
);
}).toList(),
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
onTabChanged: (ProductPageTab tab) {},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing here?

)),
),
pinned: true,
);
}

static List<ProductPageTab> extractTabsFromProduct({
required BuildContext context,
required Product product,
}) {
final List<ProductPageTab> tabs = <ProductPageTab>[];

final List<KnowledgePanelElement> roots =
KnowledgePanelsBuilder.getRootPanelElements(product);
for (final KnowledgePanelElement root in roots) {
List<Widget> children = KnowledgePanelsBuilder.getChildren(
context,
panelElement: root,
product: product,
onboardingMode: false,
);

final KnowledgePanelTitle knowledgePanelTitle =
children.first as KnowledgePanelTitle;

children = children.sublist(1);

tabs.add(
ProductPageTab(
labelBuilder: (BuildContext c) => knowledgePanelTitle.title,
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
builder: (Product p) => ListView.builder(
padding: EdgeInsetsDirectional.zero,
itemCount: children.length - 1,
itemBuilder: (BuildContext context, int index) => children[index],
),
),
);
}

return _addHardCodedTabs(context, product, tabs);
}

static List<ProductPageTab> _addHardCodedTabs(
BuildContext context,
Product product,
List<ProductPageTab> tabs,
) {
tabs.insert(
0,
ProductPageTab(
labelBuilder: (BuildContext c) =>
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
AppLocalizations.of(c).product_page_tab_for_me,
builder: (Product p) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SmoothSimpleButton(
onPressed: () {
showSmoothReorderBottomSheet<ProductPageTab>(
context,
items: tabs.map((ProductPageTab tab) {
return tab;
}).toList(),
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
onReorder: (List<ProductPageTab> reorderedItems) {
tabs.clear();
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
tabs.addAll(reorderedItems);
},
labelBuilder: (
BuildContext context,
ProductPageTab item,
int index,
) {
return DefaultTextStyle.merge(
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
style: const TextStyle(
fontWeight: FontWeight.bold,
),
child: Text(item.labelBuilder(context)),
);
},
);
},
child: const Text('Reorder tabs'),
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
),
],
),
),
);
if (product.website?.trim().isNotEmpty == true) {
tabs.add(
ProductPageTab(
labelBuilder: (BuildContext c) =>
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
AppLocalizations.of(c).product_page_tab_website,
builder: (Product p) => ListView(
padding: EdgeInsetsDirectional.zero,
children: <Widget>[
WebsiteCard(p.website!),
],
),
),
);
}
tabs.add(
ProductPageTab(
labelBuilder: (BuildContext c) =>
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
AppLocalizations.of(c).product_page_tab_prices,
builder: (Product p) => ListView(
padding: EdgeInsetsDirectional.zero,
children: <Widget>[
PricesCard(p),
],
),
),
);

if (context.read<UserPreferences>().getFlag(
UserPreferencesDevMode.userPreferencesFlagHideFolksonomy) ==
false) {
tabs.add(
ProductPageTab(
labelBuilder: (BuildContext c) =>
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
AppLocalizations.of(c).product_page_tab_folksonomy,
builder: (Product p) => ListView(
padding: EdgeInsetsDirectional.zero,
children: <Widget>[FolksonomyCard(p)],
),
),
);
}

return tabs;
}

static void showSmoothReorderBottomSheet<T>(
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
BuildContext context, {
required List<T> items,
required ValueChanged<List<T>> onReorder,
ValueChanged<T>? onVisibilityToggle,
required LabelBuilder<T> labelBuilder,
String title = 'Reorder Items',
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
}) {
showSmoothModalSheet(
context: context,
minHeight: 0.6,
builder: (_) => ReorderBottomSheet<T>(
items: items,
onReorder: onReorder,
onVisibilityToggle: onVisibilityToggle,
labelBuilder: labelBuilder,
title: title,
),
);
}
}

class _TabBarDelegate extends SliverPersistentHeaderDelegate {
_TabBarDelegate(this.tabBar);

final PreferredSizeWidget tabBar;

@override
double get minExtent => tabBar.preferredSize.height;

@override
double get maxExtent => minExtent;

@override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
return Container(
PrimaelQuemerais marked this conversation as resolved.
Show resolved Hide resolved
color: Theme.of(context).scaffoldBackgroundColor,
child: tabBar,
);
}

@override
bool shouldRebuild(covariant _TabBarDelegate oldDelegate) {
return tabBar != oldDelegate.tabBar;
}
}
Loading
Loading