OpenContainer animation not working in a SingleChildScrollView - flutter

This is my code:
child: SingleChildScrollView(
padding:
const EdgeInsets.only(top: 20, bottom: 20, left: 15, right: 15),
child: Column(
children: <Widget>[
OpenContainer(
transitionType: ContainerTransitionType.fadeThrough,
closedBuilder: (BuildContext _, VoidCallback opencontainer) {
return GestureDetector(...);
},
openBuilder: (BuildContext _, VoidCallback __) {
return SizedBox(
height: 500,
child: Text("Works"),
);
},
),
...
The SingleChildScrollView is also within a container for alignment in the page. GestureDetector has an InkWell within it which has a Card.
I have the animations package installed and working, what is going wrong here?

Perks of being a programmer; due to going through a long process of getting widgets to work by putting them within other widgets, I just copied and pasted them into the closedBuilder.
The simple solution was to just take the Card and place it within the return statement of the closedBuilder, without its GestureDetector and InkWell (of course, both of them have onTaps as well, would make things very wonky).

Related

Wrapping contents of Row widget if overflowed

I have a list storing the asset image location. Using that list I display those many cards of certain width side by side inside a website.
When the items in list increases, the row overflows. I want those cards to appear below, To have rest of cards in another line.
I'm returning this row to a singleChildScrollView parent widget.
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: ProjectData.map((item) => Container(
constraints: BoxConstraints(maxHeight: 320, maxWidth: 240),
child: Card(
child: Image.asset(item.fileLoc),
),
)).toList(),
),
This row sits under a Column() widget and this entire Column is returned to a SingleChildScrollView()
Use the Wrap widget:
A widget that displays its children in multiple horizontal or vertical runs.
In your case, you can use as follows:
Wrap(
spacing: 8.0, // gap between adjacent cards
runSpacing: 4.0, // gap between lines
children: ProjectData.map((item) => Container(
constraints: BoxConstraints(maxHeight: 320, maxWidth: 240),
child: Card(
child: Image.asset(item.fileLoc),
),
)).toList(),
)
You can use GridView class, since, you can keep count of the item you want to show in per row. Basically GridView.count() is the one for you.
To control the number of items in it, the power will be given by crossAxisCount
Do not forget to give height to the GridView(), else, you will see a lot of errors coming up. It needs a parent height
We are passing the item as arrayname.map((item) => Your_Widget()).toList().cast<Widget>()
I have used my myArray for dummy show purpose. Feel free to use yours. The idea is to give you the best possible option
In case you wanna explore SingleChildScrollView class it will come in handy
LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width,
child: GridView.count(
crossAxisCount: 2, // here you keep track of count
children: myArray.map((item) => Container(
margin: EdgeInsets.only(left: 15.0, right: 15.0, top: 15.0),
constraints: BoxConstraints(maxHeight: 320, maxWidth: 240),
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(15.0)
)
)).toList().cast<Widget>()
)
)
)
);
}
)
Result
Feel free to try it out.

Setting a specific height to Flutter's Card widget

I'm trying to expand the height of a given grid of cards, so they are able to fit some more information than they currently do. These cards are wrapped by a GridView.count() that is shrinked, since I'm going to put more things below this widget.
As for now, the cards look like these, in which you can see that one of them overflows the text at the bottom, which is an undesired behavior (especially when I want the cards to have some bottom padding):
Being this the case, I would like to know if it's possible to manually change the card's height. I'm maybe letting this concrete configuration stay and remove some info, since I like the fact that the cards currently maintain their 1x1 proportion, but for curiosity sake, I would like to discover how to do this.
I tried many things, such as wrapping the Card widget with a Container or a SizedBox and manually setting the height, but none of these approaches change anything.
I guess that the problem may be in the GridView itself. This is how it looks:
return FutureBuilder<List<Event>>(
future: new EventsService().getEventsForCoords(),
builder: (context, AsyncSnapshot<List<Event>> snapshot) {
if (snapshot.hasData) {
return GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
children: generateProximityEventCards(snapshot.data));
} else {
return CircularProgressIndicator();
}
});
As you can guess, the generateProximityEventCards method is the one that prints the Card widgets at the end. This is how the method looks as for now:
List<Widget> generateProximityEventCards(List<Event> eventList) {
// Render each card
return eventList.map((Event ev) {
return Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 5,
margin: EdgeInsets.all(7),
child: SizedBox(
height: 600,
child: Column(
children: <Widget>[
Image(
alignment: Alignment.topCenter,
fit: BoxFit.fitWidth,
image: ev.imageUrl,
height: 110,
width: 200,
),
Container(
child: Text(ev.name),
padding: EdgeInsets.only(left: 10, right: 10),
),
Container(
child: Text(ev.startDate.toString()),
padding: EdgeInsets.only(left: 10, right: 10),
),
Container(
child: Text(ev.address),
padding: EdgeInsets.only(left: 10, right: 10),
),
],
),
));
}).toList();
}
So, in conclussion: how can I change the height of the cards so they can hold more information?
Thanks!
GridView isn't really designed to have tiles of different size. A good option is to use the package flutter_staggered_grid_view.
Now your tile sizes can even be dynamic, check out the code for the gif above!
To automatically fit some variable length text somewhere you can use the package auto_size_text.

How to make a container 'lighten' on hold / on tap in Flutter?

