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

Update scan page as per the UX spec (well, roughly) #659

Merged
merged 29 commits into from
Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5c4f815
Reuse summary card as scan card
slava-sh Oct 28, 2021
ca59975
Minor changes
Nov 2, 2021
308feb1
Formatting fix
Nov 2, 2021
a4be0af
Remove unused imports
Nov 2, 2021
fffd378
Create a separate file for ScanCard
Nov 2, 2021
17e6cdf
Format
Nov 2, 2021
f48e785
Formatting
Nov 2, 2021
c75c5f0
Rename product_page_helper -> product_card_helper
Nov 2, 2021
a8258e4
Organize sort
Nov 2, 2021
a132e53
Merge branch 'openfoodfacts:develop' into main
jasmeet0817 Nov 3, 2021
996bf5d
Dynamically limit the number of elements rendered on Scan card.
Nov 3, 2021
4e7428d
Minor fixes
Nov 3, 2021
75fde2e
Standardize summary card
Nov 3, 2021
03753fa
Test changes
Nov 8, 2021
9e32738
Temp changes, remove later
Nov 9, 2021
b773e19
Clip Summary card when it overflows
Nov 11, 2021
60a75a1
Format
Nov 11, 2021
0c7326c
Merge branch 'openfoodfacts:develop' into main
jasmeet0817 Nov 12, 2021
c271b90
Revert "Update localization for refs/heads/gha-l10n version"
Nov 12, 2021
058fc9f
Merge remote-tracking branch 'mine/main'
Nov 12, 2021
a983b6c
Merge branch 'openfoodfacts:develop' into main
jasmeet0817 Nov 15, 2021
b85f7fe
Merge remote-tracking branch 'mine/main'
Nov 15, 2021
7eb49a5
Update buttons on scan page as per the spec
Nov 15, 2021
fa59490
Update buttons on scan page as per the spec
Nov 15, 2021
771bef5
Update buttons on scan page as per the spec
Nov 15, 2021
70b693c
Update buttons on scan page as per the spec
Nov 15, 2021
a82aedc
Adjust the scanner to be placed symmetrically in the center of button…
Nov 15, 2021
88bbb66
Adjust the scanner to be placed symmetrically in the center of button…
Nov 15, 2021
8ba7bd9
revert pub_upgrade's weird changes
Nov 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Empty file modified ci/pub_upgrade.sh
100755 → 100644
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ContinuousScanModel with ChangeNotifier {
final String languageCode;
final String countryCode;

bool get isNotEmpty => getBarcodes().isNotEmpty;
bool get hasMoreThanOneProduct => getBarcodes().length > 1;
ProductList get productList => _productList;

List<String> getBarcodes() => _barcodes;
Expand Down
201 changes: 115 additions & 86 deletions packages/smooth_app/lib/pages/scan/continuous_scan_page.dart
Original file line number Diff line number Diff line change
@@ -1,114 +1,140 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:smooth_app/data_models/continuous_scan_model.dart';
import 'package:smooth_app/pages/personalized_ranking_page.dart';
import 'package:smooth_app/widgets/smooth_product_carousel.dart';
import 'package:smooth_ui_library/smooth_ui_library.dart';
import 'package:smooth_ui_library/util/ui_helpers.dart';

class ContinuousScanPage extends StatelessWidget {
final GlobalKey _scannerViewKey = GlobalKey(debugLabel: 'Barcode Scanner');

@override
Widget build(BuildContext context) {
final ContinuousScanModel model = context.watch<ContinuousScanModel>();
final AppLocalizations localizations = AppLocalizations.of(context)!;
final Size screenSize = MediaQuery.of(context).size;
final double carouselHeight = screenSize.height / 2;
final double viewFinderBottomOffset = carouselHeight / 2.0;
return Scaffold(
appBar: AppBar(toolbarHeight: 0.0),
body: Stack(
children: <Widget>[
Container(
alignment: Alignment.center,
color: Colors.black,
child: Padding(
// Double the offset to account for the vertical centering.
padding: EdgeInsets.only(bottom: 2 * viewFinderBottomOffset),
child: SvgPicture.asset(
'assets/actions/scanner_alt_2.svg',
width: 60.0,
height: 60.0,
color: Colors.white,
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final Size screenSize = MediaQuery.of(context).size;
final Size scannerSize = Size(
screenSize.width * 0.6,
screenSize.width * 0.33,
);
Comment on lines +20 to +23
Copy link
Member

Choose a reason for hiding this comment

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

Is it right that both use the width

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, they are supposed to be proportional to one another

final ContinuousScanModel model = context.watch<ContinuousScanModel>();
final double carouselHeight =
constraints.maxHeight / 1.81; // roughly 55% of the available height
final double buttonRowHeight = areButtonsRendered(model) ? 48 : 0;
final double availableScanHeight =
constraints.maxHeight - carouselHeight - buttonRowHeight;
// Padding for the qr code scanner. This ensures the scanner has equal spacing between buttons and carousel.
final EdgeInsets qrScannerPadding = EdgeInsets.only(
top:
(availableScanHeight - scannerSize.height) / 2 + buttonRowHeight);
final double viewFinderBottomOffset = carouselHeight / 2.0;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

not entirely sure if this is needed.... (?)

Copy link
Member

Choose a reason for hiding this comment

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

It was added by @slava-sh a while ago, but I don't know what it's exact purpose is

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will try and test on an iOS device, (device_preview renders fine without this offset but maybe there's some issue on iOS)

return Scaffold(
appBar: AppBar(toolbarHeight: 0.0),
body: Stack(
children: <Widget>[
Container(
alignment: Alignment.center,
color: Colors.black,
child: Padding(
padding: qrScannerPadding,
child: SvgPicture.asset(
'assets/actions/scanner_alt_2.svg',
width: 60.0,
height: 60.0,
color: Colors.white,
),
),
),
),
SmoothRevealAnimation(
delay: 400,
startOffset: Offset.zero,
animationCurve: Curves.easeInOutBack,
child: QRView(
overlay: QrScannerOverlayShape(
// We use [SmoothViewFinder] instead of the overlay.
overlayColor: Colors.transparent,
// This offset adjusts the scanning area on iOS.
cutOutBottomOffset: viewFinderBottomOffset,
SmoothRevealAnimation(
delay: 400,
startOffset: Offset.zero,
animationCurve: Curves.easeInOutBack,
child: QRView(
overlay: QrScannerOverlayShape(
// We use [SmoothViewFinder] instead of the overlay.
overlayColor: Colors.transparent,
// This offset adjusts the scanning area on iOS.
cutOutBottomOffset: viewFinderBottomOffset,
),
key: _scannerViewKey,
onQRViewCreated: model.setupScanner,
),
key: _scannerViewKey,
onQRViewCreated: model.setupScanner,
),
),
SmoothRevealAnimation(
delay: 400,
startOffset: const Offset(0.0, 0.1),
animationCurve: Curves.easeInOutBack,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
// Double the offset to account for the vertical centering.
padding: EdgeInsets.only(bottom: 2 * viewFinderBottomOffset),
child: SmoothViewFinder(
boxSize: Size(
screenSize.width * 0.6,
screenSize.width * 0.33,
SmoothRevealAnimation(
delay: 400,
startOffset: const Offset(0.0, 0.1),
animationCurve: Curves.easeInOutBack,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: qrScannerPadding,
child: SmoothViewFinder(
boxSize: scannerSize,
lineLength: screenSize.width * 0.8,
),
lineLength: screenSize.width * 0.8,
),
),
],
],
),
),
),
SmoothRevealAnimation(
delay: 400,
startOffset: const Offset(0.0, -0.1),
animationCurve: Curves.easeInOutBack,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
AnimatedOpacity(
opacity: model.isNotEmpty ? 1.0 : 0.0,
duration: const Duration(milliseconds: 50),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton.icon(
icon: const Icon(Icons.cancel_outlined),
onPressed: model.clearScanSession,
label: const Text('Clear'),
),
ElevatedButton.icon(
icon: const Icon(Icons.emoji_events_outlined),
onPressed: () => _openPersonalizedRankingPage(context),
label: Text(localizations.myPersonalizedRanking),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: SmoothProductCarousel(
SmoothRevealAnimation(
delay: 400,
startOffset: const Offset(0.0, -0.1),
animationCurve: Curves.easeInOutBack,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
_buildButtonsRow(context, model),
const Spacer(),
SmoothProductCarousel(
showSearchCard: true,
height: carouselHeight,
),
),
],
],
),
),
],
),
);
});
}

Widget _buildButtonsRow(BuildContext context, ContinuousScanModel model) {
final ButtonStyle buttonStyle = ButtonStyle(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
),
);
return AnimatedOpacity(
opacity: areButtonsRendered(model) ? 0.8 : 0.0,
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess it would make sense to display the "clear" button when we only have one product.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm good point, I'm still not sold on this app behavior to be honest, because both buttons are very interdependent in functionality and the functionality is super unclear to the user unless they experiment with the buttons.

  • The Compare button compares all products in scan history
  • Clear button clears scan history

So I personally think it makes sense to pair and show them together, but I will bring it up with Pierre, Stephane and others this week

duration: const Duration(milliseconds: 50),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: VERY_SMALL_SPACE, horizontal: MEDIUM_SPACE),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
ElevatedButton.icon(
style: buttonStyle,
icon: const Icon(Icons.cancel_outlined),
onPressed: model.clearScanSession,
// TODO(jasmeet): Internationalize
label: const Text('Clear'),
),
ElevatedButton.icon(
style: buttonStyle,
icon: const Icon(Icons.emoji_events_outlined),
onPressed: () => _openPersonalizedRankingPage(context),
// TODO(jasmeet): Internationalize
label: Text('Compare ${model.getBarcodes().length} Products'),
),
),
],
],
),
),
);
}
Expand All @@ -126,4 +152,7 @@ class ContinuousScanPage extends StatelessWidget {
);
await model.refresh();
}

bool areButtonsRendered(ContinuousScanModel model) =>
model.hasMoreThanOneProduct;
}