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;
///
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<PlantingQuantityPage> {
// 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<PlantingQuantityPage> {
///
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<PlantingQuantityPage> {
///
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,
),
],
),
],
);
}