enter image description here
enter image description here
``
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Scaffold(
appBar: AppBar(automaticallyImplyLeading: false),
backgroundColor: Colors.black,
body: StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance.collection('History').doc(userId).snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
final DocumentSnapshot<Map<String, dynamic>>? getDocument = snapshot.data;
final Map<String, dynamic>? map = getDocument?.data();
var setList = map!['userchat'];
return ListView.builder(
itemCount: setList.length,
itemBuilder: (context, index) {
final get = setList[index];
print(get);
return const Text('data');
},
);
}
return const Text('data');
},
),
),
);
}
``
For getting the required text, i.e., the text in index = 3, you can use a conditional check in itemBuilder.
if(index==3){
final get = setList[index];
print(get);
return const Text('data');
}else{
return const SizedBox.shrink();
}
SizedBox.shrink() helps in avoiding to return a null value, but in turn returns a widget which doesn't show up, or simply nothing.
You can use it if you want, or can go with Text('data').
Related
I have future function and I want show this in the listview.seprator, but the listview do not get the future value. how can i fix this?
this is my code:
my hive class:
#HiveType(typeId: 3)
class TaskCat extends HiveObject{
#HiveField(0)
String catName;
#HiveField(1)
int userId;
#HiveField(2)
User? user;
TaskCat(this.catName,this.user,this.userId);
}
This is my function:
Future<List> showCategoryInHome() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
var taskCatBox = await Hive.openBox('taskCat');
var filtertaskCat = taskCatBox.values
.where(
(TaskCat) => TaskCat.userId == sharedPreferences.getInt('key'))
.toList();
return filtertaskCat;
}
and this is my listview code:
FutureBuilder(
future: controller.showCategoryInHome(),
builder: (context, snapshot) {
Future<List> test = controller.showCategoryInHome();
return ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: 11 , // here currently I set the fix value but I want the length of my list
itemBuilder: (context, index) {
return TextButton(
onPressed: () {
},
child: Text(
test[index].[catName], // and here not working too bucouse the list is future
style: normalTextForCategory,
),
);
},
separatorBuilder:
(BuildContext context, int index) {
return const VerticalDivider(
width: 15,
color: Colors.transparent,
);
},
);
},
)
Try this:
FutureBuilder<List>(
future: controller.showCategoryInHome(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List data = snapshot.data ?? [];
return ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount:data.length,
itemBuilder: (context, index) {
return TextButton(
onPressed: () {},
child: Text(
data[index]['catName'],
style: normalTextForCategory,
),
);
},
separatorBuilder: (BuildContext context, int index) {
return const VerticalDivider(
width: 15,
color: Colors.transparent,
);
},
);
}
}
},
),
Inside your Future you have builder, where you can access to your future when it ready via snapshot.
I've added the precise type where to make the use of Futurebuilder easier.
Future<List<TaskCat>> showCategoryInHome() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
var taskCatBox = await Hive.openBox('taskCat');
var filtertaskCat = taskCatBox.values
.where(
(TaskCat) => TaskCat.userId == sharedPreferences.getInt('key'))
.toList();
return filtertaskCat;
}
And adjust your Futurebuilder to the following:
FutureBuilder<List<TaskCat>>(
future: controller.showCategoryInHome(),
builder: (context, snapshot) {
// Check if your future has finished
if(snapshot.hasData) {
// Here you have the result of your future, which is a List<TaskCat>
final tasks = snapshot.data;
return ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: tasks.length,
itemBuilder: (context, index) {
return TextButton(
onPressed: () {
},
child: Text(
tasks[index].catName,
style: normalTextForCategory,
),
);
},
separatorBuilder:
(BuildContext context, int index) {
return const VerticalDivider(
width: 15,
color: Colors.transparent,
);
},
);
} else {
// Here you can show e.g. a CircularProgressIndicator
return SizedBox();
}
},
)
I want to make a to-do list with task due date as an optional field, so I need to check if some tasks have dueDate and add it as a subtitle based on that. How can I check if a field exists inside a doc in a StreamBuilder?
class _TaskListState extends State<TaskList> {
var myStream;
#override
void initState() {
myStream = FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.snapshots();
super.initState();
}
...
void _updateTaskDesc(
dynamic currTask, String newDesc, DateTime newDate, TimeOfDay newTime) {
FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.doc(currTask['id'])
.update({
'desc': newDesc,
'dueDate': newDate.toString(),
'dueTime': newTime.toString(),
});
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: myStream,
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;
bool hasDateTime = ????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
return ListView.builder(
itemCount: docs.length,
itemBuilder: (ctx, index) {
final currTask = docs[index];
return InkWell(
highlightColor: Theme.of(context).secondaryHeaderColor,
splashColor: Theme.of(context).secondaryHeaderColor,
onLongPress: () {
showModalBottomSheet<dynamic>(
isScrollControlled: true,
context: context,
builder: (bCtx) {
FocusManager.instance.primaryFocus?.unfocus();
return TaskOptions(_updateTaskDesc,
() => _updateHasImage(docs[index]), currTask);
},
);
},
child: Dismissible(
direction: DismissDirection.startToEnd,
key: UniqueKey(),
onDismissed: (_) async {
FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.doc(currTask['id'])
.delete();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("${currTask['desc']} dismissed"),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
FirebaseFirestore.instance
.collection("tasks")
.doc(widget.uid)
.collection("mytasks")
.doc(currTask['id'])
.set({
"desc": currTask['desc'],
"id": currTask['id'],
"isDone": currTask['isDone'],
"hasImage": currTask['hasImage'],
});
try {
FirebaseFirestore.instance
.collection("tasks")
.doc(widget.uid)
.collection("mytasks")
.doc(currTask['id'])
.update({
"dueDate": currTask['dueDate'],
"dueTime": currTask['dueTime'],
});
} catch (e) {}
},
),
),
);
},
child: ListTile(
...
subtitle: Text(hasDateTime
? DateFormat('dd/MM')
.format(DateTime.parse(currTask['dueDate']))
: ''),
...
I saw that a containsKey('key') method works for some people but I get NoSuchMethod when I try that. What can I do?
The single document is just a normal Dart Map, so you can check if a key exists or not using containsKey method.
So you condition becomes the following:
bool hasDateTime = currTask.containsKey('dueDate`);
NOTE: In the question I can see that you are defining the condition in the wrong place which is outside the itemBuilder method in the ListView so that it is not item based and well not work because it does not make sense.
You can have it in this place:
...
itemBuilder: (ctx, index) {
final currTask = docs[index];
bool hasDateTime = currTask.containsKey('dueDate`);
return InkWell(
...
I want to fetch all the infinite list view data in parts, means Initially it load 10 data item and on scroll more it should fetch next 10 items.
I am fetching data from my Laravel api and in my Laravel api endpoint there is not option for per_page, so Please help me to integrate load more option of list view data.
Here is my List data.
List<Category> _categoryList = List<Category>();
CategoryService _categoryService = CategoryService();
bool isLoading = true;
#override
void initState() {
super.initState();
_getAllCategories();
}
_getAllCategories() async {
var categories = await _categoryService.getCategories();
var result = json.decode(categories.body);
result['data'].forEach((data) {
var model = Category();
model.id = data["id"];
model.name = data["categoryName"];
model.icon = data["categoryIcon"];
setState(() {
_categoryList.add(model);
isLoading = false;
});
});
}
And I am fetching all data in simple ListTile.
child: ListView.builder(
itemCount: //_categoryList.length,
itemBuilder: (context, index) {
return ListTile(
title: _categoryList.name
);
},
),
So I have a trick and I am using it in my project. What we need here is to load more data when we are at the end of the list. So to do that we can use the ListView.builder() only:
child: ListView.builder(
itemCount: _categoryList.length + 1,
itemBuilder: (context, index) {
if(index == _categoryList.length){
// loadMore();
// return Loading();
}
return ListTile(
title: _categoryList.name
);
}),
So what we are doing is that we have set _categoryList.length + 1 to the itemCount. If _categoryList.length was 10 then we will have 11 items in the ListView and the index range will be 0 - 10. So inside the builder, we are checking that the index is equals to _categoryList.length which is 10. If the index is equals to the length of _categoryList.length, then we are at the end of the List and we can simply call some functions to load more data from Api or to show a loading widget.
I guess I got your question right, in this way you can simply lazy load data when user gets to the end of the List without using any third party libraries.
This process called pagination. Laravel provides a function for it check Pagination in Laravel
For example
$users = DB::table('users')->paginate(15);// 15 is limit per page
And check it too Paging lib in flutter dev.
I have created a sample to load more data using web service
You can see this example and you can implement for you json data.
https://android-pratap.blogspot.com/2018/12/flutter-infinite-listview-using.html
import 'package:akeepo/randomuser_infinitelist.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: InfiniteUsersList(),
);
}
}
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class InfiniteUsersList extends StatefulWidget {
static String tag = 'users-page';
#override
State<StatefulWidget> createState() {
return new _InfiniteUsersListState();
}
}
class _InfiniteUsersListState extends State<InfiniteUsersList> {
List<User> users = new List<User>();
ScrollController _scrollController = new ScrollController();
bool isPerformingRequest = false;
int pageNumber = 0;
#override
void initState() {
super.initState();
// Loading initial data or first request to get the data
_getMoreData();
// Loading data after scroll reaches end of the list
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_getMoreData();
}
});
}
// to show progressbar while loading data in background
Widget _buildProgressIndicator() {
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
child: new Opacity(
opacity: isPerformingRequest ? 1.0 : 0.0,
child: new CircularProgressIndicator(),
),
),
);
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// Webservice request to load 20 users data using paging
Future<List<User>> _getUsers() async {
List<User> users = new List<User>();
setState(() {
pageNumber++;
});
String url =
"https://api.randomuser.me/?page=$pageNumber&results=20&seed=abc";
print(url);
var response = await http.get(url);
var jsonData = json.decode(response.body);
print(jsonData);
var usersData = jsonData["results"];
for (var user in usersData) {
User newUser = User(user["name"]["first"] + user["name"]["last"],
user["email"], user["picture"]["large"], user["phone"]);
users.add(newUser);
}
return users;
}
_getMoreData() async {
if (!isPerformingRequest) {
setState(() {
isPerformingRequest = true;
});
List<User> newEntries = await _getUsers(); //returns empty list
if (newEntries.isEmpty) {
double edge = 50.0;
double offsetFromBottom = _scrollController.position.maxScrollExtent -
_scrollController.position.pixels;
if (offsetFromBottom < edge) {
_scrollController.animateTo(
_scrollController.offset - (edge - offsetFromBottom),
duration: new Duration(milliseconds: 500),
curve: Curves.easeOut);
}
}
setState(() {
users.addAll(newEntries);
isPerformingRequest = false;
});
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Users',
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.bold))),
body: Container(
child: ListView.builder(
shrinkWrap: true,
controller: _scrollController,
itemCount: users.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == users.length) {
return _buildProgressIndicator();
} else {
return ListTile(
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) =>
UserDetailPage(users[index])));
},
title: Text(users[index].fullName),
subtitle: Text(users[index].mobileNumber),
leading: CircleAvatar(
backgroundImage: NetworkImage(users[index].imageUrl)),
);
}
})),
);
}
}
class User {
final String fullName;
final String email;
final String imageUrl;
final String mobileNumber;
User(this.fullName, this.email, this.imageUrl, this.mobileNumber);
}
// User Detail Page
class UserDetailPage extends StatelessWidget {
final User user;
UserDetailPage(this.user);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("User Details"),
),
body: Center(
child: Text(
user.fullName,
style: TextStyle(fontSize: 35.0),
),
),
);
}
}
I have a graph view my app, whenever my function works it's also rebuilding
how to control my UI
var response = await getdashboarddata(tokenkey);
setState(() {
});
use FutureBuilder
Widget projectWidget() {
return FutureBuilder(
builder: (context, projectSnap) {
if (projectSnap.connectionState == ConnectionState.none &&
projectSnap.hasData == null) {
//print('project snapshot data is: ${projectSnap.data}');
return Container();
}
return ListView.builder(
itemCount: projectSnap.data.length,
itemBuilder: (context, index) {
ProjectModel project = projectSnap.data[index];
return Column(
children: <Widget>[
// Widget to display the list of project
],
);
},
);
},
future: getdashboarddata(tokenkey),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ProjectList'),
),
body: projectWidget(),
);
}
I want to access all the reg_events for the currently logged in user. I have the following code right now
stream: Firestore.instance.collection('users').document(email).snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData){
return Text("Loading..");
}
return Center(
child: new Container(
child: new PageView.builder(
onPageChanged: (value) {
setState(() {
currentpage = value;
});
},
controller: controller,
itemCount: snapshot.data['reg_events'].length,
itemBuilder: (context, index) => builder(index, snapshot.data)),
),
);
}
),
The 'builder' is:
builder(int index, DocumentSnapshot document) {
return new AnimatedBuilder(
animation: controller,
builder: (context, child) {
double value = 1.0;
if (controller.position.haveDimensions) {
value = controller.page - index;
value = (1 - (value.abs() * .5)).clamp(0.0, 1.0);
}
return new Center(
child: new SizedBox(
height: Curves.easeOut.transform(value) * 200,
width: Curves.easeOut.transform(value) * 1000,
child: child,
),
);
},
child: new Card(
child: Text(document.data['reg_events'][0].toString(),
textAlign: TextAlign.center,
style: TextStyle(fontSize: 15, color: Colors.white),),
margin: const EdgeInsets.all(10.0),
color: index % 2 == 0 ? Colors.blue : Colors.red,
),
);
}
But it outputs "Instance of 'DocumentReference'". How do I access this Document Reference?
Maybe it not understand reg_event as List so try this,
stream: Firestore.instance.collection('users').document(email).snapshots(),
builder: (context, snapshot){
List regEvent = new List();
if(snapshot.hasData){
regEvent = snapshot.data['reg_events'];
}
if(!snapshot.hasData){
return Text("Loading..");
}
return Center(
child: new Container(
child: new PageView.builder(
onPageChanged: (value) {
setState(() {
currentpage = value;
});
},
controller: controller,
itemCount: regEvent.length,
itemBuilder: (context, index) {
print(regEvent[index]);
return builder(index, snapshot.data)),}
),
);
}
),
DocumentReference is like a pointer to one document. You can get the single document using .get method which returns Future<DocumentSnapshot>. Since you have an array of them, you can then use Streams to get a bunch of Futures.
List<DocumentReference> references = [ref, ref, ref];
var myStream = Stream.fromFutures(references.map((ref) => ref.get()).toList());
StreamBuilder(builder: ..., stream: myStream);
But...
Firestore has querying, so it should be better if you actually use it. You should be able to reference your reg_events like that:
Firestore.instance.collection('users').document("$email/reg_events").snapshots();
In this example a User object is created which contains a list of references of the entities (or events). This list is then passed to the DatabaseService class which returns a list of EntityDetails stream objects.
DatabaseService Class:
final CollectionReference entityCollection =
Firestore.instance.collection('entities');
final CollectionReference userCollection =
Firestore.instance.collection('user');
Stream<UserDetails> get userDetails {
return userCollection
.document(uid)
.snapshots()
.map(_userDetailsFromSnapshot);
}
UserDetails _userDetailsFromSnapshot(DocumentSnapshot snapshot) {
return UserDetails(
email: snapshot.data['email'],
phone: snapshot.data['phone'],
fname: snapshot.data['fname'],
lname: snapshot.data['lname'],
streetNr: snapshot.data['street_nr'],
city: snapshot.data['city'],
entities: List.from(snapshot.data['entities']));
}
List<Stream<EntityDetails>> getEntitiesFromDRList(
List<DocumentReference> entities) {
List<Stream<EntityDetails>> elist = new List();
entities.forEach((element) {
elist.add(element.snapshots().map((_entityDetailsFromSnapshot)));
});
return elist;
}
EntityDetails _entityDetailsFromSnapshot(DocumentSnapshot snapshot) {
return EntityDetails(
uid: uid,
name: snapshot.data['name'],
description: snapshot.data['description'],
type: snapshot.data['type'],
geoPoint: snapshot.data['geopoint'],
adressString: snapshot.data['adress_string'],
email: snapshot.data['email'],
phone: snapshot.data['phone'],
);}
Widget
stream: DatabaseService(uid: uid).userDetails,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserDetails userDetails = snapshot.data;
//Get the Streams
DatabaseService(uid: uid)
.getEntitiesFromDRList(userDetails.entities)
.forEach((element) {
element.listen((data) {
print("DataReceived: " + data.name);
}, onDone: () {
print("Task Done");
}, onError: (error) {
print("Some Error");
});
});
User-Object
class UserDetails {
final String email;
final String phone;
final String fname;
final String lname;
final String streetNr;
final String city;
final List<DocumentReference> entities;
UserDetails(
{this.email,
this.phone,
this.fname,
this.lname,
this.streetNr,
this.city,
this.entities});
}