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: Make attributes clickable #1654

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import 'package:flutter/material.dart';
import 'package:openfoodfacts/model/KnowledgePanel.dart';
import 'package:openfoodfacts/model/KnowledgePanels.dart';
import 'package:smooth_app/cards/product_cards/knowledge_panels/knowledge_panel_expanded_card.dart';
import 'package:smooth_app/cards/product_cards/knowledge_panels/knowledge_panel_full_page.dart';
import 'package:smooth_app/cards/product_cards/knowledge_panels/knowledge_panel_summary_card.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_card.dart';
import 'package:smooth_app/helpers/analytics_helper.dart';
import 'package:smooth_app/themes/smooth_theme.dart';

class KnowledgePanelCard extends StatelessWidget {
const KnowledgePanelCard({
Expand All @@ -19,7 +17,6 @@ class KnowledgePanelCard extends StatelessWidget {

@override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
// If [expanded] = true, render all panel elements (including summary), otherwise just renders panel summary.
if (panel.expanded ?? false) {
return KnowledgePanelExpandedCard(
Expand All @@ -36,24 +33,9 @@ class KnowledgePanelCard extends StatelessWidget {
Navigator.push<Widget>(
context,
MaterialPageRoute<Widget>(
builder: (BuildContext context) => Scaffold(
backgroundColor: SmoothTheme.getColor(
themeData.colorScheme,
SmoothTheme.getMaterialColor(context),
ColorDestination.SURFACE_BACKGROUND,
),
appBar: AppBar(),
body: SingleChildScrollView(
child: SmoothCard(
padding: const EdgeInsets.all(
SMALL_SPACE,
),
child: KnowledgePanelExpandedCard(
panel: panel,
allPanels: allPanels,
),
),
),
builder: (BuildContext context) => KnowledgePanelFullPage(
panel: panel,
allPanels: allPanels,
),
),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/model/KnowledgePanel.dart';
import 'package:openfoodfacts/model/KnowledgePanels.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/cards/product_cards/knowledge_panels/knowledge_panel_full_page.dart';
import 'package:smooth_app/data_models/data_provider.dart';

class KnowledgePanelFullLoadingPage extends StatelessWidget {
Copy link
Contributor

Choose a reason for hiding this comment

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

Very surprised that you don't use LoadingDialog instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought about that too, but that would have meant that I would have had to distribute the Future through the provider, this seemed to add even more complexity especially when you try to avoid having two calls (when opening the product page and when opening the attributes)

const KnowledgePanelFullLoadingPage(
{required this.panelId, required this.barcode});

final String panelId;
final String barcode;

@override
Widget build(BuildContext context) {
final KnowledgePanels? knowledgePanels = context
.select<DataProvider<Map<String, KnowledgePanels?>>, KnowledgePanels?>(
(DataProvider<Map<String, KnowledgePanels?>> value) =>
value.value[barcode]);

if (knowledgePanels == null) {
return Scaffold(
appBar: AppBar(
title:
Text(AppLocalizations.of(context)!.loading_dialog_default_title),
),
body: const Center(
child: CircularProgressIndicator(),
),
);
}

final KnowledgePanel? knowledgePanel =
knowledgePanels.panelIdToPanelMap[panelId];

if (knowledgePanel == null) {
Future<void>.delayed(Duration.zero, () {
Navigator.pop(context);
});
return Container();
}
Future<void>.delayed(Duration.zero, () {
Navigator.pushReplacement(
context,
MaterialPageRoute<Widget>(
builder: (BuildContext context) => KnowledgePanelFullPage(
panel: knowledgePanel,
allPanels: knowledgePanels,
),
),
);
});

return Scaffold(
body: Container(),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:openfoodfacts/model/KnowledgePanel.dart';
import 'package:openfoodfacts/model/KnowledgePanels.dart';
import 'package:smooth_app/cards/product_cards/knowledge_panels/knowledge_panel_expanded_card.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_card.dart';
import 'package:smooth_app/themes/smooth_theme.dart';

class KnowledgePanelFullPage extends StatelessWidget {
const KnowledgePanelFullPage({
required this.panel,
required this.allPanels,
});

final KnowledgePanel panel;
final KnowledgePanels allPanels;

@override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
return Scaffold(
backgroundColor: SmoothTheme.getColor(
themeData.colorScheme,
SmoothTheme.getMaterialColor(context),
ColorDestination.SURFACE_BACKGROUND,
),
appBar: AppBar(
title: Text(panel.titleElement?.title ?? ''),
),
body: SingleChildScrollView(
child: SmoothCard(
padding: const EdgeInsets.all(
SMALL_SPACE,
),
child: KnowledgePanelExpandedCard(
panel: panel,
allPanels: allPanels,
),
),
),
);
}
}
14 changes: 14 additions & 0 deletions packages/smooth_app/lib/data_models/data_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';

class DataProvider<T> with ChangeNotifier {
DataProvider(this._value);

T _value;

T get value => _value;

void setValue(T newValue) {
_value = newValue;
notifyListeners();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:openfoodfacts/utils/OpenFoodAPIConfiguration.dart';
import 'package:smooth_app/database/dao_secured_string.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import 'package:openfoodfacts/model/Product.dart';
import 'package:openfoodfacts/personalized_search/preference_importance.dart';
import 'package:openfoodfacts/personalized_search/product_preferences_manager.dart';
import 'package:smooth_app/data_models/user_preferences.dart';
import 'package:smooth_app/helpers/attributes_card_helper.dart';
import 'package:smooth_app/pages/user_preferences_dev_mode.dart';
import 'attributes_card_helper.dart';

/// Match and score of a [Product] vs. Preferences
///
Expand Down
8 changes: 8 additions & 0 deletions packages/smooth_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:openfoodfacts/model/KnowledgePanels.dart';
import 'package:openfoodfacts/model/UserAgent.dart';
import 'package:openfoodfacts/personalized_search/product_preferences_selection.dart';
import 'package:openfoodfacts/utils/OpenFoodAPIConfiguration.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:smooth_app/data_models/data_provider.dart';
import 'package:smooth_app/data_models/product_preferences.dart';
import 'package:smooth_app/data_models/user_management_provider.dart';
import 'package:smooth_app/data_models/user_preferences.dart';
Expand Down Expand Up @@ -167,6 +169,12 @@ class _SmoothAppState extends State<SmoothApp> {
provide<LocalDatabase>(_localDatabase),
provide<ThemeProvider>(_themeProvider),
provide<UserManagementProvider>(_userManagementProvider),
//Needs to be created here to be visible after calling Navigator.push
provide<DataProvider<Map<String, KnowledgePanels?>>>(
DataProvider<Map<String, KnowledgePanels?>>(
<String, KnowledgePanels?>{},
),
),
],
builder: _buildApp,
);
Expand Down
130 changes: 53 additions & 77 deletions packages/smooth_app/lib/pages/product/new_product_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/model/KnowledgePanels.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/cards/product_cards/knowledge_panels/knowledge_panels_builder.dart';
import 'package:smooth_app/cards/product_cards/product_image_carousel.dart';
import 'package:smooth_app/data_models/data_provider.dart';
import 'package:smooth_app/data_models/fetched_product.dart';
import 'package:smooth_app/data_models/product_list.dart';
import 'package:smooth_app/data_models/product_preferences.dart';
Expand All @@ -23,7 +23,7 @@ import 'package:smooth_app/pages/product/category_picker_page.dart';
import 'package:smooth_app/pages/product/common/product_dialog_helper.dart';
import 'package:smooth_app/pages/product/common/product_list_page.dart';
import 'package:smooth_app/pages/product/edit_product_page.dart';
import 'package:smooth_app/pages/product/knowledge_panel_product_cards.dart';
import 'package:smooth_app/pages/product/product_knowledge_panels.dart';
import 'package:smooth_app/pages/product/summary_card.dart';
import 'package:smooth_app/pages/product_list_user_dialog_helper.dart';
import 'package:smooth_app/pages/user_preferences_dev_mode.dart';
Expand All @@ -39,8 +39,6 @@ class ProductPage extends StatefulWidget {
State<ProductPage> createState() => _ProductPageState();
}

enum ProductPageMenuItem { WEB, REFRESH }

class _ProductPageState extends State<ProductPage> {
late Product _product;
late ProductPreferences _productPreferences;
Expand All @@ -63,28 +61,48 @@ class _ProductPageState extends State<ProductPage> {
final ThemeData themeData = Theme.of(context);
final ColorScheme colorScheme = themeData.colorScheme;
final MaterialColor materialColor = SmoothTheme.getMaterialColor(context);
return Scaffold(
backgroundColor: SmoothTheme.getColor(
colorScheme,
materialColor,
ColorDestination.SURFACE_BACKGROUND,
),
floatingActionButton: scrollingUp
? FloatingActionButton(
backgroundColor: colorScheme.primary,
onPressed: () {
Navigator.maybePop(context);
},
child: Icon(
ConstantIcons.instance.getBackIcon(),
color: Colors.white,
),
)
: null,
floatingActionButtonLocation: FloatingActionButtonLocation.startTop,
body: Stack(
children: <Widget>[
NotificationListener<UserScrollNotification>(

final DataProvider<Map<String, KnowledgePanels?>> knowledgePanelsProvider =
context.read<DataProvider<Map<String, KnowledgePanels?>>>();

KnowledgePanelsQuery(
barcode: _product.barcode!,
).getKnowledgePanels().then((KnowledgePanels value) {
final Map<String, KnowledgePanels?> data = knowledgePanelsProvider.value;
data.putIfAbsent(_product.barcode!, () => value);
knowledgePanelsProvider.setValue(data);
});

return WillPopScope(
onWillPop: () async {
final Map<String, KnowledgePanels?> data =
knowledgePanelsProvider.value;
data.remove(_product.barcode);
knowledgePanelsProvider.setValue(data);
return true;
},
child: Scaffold(
backgroundColor: SmoothTheme.getColor(
colorScheme,
materialColor,
ColorDestination.SURFACE_BACKGROUND,
),
floatingActionButton: scrollingUp
? FloatingActionButton(
backgroundColor: colorScheme.primary,
onPressed: () {
Navigator.maybePop(context);
},
child: Icon(
ConstantIcons.instance.getBackIcon(),
color: Colors.white,
),
)
: null,
floatingActionButtonLocation: FloatingActionButtonLocation.startTop,
body: Stack(
children: <Widget>[
NotificationListener<UserScrollNotification>(
onNotification: (UserScrollNotification notification) {
if (notification.direction == ScrollDirection.forward) {
if (!scrollingUp) {
Expand All @@ -101,8 +119,10 @@ class _ProductPageState extends State<ProductPage> {
}
return true;
},
child: _buildProductBody(context)),
],
child: _buildProductBody(context),
),
],
),
),
);
}
Expand Down Expand Up @@ -173,7 +193,10 @@ class _ProductPageState extends State<ProductPage> {
),
),
),
_buildKnowledgePanelCards(),
ProductPageKnowledgePanels(
product: _product,
setState: setState,
),
_buildActionBar(appLocalizations),
if (productListNames.isNotEmpty)
_buildListWidget(appLocalizations, productListNames, daoProductList),
Expand Down Expand Up @@ -219,59 +242,12 @@ class _ProductPageState extends State<ProductPage> {
setState(() {});
}
},
child: const Text('Additional Button'),
child: const Text('Additional Button (CategoryPicker)'),
),
]),
);
}

FutureBuilder<KnowledgePanels> _buildKnowledgePanelCards() {
// Note that this will make a new request on every rebuild.
// TODO(jasmeet): Avoid additional requests on rebuilds.
final Future<KnowledgePanels> knowledgePanels = KnowledgePanelsQuery(
barcode: _product.barcode!,
).getKnowledgePanels();
return FutureBuilder<KnowledgePanels>(
future: knowledgePanels,
builder:
(BuildContext context, AsyncSnapshot<KnowledgePanels> snapshot) {
List<Widget> knowledgePanelWidgets = <Widget>[];
if (snapshot.hasData) {
// Render all KnowledgePanels
knowledgePanelWidgets =
KnowledgePanelsBuilder(setState: () => setState(() {}))
.buildAll(
snapshot.data!,
context: context,
product: _product,
);
} else if (snapshot.hasError) {
// TODO(jasmeet): Retry the request.
// Do nothing for now.
} else {
// Query results not available yet.
knowledgePanelWidgets = <Widget>[_buildLoadingWidget()];
}
return KnowledgePanelProductCards(knowledgePanelWidgets);
});
}

Widget _buildLoadingWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
],
),
);
}

Future<void> _editList() async {
final LocalDatabase localDatabase = context.read<LocalDatabase>();
final DaoProductList daoProductList = DaoProductList(localDatabase);
Expand Down
Loading