Display single item from Stream in PageView - flutter

I have the following widget that uses PageView to display each book in a separate page.
class Books extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _BooksState();
}
}
class _BooksState extends State<Books> {
PageController controller = PageController();
String title = 'Title of the AppBar';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: PageView.builder(
controller: controller,
scrollDirection: Axis.horizontal,
itemBuilder: (context, position) {
return Center(position.toString());
// return BooksStream();
},
onPageChanged: (x) {
setState(() {
title = '$x';
});
},
),
);
}
}
I also have this example widget from FlutterFire docs to get all documents from a firestore collection:
class BooksStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('Books').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['title']),
subtitle: new Text(document['author']),
);
}).toList(),
);
}
},
);
}
}
In the PageView.builder, when I return BooksStream() it displays all books on every page which is totally normal.
But how would I use the StreamBuilder to display each book on a separate page in PageView?
Thanks.

You can return PageView.builder in your StreamBuilder builder field like that:
class BooksStream extends StatelessWidget {
PageController controller = PageController();
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('Books').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return PageView.builder( // Changes begin here
controller: controller,
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, position) {
final document = snapshot.data.documents[position];
return ListTile(
title: new Text(document['title']),
subtitle: new Text(document['author']));
}
);
}
},
);
}
}
And then just inside the body of your Scaffold you can instantiate your BooksStream.

Related

Retrieving Firestore data in ListView but Failing

Currently struggling to make a ListView data retrieved from Firestore.
I am trying to get "kids name" saved under in the firestore as linked photo.
Firestore
No error message is shown up but the data is not retrieved correctly and shown blank screen...hope anyone can correct my code!
and here is my code:
class kidsNamePick extends StatefulWidget {
#override
_kidsNamePickState createState() => _kidsNamePickState();
}
class _kidsNamePickState extends State<kidsNamePick> {
List<Memo> kidsnamelist = [];
Future<void>fetchMemo()async{
final kidsnames = await FirebaseFirestore.instance.collection('useraccount').doc(FirebaseAuth.instance.currentUser!.uid)
.collection('kidsname').get();
final docs = kidsnames.docs;for (var doc in docs){
Memo fetchMemo = Memo(kidsname: doc.data()['kids name'],
);
kidsnamelist.add(fetchMemo);}
setState(() {
});}
#override
void initState(){
super.initState();
fetchMemo();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add/Select Kids'),
),
body: ListView.builder(
itemCount: kidsnamelist.length,
itemBuilder: (context, index){
return ListTile(
title: Text(kidsnamelist[index].kidsname),
);
},
)
);
}
}
The best way to call future method is using FutureBuilder, first change your fetchMemo to this:
Future<List<Memo>> fetchMemo() async {
try {
final kidsnames = await FirebaseFirestore.instance
.collection('useraccount')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection('kidsname')
.get();
final docs = kidsnames.docs;
return docs
.map((doc) => Memo(
kidsname: doc.data()['kids name'],
))
.toList();
} catch (e) {
return [];
}
}
then change your build method to this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add/Select Kids'),
),
body: FutureBuilder<List<Memo>>(
future: fetchMemo(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Memo> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(data[index].kidsname),
);
},
);
}
}
},
),
);
}

Hi, I have problem with the property (title) and the bracket of (bluilder)... I'm just fetch data from API

class _Requests_TabState extends State<Requests_Tab> {
Future<Product> productdata = getPostById();
#override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
body: Center(
child: FutureBuilder(
future: productdata,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
CircularProgressIndicator();
},
),
),
return Text(snapshot.data[title].toString());

Display Data as GridView instead of ListView

I've successfully displayed my Firestore Data as a ListView() unfortunately I can't get it to display as a GridView()
I've tried many different methods but can't seem to find the right fit - I'd like a two columns GridView
Here is my ListView code :
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
shrinkWrap: true,
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['num']),
subtitle: Text(data['num']),
);
}).toList(),
);
},
);
Turns out it was easier than I thought :
I just had to change ListView to GridView.count for it to display as a GridView & set crossAxisCount: 2 for two columns
class UserGrid extends StatefulWidget {
#override
_UserGridState createState() => _UserGridState();
}
class _UserGridState extends State<UserGrid> {
final Stream<QuerySnapshot> _usersStream =
FirebaseFirestore.instance.collection('User Data').snapshots();
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
children: [
...snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['num']),
subtitle: Text(data['num']),
);
}).toList(),
ElevatedButton(onPressed: () {}, child: Text("Hello")),
],
);
});
}
}
SliverGrid must also be a part of CustomScrollView
class SliverGridWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
///no.of items in the horizontal axis
crossAxisCount: 4,
),
///Lazy building of list
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
/// To convert this infinite list to a list with "n" no of items,
/// uncomment the following line:
/// if (index > n) return null;
return listItem(Utils.getRandomColor(), "Sliver Grid item:\n$index");
},
/// Set childCount to limit no.of items
/// childCount: 100,
),
)
],
),
);
}

