I'm using this dart package here
There's an example in the flutterio cookbook. My problem is, when the image loads the CircularProgressIndicator doesn't disappear. Here is the item for a ListView.
import 'package:flutter/material.dart';
import 'package:transparent_image/transparent_image.dart';
import 'models/post.dart';
class PostItem extends StatelessWidget {
const PostItem(this.post);
final Post post;
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Stack(
children: <Widget>[
const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
)),
ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Center(
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: post.imageUrl,
),
),
),
],
),
const SizedBox(height: 8.0),
Text(
post.username,
style: Theme.of(context).textTheme.headline,
),
const SizedBox(height: 8.0),
Text(
post.text,
style: Theme.of(context).textTheme.body1,
),
]);
}
}
A gif of the problem:
My understanding is, that once the image loads the CircularProgressIndicator should disappear?
You may try with cached_network_image plugin
new CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
),
That happens because you're stacking a CircularProgressIndicator widget with a ClipRect.
When its first build, you don't have an url and that's the reason why the image isn't showing up. However, as soon as it has content and you rebuild your parent tree (where your PostItem widget is inserted) it will refresh with the image but the progress bar won't be gone because doesn't have any condition to prevent its render.
To solve it, the easy way, just add a condition, if you have an url, then you show a CircularProgressBar otherwise, a ClipRect with your content.
(...)
Stack(
children: <Widget>[
post?.url != null
? const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
))
: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Center(
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: post.imageUrl,
),
),
),
],
),
(...)
If you don't plan to stack items, you can actually remove the Stack widget and place it directly within your column. Below is the complete example (without the Stack).
import 'package:flutter/material.dart';
import 'package:transparent_image/transparent_image.dart';
import 'models/post.dart';
class PostItem extends StatelessWidget {
const PostItem(this.post);
final Post post;
#override
Widget build(BuildContext context) {
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
post?.url != null
? const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
))
: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Center(
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: post.imageUrl,
),
),
),
const SizedBox(height: 8.0),
Text(
post.username,
style: Theme.of(context).textTheme.headline,
),
const SizedBox(height: 8.0),
Text(
post.text,
style: Theme.of(context).textTheme.body1,
),
]);
}
}
Related
I would like to create list item widget with photo, and some information on the right side of this photo. Since web Image may be way to large, I also want my photo widget to match the height (while maintaining aspect ration) with "content" widgets height, possibly without giving an explicit height.
For now my widget looks like this:
In Android I would restrict image size with:
constraintTop_toTopOf(contentId)
constraintBottom_toBottomOf(contentId)
Is it possible to do similar thing in Flutter ?
For now my code looks like this (I would like to avoid giving explicit height in 'maxHeight' parameter of ConstrainedBox):
class SpeakerItemWidget extends StatelessWidget{
final Speaker _speaker;
SpeakerItemWidget(this._speaker);
Widget _speakerItemContent() =>
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'${_speaker.name} ${_speaker.surname}',
style: TitleTextStyle(),
),
SizedBox(height: Margin.small),
Text(
_speaker.title,
style: DescriptionTextStyle(),
),
Text(
_speaker.company,
style: DescriptionTextStyle(),
)
],
);
#override
Widget build(BuildContext context) =>
InkWell(
splashColor: Theme.of(context).accentColor,
onTap: () => debugPrint('open event details'),
child: Padding(
padding: EdgeInsets.all(Margin.small),
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 60,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
FittedBox(
fit: BoxFit.contain,
child: SpeakerPhotoWidget(_speaker),
),
_speakerItemContent(),
],
),
),
),
);
}
class SpeakerPhotoWidget extends StatelessWidget{
final Speaker _speaker;
SpeakerPhotoWidget(this._speaker);
#override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: BoxConstraints(
//initial size of widget is 0,0 - causes crash when inside FittedBox
minHeight: 1,
minWidth: 1,
),
child: Padding(
padding: EdgeInsets.only(right: Margin.small),
child: FadeInImage.assetNetwork(
placeholder: 'assets/images/speaker_placeholder.png',
image: 'some uri',
fit: BoxFit.contain,
),
)
);
}
}
I'm trying to build a layout where there are two Text objects at the top and bottom which stays stationery and a ListView at their center.
Here's the code for the Screen
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 40.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 40.0),
child: Text(
DateFormat("hh:mm 'PM ,' MMMM d").format(DateTime.now()),
style: Theme.of(context).textTheme.title,
),
),
Expanded(
child: ListView.builder(
itemCount: 4,
itemBuilder: (BuildContext context, int index) =>
CustomAppText(
text: 'Custom App',
),
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 40.0),
child: Text(
"Settings",
style: Theme.of(context).textTheme.title,
),
),
],
),
),
),
);
}
}
The output of the given code
The Design I'm looking for
I have tried using the Center widget but it does not center the ListView
The ListView fills the entire Expanded Widget, that's why using the Center widget didn't work, so shrinkWrap: true should be added so the ListView takes only the height of it's children.
After skimming through the documentation I found about Flexible Widget
Flexible, which does not force the child to fill the available space.
Made the change and works like a charm
Flexible(
child: ListView.builder(
shrinkWrap: true,
itemCount: 4,
itemBuilder: (BuildContext context, int index) =>
CustomAppText(
text: 'Custom App',
),
),
),
For those still looking for an answer, this is what worked for me:
Column(
children: [
Container(), // some top content
Expanded(
child: Center(
child: ListView(
shrinkWrap: true,
children: [] //your list view content here
)
)
),
Container(), // some bottom content
]
)
The Expanded widget makes the content take up all available space.
The Center widget centers the content you want to display.
The ListView holds your list content and the "shrinkWrap: true" property makes your list view shrink according to content size(allowing it to centralized by the Center widget when it's not taking a lot of space).
Hope it helps. Give the top and bottom widgets the 25% of the screen size. Give the listview the 50% of the screen size.
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
#override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
#override
Widget build(BuildContext context) {
final _size = MediaQuery.of(context).size;
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(28.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Top Widgets
Container(
width: double.infinity,
// color: Colors.green,
height: _size.height * 0.25, // Take 25% width of the screen height
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('11: 25 AM', style: TextStyle(fontSize: 23.0),),
Text('Set As Launcher', style: TextStyle(fontSize: 23.0),)
],
),
),
Expanded(
child: Container(
// color: Colors.yellow,
child: ListView(
children: List.generate(25, (index){
return Text('Custom App $index', style: TextStyle(fontSize: 45.0),);
}),
),
),
),
// Bottom Widgets
Container(
width: double.infinity,
// color: Colors.blue,
height: _size.height * 0.25, // Take 25% width of the screen height
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Settings', style: TextStyle(fontSize: 23.0),),
],
),
)
],
),
),
),
);
}
}
Just add an Expanded as a wrapper for your first Container inside the Column
Expanded(
child: Container(
margin: EdgeInsets.symmetric(vertical: 40.0),
child: Text(
DateFormat("hh:mm 'PM ,' MMMM d").format(DateTime.now()),
style: Theme.of(context).textTheme.title,
),
),
),
Just wrap your ListView.builder inside a container class and give it a height, either explicitly in double or as a percentage of screen height.
class Trial extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 40.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 40.0),
child: Text(
"Some Text",
style: Theme.of(context).textTheme.title,
),
),
Container(
// height: 40,
height: MediaQuery.of(context).size.height * 0.4,
child: ListView.builder(
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
return Text("Hello");
}
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 40.0),
child: Text(
"Settings",
style: Theme.of(context).textTheme.title,
),
),
],
),
),
),
);
}
}
Add two spacer it will give you 100% same result as you expected
Spacer(),
Expanded(
child: ListView.builder(
itemCount: 4,
itemBuilder: (BuildContext context, int index) =>
CustomAppText(
text: 'Custom App',
),
),
),
Spacer(),
It have to use shrinkWrap: true and Flexible together to show
all items of ListView.
Flexible(
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
itemCount: models.length,
If you replace your Expanded widget with a Container and give that a fixed height, I think you get what you are looking for.
When I'm, not using the ListView.builder constructor in Flutter, the individual item is shown as expected from the JSON API:
when I use ListView.builder, nothing shows up. no Error!
I also tried a listview with Texts only that doesn't seem to work either.
Here's the code:
#override
Widget build(BuildContext context) {
return Container(
child: new Scaffold(
appBar: AppBar(
title: Text("the title"),//TODO edit this
backgroundColor: Colors.blueAccent),
body:
Column(
children: <Widget>[
FutureBuilder<List<dynamic>>(
future: getPosts2(),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? ListViewPosts(postsFrom: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
],
),
),
);
}
}
And here is the ListViewPosts Stateless Widget:
class ListViewPosts extends
StatelessWidget {
final List<dynamic> postsFrom;
ListViewPosts({Key key,
this.postsFrom}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
FadeInImage.assetNetwork(
placeholder: 'assets/images/placeholder.png',
image: postsFrom[1]["featured_media"] == 0
? 'assets/images/placeholder.png'
: postsFrom[1]["_embedded"]["wp:featuredmedia"]
[0]["source_url"],
),
FadeInImage.assetNetwork(
placeholder: 'assets/images/placeholder.png',
image: postsFrom[2]["featured_media"] == 0
? 'assets/images/placeholder.png'
: postsFrom[2]["_embedded"]["wp:featuredmedia"]
[0]["source_url"],
),
new Row(
children: <Widget>[
Expanded(
child: Text(
"نووسهر: " +
postsFrom[1]["_embedded"]["author"][0]
["name"],
textAlign: TextAlign.right,
),
),
Expanded(
child: Text(
dateConvertor(
postsFrom[2]["date"].toString()),
textAlign: TextAlign.left,
),
),
],
),
ListView.builder(
itemCount: postsFrom.length, //== null ? 0 : postsFrom.length,
itemBuilder: (context, int index) {
Card(
child: Column(
children: <Widget>[
Text(postsFrom.toString()),
Container(
child: hawalImage(postsFrom, index),
),
new Padding(
padding: EdgeInsets.all(5.0),
child: new ListTile(
title: new Text("whatEver"),
subtitle: new Row(
children: <Widget>[
Expanded(
child: new Text(postsFrom[index]["title"]["rendered"]),
),
Expanded(
child: hawalDate(postsFrom, index),
),
],
),
),
),
new ButtonTheme.bar(
child: hawalBtnBar(),
),
],
),
);
},
),
You have to write return Card at the beginning of the curly brackets in the builder function. Also I would be cautious with using Expanded there, it might cause some errors.
Also you put a ListView inside a Column without defining it's height, so it will take up space indefinitely. Wrap it in a widget that provides height constraints(SizedBox, Expanded, ...)
Inside the Listview.builder() you could try to add the property shrinkWrap: true.
This worked for me. I was facing a similar issue.
Use with Expanded. Hopefully, it will solve your problem.
You have to write return Card at the beginning of the curly brackets in the builder function. Also I would be cautious with using Expanded there, it might cause some errors.
Using this simple design, how can I display the second image under the listview? In reality the list will be fetched from firebase where each item is an ExpansionTile, so the height of the listview can in no way be fixed.
The column should be scrollable so you can see the full image if you scroll down below the list.
import 'package:flutter/material.dart';
List<Widget> list = <Widget>[
ListTile(
title: Text('CineArts at the Empire',
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20.0)),
subtitle: Text('85 W Portal Ave'),
leading: Icon(
Icons.theaters,
color: Colors.blue[500],
),
),
ListTile(
title: Text('The Castro Theater',
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20.0)),
subtitle: Text('429 Castro St'),
leading: Icon(
Icons.theaters,
color: Colors.blue[500],
),
),
];
class CartWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Column(children: <Widget>[
Image.network("https://via.placeholder.com/350x150"),
Expanded(
child: ListView(
children: list,
),
),
Image.network("https://via.placeholder.com/350x500"), // error: hides above widget
]));
}
}
The way I understood your problem is that you want the bottom image to appear inside the list view instead of under it, as if it was just another item. Solution: Make it just another item!
More concrete, this is how your implementation for a helper function that enriches the list with the image may look like:
List<Widget> _buildListWithFooterImage(List<Widget> items) {
return items.followedBy([
Image.network("https://via.placeholder.com/350x500")
]);
}
Then, you could use that function during your build:
Widget build(BuildContext context) {
return SafeArea(
child: Column(
children: <Widget>[
Image.network("https://via.placeholder.com/350x150"),
Expanded(
child: ListView(
children: _buildListWithFooterImage(list)
)
),
]
)
);
}
Also, I believe your question is similar to this one.
I am trying to move my project from Java/Android studio to flutter but I have a stutter/lag issue when I try to change "Activity"...
As soon as I press the "Sign Up" button I want to transition to the sign up screen but when I do there is a stutter and the animation starts from the middle of the screen. The same is when I navigate back with the back button.
I started learning flutter yesterday so if you have any tips with how I can improve my layout that would also be a lot of help! :)
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
routes: <String, WidgetBuilder>{
"/SignUp": (BuildContext context) => new SignUp()
},
home: new Scaffold(
body: new WelcomePage(),
)
);
}
}
class WelcomePage extends StatelessWidget{
#override
Widget build (BuildContext context){
return new Container(
padding: const EdgeInsets.all(32.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Row(
children: <Widget>[
new Expanded(
child: new Container(
height: 60.0,
margin: const EdgeInsets.only(right: 5.0),
child: new RaisedButton(
onPressed: _SignIn,
color: Colors.blueAccent,
child: const Text('Sign In'),
textColor: Colors.white,
),
)
),
new Expanded(
child: new Container(
height: 60.0,
margin: const EdgeInsets.only(left: 5.0),
child: new RaisedButton(
onPressed: (){Navigator.of(context).pushNamed("/SignUp");},
color: Colors.blueAccent,
child: const Text('Sign Up'),
textColor: Colors.white,
),
)
)
],
),
new Row(
children: <Widget>[
new Expanded(
child: new Container(
height: 60.0,
margin: const EdgeInsets.only(top: 10.0),
child: new RaisedButton(
onPressed: _GoogleSignIn,
color: Colors.blueAccent,
child: const Text('Google Sign In'),
textColor: Colors.white,
),
)
)
],
)
],
),
);
}
void _signUp(BuildContext context){
}
void _signIn(){
}
void _googleSignIn(){
}
}
class SignUp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("SignUp"),),
body: new Container(
padding: const EdgeInsets.all(32.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Row(
children: <Widget>[
new Expanded(
child: new Container(
child: new TextField(
decoration: new InputDecoration(
labelText: "Email",
),
keyboardType: TextInputType.emailAddress,
)
)
)
],
),
new Row(
children: <Widget>[
new Expanded(
child: new Container(
child: new TextField(
decoration: new InputDecoration(
labelText: "Password",
),
obscureText: true,
)
)
)
],
),
],
),
),
);
}
}
I've tried your sample code and It is working fine now.
I've traced the reported bugs in GitHub and it seems that the issue's were closed.
Also, I've found this topic on YouTube interesting, Flutter Europe: Optimizing your Flutter App whenever you've encountered performance issues in your Flutter app.