Flutter animation on grid expand - flutter

I want animation on grid expand. At first gird shows 4 items only and later on click button grid shows all item i.e more than 4 (with an animation). In a row there are 4 items.
bool click = false;
return Container(
width: double.infinity,
child: GridView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4),
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
if (index % 3 == 0) {
Navigator.of(context).push(SlideLeftRoute(
widget: Search(
"Restaurant",
isSearchFromTextField: false,
list: list,
)));
} else {
Navigator.of(context)
.push(SlideLeftRoute(widget: RestaurantDetail()));
}
},
child: Container(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: HexColor(widget._homeScopedModel.homePageModel
.category[index].color),
),
child: SvgPicture.asset(
'assets/images/restaurant.svg',
width: 30,
height: 30,
color: CustomColor.white,
),
),
SizedBox(
height: 10,
),
Text(
widget._homeScopedModel.homePageModel.category[index].name,
style: TextStyle(
color: CustomColor.black,
fontSize: 12.0,
fontFamily: 'SFProDisplay',
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.center,
),
],
),
),
);
},
itemCount:
click ? widget._homeScopedModel.homePageModel.category.length : 4,
),
);
//on click image
new GestureDetector(
child: RotatedBox(
quarterTurns: click ? 2 : 0,
child: SvgPicture.asset(
'assets/images/icon_down.svg',
width: 15,
height: 15,
color: CustomColor.grey,
),
),
onTap: () {
click = !click;
setState(() {});
},
),
On Click image the grid view will expand. I want slide down and slide up animation on click image. I tried Animated Container but it doesn't work for me. I found animation only on expanding other view like below. how to animate collapse elements in flutter

I understood your question. I think you should try to use expansionTile, expandable and there are much more things like that. Whether you want you can create your own animating expandable widget with animations in stateful widget

Related

Flutter: Make all screen scrollable with GridView.builder inside