About preventing Listview.Builder() to rebulding it all items

I am getting users data from firebase by stream builder,
And I am rendering those data using Listview.builder(),
one problem is that when I get snapshot from firestore my all items in listview.builder() is rebuilding.
how do I prevent this thing reRendering or Rebuilding()
Hear is my Code
class GetUser extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("// <1> Use StreamBuilder");
return StreamBuilder<QuerySnapshot>(
// <2> Pass `Stream<QuerySnapshot>` to stream
stream: FirebaseFirestore.instance
.collection('users')
.orderBy('createdAt', descending: false)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// <3> Retrieve `List<DocumentSnapshot>` from snapshot
final List<DocumentSnapshot> documents = snapshot.data.docs;
//documents.forEach((doc) => print(doc));
print(documents.length);
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height,
child: ListView.builder(
addAutomaticKeepAlives: true,
addRepaintBoundaries: false,
itemCount: documents.length,
itemBuilder: (context, i) {
return Items(
doc: documents[i],
);
},
),
)
],
),
);
} else if (snapshot.hasError) {
return Text("Error");
} else {
return Text("Waiting");
}
});
}
}
//this Item class is invoked every time I add a user to the collection
class Item extends StatefulWidget {
final DocumentSnapshot doc;
const Item({Key key, this.doc}) : super(key: key);
#override
_ItemState createState() => _ItemState();
}
class _ItemState extends State<Item> {
#override
Widget build(BuildContext context) {
print("Build called ${widget.doc.id}");
return Text("${widget.doc["full_name"]}--${widget.doc.id}");
}
}

Flutter: Dynamic Widget as argument (A) of another widget (B), which Widget (A) receives an argument set on Widget B

First of all, sorry about the title, sounds messy, but I hope someone already come across with this query. Will try to explain what I'm trying to achieve here.
I'm trying to reuse futureBuilder widget, as my app has lots of lists.
//flutter
import 'package:flutter/material.dart';
class StreamBuilderWidget extends StatelessWidget {
Stream streamFunction;
ScrollController scrollController;
Widget customWidget;
StreamBuilderWidget({
this.streamFunction,
this.scrollController,
this.customWidget,
});
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: streamFunction,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.none:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
case ConnectionState.done:
if (snapshot.hasData) {
return IgnorePointer(
child: ListView.builder(
controller: scrollController,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
final item = snapshot.data[index];
return customWidget(item);
//PostItem(item) //example
//VideosItem(item) //example
//BooksItem(item) //example
;
},
),
);
}
}
return Center(
child: CircularProgressIndicator(),
);
},
);
}
}
My headache comes with the argument Widget, which I need to receive its own argument "item".
While reading this, seems to be fairly easy, although I could not get it solve.
A nasty work around is to replace the widget argument with a string argument, using it with a switch statement:
switch (widgetType) {
case 'PostItemWidget':
return PostItemWidget(post: item);
case 'YouTubeItemWidget':
return VideosItem(video: item)
... //and so on
You get the idea, anyway would be great to make it a more cleaver / clean solution.
Let me know if there's any further input you need.
Thanks in advance.
Code Update. use like this
final item = snapshot.data
customWidget(item[index]['name'])
import 'package:flutter/material.dart';
class StreamBuilderWidget extends StatelessWidget {
Stream streamFunction;
ScrollController scrollController;
Widget customWidget;
StreamBuilderWidget({
this.streamFunction,
this.scrollController,
this.customWidget,
});
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: streamFunction,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.none:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
case ConnectionState.done:
if (snapshot.hasData) {
return IgnorePointer(
child: ListView.builder(
controller: scrollController,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
final item = snapshot.data;
return customWidget(item[index]['name']); ///use like that
//PostItem(item[index]) //example
//VideosItem(item[index]) //example
//BooksItem(item[index]) //example
;
},
),
);
}
}
return Center(
child: CircularProgressIndicator(),
);
},
);
}
}