diff --git a/assets/ic_flash_off_white_48dp.png b/assets/ic_flash_off_white_48dp.png deleted file mode 100644 index 93a2626..0000000 Binary files a/assets/ic_flash_off_white_48dp.png and /dev/null differ diff --git a/assets/ic_flash_on_white_48dp.png b/assets/ic_flash_on_white_48dp.png deleted file mode 100644 index c9a3539..0000000 Binary files a/assets/ic_flash_on_white_48dp.png and /dev/null differ diff --git a/assets/scan/flashlightOff.svg b/assets/scan/flashlightOff.svg new file mode 100644 index 0000000..4b8de8e --- /dev/null +++ b/assets/scan/flashlightOff.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/scan/flashlightOn.svg b/assets/scan/flashlightOn.svg new file mode 100644 index 0000000..e46c693 --- /dev/null +++ b/assets/scan/flashlightOn.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/scan/showMore.svg b/assets/scan/showMore.svg new file mode 100644 index 0000000..b3f1efa --- /dev/null +++ b/assets/scan/showMore.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 5d5a61c..1c0002d 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 1 -/// Strings: 22 +/// Strings: 28 /// -/// Built on 2024-08-21 at 08:34 UTC +/// Built on 2024-09-24 at 17:35 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -199,6 +199,12 @@ class _StringsScanEn { // Translations String get scanning => 'Skanowanie'; + String get tryAgain => 'Niestety nie udało się pobrać danych. Spróbuj ponownie.'; + String get pkt => ' pkt'; + String get wait => 'Proszę czekać, trwa Ładowanie...'; + String get lastScans => 'Ostatnie skany:'; + String get error => 'Wystąpił błąd'; + String get closeError => 'Zamknij.'; } /// Flat map(s) containing all translations. @@ -229,6 +235,12 @@ extension on Translations { case 'companyScreen.companyFriend': return ' Ta firma jest przyjacielem Poli'; case 'companyScreen.polaFriends': return 'Przyjaciele Poli'; case 'scan.scanning': return 'Skanowanie'; + case 'scan.tryAgain': return 'Niestety nie udało się pobrać danych. Spróbuj ponownie.'; + case 'scan.pkt': return ' pkt'; + case 'scan.wait': return 'Proszę czekać, trwa Ładowanie...'; + case 'scan.lastScans': return 'Ostatnie skany:'; + case 'scan.error': return 'Wystąpił błąd'; + case 'scan.closeError': return 'Zamknij.'; default: return null; } } diff --git a/lib/i18n/strings.i18n.json b/lib/i18n/strings.i18n.json index 71b4726..71a16a8 100644 --- a/lib/i18n/strings.i18n.json +++ b/lib/i18n/strings.i18n.json @@ -25,6 +25,13 @@ "polaFriends": "Przyjaciele Poli" }, "scan": { - "scanning": "Skanowanie" + "scanning": "Skanowanie", + "tryAgain": "Niestety nie udało się pobrać danych. Spróbuj ponownie.", + "pkt":" pkt", + "wait": "Proszę czekać, trwa Ładowanie...", + "lastScans": "Ostatnie skany:", + "error": "Wystąpił błąd", + "closeError": "Zamknij." } + } diff --git a/lib/main.dart b/lib/main.dart index 2a16e46..5e18481 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -70,22 +70,15 @@ class _PolaAppState extends State { body: IndexedStack( index: _selectedIndex, children: _tabs, - ) - ), + )), ), ); } final List _tabs = [ MainPage(), - WebViewTab( - title: "Wyszukiwarka", - url: "https://www.pola-app.pl/m/search/" - ), - WebViewTab( - title: "Wiadomości", - url: "https://www.pola-app.pl/m/blog/" - ) + WebViewTab(title: "Wyszukiwarka", url: "https://www.pola-app.pl/m/search/"), + WebViewTab(title: "Wiadomości", url: "https://www.pola-app.pl/m/blog/") ]; AnalyticsMainTab _getTabParameter(int index) { diff --git a/lib/pages/scan/companies_list.dart b/lib/pages/scan/companies_list.dart index f3ecb45..b3c8199 100644 --- a/lib/pages/scan/companies_list.dart +++ b/lib/pages/scan/companies_list.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:pola_flutter/analytics/pola_analytics.dart'; +import 'package:pola_flutter/i18n/strings.g.dart'; import 'package:pola_flutter/pages/scan/remote_button.dart'; import 'package:pola_flutter/pages/scan/scan_state.dart'; +import 'package:pola_flutter/theme/text_size.dart'; import 'package:pola_flutter/ui/list_item.dart'; +import 'dart:math'; class CompaniesList extends StatelessWidget { CompaniesList(this.state, this.listScrollController); @@ -13,35 +16,48 @@ class CompaniesList extends StatelessWidget { @override Widget build(BuildContext context) { + final int listSize = state.list.length; + _scrollToTop(); - return Column(mainAxisAlignment: MainAxisAlignment.end, children: [ - Container( - height: 200, - child: Align( - alignment: Alignment.bottomCenter, - child: ListView.builder( - controller: listScrollController, - reverse: true, - itemCount: state.list.length + (state.isLoading ? 1 : 0), - itemBuilder: (BuildContext context, int index) { - if (index == state.list.length) { - return LoadingListItem(); - } - return GestureDetector( - child: ResultListItem(state.list[index]), - onTap: () { - final result = state.list[index]; - _analytics.opensCard(result); - Navigator.pushNamed(context, '/detail', arguments: result); - }, - ); - }, + + double maxHeight = min(listSize * 47.5, 190.0); + + return Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + _ListHeader(listSize: listSize), + Container( + constraints: BoxConstraints( + maxHeight: maxHeight, + ), + child: Align( + alignment: Alignment.bottomCenter, + child: ListView.builder( + controller: listScrollController, + reverse: true, + itemCount: listSize + (state.isLoading ? 1 : 0), + itemBuilder: (BuildContext context, int index) { + if (index == listSize) { + return LoadingListItem(); + } + return GestureDetector( + child: ResultListItem(state.list[index]), + onTap: () { + final result = state.list[index]; + _analytics.opensCard(result); + Navigator.pushNamed(context, '/detail', arguments: result); + }, + ); + }, + ), ), ), - ), - RemoteButton(RemoteButtonState( - state.list.firstOrNull?.donate, state.list.firstOrNull?.code)) - ]); + RemoteButton(RemoteButtonState( + state.list.firstOrNull?.donate, + state.list.firstOrNull?.code, + )), + ], + ); } void _scrollToTop() { @@ -53,3 +69,31 @@ class CompaniesList extends StatelessWidget { }); } } + +class _ListHeader extends StatelessWidget { + final int listSize; + + const _ListHeader({required this.listSize}); + + @override + Widget build(BuildContext context) { + if (listSize > 0) { + return Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + t.scan.lastScans, + style: TextStyle( + fontSize: TextSize.mediumTitle, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ); + } else { + return Container(); + } + } +} diff --git a/lib/pages/scan/scan.dart b/lib/pages/scan/scan.dart index d6141ef..9589e27 100644 --- a/lib/pages/scan/scan.dart +++ b/lib/pages/scan/scan.dart @@ -29,6 +29,7 @@ class _MainPageState extends State { final PolaAnalytics _analytics = PolaAnalytics.instance(); ScrollController listScrollController = ScrollController(); + bool _isTorchOn = false; @override void initState() { @@ -46,10 +47,9 @@ class _MainPageState extends State { onPressed: () { _analytics.aboutPolaOpened(); showWebViewDialog( - context: context, - url: "https://www.pola-app.pl/m/about", - title: t.menu.aboutPola - ); + context: context, + url: "https://www.pola-app.pl/m/about", + title: t.menu.aboutPola); }, icon: Assets.icLauncher.image(), ), @@ -87,55 +87,82 @@ class _MainPageState extends State { child: Column( children: [ Center( - child: Padding( - padding: const EdgeInsets.only(top: 20.0), - child: Text( - "Umieść kod kreskowy produktu w prostokącie powyżej aby dowiedzieć się więcej o firmie, która go wyprodukowała.", - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white, - )))), + child: Padding( + padding: const EdgeInsets.only(top: 20.0), + child: Text( + "Umieść kod kreskowy produktu w prostokącie powyżej aby dowiedzieć się więcej o firmie, która go wyprodukowała.", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.white), + ), + ), + ), ], ), ), SafeArea( - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Spacer(), - BlocBuilder( - bloc: _scanBloc, - builder: (context, state) { - if (state.isError) { - SchedulerBinding.instance.addPostFrameCallback((_) { - showDialog( - context: context, - barrierDismissible: false, - builder: (_) { - return AlertDialog( - title: Text('Wystąpił błąd'), - content: Text('Niestety nie udało się pobrać danych. Spróbuj ponownie.'), - actions: [ - TextButton( - child: Text('Zamknij.'), - onPressed: () { - _scanBloc.add(ScanEvent.alertDialogDismissed()); - SchedulerBinding.instance.addPostFrameCallback((_) { + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Spacer(), + BlocBuilder( + bloc: _scanBloc, + builder: (context, state) { + if (state.isError) { + SchedulerBinding.instance.addPostFrameCallback((_) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + title: Text(t.scan.error), + content: Text(t.scan.tryAgain), + actions: [ + TextButton( + child: Text(t.scan.closeError), + onPressed: () { + _scanBloc + .add(ScanEvent.alertDialogDismissed()); Navigator.pop(context); - }); - }, + }, + ), + ], + ); + }, + ); + }); + } + return Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Expanded( + child: CompaniesList(state, listScrollController)), + Column( + children: [ + GestureDetector( + onTap: () { + setState(() { + _isTorchOn = !_isTorchOn; + cameraController.toggleTorch(); + }); + }, + child: Container( + decoration: BoxDecoration( + boxShadow: [], + ), + child: _isTorchOn + ? Assets.scan.flashlightOn.svg() + : Assets.scan.flashlightOff.svg(), ), - ], - ); - }, - ); - }); - } - return CompaniesList(state, listScrollController); - }, - ), - ], - )), + ) + ], + ) + ], + ); + }, + ), + ], + ), + ), ], ), extendBodyBehindAppBar: true, @@ -151,15 +178,16 @@ class _MainPageState extends State { children: [ Positioned.fill( child: MobileScanner( - controller: cameraController, - onDetect: (capture) { - final List barcodes = capture.barcodes; - for (final barcode in barcodes) { - final String code = barcode.rawValue!; - debugPrint('Barcode found! $code'); - _scanBloc.add(ScanEvent.barcodeScanned(code)); - } - }), + controller: cameraController, + onDetect: (capture) { + final List barcodes = capture.barcodes; + for (final barcode in barcodes) { + final String code = barcode.rawValue!; + debugPrint('Barcode found! $code'); + _scanBloc.add(ScanEvent.barcodeScanned(code)); + } + }, + ), ), Positioned.fill( child: Align( diff --git a/lib/theme/assets.gen.dart b/lib/theme/assets.gen.dart index d79e809..133472d 100644 --- a/lib/theme/assets.gen.dart +++ b/lib/theme/assets.gen.dart @@ -72,6 +72,22 @@ class $AssetsNavigationGen { List get values => [close]; } +class $AssetsScanGen { + const $AssetsScanGen(); + + /// File path: assets/scan/flashlightOff.svg + SvgGenImage get flashlightOff => const SvgGenImage('assets/scan/flashlightOff.svg'); + + /// File path: assets/scan/flashlightOn.svg + SvgGenImage get flashlightOn => const SvgGenImage('assets/scan/flashlightOn.svg'); + + /// File path: assets/scan/showMore.svg + SvgGenImage get showMore => const SvgGenImage('assets/scan/showMore.svg'); + + /// List of all assets + List get values => [flashlightOff, flashlightOn, showMore]; +} + class Assets { Assets._(); @@ -80,24 +96,15 @@ class Assets { static const AssetGenImage icBackspaceWhite36dp = AssetGenImage('assets/ic_backspace_white_36dp.png'); static const AssetGenImage icDialpadWhite36dp = AssetGenImage('assets/ic_dialpad_white_36dp.png'); static const AssetGenImage icDoneWhite36dp = AssetGenImage('assets/ic_done_white_36dp.png'); - static const AssetGenImage icFlashOffWhite48dp = AssetGenImage('assets/ic_flash_off_white_48dp.png'); - static const AssetGenImage icFlashOnWhite48dp = AssetGenImage('assets/ic_flash_on_white_48dp.png'); static const AssetGenImage icLauncher = AssetGenImage('assets/ic_launcher.png'); static const AssetGenImage menu = AssetGenImage('assets/menu.png'); static const $AssetsMenuPageGen menuPage = $AssetsMenuPageGen(); static const $AssetsNavigationGen navigation = $AssetsNavigationGen(); + static const $AssetsScanGen scan = $AssetsScanGen(); /// List of all assets - static List get values => [ - icAddBlack24dp, - icBackspaceWhite36dp, - icDialpadWhite36dp, - icDoneWhite36dp, - icFlashOffWhite48dp, - icFlashOnWhite48dp, - icLauncher, - menu - ]; + static List get values => + [icAddBlack24dp, icBackspaceWhite36dp, icDialpadWhite36dp, icDoneWhite36dp, icLauncher, menu]; } class AssetGenImage { diff --git a/lib/ui/list_item.dart b/lib/ui/list_item.dart index e9653d9..6d74364 100644 --- a/lib/ui/list_item.dart +++ b/lib/ui/list_item.dart @@ -1,42 +1,83 @@ import 'package:flutter/material.dart'; +import 'package:pola_flutter/i18n/strings.g.dart'; import 'package:pola_flutter/models/search_result.dart'; +import 'package:pola_flutter/theme/assets.gen.dart'; +import 'package:pola_flutter/theme/colors.dart'; +import 'package:pola_flutter/theme/fonts.gen.dart'; import 'package:pola_flutter/theme/text_size.dart'; class ResultListItem extends StatelessWidget { ResultListItem(this.searchResult); final SearchResult searchResult; + static const double leftBoxSize = 40.0; @override Widget build(BuildContext context) { - final textStyle = TextStyle(fontWeight: FontWeight.normal, fontSize: TextSize.mediumTitle); + final pointValueStyle = TextStyle( + height: 0, + fontWeight: FontWeight.w700, + fontFamily: FontFamily.roboto, + fontSize: TextSize.mediumTitle, + color: Colors.white, + ); + + final pointDescriptionStyle = TextStyle( + height: 0.1, + fontWeight: FontWeight.w700, + fontFamily: FontFamily.roboto, + fontSize: 9, + color: Colors.white, + ); + return _ListItem( - child: Column( - children: [ - Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: EdgeInsets.all(4.0), - child: Text(searchResult.name!, style: textStyle,) - ) - ) - ), - Align( - alignment: Alignment.bottomCenter, - child: ClipRRect( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(5), - bottomRight: Radius.circular(5) + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: leftBoxSize, + height: leftBoxSize, + decoration: BoxDecoration( + color: AppColors.defaultRed, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(leftBoxSize / 2), + bottomLeft: Radius.circular(leftBoxSize / 2), + ), + ), + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '${searchResult.companies?.first.plScore ?? 0}', + style: pointValueStyle, + textAlign: TextAlign.center, + ), + Text( + t.scan.pkt, + style: pointDescriptionStyle, + textAlign: TextAlign.center, + ), + ], ), - child: LinearProgressIndicator( - value:(searchResult.companies?.first.plScore ?? 0) / 100.toDouble(), - backgroundColor: Colors.white, - semanticsLabel: 'Linear progress indicator', + ), + SizedBox(width: 8.0), + Expanded( + child: Container( + alignment: Alignment.centerLeft, + child: Text( + searchResult.name!, + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: TextSize.smallTitle, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), ), ), - )], - ) + ], + ), ); } } @@ -46,49 +87,71 @@ class LoadingListItem extends StatelessWidget { @override Widget build(BuildContext context) { - final textStyle = TextStyle(fontWeight: FontWeight.normal, fontSize: TextSize.mediumTitle); return _ListItem( - child: Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: EdgeInsets.all(4.0), - child: Row( - children: [ + child: Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: EdgeInsets.all(4.0), + child: Row( + children: [ CircularProgressIndicator(), Padding( - padding: EdgeInsets.only(left: 8.0), - child: Text("Ładowanie...",style: textStyle,) - )] - ) + padding: EdgeInsets.only(left: 8.0), + child: Text( + t.scan.wait, + style: TextStyle( + fontWeight: + FontWeight.w400, + fontSize: + TextSize.smallTitle, + ), + ), + ), + ], ), ), - ) + ), + showMore: false, ); } } class _ListItem extends StatelessWidget { final Widget child; + final bool showMore; - const _ListItem({required this.child}); + const _ListItem({ + required this.child, + this.showMore = true, + }); @override Widget build(BuildContext context) { return Padding( - padding: EdgeInsets.only(top: 4.0, left: 8.0, right: 8.0, bottom: 4.0), + padding: EdgeInsets.only(top: 4.0, left: 16.0, right: 8.0, bottom: 4.0), child: Container( - height: 50, + height: 40, child: DecoratedBox( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( - topLeft: Radius.circular(5), - topRight: Radius.circular(5), - bottomRight: Radius.circular(5), - bottomLeft: Radius.circular(5)), + topLeft: Radius.circular(30), + topRight: Radius.circular(30), + bottomRight: Radius.circular(30), + bottomLeft: Radius.circular(30), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded(child: child), + if (showMore) + Padding( + padding: EdgeInsets.only(right: 8.0), + child: Assets.scan.showMore.svg(), + ), + ], ), - child: child, ), ), ); diff --git a/pubspec.yaml b/pubspec.yaml index cc66e2e..ab1b19b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,6 +58,7 @@ flutter: - assets/fonts/ - assets/company/ - assets/navigation/ + - assets/scan/ fonts: - family: Roboto