feat: add input validation to search, update agent card layout, and extend ProfileFormField with input constraints
This commit is contained in:
@@ -1598,9 +1598,13 @@ class _SpecializationCardWidgetState extends State<_SpecializationCardWidget> {
|
|||||||
: widget.card.values.take(_initialCount).toList();
|
: widget.card.values.take(_initialCount).toList();
|
||||||
final hasMore = widget.card.values.length > _initialCount;
|
final hasMore = widget.card.values.length > _initialCount;
|
||||||
|
|
||||||
|
// Consistent minimum height across all cards so the layout stays uniform
|
||||||
|
// regardless of how many values each card has (icon + title + up to 3
|
||||||
|
// rows + show more button fits comfortably).
|
||||||
return Container(
|
return Container(
|
||||||
width: 298,
|
width: 298,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
constraints: const BoxConstraints(minHeight: 240),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
@@ -1611,11 +1615,13 @@ class _SpecializationCardWidgetState extends State<_SpecializationCardWidget> {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Icon(_icon, size: 32, color: AppColors.accentOrange),
|
Icon(_icon, size: 32, color: AppColors.accentOrange),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
widget.card.name,
|
widget.card.name,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontFamily: 'Fractul',
|
fontFamily: 'Fractul',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ class _AgentSearchScreenState extends ConsumerState<AgentSearchScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
|
if (value.trim().isEmpty) return;
|
||||||
ref.read(searchAgentsProvider.notifier).search(value);
|
ref.read(searchAgentsProvider.notifier).search(value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -196,13 +197,24 @@ class _AgentSearchScreenState extends ConsumerState<AgentSearchScreen> {
|
|||||||
const SizedBox(width: 0),
|
const SizedBox(width: 0),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
ref
|
final query = _searchController.text.trim();
|
||||||
.read(searchAgentsProvider.notifier)
|
if (query.isEmpty) {
|
||||||
.search(_searchController.text);
|
// No input — don't trigger a refresh
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Please enter a keyword'),
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
duration: Duration(seconds: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ref.read(searchAgentsProvider.notifier).search(query);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 62,
|
width: 62,
|
||||||
height: 42,
|
height: 42,
|
||||||
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.accentOrange,
|
color: AppColors.accentOrange,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
@@ -211,12 +223,10 @@ class _AgentSearchScreenState extends ConsumerState<AgentSearchScreen> {
|
|||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(7),
|
borderRadius: BorderRadius.circular(7),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: const Icon(
|
||||||
child: SvgPicture.string(
|
Icons.search,
|
||||||
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" fill="white"/></svg>',
|
color: Colors.white,
|
||||||
width: 24,
|
size: 24,
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:real_estate_mobile/core/constants/app_colors.dart';
|
import 'package:real_estate_mobile/core/constants/app_colors.dart';
|
||||||
|
|
||||||
class ProfileFormField extends StatelessWidget {
|
class ProfileFormField extends StatelessWidget {
|
||||||
@@ -10,6 +11,8 @@ class ProfileFormField extends StatelessWidget {
|
|||||||
final TextCapitalization textCapitalization;
|
final TextCapitalization textCapitalization;
|
||||||
final bool obscureText;
|
final bool obscureText;
|
||||||
final Widget? suffixIcon;
|
final Widget? suffixIcon;
|
||||||
|
final List<TextInputFormatter>? inputFormatters;
|
||||||
|
final int? maxLength;
|
||||||
|
|
||||||
const ProfileFormField({
|
const ProfileFormField({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -21,6 +24,8 @@ class ProfileFormField extends StatelessWidget {
|
|||||||
this.textCapitalization = TextCapitalization.words,
|
this.textCapitalization = TextCapitalization.words,
|
||||||
this.obscureText = false,
|
this.obscureText = false,
|
||||||
this.suffixIcon,
|
this.suffixIcon,
|
||||||
|
this.inputFormatters,
|
||||||
|
this.maxLength,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -53,6 +58,8 @@ class ProfileFormField extends StatelessWidget {
|
|||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType,
|
||||||
textCapitalization: textCapitalization,
|
textCapitalization: textCapitalization,
|
||||||
obscureText: obscureText,
|
obscureText: obscureText,
|
||||||
|
inputFormatters: inputFormatters,
|
||||||
|
maxLength: maxLength,
|
||||||
textAlignVertical: TextAlignVertical.center,
|
textAlignVertical: TextAlignVertical.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontFamily: 'SourceSerif4',
|
fontFamily: 'SourceSerif4',
|
||||||
@@ -62,6 +69,7 @@ class ProfileFormField extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
isDense: true,
|
isDense: true,
|
||||||
|
counterText: '',
|
||||||
contentPadding:
|
contentPadding:
|
||||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
border: border,
|
border: border,
|
||||||
|
|||||||
Reference in New Issue
Block a user