I am currently working on the chat aspect of my app.
and I set up an AnimatedList inside of a StreamBuilder in order to make the messages appear in reverse.
This is my code
children: <Widget>[
new Flexible(
child: new StreamBuilder<QuerySnapshot> (
stream: chatRoomRef.document(widget.chatRoomID).collection('messages')
.orderBy('time').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
return new AnimatedList(
reverse: true,
padding: const EdgeInsets.all(8.0),
itemBuilder: (BuildContext context, int index, Animation<double> animation) {
return new ChatMessageListItem(
context: context,
index: index,
animation: animation,
reference: snapshot,
);
}
);
},
),
),
My problem is that the builder is never hit, so the AnimatedList is never called. I am not sure the setup is correct so any help on this is much appreciated.
Edit:
I am trying to make it work like the FirebaseAnimatedList widget. I dont know if that helps with understanding my goal here.
Thank you
Try to validate if your snapshot has data
children: <Widget>[
new Flexible(
child: new StreamBuilder<QuerySnapshot> (
stream: chatRoomRef.document(widget.chatRoomID).collection('messages')
.orderBy('time').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
return snapshot.hasData ? new AnimatedList(
reverse: true,
padding: const EdgeInsets.all(8.0),
itemBuilder: (BuildContext context, int index, Animation<double> animation) {
return new ChatMessageListItem(
context: context,
index: index,
animation: animation,
reference: snapshot,
);
}
): new CircularProgressIndicator();
},
),
),
Update:
I fixed it by adding a custom animation as well as modifying the code in the documentation of cloud_firestore.
My code looks like this now
new Flexible(
child: new StreamBuilder<QuerySnapshot> (
stream: chatRoomRef.document(widget.chatRoomID).collection('messages')
.orderBy('time').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
return snapshot.hasData ? new ListView(
physics: const AlwaysScrollableScrollPhysics(),
reverse: true,
padding: const EdgeInsets.all(8.0),
children: snapshot.data.documents.map((DocumentSnapshot messageSnapshot) {
return new ChatMessageListItem(
context: context,
animation: _animation,
messageSnapshot: messageSnapshot,
currentUserEmail: curUserEmail,
);
}).toList(),
): const CircularProgressIndicator();
Related
I have a NestedScrollView whose body contains a TabBarView. And one of the tabs has a column with an image and a scrollable widget(GridView.builder). When scrolling this scrollable widget, the images get stuck halfway(as if it was pinned in that position).
Here's the code
//home.dart
class _HomePage extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Container( //this is the image that gets stuck
child: Column(
children: <Widget>[
new Container(
child: new Image.asset(
"images/product.jpg",
fit: BoxFit.fitWidth,
),
),
Expanded(
child: FreshFinds(), //this is the scrollable widget
),
],
),
);
}
}
// Freshfind.dart
#override
Widget build(BuildContext context) {
return Card(
child: GridView.builder(
// shrinkWrap: true,
itemCount: 50,
physics: ScrollPhysics(),
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return FutureBuilder(
future: fetchdata(index),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData)
return buildfake(index);
else
return buildcard(snapshot.data, index);
},
);
},
),
);
}
Here is a video of the problem
I think you need to use SliverAppBar() widget alongside with TabBar().
Here's a resource that might help you
In my search there is some ways to do this. One way is to use FutureBuilder and StreamBuilder at the same time. But I want to avoid this nesting StreamBuilder inside FutureBuilder?
I tried to create a method and call this from stream builder.
_getData() async{
FirebaseUser user = await FirebaseAuth.instance.currentUser();
String uid = user.uid.toString();
var snapshots =
Firestore.instance.collection('userinfo').document(uid).snapshots();
return snapshots;
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Retrieve Text Input'),
),
body: new Container(
padding: EdgeInsets.only(top: 20.0, left: 10.0, right: 10.0),
child: new StreamBuilder(
stream: _getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var userDocument = snapshot.data;
return new Column(
children: <Widget>[
TextFormField(
initialValue: userDocument["name"].toString(),
//controller: _AdContr,
decoration: new InputDecoration(
labelText: 'Name',
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(25.0),
borderSide: new BorderSide(),
),
//fillColor: Colors.green
),
),
SizedBox(height: 20),
TextFormField(
//initialValue: Text(userDocument["Surname"]).toString(),
//controller: _AdContr,
decoration: new InputDecoration(
labelText: 'Surname',
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(25.0),
borderSide: new BorderSide(),
),
//fillColor: Colors.green
),
)
],
);
}
}),
),
);
}
But I am getting this error:
type 'Future' is not a subtype of type 'Stream'
How do I solve this problem?
You will need to nest a StreamBuilder inside a FutureBuilder because you will need to await the user from FirebaseAuth before being able to get the stream. However, this is not bad at all.
This is how Flutter works: nesting widgets.
..., child: FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState != ConnectionState.done) return Container();
return StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance.collection('userinfo').document(snapshot.data.uid).snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) return Container();
return Column(...);
},
);
})
Another aspect that is crucial for this to work is that you always return a widget from any builder. If you do not return a widget to a builder in Flutter, your app will break, i.e. a runtime exception will be thrown.
I am retrieving data from a collection of "players" in my code in flutter project. I am right now building a listview from the data i am receiving through the code below. However, I would like to retrieve some more data, that would just need to be saved in the background and not used visually. However, I can't figure out how to retrieve the data, without having to visually show it with a widget.
I tried using the same procedure as the code showing here. I can only retrieve one collection at the time, and not both.
Can anyone point me in the direction of how to retrieve data without having to "show" it in the build widget?
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('Spillere').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});
}
}
),
),
),
);
}
This is the List View Builder of Chat Messages below here--
child: new ListView.builder(
padding: new EdgeInsets.all(8.0),
reverse: true,
itemCount: _messages.length,
itemBuilder: (context, int index) => _messages[index]),
),
Here I defined _messages as under--
final List<ChatMessage> _messages = <ChatMessage>[];
But When I am Applying "Dismissible" Widget in this, It iS not PIcking Up that correctly..
Here Is What I did..
child: new ListView.builder(
padding: new EdgeInsets.all(8.0),
reverse: true,
itemCount: _messages.length,
itemBuilder: (context, int index) => _messages[index]){
return new Dismissible(
key: new Key(_messages[index]),
onDismissed: (direction) {
items.removeAt(index);
Scaffold.of(context).showSnackBar(new SnackBar(
content: new Text("Item is Dismissed"),
));
},
background: new Container(
color: Colors.lightBlueAccent,
),
child: new ListTile(
title: new Text("${_messages[index]}")
)
);
}
But IT IS Showing error. Please Help me in this!!
I know this code only displays title and i want to make a onTap method to navigate to a new route, but this is how fare i made it, any help, hint, tip, even shaming me for how stupid i am would be very much appreciated.
Edit: I did posted the whole code because something is going wrong even after help that i got here. maybe is a syntax problem or maybe i am just too stupid
Widget build(BuildContext context) {
return new Scaffold(
body: new ListView.builder(
itemCount: data == null ? 0 : 10,
itemBuilder: (BuildContext context, int index){
return new Card(
child: new ListTile(
onTap: _onTapped,
title : new Text(data[index]["title"]),
),
);
},
),
);
}
}
Just wrap your title in a GestureDecector to handle clicks.
Then call Navigator's pushNamed to redirect to a new route.
new GestureDetector(
onTap: () {
Navigator.pushNamed(context, "myRoute");
},
child: new Text("my Title"),
);
An easier approach I found is to just wrap the item inside the ListTile with a FlatButton (or some interactive widget). In your code, for example:
Widget build(BuildContext context) {
return new Scaffold(
body: new ListView.builder(
itemCount: data == null ? 0 : 10,
itemBuilder: (BuildContext context, int index){
return new Card(
child: new ListTile(
title: FlatButton(
child: new Text(data[index]["title"]),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => YourPage()),
);
},
),
),
);
},
),
);