I am new to flutter and wanted to build some widgets to practise. I am trying to develop this search field from Zomato app which has some animation. I have completed UI but I am not able to implement the animation. GIF for textfield
I have implemented slide transition on hint text but it can slide from Offset(0, 1) to Offset(0, 0) or Offset(0, 0) to Offset(0, -1). Following is code I did for slide from (0, 1) to (0, 0). Please give me some idea how can I implement this animation ?
import 'dart:async';
import 'package:flutter/material.dart';
class ZomatoTextField extends StatefulWidget {
const ZomatoTextField({super.key});
@override
State<ZomatoTextField> createState() => _ZomatoTextFieldState();
}
class _ZomatoTextFieldState extends State<ZomatoTextField>
with SingleTickerProviderStateMixin {
late AnimationController controller;
late Animation<Offset> slideAnimation;
// late List<Text> textWidgetList;
// final List<String> hintList = ['Ice Cream', 'Samosa', 'Biryani'];
@override
void initState() {
controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
slideAnimation = Tween<Offset>(
begin: const Offset(0, 1.5),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: controller,
curve: Curves.ease,
),
);
Timer(const Duration(milliseconds: 2000), () {
controller.forward();
});
// textWidgetList = List.generate(
// hintList.length,
// (index) => Text(
// hintList[index],
// style: const TextStyle(color: Color(0xff767C8F), fontSize: 16),
// ));
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
clipBehavior: Clip.hardEdge,
height: 48,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(16)),
boxShadow: [
BoxShadow(
blurRadius: 2,
color: Colors.grey.withOpacity(0.2),
offset: const Offset(0, 0),
spreadRadius: 3,
),
],
),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
children: [
const Icon(
Icons.search,
color: Color(0xffE95161),
),
const SizedBox(
width: 8,
),
Expanded(
child: SlideTransition(
position: slideAnimation,
child: const Text(
'Search "Ice Cream"',
style: TextStyle(color: Color(0xff767C8F), fontSize: 16),
),
),
),
// Expanded(
// child: Container(
// // color: Colors.red,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [...textWidgetList],
// ),
// ),
// ),
const VerticalDivider(
endIndent: 5,
indent: 5,
),
const Icon(
Icons.mic_none_outlined,
color: Color(0xffE95161),
),
],
),
);
// TextField(
// controller: TextEditingController(),
// showCursor: false,
// decoration: const InputDecoration(
// contentPadding: EdgeInsets.all(4),
// hintText: 'Search "Ice Cream"',
// border: OutlineInputBorder(
// borderRadius: BorderRadius.all(Radius.circular(16)),
// ),
// prefixIcon: Icon(
// Icons.search,
// ),
// suffixIcon: Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// Icon(Icons.mic),
// ],
// )),
// );
}
}
You can use ListWheelScrollView
to archive the similar effect.
class ZomatoTextField extends StatefulWidget {
const ZomatoTextField({super.key});
@override
State<ZomatoTextField> createState() => _ZomatoTextFieldState();
}
class _ZomatoTextFieldState extends State<ZomatoTextField> with SingleTickerProviderStateMixin {
late ScrollController controller = ScrollController();
// late List<Text> textWidgetList;
final List<String> hintList = ['Biriyani 😁', 'Ice Cream', 'Samosa'];
late Timer timer;
@override
void initState() {
super.initState();
timer = Timer.periodic(const Duration(seconds: 2), (timer) {
controller.animateTo(
timer.tick % hintList.length * 48,
duration: const Duration(milliseconds: 200),
curve: Curves.easeIn,
);
});
}
@override
void dispose() {
timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
clipBehavior: Clip.hardEdge,
height: 48,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(16)),
boxShadow: [
BoxShadow(
blurRadius: 2,
color: Colors.grey.withOpacity(0.2),
offset: const Offset(0, 0),
spreadRadius: 3,
),
],
),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
children: [
const Icon(Icons.search, color: Color(0xffE95161)),
const SizedBox(width: 8),
Expanded(
child: ListWheelScrollView(
itemExtent: 48,
controller: controller,
physics: const NeverScrollableScrollPhysics(),
children: hintList
.map((e) => Align(
alignment: Alignment.centerLeft,
child: Text(
e,
style: TextStyle(color: Color(0xff767C8F), fontSize: 16),
),
))
.toList(),
),
),
const VerticalDivider(endIndent: 5, indent: 5),
const Icon(Icons.mic_none_outlined, color: Color(0xffE95161)),
],
),
);
}
}
AnimatedBuilder will provide more control over ux, you can play with Transfrom.Offset as well.