I am trying to create an app with a scroll view and the objects are clickable like the google news app. Can anyone answer how to animate the container to have a white glow on holding the tile?
Here is the list view builder I have for the app
Container(
padding: EdgeInsets.only(top: 16),
child: ListView.builder(
physics: ClampingScrollPhysics(),
itemCount: article.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) {
return news_tile(
imageurl: article[index].urlToimage,
news_title: article[index].title,
news_desc: article[index].description,
web_url: article[index].url
);
}),
)
and this is the contents of the tile which the list view builder calls
class news_tile extends StatelessWidget {
String imageurl, news_title, news_desc,web_url;
news_tile({this.imageurl, this.news_title, this.news_desc,this.web_url});
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => article_view(
web_url: web_url,
)
));
},
child: Container(
margin: EdgeInsets.only(bottom: 16),
child: Column(
children: <Widget>[
ClipRRect(borderRadius: BorderRadius.circular(6), child: Image.network(imageurl)),
SizedBox(
height: 8,
),
Text(news_title, style: TextStyle(fontSize: 17,fontWeight: FontWeight.w600)),
SizedBox(
height: 8,
),
Text(news_desc, style: TextStyle(color: Colors.black54))
],
),
),
);
}
}
You could go with the InkWell Widget. It provides a tapping/holding color effect similar to that. Have a look at the official docs here:
https://api.flutter.dev/flutter/material/InkWell-class.html
Note that you need a Material Widget as an ancestor of your InkWell, but the docs explain that more.
Hope it works for you!
Edit: Sorry, since you are working with a Container, Ink is also important for you:
https://api.flutter.dev/flutter/material/Ink-class.html
Check the docs section "The ink splashes aren't visible!" for why that is.

How to fix text overflow problem and make a card larger in Flutter?

I'm building out an app in flutter and I want to display a few cards in a list-view. The problems I'm facing are the following:
I get a text-overflow when I have Text widgets in a column.I semi-fixed this by wrapping the Text widget in an Expanded widget, but it looks a bit cutoff (ex. "Question: How do you do problem 2?" (refer to picture below)).
I want to build a larger card with a greater height and more room/white space. I've tried wrapping my column widget in a container and set the height manually, but that doesn't seem to do anything.
Here is what the card looks like right now:
Relevant code shown below:
itemBuilder: (context, index) => snapshot
.data.metadata.isFromCache
? _buildListItem(context,
snapshot.data.documents[index])
Widget _buildListItem(BuildContext context, DocumentSnapshot document) {
return document.data['didProblems']
? Card(
child: Center(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('${document.data['fullName'].toString()}'),
SizedBox(
height: 5.0,
),
Text('HW Assignment: ${document.data['title'].toString()}'),
SizedBox(
height: 5.0,
),
Text('Problems attempted: ${document.data['problems']}'),
Expanded(child: Text('Question: ${document.data['question'].toString()}'), flex: 1,),
],
),
),
),
)
Any help is appreciated. Thanks in advance!
if u r using listview then set itemExtent property to ur convenience e.g.
ListView.builder(
itemCount: 10,
itemBuilder: (_, int index) => YourWidget(),
itemExtent: 150,
),
Even after wrapping your text in an Expanded you have to set overflow to TextOverflow.clip
Try this
Expanded(child: Text('Question: ${document.data['question'].toString()}', overflow: TextOverflow.clip), flex:1,),

SingleChildScrollView not keeping scroll position

I have a SingleChildScrollView widget in my app that contains a Column as a child.
The Column has many children and the last one in the very bottom of the scrolled screen is a StreamBuilder that I use to change a child Image
The issue is that when I tap on the image, the logic of the StreamBuilder works and the image is changed, but then the SingleChildScrollView scrolls a bit up so that the image is not visible and forces the user to scroll down again to be able to see the new loaded image.
Widget _buildScroll() => SingleChildScrollView(
child: Container(
width: 2080,
child: Column(
children: <Widget>[
_buildTopBar(),
_buildMainContent(),
SizedBox(height: 30),
Container(
child: Image.asset(
"assets/images/chart_legend.png",
width: 300,
),
),
SizedBox(height: 30),
Image.asset("assets/images/road_map.png", width: 600),
StreamBuilder<int>(
initialData: 1,
stream: _compareStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data == 1) {
return Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
child: Image.asset("assets/images/compare1.png"),
onTap: () => _compareSubject.add(2),
),
);
} else if (snapshot.data == 2) {
return Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
child: Image.asset("assets/images/compare2.png"),
onTap: () => _compareSubject.add(3),
),
);
} else {
return Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
child: Image.asset("assets/images/compare3.png"),
onTap: () => _compareSubject.add(1),
),
);
}
}
return Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
child: Image.asset("assets/images/compare1.png"),
onTap: () {},
),
);
}),
],
),
),
);
However, even more weird is that, once I have done tap on all images, they will be showed as expected without scrolling up...meaning that, if there is the second time i tap on a image, the second time the image is replaced the scrolling up in not happening.
The problem here is that the size of your children changes and the SingleChildScrollView cannot handle that.
I think there could be two solutions that might work here:
If you know the sizes of your images before they are loaded, you should enforce it using a SizedBox. This way, the scroll position will stay the same:
SizedBox(
width: 300,
height: 120,
child: StreamBuilder<int>(...),
)
Use ensureVisible that you trigger once the stream builder is updated, which lets you control exactly where the image should be displayed.
You would need to assign a ScrollController to your SingleChildScrollView (controller parameter). Then, you also need a GlobalKey for your StreamBuilder that you want to show (key parameter).
If you have saved instances of the two to variables, you will be able to call the following once your image is loaded:
scrollController.position.ensureVisible(
globalKey.currentContext.findRenderObject(),
alignment: 0.5, // Aligns the image in the middle.
duration: const Duration(milliseconds: 120), // So it does not jump.
);