feat(shopinbit): migrate forms to AdaptiveTextField, delete step4 field
What changed, and why it matters
This commit is a routine user-interface cleanup. It replaces several custom-built text input boxes in the ShopInBit shopping flow with a shared, reusable component called AdaptiveTextField, and removes an old, now-unused custom text field file. There is no indication this change fixes or introduces a security problem.
No security action required. Treat as a normal UI refactor; standard code-review and regression testing for the ShopInBit forms are sufficient.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff refactors six Dart files in the ShopInBit feature. It deletes the local ShopInBitStep4TextField widget and the inline _buildField helpers in shopinbit_car_fee_view.dart and shopinbit_shipping_view.dart, replacing them with the centralized AdaptiveTextField from widgets/textfields/adaptive_text_field.dart. The visible properties (controllers, focus nodes, labels/hints, autocorrect=false, enableSuggestions=false, onChanged callbacks, input formatters, etc.) are preserved. No logic changes related to validation, network handling, cryptography, or data storage are visible.
Changed components
lib/pages/shopinbit/shopinbit_car_fee_view.dartlib/pages/shopinbit/shopinbit_shipping_view.dartlib/pages/shopinbit/step_4_components/shopinbit_car_research_form.dartlib/pages/shopinbit/step_4_components/shopinbit_concierge_form.dartlib/pages/shopinbit/step_4_components/shopinbit_generic_form.dartlib/pages/shopinbit/step_4_components/shopinbit_step4_text_field.dartInspect captured patch +113 / −238
diff --git a/lib/pages/shopinbit/shopinbit_car_fee_view.dart b/lib/pages/shopinbit/shopinbit_car_fee_view.dart
index e822e87..8692e3e 100644
--- a/lib/pages/shopinbit/shopinbit_car_fee_view.dart
+++ b/lib/pages/shopinbit/shopinbit_car_fee_view.dart
@@ -24,7 +24,7 @@ import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
import '../../widgets/rounded_white_container.dart';
-import '../../widgets/stack_text_field.dart';
+import '../../widgets/textfields/adaptive_text_field.dart';
import '../more_view/services_view.dart';
import 'shopinbit_car_research_payment_view.dart';
import 'shopinbit_step_2.dart';
@@ -380,45 +380,6 @@ class _ShopInBitCarFeeViewState extends ConsumerState<ShopInBitCarFeeView> {
// placeholder in place rather than showing "--".
}
- Widget _buildField({
- required TextEditingController controller,
- required FocusNode focusNode,
- required String label,
- required bool isDesktop,
- }) {
- return ClipRRect(
- borderRadius: BorderRadius.circular(Constants.size.circularBorderRadius),
- child: TextField(
- controller: controller,
- focusNode: focusNode,
- autocorrect: false,
- enableSuggestions: false,
- onChanged: (_) => setState(() {}),
- style: isDesktop
- ? STextStyles.desktopTextExtraSmall(context).copyWith(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textFieldActiveText,
- height: 1.8,
- )
- : STextStyles.field(context),
- decoration:
- standardInputDecoration(
- label,
- focusNode,
- context,
- desktopMed: isDesktop,
- ).copyWith(
- filled: true,
- contentPadding: const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 12,
- ),
- ),
- ),
- );
- }
-
Widget _buildCountryDropdown({
required String? value,
required ValueChanged<String?> onChanged,
@@ -579,37 +540,45 @@ class _ShopInBitCarFeeViewState extends ConsumerState<ShopInBitCarFeeView> {
: STextStyles.titleBold12(context),
),
SizedBox(height: isDesktop ? 16 : 12),
- _buildField(
+ AdaptiveTextField(
controller: _nameController,
focusNode: _nameFocusNode,
- label: "Full name",
- isDesktop: isDesktop,
+ labelText: "Full name",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
spacing,
- _buildField(
+ AdaptiveTextField(
controller: _streetController,
focusNode: _streetFocusNode,
- label: "Street address",
- isDesktop: isDesktop,
+ labelText: "Street address",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
spacing,
Row(
children: [
Expanded(
- child: _buildField(
+ child: AdaptiveTextField(
controller: _cityController,
focusNode: _cityFocusNode,
- label: "City",
- isDesktop: isDesktop,
+ labelText: "City",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
),
SizedBox(width: isDesktop ? 16 : 12),
Expanded(
- child: _buildField(
+ child: AdaptiveTextField(
controller: _postalCodeController,
focusNode: _postalCodeFocusNode,
- label: "Postal code",
- isDesktop: isDesktop,
+ labelText: "Postal code",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
),
],
@@ -671,37 +640,45 @@ class _ShopInBitCarFeeViewState extends ConsumerState<ShopInBitCarFeeView> {
: STextStyles.titleBold12(context),
),
SizedBox(height: isDesktop ? 16 : 12),
- _buildField(
+ AdaptiveTextField(
controller: _billingNameController,
focusNode: _billingNameFocusNode,
- label: "Full name",
- isDesktop: isDesktop,
+ labelText: "Full name",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
spacing,
- _buildField(
+ AdaptiveTextField(
controller: _billingStreetController,
focusNode: _billingStreetFocusNode,
- label: "Street address",
- isDesktop: isDesktop,
+ labelText: "Street address",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
spacing,
Row(
children: [
Expanded(
- child: _buildField(
+ child: AdaptiveTextField(
controller: _billingCityController,
focusNode: _billingCityFocusNode,
- label: "City",
- isDesktop: isDesktop,
+ labelText: "City",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
),
SizedBox(width: isDesktop ? 16 : 12),
Expanded(
- child: _buildField(
+ child: AdaptiveTextField(
controller: _billingPostalCodeController,
focusNode: _billingPostalCodeFocusNode,
- label: "Postal code",
- isDesktop: isDesktop,
+ labelText: "Postal code",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
),
],
diff --git a/lib/pages/shopinbit/shopinbit_shipping_view.dart b/lib/pages/shopinbit/shopinbit_shipping_view.dart
index 03ae923..ccebcb3 100644
--- a/lib/pages/shopinbit/shopinbit_shipping_view.dart
+++ b/lib/pages/shopinbit/shopinbit_shipping_view.dart
@@ -18,7 +18,7 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
-import '../../widgets/stack_text_field.dart';
+import '../../widgets/textfields/adaptive_text_field.dart';
import 'shopinbit_payment_view.dart';
class ShopInBitShippingView extends ConsumerStatefulWidget {
@@ -252,45 +252,6 @@ class _ShopInBitShippingViewState extends ConsumerState<ShopInBitShippingView> {
}
}
- Widget _buildField({
- required TextEditingController controller,
- required FocusNode focusNode,
- required String label,
- required bool isDesktop,
- }) {
- return ClipRRect(
- borderRadius: BorderRadius.circular(Constants.size.circularBorderRadius),
- child: TextField(
- controller: controller,
- focusNode: focusNode,
- autocorrect: false,
- enableSuggestions: false,
- onChanged: (_) => setState(() {}),
- style: isDesktop
- ? STextStyles.desktopTextExtraSmall(context).copyWith(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textFieldActiveText,
- height: 1.8,
- )
- : STextStyles.field(context),
- decoration:
- standardInputDecoration(
- label,
- focusNode,
- context,
- desktopMed: isDesktop,
- ).copyWith(
- filled: true,
- contentPadding: const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 12,
- ),
- ),
- ),
- );
- }
-
@override
Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
@@ -313,37 +274,45 @@ class _ShopInBitShippingViewState extends ConsumerState<ShopInBitShippingView> {
: STextStyles.itemSubtitle(context),
),
SizedBox(height: isDesktop ? 32 : 24),
- _buildField(
+ AdaptiveTextField(
controller: _nameController,
focusNode: _nameFocusNode,
- label: "Full name",
- isDesktop: isDesktop,
+ labelText: "Full name",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
spacing,
- _buildField(
+ AdaptiveTextField(
controller: _streetController,
focusNode: _streetFocusNode,
- label: "Street address",
- isDesktop: isDesktop,
+ labelText: "Street address",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
spacing,
Row(
children: [
Expanded(
- child: _buildField(
+ child: AdaptiveTextField(
controller: _cityController,
focusNode: _cityFocusNode,
- label: "City",
- isDesktop: isDesktop,
+ labelText: "City",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
),
SizedBox(width: isDesktop ? 16 : 12),
Expanded(
- child: _buildField(
+ child: AdaptiveTextField(
controller: _postalCodeController,
focusNode: _postalCodeFocusNode,
- label: "Postal code",
- isDesktop: isDesktop,
+ labelText: "Postal code",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
),
],
@@ -523,37 +492,45 @@ class _ShopInBitShippingViewState extends ConsumerState<ShopInBitShippingView> {
: STextStyles.titleBold12(context),
),
spacing,
- _buildField(
+ AdaptiveTextField(
controller: _billingNameController,
focusNode: _billingNameFocusNode,
- label: "Full name",
- isDesktop: isDesktop,
+ labelText: "Full name",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
spacing,
- _buildField(
+ AdaptiveTextField(
controller: _billingStreetController,
focusNode: _billingStreetFocusNode,
- label: "Street address",
- isDesktop: isDesktop,
+ labelText: "Street address",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
spacing,
Row(
children: [
Expanded(
- child: _buildField(
+ child: AdaptiveTextField(
controller: _billingCityController,
focusNode: _billingCityFocusNode,
- label: "City",
- isDesktop: isDesktop,
+ labelText: "City",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
),
SizedBox(width: isDesktop ? 16 : 12),
Expanded(
- child: _buildField(
+ child: AdaptiveTextField(
controller: _billingPostalCodeController,
focusNode: _billingPostalCodeFocusNode,
- label: "Postal code",
- isDesktop: isDesktop,
+ labelText: "Postal code",
+ autocorrect: false,
+ enableSuggestions: false,
+ onChanged: (_) => setState(() {}),
),
),
],
diff --git a/lib/pages/shopinbit/step_4_components/shopinbit_car_research_form.dart b/lib/pages/shopinbit/step_4_components/shopinbit_car_research_form.dart
index 9a8ff9d..30df425 100644
--- a/lib/pages/shopinbit/step_4_components/shopinbit_car_research_form.dart
+++ b/lib/pages/shopinbit/step_4_components/shopinbit_car_research_form.dart
@@ -10,6 +10,7 @@ import "../../../themes/stack_colors.dart";
import "../../../utilities/text_styles.dart";
import "../../../utilities/util.dart";
import "../../../widgets/rounded_white_container.dart";
+import "../../../widgets/textfields/adaptive_text_field.dart";
import "../shopinbit_car_fee_view.dart";
import "../shopinbit_tickets_view.dart";
import "shopinbit_country_picker.dart";
@@ -18,7 +19,6 @@ import "shopinbit_privacy_checkbox.dart";
import "shopinbit_step4_dropdown.dart";
import "shopinbit_step4_header.dart";
import "shopinbit_step4_submit_button.dart";
-import "shopinbit_step4_text_field.dart";
const List<String> _carConditions = ["NEW", "PREOWNED"];
@@ -231,18 +231,22 @@ class _ShopInBitCarResearchFormState
onChanged: (iso) => setState(() => _selectedCountryIso = iso),
),
SizedBox(height: isDesktop ? 24 : 16),
- ShopInBitStep4TextField(
+ AdaptiveTextField(
controller: _brandController,
focusNode: _brandFocusNode,
- hintText: "Car brand (e.g., BMW, Mercedes, Toyota...)",
+ labelText: "Car brand (e.g., BMW, Mercedes, Toyota...)",
+ autocorrect: false,
+ enableSuggestions: false,
errorText: brandError,
onChanged: (_) => setState(() {}),
),
SizedBox(height: isDesktop ? 24 : 16),
- ShopInBitStep4TextField(
+ AdaptiveTextField(
controller: _modelController,
focusNode: _modelFocusNode,
- hintText: "Car model (e.g., 3 Series, E-Class, Camry...)",
+ labelText: "Car model (e.g., 3 Series, E-Class, Camry...)",
+ autocorrect: false,
+ enableSuggestions: false,
errorText: modelError,
onChanged: (_) => setState(() {}),
),
@@ -254,25 +258,29 @@ class _ShopInBitCarResearchFormState
onChanged: (value) => setState(() => _selectedCarCondition = value),
),
SizedBox(height: isDesktop ? 24 : 16),
- ShopInBitStep4TextField(
+ AdaptiveTextField(
controller: _carDescriptionController,
focusNode: _carDescriptionFocusNode,
- hintText:
+ labelText:
"Describe your requirements "
"(year, mileage, features...)",
minLines: 3,
maxLines: 6,
+ autocorrect: false,
+ enableSuggestions: false,
errorText: carDescriptionError,
onChanged: (_) => setState(() {}),
),
SizedBox(height: isDesktop ? 24 : 16),
- ShopInBitStep4TextField(
+ AdaptiveTextField(
controller: _carBudgetController,
focusNode: _carBudgetFocusNode,
- hintText: "Budget (\u20AC, minimum 20,000)",
+ labelText: "Budget (\u20AC, minimum 20,000)",
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
suffixText: "\u20AC",
+ autocorrect: false,
+ enableSuggestions: false,
errorText: carBudgetError,
onChanged: (_) => setState(() {}),
),
diff --git a/lib/pages/shopinbit/step_4_components/shopinbit_concierge_form.dart b/lib/pages/shopinbit/step_4_components/shopinbit_concierge_form.dart
index 480f427..1c8a07e 100644
--- a/lib/pages/shopinbit/step_4_components/shopinbit_concierge_form.dart
+++ b/lib/pages/shopinbit/step_4_components/shopinbit_concierge_form.dart
@@ -6,6 +6,7 @@ import "../../../models/shopinbit/shopinbit_order_model.dart";
import "../../../providers/db/drift_provider.dart";
import "../../../providers/global/shopin_bit_service_provider.dart";
import "../../../utilities/util.dart";
+import "../../../widgets/textfields/adaptive_text_field.dart";
import "shopinbit_country_picker.dart";
import "shopinbit_labeled_checkbox.dart";
import "shopinbit_privacy_checkbox.dart";
@@ -13,7 +14,6 @@ import "shopinbit_step4_dropdown.dart";
import "shopinbit_step4_header.dart";
import "shopinbit_step4_submit.dart";
import "shopinbit_step4_submit_button.dart";
-import "shopinbit_step4_text_field.dart";
const List<String> _conciergeConditions = ["NEW", "USED"];
@@ -143,14 +143,16 @@ class _ShopInBitConciergeFormState
"for you.",
),
SizedBox(height: isDesktop ? 16 : 12),
- ShopInBitStep4TextField(
+ AdaptiveTextField(
controller: _whatToPurchaseController,
focusNode: _whatToPurchaseFocusNode,
- hintText:
+ labelText:
"Describe what you'd like to purchase "
"(e.g., electronics, luxury goods, services...)",
minLines: 3,
maxLines: 6,
+ autocorrect: false,
+ enableSuggestions: false,
errorText: whatToPurchaseError,
onChanged: (_) => setState(() {}),
),
@@ -162,14 +164,16 @@ class _ShopInBitConciergeFormState
onChanged: (value) => setState(() => _selectedCondition = value),
),
SizedBox(height: isDesktop ? 24 : 16),
- ShopInBitStep4TextField(
+ AdaptiveTextField(
controller: _budgetController,
focusNode: _budgetFocusNode,
- hintText: "Budget (\u20AC)",
+ labelText: "Budget (\u20AC)",
enabled: !_noLimit,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
suffixText: "\u20AC",
+ autocorrect: false,
+ enableSuggestions: false,
errorText: budgetError,
onChanged: (_) => setState(() {}),
),
diff --git a/lib/pages/shopinbit/step_4_components/shopinbit_generic_form.dart b/lib/pages/shopinbit/step_4_components/shopinbit_generic_form.dart
index 9fcb5b9..82b862e 100644
--- a/lib/pages/shopinbit/step_4_components/shopinbit_generic_form.dart
+++ b/lib/pages/shopinbit/step_4_components/shopinbit_generic_form.dart
@@ -5,12 +5,12 @@ import "../../../models/shopinbit/shopinbit_order_model.dart";
import "../../../providers/global/shopin_bit_service_provider.dart";
import "../../../providers/providers.dart";
import "../../../utilities/util.dart";
+import "../../../widgets/textfields/adaptive_text_field.dart";
import "shopinbit_country_picker.dart";
import "shopinbit_privacy_checkbox.dart";
import "shopinbit_step4_header.dart";
import "shopinbit_step4_submit.dart";
import "shopinbit_step4_submit_button.dart";
-import "shopinbit_step4_text_field.dart";
/// Fallback Step 4 form used when no category was selected. Collects a free
/// text description and a delivery country.
@@ -90,13 +90,15 @@ class _ShopInBitGenericFormState extends ConsumerState<ShopInBitGenericForm> {
subtitle: "Provide details about your trip.",
),
SizedBox(height: isDesktop ? 32 : 24),
- ShopInBitStep4TextField(
+ AdaptiveTextField(
controller: _descriptionController,
focusNode: _descriptionFocusNode,
- hintText:
+ labelText:
"Describe your travel request (destinations, dates, passengers)",
minLines: 3,
maxLines: 6,
+ autocorrect: false,
+ enableSuggestions: false,
onChanged: (_) => setState(() {}),
),
SizedBox(height: isDesktop ? 24 : 16),
diff --git a/lib/pages/shopinbit/step_4_components/shopinbit_step4_text_field.dart b/lib/pages/shopinbit/step_4_components/shopinbit_step4_text_field.dart
deleted file mode 100644
index e43cf43..0000000
--- a/lib/pages/shopinbit/step_4_components/shopinbit_step4_text_field.dart
+++ /dev/null
@@ -1,93 +0,0 @@
-import "package:flutter/material.dart";
-import "package:flutter/services.dart";
-
-import "../../../themes/stack_colors.dart";
-import "../../../utilities/constants.dart";
-import "../../../utilities/text_styles.dart";
-import "../../../utilities/util.dart";
-import "../../../widgets/stack_text_field.dart";
-
-class ShopInBitStep4TextField extends StatelessWidget {
- const ShopInBitStep4TextField({
- super.key,
- required this.controller,
- required this.focusNode,
- required this.hintText,
- this.errorText,
- this.minLines,
- this.maxLines = 1,
- this.keyboardType,
- this.inputFormatters,
- this.enabled = true,
- this.suffixText,
- this.suffixIcon,
- this.labelText,
- this.readOnly = false,
- this.onTap,
- this.onChanged,
- });
-
- final TextEditingController controller;
- final FocusNode focusNode;
- final String hintText;
- final String? errorText;
- final int? minLines;
- final int? maxLines;
- final TextInputType? keyboardType;
- final List<TextInputFormatter>? inputFormatters;
- final bool enabled;
- final String? suffixText;
- final Widget? suffixIcon;
- final String? labelText;
- final bool readOnly;
- final VoidCallback? onTap;
- final ValueChanged<String>? onChanged;
-
- @override
- Widget build(BuildContext context) {
- final TextStyle style = Util.isDesktop
- ? STextStyles.desktopTextExtraSmall(context).copyWith(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textFieldActiveText,
- height: 1.8,
- )
- : STextStyles.field(context);
-
- return ClipRRect(
- borderRadius: BorderRadius.circular(Constants.size.circularBorderRadius),
- child: TextField(
- controller: controller,
- focusNode: focusNode,
- autocorrect: false,
- enableSuggestions: false,
- enabled: enabled,
- readOnly: readOnly,
- onTap: onTap,
- minLines: minLines,
- maxLines: maxLines,
- keyboardType: keyboardType,
- inputFormatters: inputFormatters,
- onChanged: onChanged,
- style: style,
- decoration:
- standardInputDecoration(
- hintText,
- focusNode,
- context,
- desktopMed: Util.isDesktop,
- ).copyWith(
- filled: true,
- contentPadding: const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 12,
- ),
- errorText: errorText,
- suffixText: suffixText,
- suffixIcon: suffixIcon,
- labelText: labelText,
- ),
- ),
- );
- }
-}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.