From 4074281088baeda1c0712282a8d9ca5d36790d2f Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 10 Dec 2025 08:16:23 -0800 Subject: [PATCH] feat(mobile): allow manual input for planting quantity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace static text with editable TextField for quantity - Add warning when input exceeds max quantity based on balance - Sync +/- buttons with input field value 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../pages/planting_quantity_page.dart | 239 ++++++++++++++---- 1 file changed, 192 insertions(+), 47 deletions(-) diff --git a/frontend/mobile-app/lib/features/planting/presentation/pages/planting_quantity_page.dart b/frontend/mobile-app/lib/features/planting/presentation/pages/planting_quantity_page.dart index 8b36998a..5ade4978 100644 --- a/frontend/mobile-app/lib/features/planting/presentation/pages/planting_quantity_page.dart +++ b/frontend/mobile-app/lib/features/planting/presentation/pages/planting_quantity_page.dart @@ -34,10 +34,58 @@ class _PlantingQuantityPageState extends ConsumerState { /// 加载错误信息 String? _errorMessage; + /// 数量输入框控制器 + final TextEditingController _quantityController = TextEditingController(); + + /// 数量输入框焦点 + final FocusNode _quantityFocusNode = FocusNode(); + + /// 是否显示超出警告 + bool _showExceedWarning = false; + @override void initState() { super.initState(); _loadBalance(); + _quantityController.addListener(_onQuantityChanged); + } + + @override + void dispose() { + _quantityController.removeListener(_onQuantityChanged); + _quantityController.dispose(); + _quantityFocusNode.dispose(); + super.dispose(); + } + + /// 输入框数量变化监听 + void _onQuantityChanged() { + final text = _quantityController.text; + if (text.isEmpty) { + setState(() { + _quantity = 0; + _showExceedWarning = false; + }); + return; + } + + final inputQty = int.tryParse(text) ?? 0; + setState(() { + _quantity = inputQty; + _showExceedWarning = inputQty > _maxQuantity; + }); + } + + /// 更新输入框显示的数量 + void _updateQuantityText(int qty) { + final newText = qty > 0 ? qty.toString() : ''; + if (_quantityController.text != newText) { + _quantityController.text = newText; + // 将光标移到末尾 + _quantityController.selection = TextSelection.fromPosition( + TextPosition(offset: newText.length), + ); + } } /// 加载用户余额 @@ -69,7 +117,10 @@ class _PlantingQuantityPageState extends ConsumerState { // 自动填入最大可认种数量,至少为1(如果有足够余额) _quantity = maxQty > 0 ? maxQty : 0; _isLoading = false; + _showExceedWarning = false; }); + // 更新输入框 + _updateQuantityText(_quantity); } catch (e) { debugPrint('加载余额失败: $e'); setState(() { @@ -89,14 +140,24 @@ class _PlantingQuantityPageState extends ConsumerState { /// 减少数量 void _decreaseQuantity() { if (_quantity > 1) { - setState(() => _quantity--); + final newQty = _quantity - 1; + setState(() { + _quantity = newQty; + _showExceedWarning = false; + }); + _updateQuantityText(newQty); } } /// 增加数量 void _increaseQuantity() { if (_quantity < _maxQuantity) { - setState(() => _quantity++); + final newQty = _quantity + 1; + setState(() { + _quantity = newQty; + _showExceedWarning = false; + }); + _updateQuantityText(newQty); } } @@ -403,55 +464,139 @@ class _PlantingQuantityPageState extends ConsumerState { /// 构建数量选择器 Widget _buildQuantitySelector() { - return Container( - width: double.infinity, - padding: const EdgeInsets.all(24), - decoration: BoxDecoration( - color: const Color(0x99FFFFFF), - borderRadius: BorderRadius.circular(12), - boxShadow: const [ - BoxShadow( - color: Color(0x1A000000), - blurRadius: 6, - offset: Offset(0, 4), + return Column( + children: [ + Container( + width: double.infinity, + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: const Color(0x99FFFFFF), + borderRadius: BorderRadius.circular(12), + boxShadow: const [ + BoxShadow( + color: Color(0x1A000000), + blurRadius: 6, + offset: Offset(0, 4), + ), + BoxShadow( + color: Color(0x1A000000), + blurRadius: 4, + offset: Offset(0, 2), + ), + ], ), - BoxShadow( - color: Color(0x1A000000), - blurRadius: 4, - offset: Offset(0, 2), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // 减少按钮 + _buildQuantityButton( + icon: '-', + onTap: _canDecrease ? _decreaseQuantity : null, + enabled: _canDecrease, + ), + const SizedBox(width: 24), + // 数量输入框 + SizedBox( + width: 80, + child: TextField( + controller: _quantityController, + focusNode: _quantityFocusNode, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 36, + fontFamily: 'Inter', + fontWeight: FontWeight.w400, + height: 1.5, + color: _showExceedWarning + ? const Color(0xFFE65100) + : const Color(0xFF5D4037), + ), + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: _showExceedWarning + ? const Color(0xFFE65100) + : const Color(0xFFD4AF37), + width: 1, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: _showExceedWarning + ? const Color(0xFFE65100) + : const Color(0xFFD4AF37), + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: _showExceedWarning + ? const Color(0xFFE65100) + : const Color(0xFFD4AF37), + width: 2, + ), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + isDense: true, + ), + ), + ), + const SizedBox(width: 24), + // 增加按钮 + _buildQuantityButton( + icon: '+', + onTap: _canIncrease ? _increaseQuantity : null, + enabled: _canIncrease, + ), + ], ), - ], - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // 减少按钮 - _buildQuantityButton( - icon: '-', - onTap: _canDecrease ? _decreaseQuantity : null, - enabled: _canDecrease, - ), - const SizedBox(width: 38), - // 数量显示 - Text( - '$_quantity', - style: const TextStyle( - fontSize: 36, - fontFamily: 'Inter', - fontWeight: FontWeight.w400, - height: 1.5, - color: Color(0xFF5D4037), + ), + // 超出警告提示 + if (_showExceedWarning) + Padding( + padding: const EdgeInsets.only(top: 12), + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: const Color(0xFFFFF3E0), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: const Color(0xFFE65100), + width: 1, + ), + ), + child: Row( + children: [ + const Icon( + Icons.warning_amber_rounded, + color: Color(0xFFE65100), + size: 20, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + '输入数量 $_quantity 超过最大可认种数量 $_maxQuantity,请调整', + style: const TextStyle( + fontSize: 14, + fontFamily: 'Inter', + height: 1.5, + color: Color(0xFFE65100), + ), + ), + ), + ], + ), ), ), - const SizedBox(width: 38), - // 增加按钮 - _buildQuantityButton( - icon: '+', - onTap: _canIncrease ? _increaseQuantity : null, - enabled: _canIncrease, - ), - ], - ), + ], ); }