In my home screen my app shows carousel first then a vertical list of challenges cards retrieved from Cloud Firestore using GridView.builder as follows:
GridView.builder(
scrollDirection: Axis.vertical,
itemCount: _challenges.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 4),
),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
if (_challenges[index]["isLocked"] == "true") {
showLockedDialog();
} else {
checkParticipation(index);
if (checkPart == true) {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
ChallengeDetails(_challenges[index])));
}
checkPart = true;
}
},
child: Stack(
children: [
Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Image(
image: NetworkImage(_challenges[index]["image-path"]),
fit: BoxFit.cover,
height: 150,
width: 350,
opacity: _challenges[index]["isLocked"] == "true"
? AlwaysStoppedAnimation(.4)
: null,
),
),
),
Center(
child: Text(
"${_challenges[index]["name"]}\n",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
Center(
child: Text(
"\n${_challenges[index]["date"]}",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
textDirection: TextDirection.ltr,
)),
Center(
child: SizedBox(
height: 130,
width: 130,
child: _challenges[index]["isLocked"] == "true"
? Image.asset("assets/lock-icon.jpg")
: null,
),
)
],
),
);
});
Everything retrieving fine and it is rendered in my home_screen as follows:
body: Column(
children: [
AdsBanner(),
SizedBox(
height: 30,
),
Padding(
padding: const EdgeInsets.only(right: 8, left: 8, bottom: 5),
child: Row(
children: [
Text(
AppLocalizations.of(context)!.challenges + " ",
style: TextStyle(fontSize: 20),
),
Text(
AppLocalizations.of(context)!.clickToParticipate,
style: TextStyle(fontSize: 15),
)
],
),
),
Expanded(child: ChallengeCard()),
],
),
The problem is that only the GridView area is scrolling and what am seeking for is to scroll the whole screen with the GridView area, I was trying to use the CustomScrollView() but its not working properly.
I'll be thankful for any help.
First in your GridView.builder add these:
GridView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
...
)
then in your home_screen wrap your column with SingleChildScrollView:
SingleChildScrollView(
child: Column(
children: [
AdsBanner(),
SizedBox(
height: 30,
),
Padding(
...
),
),
You can provide physics: NeverScrollableScrollPhysics() on GridView to disable scroll effect. If you want scrollable as secondary widget use primary: false, to have Full Page scrollable, you can use body:SingleChildScrollView(..) or better using body:CustomScrollView(..)

How do I get a black banner with text to dynamically show depending upon what a user does on another screen in Flutter?

I am tasked with the challenge of getting a black banner to show when a user returns from a screen on that same tab upon ordering an item on another screen. The current code below only shows the black banner appropriately if the user leaves by selecting another tab then returns to the screen. The part that I need to be dynamically presented starts with "if (OrderProvider.listOrderSummary.isNotEmpty)". I believe that this involves making a stateless widget stateful, but that results in errors that suggest that may not be the right approach.
class GridItems extends StatelessWidget {
GridItems({#required this.infinityPageController});
final InfinityPageController infinityPageController;
#override
Widget build(BuildContext context) {
final List<Item> itemList = Provider.of<List<Item>>(context);
if (itemList == null) {
return Center(
child: CupertinoActivityIndicator(
radius: 20,
),
);
}
final List<Item> mapItemList = itemList.toList();
return Column(
children: <Widget>[
TopMenuNavigation(
infinityPageController: infinityPageController,
title: 'ALL ITEMS',
),
Divider(
color: Colors.grey,
),
Expanded(
child: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: itemList.length,
itemBuilder: (BuildContext contex, int index) {
return Center(
child: InkWell(
onTap: () {
Navigator.of(context)
.pushNamed('/order', arguments: mapItemList[index]);
},
child: Container(
width: 310,
margin: EdgeInsets.all(7),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 1, color: Colors.grey),
borderRadius: BorderRadius.all(
Radius.circular(7.0),
),
),
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10, top: 10),
width: double.infinity,
child: Text('${mapItemList[index].itemName}'),
),
Expanded(
child: Image.network(
'${mapItemList[index].imageUrl}',
),
),
],
),
),
),
);
},
),
),
if (OrderProvider.listOrderSummary.isNotEmpty)
GestureDetector(
onTap: () {
Navigator.of(context).pushNamed('/review_order');
},
child: Container(
height: 55,
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Spacer(),
Text(
'REVIEW ORDER (${OrderProvider.listOrderSummary.length} items)',
style: TextStyle(
fontSize: 17,
color: Colors.white,
fontFamily: 'OpenSans',
fontWeight: FontWeight.bold),
),
SizedBox(width: 8),
Icon(
Icons.navigate_next,
color: Colors.white,
size: 35,
),
SizedBox(
width: MediaQuery.of(context).size.height * 0.065,
),
],
),
),
)
else
SizedBox(),
],
);
}
}
You have to somehow rebuild your screen (or part of it) where you want to return to. This is where a state-management solution should come into place :).
Create a BLoC or a ValueNotifier and rebuild the widget with a StreamBuilder or a ValueListenableBuilder. If you want to make it simple just create a shared state inside a stateful widget between your tab screens and call setState if the black banner should appear

How to manipulate only one element of ListView.builder

