I have implemented a custom search bar to be toggled as the user selects it. I'm replacing the SearchBar in the appBar where i need it. Initially the search bar will show an Icon(search) with a String("Search")==> view1 and when click on either of them, it will be replaced with another view which has a searchable textfield ==> view 2. i will post what i did so far. I need to give it a nice animation. The animation should move like this. initiallly the view1 is showing then ==> animate view2 from right to left replacing view1 when need to go back to view1 again then ==> inverse the above animation.
The Workaround so far
class SearchBar extends StatefulWidget with PreferredSizeWidget {
const SearchBar({Key? key}) : super(key: key);
@override
_SearchBarState createState() => _SearchBarState();
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
}
class _SearchBarState extends State<SearchBar> {
bool _toggle = true;
@override
Widget build(BuildContext context) {
return AppBar(
elevation: 0.0,
backgroundColor: CustomColors.mWhite,
automaticallyImplyLeading: false,
title:
AnimatedContainer(
width: MediaQuery.of(context).size.width * 0.8,
decoration: _toggle
? null
: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
border: Border.all(
width: 1,
color: CustomColors.grey600,
),
),
duration: Duration(seconds: 2),
child: _toggle
? GestureDetector(
onTap: () {
setState(() {
_toggle = !_toggle;
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.search,
size: 24.0,
),
SizedBox(
width: 10.0,
),
Text(
"Search",
style: tBody1,
),
],
),
)
: Center(
child: TextField(
textInputAction: TextInputAction.search,
decoration: InputDecoration(
prefixIcon: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
setState(() {
_toggle = !_toggle;
});
},
),
border: InputBorder.none),
),
),
),
actions: [
Container(
width: 50.0,
height: 50.0,
padding: EdgeInsets.only(right: 8.0),
child: Image.asset(
'assets/images/settings.png',
),
),
],
);
}
}
AnimatedContainer
should have a width
that is depending on the _toggle
value, and in your case only the search field should be included in the AnimatedContainer
.
I simplified your code a little to show how to get the animation working, see below. The transform
parameter is responsible for the right-to-left animation, if you comment it, it will animate from left to right.
I also added an AnimatedOpacity
to fade in / out the back icon.
(You can paste the code as it is into DartPad to see how it works.)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const MaterialApp(home: Scaffold(appBar: SearchBar()));
}
}
class SearchBar extends StatefulWidget with PreferredSizeWidget {
const SearchBar({Key? key}) : super(key: key);
@override
_SearchBarState createState() => _SearchBarState();
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}
class _SearchBarState extends State<SearchBar> {
bool _toggle = true;
void _doToggle() => setState(() => _toggle = !_toggle);
@override
Widget build(BuildContext context) {
return AppBar(
title: Stack(children: [
GestureDetector(
onTap: _doToggle,
child: SizedBox(
height: kToolbarHeight * 0.8,
child: Row(
children: const [
Icon(
Icons.search,
size: 24.0,
),
SizedBox(
width: 10.0,
),
Text("Search"),
],
)),
),
AnimatedContainer(
width: _toggle ? 0 : MediaQuery.of(context).size.width,
transform: Matrix4.translationValues(_toggle ? MediaQuery.of(context).size.width : 0, 0, 0),
duration: const Duration(seconds: 1),
height: kToolbarHeight * 0.8,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
border: Border.all(
width: 1,
color: Colors.grey[600]!,
),
),
child: TextField(
textInputAction: TextInputAction.search,
decoration: InputDecoration(
prefixIcon: AnimatedOpacity(
duration: const Duration(seconds: 1),
opacity: _toggle ? 0 : 1,
child: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: _doToggle,
)),
border: InputBorder.none),
),
)
]),
);
}
}