feat(mobile): allow manual input for planting quantity

- 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 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-10 08:16:23 -08:00
parent 3dcf685715
commit 4074281088
1 changed files with 192 additions and 47 deletions

View File

@ -34,10 +34,58 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
/// ///
String? _errorMessage; String? _errorMessage;
///
final TextEditingController _quantityController = TextEditingController();
///
final FocusNode _quantityFocusNode = FocusNode();
///
bool _showExceedWarning = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadBalance(); _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<PlantingQuantityPage> {
// 1 // 1
_quantity = maxQty > 0 ? maxQty : 0; _quantity = maxQty > 0 ? maxQty : 0;
_isLoading = false; _isLoading = false;
_showExceedWarning = false;
}); });
//
_updateQuantityText(_quantity);
} catch (e) { } catch (e) {
debugPrint('加载余额失败: $e'); debugPrint('加载余额失败: $e');
setState(() { setState(() {
@ -89,14 +140,24 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
/// ///
void _decreaseQuantity() { void _decreaseQuantity() {
if (_quantity > 1) { if (_quantity > 1) {
setState(() => _quantity--); final newQty = _quantity - 1;
setState(() {
_quantity = newQty;
_showExceedWarning = false;
});
_updateQuantityText(newQty);
} }
} }
/// ///
void _increaseQuantity() { void _increaseQuantity() {
if (_quantity < _maxQuantity) { if (_quantity < _maxQuantity) {
setState(() => _quantity++); final newQty = _quantity + 1;
setState(() {
_quantity = newQty;
_showExceedWarning = false;
});
_updateQuantityText(newQty);
} }
} }
@ -403,55 +464,139 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
/// ///
Widget _buildQuantitySelector() { Widget _buildQuantitySelector() {
return Container( return Column(
width: double.infinity, children: [
padding: const EdgeInsets.all(24), Container(
decoration: BoxDecoration( width: double.infinity,
color: const Color(0x99FFFFFF), padding: const EdgeInsets.all(24),
borderRadius: BorderRadius.circular(12), decoration: BoxDecoration(
boxShadow: const [ color: const Color(0x99FFFFFF),
BoxShadow( borderRadius: BorderRadius.circular(12),
color: Color(0x1A000000), boxShadow: const [
blurRadius: 6, BoxShadow(
offset: Offset(0, 4), color: Color(0x1A000000),
blurRadius: 6,
offset: Offset(0, 4),
),
BoxShadow(
color: Color(0x1A000000),
blurRadius: 4,
offset: Offset(0, 2),
),
],
), ),
BoxShadow( child: Row(
color: Color(0x1A000000), mainAxisAlignment: MainAxisAlignment.center,
blurRadius: 4, children: [
offset: Offset(0, 2), //
_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( if (_showExceedWarning)
mainAxisAlignment: MainAxisAlignment.center, Padding(
children: [ padding: const EdgeInsets.only(top: 12),
// child: Container(
_buildQuantityButton( width: double.infinity,
icon: '-', padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
onTap: _canDecrease ? _decreaseQuantity : null, decoration: BoxDecoration(
enabled: _canDecrease, color: const Color(0xFFFFF3E0),
), borderRadius: BorderRadius.circular(8),
const SizedBox(width: 38), border: Border.all(
// color: const Color(0xFFE65100),
Text( width: 1,
'$_quantity', ),
style: const TextStyle( ),
fontSize: 36, child: Row(
fontFamily: 'Inter', children: [
fontWeight: FontWeight.w400, const Icon(
height: 1.5, Icons.warning_amber_rounded,
color: Color(0xFF5D4037), 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,
),
],
),
); );
} }