I am using Animated Container to have a closing animation by manipulating the _height character of the animated container. I am using ListView.builder to create cards , with each having text that is a value in a list (the user can manipulate the list by adding and deleting items). The integrated GestureDetector looks for a double tap and removes an item of the list and manipulates the height. However , the problem is that , all the cards have their height manipulated and disapper.
How do we have the animated height manipulation occur only for the card the user double taps?
The listview.builder code is as follows :
ListView.builder(
padding: EdgeInsets.fromLTRB(0, 0, 0, 100),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: items.length,
itemBuilder: (context, index) {
return AnimatedContainer(
duration: Duration(seconds: 2),
alignment: Alignment.center,
decoration: new BoxDecoration(
color: Color(0xFF324467),
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(100.0)),
),
margin: EdgeInsets.fromLTRB(22, 15, 50, 9),
height: _height ? 80 : 0,
child: Stack(
children: <Widget>[
GestureDetector(
onDoubleTap: () {
setState(() {
_height = !_height;
});
setState(() {
items.removeAt(index);
});
},
child: Card(
color: Color(0xFF324467),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
child: Row(
children: [
SizedBox(
height: 30,
width: 63,
child: RaisedButton(
onPressed: () {
setState(() {
_height = !_height;
});
setState(() {
items.removeAt(index);
});
},
elevation: 0,
color: Colors.transparent,
shape: CircleBorder(
side: BorderSide(
width: 2.3,
color: Colors.blue,
),
),
),
),
Padding(padding: EdgeInsets.fromLTRB(0, 0, 7, 0)),
Flexible(
child: Container(
child: Text(
items[index],
overflow: TextOverflow.fade,
style: TextStyle(
fontSize: 21,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
),
),
],
),
),
),
],
),
);
},
),
The problem is with height: _height ? 80 : 0, this line. _height is a variable which is being checked against all the list item. if you set _height = false all your item list will be gone.
Use AnimatedList for remove/add animation in the list. The page has reference code lab, which uses an AnimatedList to create an effect when items are removed or added to the list.
If you want solution in your current approach, then store the _height value against every list item. You can use Map<objectId,bool> or add a bool variable in your current model. then check the item status. Hope you will get the idea ;-)

How to make ListView with the possibility of clicking on the entire width and height of text?

I would like have a square that will be clickable with text in center but with fixed distances between words.
Right now only Text is clickable, I am not able to click below or above text. Please check the image below.
This would not be a problem if not for the dynamic width of the text.
How to solve this?
Currently I have something like this:
ListView -> GestureDetector -> Container (height: 60, *without width)
-> Center -> Text
ListView. builder(
physics: ClampingScrollPhysics(),
controller:
_scrollControllerCategoryList,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
},
child: Container(
child: Center(
height: 60,
child: Text(
appConsumer.menu.keys
.toList()[index]
.baseName,
style: TextStyle(
fontSize: 14,
color:
selectedCategory ==
index
? AppTheme()
.green
: Colors
.grey[500]),
),
),
),
);
},
itemCount: appConsumer.menu.keys.length,
)
try wrapping the Text inside of a Expand Widget
What you can use is padding and margin to space out the words. But to actually give the words a box, you will be using the decoration property.
Container(
padding: EdgeInsets.symmetric(horizontal: 10),
margin: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
color: Colors.white.withOpacity(0.4)),
child: Center(
child: Text(
appConsumer.menu.keys
.toList()[index]
.baseName,
style: TextStyle(
fontSize: 14,
color:
selectedCategory ==
index
? AppTheme()
.green
: Colors
.grey[500]),
),
),
),

List menu scrolling without IgnorePointer

I created a menu list with GridView. But when I scroll, it fail because scroll inside listview hold parent element to scroll like this image
My issue is gone when I try to wrap my GridView inside IgnorePointer. But if I use IgnorePointer it is not allow me to click / tap my menu item.
How the best way to fix my issue?
My code (without IgnorePointer)
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 10.0, bottom: 10.0),
width: MediaQuery.of(context).size.width,
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 4,
children: List.generate(
menuItems.length,
(index) {
return Column(
children: <Widget>[
InkWell(
onTap: () {
showShortToast("Menu clicked " + index.toString());
},
child: Container(
width: 70.0,
height: 70.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
border: Border.all(color: Colors.grey[300]),
),
child: Center(
child: Image.asset(menuItems[index]["image"]),
),
),
),
Expanded(
child: Text(
menuItems[index]["text"],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 13.0,
),
),
),
],
);
},
),
),
);
}
If you want to disable the scroll in the grid view you can do as follows:
child: GridView.count(
physics: NeverScrollableScrollPhysics(), //Add physics
shrinkWrap: true,
crossAxisCount: 4,
...