I am consulting the news section of my website.
I'm using Future Builder to get the data from the web.
The problem I get is related to the image that I try to show on the screen.
And when there is a lot of news, the data load takes a long time and I do not know if there is a solution for loading faster.
I am consulting the text of the news through a json.
At that moment you get the URL of another JSON where the image is in thumbnail format.
I hope to solve this problem, I appreciate any help.
News.dart - Code
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: searchBar.build(context),
key: _scaffoldKey,
body: new Container(
color: Colors.grey[800],
child: new RefreshIndicator(
child: new ListView(
children: <Widget>[
new FutureBuilder<List<Post>>(
future: fetchPosts(URLWEB),
builder: (context, snapshot) {
if(snapshot.hasData) {
List<Post> posts = snapshot.data;
return new Column(
children: posts.map((post2) => new Column(
children: <Widget>[
new Card(
margin: new EdgeInsets.symmetric(vertical: 20.0, horizontal: 20.0),
color: Colors.white,
child: new GestureDetector(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new FutureBuilder(
future: fetchPostsIMG(post2.imagen),
builder: (context, AsyncSnapshot<PostImg> snapshot2){
return new Container(
height: 200.0,
decoration: new BoxDecoration(
image: new DecorationImage(
image: CachedNetworkImageProvider(snapshot2.data.imagen == null ? new AssetImage('images/logotipo.png') : snapshot2.data.imagen),
fit: BoxFit.fitWidth
)
),
width: MediaQuery.of(context).size.width,
);
},
),
new ListTile(
title: new Text(post2.titulo.replaceAll("‘", "").replaceAll(
"’", "").replaceAll("–", "")
.replaceAll("…", "").replaceAll(
"”", "")
.replaceAll("“", ""),
style: new TextStyle(
color: Colors.black,
fontSize: 18.0,
fontWeight: FontWeight.bold),),
subtitle: new HtmlView(data: post2.informacion),
dense: true,
)
],
),
onTap: () {
//Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context)=> new WebView(url: post2.urlweb, titulo: titulo)));
},
)
)
],
)).toList(),
);
}
else if(snapshot.hasError)
{
return new Container();
}
return new Center(
child: new Column(
children: <Widget>[
new Padding(padding: new EdgeInsets.all(50.0)),
new CircularProgressIndicator(),
],
),
);
},
),
],
),
onRefresh: _autoRefresh
),
),
);
}
}
It's because you are trying to access imagen on null object. You can do hasData check like below
CachedNetworkImageProvider(snapshot2.hasData ? snapshot2.data.imagen : new AssetImage('images/logotipo.png')),
Related
Good morning,
I'm new on Flutter.
I need to open a new page from a card after a button press. To open the new page I need the card data object that I use to show the information (data[X]). The button for open the new page is located in ButtonTheme.bar but in this location I don't have my "data[X]" object.
This is my code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Records Page", style: TextStyle(color: Colors.white)),
iconTheme: new IconThemeData(color: Colors.white),
backgroundColor: Color.fromRGBO(32, 38, 48, 1),
),
drawer: MainDrawer(),
body: new ListView.builder(
//reverse: true,
itemCount: reversedData != null ? reversedData.length : 0,
itemBuilder: (BuildContext ctxt, int index) {
if (reversedData[index].stop != null) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.calendar_today),
title: Text('Last Period'),
//subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
new Container(
margin: EdgeInsets.symmetric(horizontal: 15),
child: new ListView(
shrinkWrap: true,
children: <Widget>[
new Text(
"Hour: ${reversedData[index].hourType.description}"),
new Text("Job: ${reversedData[index].job.description}"),
new Text(
"Opened at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].start))}"),
new Text(
"Closed at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].stop))}"),
],
),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Modify',
style: TextStyle(
color: Color.fromRGBO(32, 38, 48, 1))),
onPressed: () {
Navigator.push(context, new MaterialPageRoute(builder: (__) => new PeriodEditPage(toChangeData: //my card data? ,)));
},
),
],
),
),
],
),
);
}
}));
}
I simply need to have the specific card data when I'm going to open the new page.
Hope I was clear.
Thank you
The trick is to declare your data in your statefulWidget (or statelessWidget) then use then wherever you want.
You can also create an object data where you'll have all your information instanciate one into your widget then pass it to another screen.
Here's an intro about passing data : https://flutter.dev/docs/cookbook/navigation/passing-data
Finally I found the solution to my problem:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Records Page", style: TextStyle(color: Colors.white)),
iconTheme: new IconThemeData(color: Colors.white),
backgroundColor: Color.fromRGBO(32, 38, 48, 1),
),
drawer: MainDrawer(),
body: new ListView.builder(
//reverse: true,
itemCount: reversedData != null ? reversedData.length : 0,
itemBuilder: (BuildContext ctxt, int index) {
if (reversedData[index].stop != null) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.calendar_today),
title: Text('Last Period'),
//subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
new Container(
margin: EdgeInsets.symmetric(horizontal: 15),
child: new ListView(
shrinkWrap: true,
children: <Widget>[
new Text(
"Hour: ${reversedData[index].hourType.description}"),
new Text(
"Job: ${reversedData[index].job.description}"),
new Text(
"Opened at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].start))}"),
new Text(
"Closed at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].stop))}"),
],
),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Modify',
style: TextStyle(
color: Color.fromRGBO(32, 38, 48, 1))),
onPressed: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (__) => new PeriodEditPage(
toChangeData: reversedData[index],
)));
},
),
],
),
),
],
),
);
}
}));
}
My problem was solved thank to this line:
toChangeData: reversedData[index],
This code permit to open every card details with the same object used to populate my card.
I haven't understand properly what happen with the object reference but it works for me.
I currently have a listview operating on the whole of my screen. I would like to have a button in the bottom of the screen, thus splitting it up so the listview doens't fill up the whole of my window.
This is the current code building the class:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('HT scoreboard'),
),
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('Spillere').orderBy("score", descending: true).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 10.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
return Padding(
key: ValueKey(record.name),
padding: const EdgeInsets.all(5.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(5.0),
),
child: ListTile(
title: Text(record.name + ": " + record.score.toString()),
trailing: new IconButton(icon: new Icon(isAdmin ? Icons.add : null, color: Colors.green),
onPressed: (){
if(isAdmin){
record.reference.updateData({'score': record.score + 1});
}
}
),
),
),
);
change your buildlist function to include a column with the button and listview as children
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return Column(
children:[
Expanded(
child: ListView(
padding: const EdgeInsets.only(top: 10.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
),
),
RaisedButton(
// fill in required params
)
])
}
To prevent the buttons being pushed above the keyboard;
return CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// list items
],
),
),
SliverFillRemaining(
hasScrollBody: false,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
RaisedButton()
],
),
)
],
);
I have an OverlayEntry that displays fullscreen. I want to dispatch an actions an close it onTap of the overlayentry's buttons
OverlayEntry _buildOverlayFeedback(BuildContext context, String tituloEvento) {
return OverlayEntry(
builder: (context) => Material(
child: Container(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
children: <Widget>[
ListTile(
leading: Icon(Icons.sentiment_dissatisfied),
title: Text('No me ha gustado'),
onTap: () {
// how to close myself????
},
),
ListTile(
leading: Icon(Icons.sentiment_very_satisfied),
title: Text('Muy bien'),
onTap: () {}),
],
),
],
),
),
),
);
}
You call remove() on the OverlayEntry itself.
This could be one way of doing it:
OverlayEntry _buildOverlayFeedback(BuildContext context, String tituloEvento) {
OverlayEntry entry;
entry = OverlayEntry(
builder: (context) => Material(
child: Container(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
children: <Widget>[
ListTile(
leading: Icon(Icons.sentiment_dissatisfied),
title: Text('No me ha gustado'),
onTap: () {
entry.remove();
},
),
ListTile(
leading: Icon(Icons.sentiment_very_satisfied),
title: Text('Muy bien'),
onTap: () {}),
],
),
],
),
),
),
);
return entry;
}
For those of you who are trying to figure out how to do this with FCM onMessage with Navigator.of(context).overlay.insert(entry) - chemamolins' answer works, you just have to adapt it slightly. Here's a similar example to get you started:
onMessage: (Map<String, dynamic> message) async {
OverlayEntry entry;
entry = OverlayEntry(builder: (context) {
return GestureDetector(
onTap: entry.remove,
child: SafeArea(
child: Align(
alignment: Alignment.topCenter,
child: Material(
type: MaterialType.transparency,
child: Container(
decoration: BoxDecoration(color: Colors.white),
width: double.infinity,
height: 60,
child: Column(
children: <Widget>[
Text(message['notification']['title']),
Text(message['notification']['body']),
],
),
),
),
),
),
);
});
Navigator.of(context).overlay.insert(entry);
},
And onTap will close out the OverlayEntry.
new Expanded(
child: _searchResult.length != 0 || controller.text.isNotEmpty
? new ListView.builder(
itemCount: _searchResult.length,
itemBuilder: (context, int i) {
return new Card(
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Row(children: <Widget>[
//new GestureDetector(),
new Container(
width: 45.0,
height: 45.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
image: new DecorationImage(
fit: BoxFit.fill,
image: new NetworkImage(
"https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg")))),
new Text(
" " +
userDetails[returnTicketDetails[i]
["user_id"]]["first_name"] +
" " +
(userDetails[returnTicketDetails[i]
["user_id"]]["last_name"]),
style: const TextStyle(
fontFamily: 'Poppins', fontSize: 20.0)),
]),
new Column(
children: <Widget>[
new Align(
alignment: FractionalOffset.topRight,
child: new FloatingActionButton(
onPressed: () {
groupId = returnTicketDetails[i]["id"];
print(returnTicketDetails[i]["id"]);
print(widget.id);
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new Tickets(groupId,widget.id)));
},
heroTag: null,
backgroundColor: Color(0xFF53DD6C),
child: new Icon(Icons.arrow_forward),
)),
new Padding(padding: new EdgeInsets.all(3.0)),
],
)
]));
},
)
: new ListView.builder(
itemCount: _searchResult.length,
itemBuilder: (context, int i) {
return new Card(
child: new ListTile(
//title: new Text(userDetails[returnTicketDetails[i]["user_id"]]["first_name"]),
),
margin: const EdgeInsets.all(0.0),
);
},
),
),
Hi everyone! As I am building dynamically a Card in a ListView, I was thinking rather than keep the FloatingActionButton in each of them as I already do, to implement a onTap method in each card and trigger something.
In other words, I would like to keep the card as simple as possible without many widget around.
Thank you in advance!
As Card is "a sheet of Material", you probably want to use InkWell, which includes Material highlight and splash effects, based on the closest Material ancestor.
return Card(
child: InkWell(
onTap: () {
// Function is executed on tap.
},
child: ..,
),
);
You should really be wrapping the child in InkWell instead of the Card:
return Card(
child: InkWell(onTap: () {},
child: Text("hello")));
This will make the splash animation appear correctly inside the card rather than outside of it.
Just wrap the Card with GestureDetector as below,
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return new ListView.builder(
itemBuilder: (context, i) {
new GestureDetector(
child: new Card(
....
),
onTap: onCardTapped(i),
);
},
);
}
onCardTapped(int position) {
print('Card $position tapped');
}
}
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.