18
18
19
19
import 'package:flutter/material.dart' ;
20
20
import 'package:flutter/services.dart' ;
21
- import 'package:flutter/widgets.dart' ;
22
21
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart' ;
23
22
import 'package:flutter_gen/gen_l10n/app_localizations.dart' ;
24
23
import 'package:flutter_typeahead/flutter_typeahead.dart' ;
25
24
import 'package:provider/provider.dart' ;
25
+ import 'package:wger/helpers/consts.dart' ;
26
26
import 'package:wger/helpers/ui.dart' ;
27
27
import 'package:wger/providers/nutrition.dart' ;
28
28
import 'package:wger/widgets/core/core.dart' ;
29
29
30
30
class IngredientTypeahead extends StatefulWidget {
31
31
final TextEditingController _ingredientController;
32
32
final TextEditingController _ingredientIdController;
33
- late String ? _barcode;
34
- late final bool ? _test;
35
- final bool _showScanner;
36
33
37
- IngredientTypeahead (this ._ingredientIdController, this ._ingredientController, this ._showScanner,
38
- [this ._barcode, this ._test]);
34
+ String ? barcode = '' ;
35
+ late final bool ? test;
36
+ final bool showScanner;
37
+
38
+ IngredientTypeahead (
39
+ this ._ingredientIdController,
40
+ this ._ingredientController, {
41
+ this .showScanner = true ,
42
+ this .test = false ,
43
+ this .barcode = '' ,
44
+ });
39
45
40
46
@override
41
47
_IngredientTypeaheadState createState () => _IngredientTypeaheadState ();
@@ -45,7 +51,12 @@ Future<String> scanBarcode(BuildContext context) async {
45
51
String barcode;
46
52
try {
47
53
barcode = await FlutterBarcodeScanner .scanBarcode (
48
- '#ff6666' , AppLocalizations .of (context).close, true , ScanMode .BARCODE );
54
+ '#ff6666' ,
55
+ AppLocalizations .of (context).close,
56
+ true ,
57
+ ScanMode .BARCODE ,
58
+ );
59
+
49
60
if (barcode.compareTo ('-1' ) == 0 ) {
50
61
return '' ;
51
62
}
@@ -57,116 +68,134 @@ Future<String> scanBarcode(BuildContext context) async {
57
68
}
58
69
59
70
class _IngredientTypeaheadState extends State <IngredientTypeahead > {
71
+ var _searchEnglish = true ;
72
+
60
73
@override
61
74
Widget build (BuildContext context) {
62
- return TypeAheadFormField (
63
- textFieldConfiguration: TextFieldConfiguration (
64
- controller: widget._ingredientController,
65
- decoration: InputDecoration (
66
- prefixIcon: const Icon (Icons .search),
67
- labelText: AppLocalizations .of (context).searchIngredient,
68
- suffixIcon: widget._showScanner ? scanButton () : null ,
75
+ return Column (
76
+ children: [
77
+ TypeAheadFormField (
78
+ textFieldConfiguration: TextFieldConfiguration (
79
+ controller: widget._ingredientController,
80
+ decoration: InputDecoration (
81
+ prefixIcon: const Icon (Icons .search),
82
+ labelText: AppLocalizations .of (context).searchIngredient,
83
+ suffixIcon: widget.showScanner ? scanButton () : null ,
84
+ ),
85
+ ),
86
+ suggestionsCallback: (pattern) async {
87
+ return Provider .of <NutritionPlansProvider >(context, listen: false ).searchIngredient (
88
+ pattern,
89
+ languageCode: Localizations .localeOf (context).languageCode,
90
+ searchEnglish: _searchEnglish,
91
+ );
92
+ },
93
+ itemBuilder: (context, dynamic suggestion) {
94
+ final url = context.read <NutritionPlansProvider >().baseProvider.auth.serverUrl;
95
+ return ListTile (
96
+ leading: suggestion['data' ]['image' ] != null
97
+ ? CircleAvatar (backgroundImage: NetworkImage (url! + suggestion['data' ]['image' ]))
98
+ : const CircleIconAvatar (Icon (Icons .image, color: Colors .grey)),
99
+ title: Text (suggestion['value' ]),
100
+ );
101
+ },
102
+ transitionBuilder: (context, suggestionsBox, controller) {
103
+ return suggestionsBox;
104
+ },
105
+ onSuggestionSelected: (dynamic suggestion) {
106
+ widget._ingredientIdController.text = suggestion['data' ]['id' ].toString ();
107
+ widget._ingredientController.text = suggestion['value' ];
108
+ },
109
+ validator: (value) {
110
+ if (value! .isEmpty) {
111
+ return AppLocalizations .of (context).selectIngredient;
112
+ }
113
+ return null ;
114
+ },
69
115
),
70
- ),
71
- suggestionsCallback: (pattern) async {
72
- return Provider .of <NutritionPlansProvider >(context, listen: false ).searchIngredient (
73
- pattern,
74
- Localizations .localeOf (context).languageCode,
75
- );
76
- },
77
- itemBuilder: (context, dynamic suggestion) {
78
- final url = context.read <NutritionPlansProvider >().baseProvider.auth.serverUrl;
79
- return ListTile (
80
- leading: suggestion['data' ]['image' ] != null
81
- ? CircleAvatar (backgroundImage: NetworkImage (url! + suggestion['data' ]['image' ]))
82
- : const CircleIconAvatar (Icon (Icons .image, color: Colors .grey)),
83
- title: Text (suggestion['value' ]),
84
- );
85
- },
86
- transitionBuilder: (context, suggestionsBox, controller) {
87
- return suggestionsBox;
88
- },
89
- onSuggestionSelected: (dynamic suggestion) {
90
- widget._ingredientIdController.text = suggestion['data' ]['id' ].toString ();
91
- widget._ingredientController.text = suggestion['value' ];
92
- },
93
- validator: (value) {
94
- if (value! .isEmpty) {
95
- return AppLocalizations .of (context).selectIngredient;
96
- }
97
- return null ;
98
- },
116
+ if (Localizations .localeOf (context).languageCode != LANGUAGE_SHORT_ENGLISH )
117
+ SwitchListTile (
118
+ title: Text (AppLocalizations .of (context).searchNamesInEnglish),
119
+ value: _searchEnglish,
120
+ onChanged: (_) {
121
+ setState (() {
122
+ _searchEnglish = ! _searchEnglish;
123
+ });
124
+ },
125
+ dense: true ,
126
+ ),
127
+ ],
99
128
);
100
129
}
101
130
102
131
Widget scanButton () {
103
132
return IconButton (
104
- key: const Key ('scan-button' ),
105
- onPressed: () async {
106
- try {
107
- if (! widget._test ! ) {
108
- widget._barcode = await scanBarcode (context);
109
- }
133
+ key: const Key ('scan-button' ),
134
+ onPressed: () async {
135
+ try {
136
+ if (! widget.test ! ) {
137
+ widget.barcode = await scanBarcode (context);
138
+ }
110
139
111
- if (widget._barcode ! .isNotEmpty) {
112
- final result = await Provider .of <NutritionPlansProvider >(context, listen: false )
113
- .searchIngredientWithCode (widget._barcode ! );
140
+ if (widget.barcode ! .isNotEmpty) {
141
+ final result = await Provider .of <NutritionPlansProvider >(context, listen: false )
142
+ .searchIngredientWithCode (widget.barcode ! );
114
143
115
- if (result != null ) {
116
- showDialog (
117
- context: context,
118
- builder: (ctx) => AlertDialog (
119
- key: const Key ('found-dialog' ),
120
- title: Text (AppLocalizations .of (context).productFound),
121
- content:
122
- Text (AppLocalizations .of (context).productFoundDescription (result.name)),
123
- actions: [
124
- TextButton (
125
- key: const Key ('found-dialog-confirm-button' ),
126
- child: Text (MaterialLocalizations .of (context).continueButtonLabel),
127
- onPressed: () {
128
- widget._ingredientController.text = result.name;
129
- widget._ingredientIdController.text = result.id.toString ();
130
- Navigator .of (ctx).pop ();
131
- },
132
- ),
133
- TextButton (
134
- key: const Key ('found-dialog-close-button' ),
135
- child: Text (MaterialLocalizations .of (context).closeButtonLabel),
136
- onPressed: () {
137
- Navigator .of (ctx).pop ();
138
- },
139
- )
140
- ],
141
- ),
142
- );
143
- } else {
144
- //nothing is matching barcode
145
- showDialog (
146
- context: context,
147
- builder: (ctx) => AlertDialog (
148
- key: const Key ('notFound-dialog' ),
149
- title: Text (AppLocalizations .of (context).productNotFound),
150
- content: Text (
151
- AppLocalizations .of (context).productNotFoundDescription (widget._barcode! ),
144
+ if (result != null ) {
145
+ showDialog (
146
+ context: context,
147
+ builder: (ctx) => AlertDialog (
148
+ key: const Key ('found-dialog' ),
149
+ title: Text (AppLocalizations .of (context).productFound),
150
+ content: Text (AppLocalizations .of (context).productFoundDescription (result.name)),
151
+ actions: [
152
+ TextButton (
153
+ key: const Key ('found-dialog-confirm-button' ),
154
+ child: Text (MaterialLocalizations .of (context).continueButtonLabel),
155
+ onPressed: () {
156
+ widget._ingredientController.text = result.name;
157
+ widget._ingredientIdController.text = result.id.toString ();
158
+ Navigator .of (ctx).pop ();
159
+ },
152
160
),
153
- actions: [
154
- TextButton (
155
- key: const Key ('notFound-dialog-close-button' ),
156
- child: Text (MaterialLocalizations .of (context).closeButtonLabel),
157
- onPressed: () {
158
- Navigator .of (ctx).pop ();
159
- },
160
- )
161
- ],
161
+ TextButton (
162
+ key: const Key ('found-dialog-close-button' ),
163
+ child: Text (MaterialLocalizations .of (context).closeButtonLabel),
164
+ onPressed: () {
165
+ Navigator .of (ctx).pop ();
166
+ },
167
+ )
168
+ ],
169
+ ),
170
+ );
171
+ } else {
172
+ //nothing is matching barcode
173
+ showDialog (
174
+ context: context,
175
+ builder: (ctx) => AlertDialog (
176
+ key: const Key ('notFound-dialog' ),
177
+ title: Text (AppLocalizations .of (context).productNotFound),
178
+ content: Text (
179
+ AppLocalizations .of (context).productNotFoundDescription (widget.barcode! ),
162
180
),
163
- );
164
- }
181
+ actions: [
182
+ TextButton (
183
+ key: const Key ('notFound-dialog-close-button' ),
184
+ child: Text (MaterialLocalizations .of (context).closeButtonLabel),
185
+ onPressed: () {
186
+ Navigator .of (ctx).pop ();
187
+ },
188
+ )
189
+ ],
190
+ ),
191
+ );
165
192
}
166
- } catch (e) {
167
- showErrorDialog (e, context);
168
193
}
169
- },
170
- icon: Image .asset ('assets/images/barcode_scanner_icon.png' ));
194
+ } catch (e) {
195
+ showErrorDialog (e, context);
196
+ }
197
+ },
198
+ icon: Image .asset ('assets/images/barcode_scanner_icon.png' ),
199
+ );
171
200
}
172
201
}
0 commit comments