My Firebase Structure is like this:
I want to get 'groupName' list in FirebaseAnimatedList.
How to achive this?
My current code:
chatGroupNameReference = FirebaseDatabase.instance.reference().child("chat_group_message_user_info").child(widget.user.uid);
body: new Container(
child: new Flexible(
child: new FirebaseAnimatedList(
query: chatGroupNameReference,
padding: const EdgeInsets.all(8.0),
defaultChild: Center(child: CircularProgressIndicator(backgroundColor: Colors.deepPurpleAccent,),),
itemBuilder: (_, DataSnapshot messageSnapshot,
Animation<double> animation, int index) {
return Text("Group Name will be here")
},
),
)
)
);
For nested child key query we need FutureBuilder with FirebaseAnimatedList:
chatGroupNameReference = FirebaseDatabase.instance.reference().child("chat_group_message_user_info").child(widget.user.uid);
body: new FirebaseAnimatedList(
defaultChild: Center(child: new CircularProgressIndicator()),
query: chatGroupNameReference,
sort: (a,b) => (b.key.compareTo(a.key)),
itemBuilder: (_, DataSnapshot messageSnapshot, Animation<double> animation, int index) {
return new FutureBuilder<DataSnapshot>(
future: chatGroupNameReference.child(messageSnapshot.key).once(),
builder: (BuildContext context, snapshot){
return snapshot.hasData ? new Text(snapshot.data.value['groupName']) : new Text("");
},
);
},
(_, DataSnapshot messageSnapshot,
Animation<double> animation, int index)
After that line you can use FutureBuilder to get data from your Firebase Database.
For example like that In a firebase animated list, is there a way to let firebase list know the expected height of a widget before it loads?
Related
I am currently attempting to make a user search list within an alert dialog, which will query users from the project's database based on the user's search input. I am doing this in Android Studio, using Flutter's native language (Dart) and Firebase Cloud Firestore. I have the search bar itself working, but for some reason, whenever I try to actually get the results from the database, my code will access the stream for the Streambuilder being used, but will never touch the actual builder, skipping it entirely. What exactly am I doing wrong here?
The function responsible for creating the alert dialog:
Future createAlertDialog(BuildContext context){
String userToSearch = '';
bool showUsers = false;
return showDialog(context: context, builder: (context){
return AlertDialog(
title: const Text("Search for a user:"),
content: StatefulBuilder(
builder: (context, setState) => Container(
child: CupertinoSearchTextField(
onChanged: (value) => {
setState(() {
showUsers = true;
}),
showUsers
? Expanded(
child: StreamBuilder(
stream: FireStoreMethods().searchUsers(value),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.connectionState ==
ConnectionState.none) {
return const Center(child: Text("Internet error"));
}
if (snapshot.hasError) {
return const Center(
child: Text("Something went wrong."),
);
}
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProfileScreen(
uid: snapshot.data!.docs[index]['uid'],
),
),
),
leading: CircleAvatar(
backgroundImage: NetworkImage(
snapshot.data!.docs[index]['photoUrl'],
),
radius: 16,
),
title: Text(
snapshot.data!.docs[index]['username'],
),
);
},
);
},
),
)
: const Expanded(child: Text("error"))
}
),
),
)
);
});
}
Function responsible for querying the database:
Stream searchUsers(String userInput){
String? currentUserID = FirebaseAuth.instance.currentUser?.uid;
//String? valueFromFirebase = '';
Stream s = FirebaseFirestore.instance.collection('users').where('username', isGreaterThanOrEqualTo: userInput).orderBy('username', descending: false).snapshots();
return s;
}
To be clear, I expected this code to create a list of users from the database, under the search bar in the alert dialog, containing the users that match the current input. I tried debugging, changing the positioning of certain lines of code, and comparing and contrasting my code to code I found all over the internet. The actual result that I received was the ability to use the search bar and have the input saved properly, but literally nothing happens after pressing enter. No list is rendered, no error is thrown, and the program continues like nothing happened.
You need to place StreamBuilder inside widget tree to make it visible. Currently having inside onChanged which is just callback method for textFiled.
Future createAlertDialog(BuildContext context) {
String userToSearch = '';
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Search for a user:"),
content: StatefulBuilder(
builder: (context, setState) => Column(
children: [
CupertinoSearchTextField(
onChanged: (value) {
setState(() {
userToSearch = value;
});
},
),
userToSearch.isNotEmpty
? Expanded(
child: StreamBuilder(
stream: FireStoreMethods().searchUsers(userToSearch),
...........
),
)
: Text("Empty")
],
),
),
);
});
I'm making a to-do list with Flutter and Firebase Firestore, but I have a slight problem. Whenever I add a new task, snapshot.data becomes null. If I do not add a check for snapshot.connectionState and render a different widget according to that, I get the following error: NoSuchMethodError (NoSuchMethodError: The getter 'docs' was called on null.
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: SizedBox(
height: 100, width: 100, child: CircularProgressIndicator()),
);
} else {
> final docs = snapshot.data.docs;
return ListView.builder(
itemCount: docs.length,
itemBuilder: (ctx, index) {
final currTask = docs[index];
return Dismissible(
direction: DismissDirection.startToEnd,
key: UniqueKey(),
onDismissed: (_) async {
FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.doc(currTask['id'])
.delete();
.
.
.
I don't want to have to display a CircularProgressIndicator or an empty screen. I want the tasks to remain visible and seamlessly add new tasks. How can I achieve that? I know what I asked for in the question title might be a little silly, what should I do instead?
I want to convert this ListView into a ListView.builder, because i need the Index. Can someone show me how to cnvert thsi ListView?`Thank for your Help :)
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['name']),
);
}).toList(),
);
Here is how you can create a ListView builder, which provides an index
var documents = snapshot.data.documents.map((DocumentSnapshot document);
return ListView.builder(
itemCount: documents.length,
itemBuilder: (context, index) => {
// Just an example, use your own fields
Text(documents[index].Name);
});
You just provide a builder function. This function gets called for each Item in your data. The amount of items you set with itemCount.
Example:
var dataList = []; //this would be your snapshot data
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(gallery),
),
body: ListView.builder(
itemBuilder: (context, index) => ListTile(
title: Text(dataList[index]),
),
),
);
}
As my first Flutter project I'm building an app for a newspaper. There are a number of news categories. For those categories, I have created a generic Widget, in the home screen that Widget will be shown multiple times for multiple categories in a Listview, those category Widgets have Listview in them too as I bring multiple news from those categories. The Widget class that has this generic Widget is Newsfeed.dart which will be given below.
This generic Widget is called from a another Widget class WidgetFactory.dart which actually calls API and builds the home screen by using the above mentioned generic Widget for categories. This class uses a Listview.builder which is inside FutureBuilder.
The problem is, when I open the app in the screen the news appears but I can't scroll, it stays fixed. I have checked if the API is actually bringing the news, in console I have printed the API response, all of the news are fetched but still I can't scroll.
the flow is main.dart -> WidgetFactory() -> Newsfeed()
WidgetFactory()
class WidgetFactory extends StatefulWidget {
#override
_WidgetFactoryState createState() => _WidgetFactoryState();
}
class _WidgetFactoryState extends State<WidgetFactory> {
List homeScreenCategories = [4, 14, 13, 23, 8015, 22];
Future<List> newsPostList;
List<List<NewsPost>> categoryNewsPostList;
#override
void initState() {
super.initState();
newsPostList = fetchNews();
}
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Container(
alignment: Alignment.center,
child: Container(
child: RefreshIndicator(
child: FutureBuilder(
future: newsPostList,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.data == null) {
return Container(
child: CircularProgressIndicator()
);
} else {
return ListView.builder(
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return _getCategoryNews(snapshot, index);
},
);
}
},
),
onRefresh: () {
fetchNews();
}
),
),
);
}
Future<List> fetchNews() async {
String url = "url";
Response response = await Dio().get(url);
if(response.statusCode == 200) {
List newsPostList = [];
for(int i=0; i<response.data.length; i++) {
newsPostList.add(response.data[i]);
}
return newsPostList;
} else {
throw Exception("Failed to fetch category");
}
}
Widget _getCategoryNews(snapshot, int index) {
List<NewsPost> newsPostList = [];
for(var c in snapshot.data[index]['items']) {
NewsPost newsPost = NewsPost.getNewsPostFromAPI(c);
newsPostList.add(newsPost);
}
return Newsfeed(newsPostList, "National");
}
}
Newsfeed()
class Newsfeed extends StatefulWidget {
String categoryName;
List<NewsPost> newsPostList;
Newsfeed(this.newsPostList, this.categoryName);
#override
_NewsfeedState createState() => _NewsfeedState(this.newsPostList, this.categoryName);
}
class _NewsfeedState extends State<Newsfeed> {
final GlobalKey<ScaffoldState> _scaffoldKeyTwo = new GlobalKey<ScaffoldState>(debugLabel: '_MainScreenKey');
String categoryName;
_NewsfeedState(this.newsPostList, this.categoryName);
List<NewsPost> newsPostList;
var dio = new Dio();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Container(
alignment: Alignment.center,
child: ListView.builder(
shrinkWrap: true,
itemCount: newsPostList.length,
itemBuilder: (BuildContext context, int index) {
print(newsPostList[index]);
return _getNewsPostWidgets(index);
}
),
);
}
Widget _getNewsPostWidgets(int index) {
var newsPost = newsPostList[index];
if(index < 5) {
if(index == 0) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
ScaleTransitionRoute(
page: NewsPostDetails(newsPostList, index)
)
);
},
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.fromLTRB(10, 0, 0, 0),
//constraints: BoxConstraints(minWidth: double.infinity, maxWidth: double.infinity),
constraints: BoxConstraints.expand(
width: double.infinity,
height: 40
),
color: const Color(0xFF2b4849),
child: Text(
this.categoryName,
style: TextStyle(
fontSize: 33,
color: Colors.white
),
),
),
BlockHeadline(newsPost)
],
)
);
}
else {
return GestureDetector(
onTap: () {
Navigator.push(
context,
ScaleTransitionRoute(
page: NewsPostDetails(newsPostList, index)
)
);
},
child: ListedNews(newsPost),
);
}
}
else {
return Container(
color: const Color(0xFF2b4849),
child: index == 5 ? FlatButton(
child: Text(
"See More",
style: TextStyle(
color: Colors.white
),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => NewsFeedForSpecificCategory(newsPostList)
)
);
},
) : Container(),
);
}
}
openNewsPostDetails(List<NewsPost> newsPostList, int index) {
Navigator.push(
context,
ScaleTransitionRoute(
page: NewsPostDetails(newsPostList, index)
)
);
}
}
What I have tried
I found some questions that relates to this problem a bit. Tried those.
I used shrinkwrap=true in my Listview.builder, but of no use.
Tried using Column inside SingleChildScrollView(), still did not work.
Inside the Listview.builder added physics as AlwaysScrollable(), also in vain.
As I'm new to flutter what I tried might seem dumb.
One more thing is, the news that shows up in the home screen, that takes me to the details page fine and there swiping left right takes me to other news also. It's the home screen that is causing trouble, not scrolling.
It would be great help if you could kindly give some clues.
I have found the solution to this problem. It was quite simple actually.
In the build() method of my _NewsfeedState class I have added ClampingScrollPhysics() as physics.
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Container(
alignment: Alignment.center,
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: newsPostList.length,
itemBuilder: (BuildContext context, int index) {
print(newsPostList[index]);
return _getNewsPostWidgets(index);
}
),
);
}
It worked.
Putting ListView inside ListView is an anti pattern.
There are several solutions for this problem,
Solution 1:
Merge those two list of items into a single list of items and
display it as a single ListView.
Solution 2:
You can use SliverList for your use case. SliverList can wrap multiple SliverLists inside a single CustomScrollView.
This will help you.
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
Thanks.
Hello i have 2 screens in flutter the 1st page contains many JSON data like image,strings, etc to be pass in 2nd page how to to this? Thank in advance.
I have the my code below i stuck here for almost 4 hours now Can anybody help me.
//first page
Map data;
List userData;
Future getItems() async {
http.Response response = await http.get("MY_IP/olstore_serv/get_items");
data = json.decode(response.body);
setState(() {
userData = data["item"];
});
onTap: () {
Navigator.of(context, rootNavigator: true).push(
new CupertinoPageRoute(
builder: (context) {
return new ItemDetails(todo:userData);
},
),
);
},
/// second page
class ItemDetails extends StatelessWidget {
final List todo;
ItemDetails({this.todo});
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('i want to display item name here pls help me'),
),
child: Container(
child: ListView(
physics: BouncingScrollPhysics(),
children: <Widget>[
Text('here too'),
]
),
),
);
}
}
Please try below code for listview.
ListView.builder(
itemCount: todo.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Text(todo[index].yourKey);
},
);