didvan-app/lib/views/widgets/search_field.dart

326 lines
12 KiB
Dart

// ignore_for_file: deprecated_member_use
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class SearchField extends StatefulWidget {
final String title;
final FocusNode focusNode;
final bool? isFiltered;
final void Function(String value) onChanged;
final VoidCallback? onFilterButtonPressed;
final VoidCallback? onGoBack;
final String? value;
final String? extraIconPath;
final VoidCallback? onExtraIconPressed;
const SearchField({
Key? key,
required this.title,
required this.onChanged,
required this.focusNode,
this.onFilterButtonPressed,
this.isFiltered,
this.onGoBack,
this.value,
this.extraIconPath,
this.onExtraIconPressed,
}) : super(key: key);
@override
State<SearchField> createState() => _SearchFieldState();
}
class _SearchFieldState extends State<SearchField>
with TickerProviderStateMixin {
late AnimationController _pulseController;
late Animation<double> _pulseAnimation;
late AnimationController _rotationController;
late Animation<double> _rotationAnimation;
@override
void initState() {
super.initState();
_pulseController = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
)..repeat(reverse: true);
_pulseAnimation = Tween<double>(begin: 1.0, end: 1.1).animate(
CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut),
);
_rotationController = AnimationController(
duration: const Duration(seconds: 4),
vsync: this,
)..repeat();
_rotationAnimation = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(parent: _rotationController, curve: Curves.linear),
);
widget.focusNode.addListener(() {
if (mounted) {
setState(() {});
}
});
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 47,
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: DesignConfig.isDark
? const Color.fromARGB(255, 188, 188, 188)
: const Color.fromARGB(255, 235, 235, 235),
borderRadius: BorderRadius.circular(40),
),
child: TextFormField(
initialValue: widget.value,
focusNode: widget.focusNode,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.black,
),
textAlignVertical: TextAlignVertical.center,
onChanged: widget.onChanged,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
decoration: InputDecoration(
suffixIcon: widget.onFilterButtonPressed != null
? SizedBox(
width: 48,
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Stack(
children: [
GestureDetector(
onTap: widget.onFilterButtonPressed!,
child: Container(
width: 27,
height: 27,
padding: const EdgeInsets.all(4),
child: SvgPicture.asset(
"lib/assets/icons/search sort.svg",
),
),
),
if (widget.isFiltered!)
Positioned(
child: Container(
width: 10,
height: 10,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.colorScheme
.secondary,
),
),
),
],
),
],
),
),
),
)
: null,
focusedBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(
Radius.circular(35),
),
borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary,
),
),
prefixIcon: GestureDetector(
onTap: widget.onGoBack,
child: Padding(
padding: const EdgeInsets.all(11.0),
child: SvgPicture.asset(
widget.onGoBack == null
? 'lib/assets/icons/search.svg'
: 'lib/assets/icons/search.svg',
width: 23,
height: 23,
),
),
),
prefixIconColor: Theme.of(context).colorScheme.inputText,
enabledBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(
Radius.circular(20),
),
borderSide: BorderSide(
color: Theme.of(context).colorScheme.border,
),
),
fillColor: Colors.red,
contentPadding: const EdgeInsets.only(
left: 12,
right: 12,
),
border: InputBorder.none,
hintText: 'جست‌وجو در ${widget.title}',
hintStyle: TextStyle(
color: DesignConfig.isDark
? const Color.fromARGB(255, 98, 98, 98)
: const Color.fromARGB(255, 122, 122, 122),
fontSize: 13,
),
),
),
),
),
if (widget.extraIconPath != null &&
widget.onExtraIconPressed != null) ...[
const SizedBox(width: 12),
AnimatedBuilder(
animation:
Listenable.merge([_pulseAnimation, _rotationAnimation]),
builder: (context, child) {
return Transform.scale(
scale: _pulseAnimation.value,
child: GestureDetector(
onTap: widget.onExtraIconPressed,
child: CustomPaint(
painter: RotatingBorderPainter(
rotation: _rotationAnimation.value,
color1: const Color(0xFFB20436),
color2: const Color(0xFFFFC8D7),
),
child: Container(
width: 50,
height: 50,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFF0066AA),
Color(0xFF00AAFF),
],
),
shape: BoxShape.circle,
// border: Border.all(
// color: Colors.white.withOpacity(0.5),
// width: 2.5,
// ),
),
child: Stack(
alignment: Alignment.center,
children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
Colors.white.withOpacity(0.3),
Colors.transparent,
],
),
),
),
Padding(
padding: const EdgeInsets.all(4.0),
child: SvgPicture.asset(
"lib/assets/icons/fluent_bot-sparkle-16-regular.svg",
width: 27,
height: 27,
colorFilter: const ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
),
],
),
),
),
),
);
},
),
],
],
),
);
}
@override
void dispose() {
_pulseController.dispose();
_rotationController.dispose();
widget.focusNode.removeListener(() {});
super.dispose();
}
}
class RotatingBorderPainter extends CustomPainter {
final double rotation;
final Color color1;
final Color color2;
RotatingBorderPainter({
required this.rotation,
required this.color1,
required this.color2,
});
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2;
// 0.0 -> 0.5: fade in
// 0.5 -> 1.0: fade out
final fadeProgress = rotation < 0.5 ? rotation * 2 : (1 - rotation) * 2;
final opacity = fadeProgress.clamp(0.0, 1.0);
final paint = Paint()
..shader = SweepGradient(
colors: [
Colors.transparent,
color1.withOpacity(0.3 * opacity),
color1.withOpacity(0.7 * opacity),
color1.withOpacity(0.9 * opacity),
color1.withOpacity(0.7 * opacity),
color1.withOpacity(0.3 * opacity),
Colors.transparent,
],
stops: const [0.0, 0.15, 0.3, 0.5, 0.7, 0.85, 1.0],
transform: GradientRotation(rotation * 6.28318),
).createShader(Rect.fromCircle(center: center, radius: radius))
..style = PaintingStyle.stroke
..strokeWidth = 2
..strokeCap = StrokeCap.round;
canvas.drawCircle(
center,
radius + 0.5,
paint,
);
}
@override
bool shouldRepaint(RotatingBorderPainter oldDelegate) {
return oldDelegate.rotation != rotation;
}
}