I am saving a list of profiles in sqflite and I am trying to make my list reorderable. In my Helper class I have these methods:
static Future insertProfile(Map<String, dynamic> profile) async {
await db.insert('Profiles', profile);
}
static Future deleteProfile(int id) async {
await db.delete('Profiles', where: 'id = ?', whereArgs: [id]);
}
static Future updateProfile(Map<String, dynamic> profile) async {
await db.update('Profiles', profile,
where: 'id = ?', whereArgs: [profile['id']]);
}
My profiles list looks like this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Profiles')),
body: FutureBuilder(
future: ProfileProvider.getProfileList(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
var profiles = snapshot.data;
return snapshot.data == null
? Container()
: ReorderableListView(
children: List.generate(
profiles.length,
(index) {
return Dismissible(
key: ValueKey("value$index"),
background: slideBackground(),
onDismissed: (direction) {
ProfileProvider.deleteProfile(
profiles[index]['id']);
showSnackBar(
context, profiles[index]['title'], [index]);
},
I am trying to replicate this type of function, but with Sqlite:
void _onReorder(int oldIndex, int newIndex) {
setState(
() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final String item = profiles.removeAt(oldIndex);
profiles.insert(newIndex, item);
},
);
}
So far I have managed deleting successfully:
onReorder: (int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
setState(
() {
ProfileProvider.deleteProfile(profiles[oldIndex]['id']);
},
);
},
The problem is I can't find how to reinsert the same profile at the new index.
I tried many things but the latest is this:
var item = profiles[oldId]['id'];
ProfileProvider.deleteProfile(profiles[oldId]['id']);
ProfileProvider.insertProfile(item);
However, I get this error message :
type 'int' is not a subtype of type 'Map<String, dynamic>'
Any ideas?
Related
This question already has answers here:
What is a Future and how do I use it?
(6 answers)
Closed 10 months ago.
The idea
I want to display followers. the page take list of followers user id and then display their username.
Error
when I tried to I get an Error say type 'Future<dynamic>' is not a subtype of type 'Widget'
The issue in this line Text(user["username"]),
Code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class Following extends StatefulWidget {
final following ;
const Following({Key? key, required this.following}) : super(key: key);
#override
_FollowingState createState() => _FollowingState();
}
class _FollowingState extends State<Following> {
/*attribute*/
var following =[];
bool islouded = false;
var usersData= [];
#override
void initState() {
super.initState();
setState(() {
following = widget.following;
});
getFollowing();
}
void getFollowing() {
for(var user in following){
setState(() {
print(user);
// print(getUser(user));
usersData.add( getUser(user));
});
}
setState(() {
islouded = true;
});
}
getUser(uid)async{
try {
if (uid != null) {
var userSnap = await FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get();
var userData = userSnap.data()!;
// print(userSnap.data()!["username"].toString());
return userData;
}
}catch(e){
showSnackBar(context, e.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: !islouded?
const Center(
child: CircularProgressIndicator(),
):following.isNotEmpty?
Column(
children: [
for(var user in usersData)
Text(user["username"]),
],
):Text("No following yet!"),
);
}
}
Tried
I tried use FutureBuilder but I did not how to use it right because it return nothing. I believe I'm using it wrong.
the code as follow:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class Following extends StatefulWidget {
final following ;
const Following({Key? key, required this.following}) : super(key: key);
#override
_FollowingState createState() => _FollowingState();
}
class _FollowingState extends State<Following> {
/*attribute*/
var following =[];
bool islouded = false;
var usersData= [];
#override
void initState() {
super.initState();
setState(() {
following = widget.following;
});
getFollowing();
}
void getFollowing() {
for(var user in following){
setState(() {
print(user);
// print(getUser(user));
usersData.add( getUser(user));
});
}
setState(() {
islouded = true;
});
}
getUser(uid) async{
try {
if (uid != null) {
var userSnap = await FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get();
return userSnap;
// print(userSnap.data()!["username"].toString());
// return userData;
}
}catch(e){
print(e.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: !islouded?
const Center(
child: CircularProgressIndicator(),
):following.isNotEmpty?
Column(
children: [
for(var user in usersData)
FutureBuilder(
future: user,
builder: (context, snapshot){
switch(snapshot.connectionState){
case ConnectionState.none:
return Text("No following yet!");
case ConnectionState.active:
return Text("active");
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
print(user);//Instance of 'Future<dynamic>'
print(snapshot);//AsyncSnapshot<Object?>(ConnectionState.done, Instance of '_JsonDocumentSnapshot', null, null)
return Text("username");//i want to display username but getting different error
default:
return Text("No following yet");
}
}
)
// Text(user["username"]),
],
):Text("No following yet!"),
);
}}
Thank you for taking the time reading my question. I hope you have beautiful day like you <3
I feel this may be the culprit:
usersData.add( getUser(user));.
Try this instead: await usersData.add( getUser(user));.
As you call the async method getUser(user) async { ... } it returns a Future, and this Future gets added to the List not the user. This would explain the error complaining about an unexpected Future.
I am trying to read a list of objects from Realtime database in Firebase with no success so far.
I am following the official tutorial from Firecasts on the following link:
https://www.youtube.com/watch?v=sXBJZD0fBa4
Here is how the database looks like:
Here is the code I have written:
The following is the class I have created for the objects I will be reading from database:
class TestData {
final String name;
final String surname;
final String age;
TestData({
required this.name,
required this.surname,
required this.age,
});
factory TestData.fromRTDB(Map<dynamic, dynamic> data) {
return TestData(
name: data["Name"],
surname: data["Surname"],
age: data["Age"],
);
}
}
Here is how I try to read it from the database:
class TestDataGetter {
final _db = FirebaseDatabase.instance.ref();
Stream<List<TestData>> getTestDataStream() {
final csPostprocessedStream = _db.child("test_data/").onValue;
var streamToPublish = csPostprocessedStream.map((event) {
final testDataMap = Map<dynamic, dynamic>.from(
event.snapshot.value as Map<dynamic, dynamic>);
final testDataList = testDataMap.entries.map((element) {
return TestData.fromRTDB(Map<dynamic, dynamic>.from(element.value));
}).toList();
return testDataList;
});
return streamToPublish;
}
}
And here is the screen where I would like to show the data:
class TestDataScreen extends StatelessWidget {
const TestDataScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
StreamBuilder(
stream: TestDataGetter().getTestDataStream(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
print("Waiting");
return const CircularProgressIndicator(color: Colors.white);
} else if (snapshot.hasError) {
print("Error occured...");
return const CircularProgressIndicator(color: Colors.white);
} else {
final testDataList = snapshot.data as List<TestData>;
return Text(
testDataList[0].name + " / " + testDataList[1].name,
style: Theme.of(context).textTheme.headline3,
textAlign: TextAlign.center);
}
}),
],
),
);
}
}
But I can never see the data on the screen. The spinning wheel is the only thing I see and on the console I see the print out as "Waiting" (as I print out this text in the code above).
It gets stuck in if (!snapshot.hasData).
I am clueless after spending hours on this.
Try the following
!snapshot.hasData || snapshot.data.documents.isEmpty
The problem here is that snapshots() will also return a QuerySnapshot when the query returns no documents. Thus, you could expand your condition like this:
Here is what I have should help you
if (snapshot.hasError) {
//TODO: we need to make sure we caputer this error message snapshot.error
return const PlaceHolder(
url: 'assets/animations/404-error.json',
message: ErrorMessages.getDogsError,
);
} else if (snapshot.data == null || snapshot.data!.isEmpty) {
return const PlaceHolder(
url: 'assets/animations/no-dog-animation.json',
message: AppMessages.noDogMessage,
);
} else if (!snapshot.hasData) {
}
Your problem is that you most likely have an error.
The issue is, your error handling is actually never reached, because in case of an error, !hasData will still be true
You may want to execute the hasError condition before !hasData:
if (snapshot.hasError) {
return Text('error');
} else if (!snapshot.hasData) {
return Text('loading');
} else {
return Text('data');
}
Why im getting this error
[VERBOSE-2:ui_dart_state.cc(186)] Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'QueryDocumentSnapshot'
#0 _OpenallsinglehashtagsState.getusers
package:wichtigdenyady/homesearchingall/openalldocs.dart:90
<asynchronous suspension>
I dont know actually why im getting this error maybe anyone can help
Heres where the error throws
List<QueryDocumentSnapshot> _allResults = [];
var firestore = FirebaseFirestore.instance;
QuerySnapshot snapshots = await firestore.collection('videos').get();
for (var doc in snapshots.docs) {
_allResults.add(doc.data()["Hashtagsforallvideos"]);
}
I think maybe it comes from data() because this is dynamic? What I trying here is getting the array from firebase that calls Hashtagsforallvideos which every video in videos collection has.
If you need more information please leave a comment
This is my howle code
class Openallsinglehashtags extends StatefulWidget {
static const route = '/Openallsinglehashtags';
final TextEditingController searchinginput;
const Openallsinglehashtags({Key key, this.searchinginput}) : super(key: key);
#override
_OpenallsinglehashtagsState createState() => _OpenallsinglehashtagsState();
}
class _OpenallsinglehashtagsState extends State<Openallsinglehashtags> {
List <QueryDocumentSnapshot>_allResults = [];
List _resultsList = [];
Future resultsLoaded;
bool nosuerfound = false;
String searchresult;
#override
void initState() {
super.initState();
widget.searchinginput.addListener(_onsearchChanged);
setState(() {
nosuerfound = true;
});
}
#override
void dispose() {
widget.searchinginput.removeListener(_onsearchChanged());
super.dispose();
}
#override
void didChangeDependencies() {
widget.searchinginput.text;
resultsLoaded = getVideos();
super.didChangeDependencies();
}
_onsearchChanged() {
setState(() {
nosuerfound = false;
});
searchResults();
}
searchResults() {
var showResults = [];
if (widget.searchinginput.text != "") {
for (var tripsnapshot in _allResults) {
var title3 = DatbaseService.instance
.videosfromsnapshot(tripsnapshot)
.allhashtagsofeveryvideo
.toLowerCase();
if (title3.contains(widget.searchinginput.text.toLowerCase())) {
setState(() {
nosuerfound = true;
});
showResults.add(tripsnapshot);
}
}
} else {
setState(() {
nosuerfound = true;
});
showResults = List.from(_allResults);
}
setState(() {
_resultsList = showResults;
});
}
Future<List<String>> getVideos() async {
List<String> allVideoHastags = [];
var firestore = FirebaseFirestore.instance;
QuerySnapshot snapshots = await firestore.collection('videos').get();
for (QueryDocumentSnapshot videoSnapshot in snapshots.docs) {
List<String> videoHastags =
List.from(videoSnapshot.data()['Hashtagsforallvideos']);
allVideoHastags.addAll(videoHastags);
}
print(allVideoHastags);
_allResults= snapshots.docs;
searchResults();
return allVideoHastags;
}
#override
Widget build(BuildContext context) {
final user = Provider.of<Userforid>(context);
if (nosuerfound == true) {
return ListView.builder(
itemCount: _resultsList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: const EdgeInsets.all(10.0), // Add margin
child: InkWell(
onTap: () {
NavigationService.instance
.navigateToRoute(MaterialPageRoute(builder: (context) {
return Beitragvideo(
_resultsList[index].data()['Hashtagsforallvideos'],
_resultsList[index].data()['Hashtagsforallvideos'],
_resultsList[index].data()['Hashtagsforallvideos'],
);
}));
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
HighlightedMatchesText(
searchString: widget.searchinginput.text,
content:
_resultsList[index].data()['Hashtagsforallvideos']),
],
),
),
);
},
);
} else {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 30, 0, 0),
child: Center(
child: Container(
child: Text(
"No Hashtag found",
style: TextStyle(fontSize: 16),
)),
),
What I do is I using a searchfield where user can search hashtags. And that hashtags are from the arrays of each video . So what I want is when user typed one hashtags only showing him this hashtags that he enter and not all hashtags of one video and also not all hashtags of the howle list . and if he not searching I just add all hashtags and user can only search in that list if theres no hashtag found then I print no hashtags is found and if any hashtag of that list contains user input I show him these hashtags. HighlightedMatchesText is just a class where I bold these letters that contains user input .
I think we dicussed this on another StackOverflow question. It is not possible to accomplishe what you want. A List<QueryDocumentSnapshot> representing your hashtags because a QueryDocumentSnapshot can only represent a whole firestore document and not just parts/Fields of it.
That means you have 2 options:
Get the whole documents in a list and using QueryDocumentSnapshot like this:
(Your can still read the hastags from the snapshots)
Future<List<QueryDocumentSnapshot>> getVideos() async {
List<QueryDocumentSnapshot> _allResults = [];
QuerySnapshot snapshots = await _firestore.collection('videos').get();
for (QueryDocumentSnapshot videoSnapshot in snapshots.docs) {
_allResults.add(videoSnapshot);
}
return _allResults;
}
Get a List of Strings having all the hashtags without the QueryDocumentSnapshot like this:
Future<List<String>> getHashtags() async {
List<String> allVideoHastags = [];
QuerySnapshot snapshots = await _firestore.collection('videos').get();
for (QueryDocumentSnapshot videoSnapshot in snapshots.docs) {
List<String> videoHastags =
List.from(videoSnapshot.data()['Hashtagsforallvideos']);
allVideoHastags.addAll(videoHastags);
}
return allVideoHastags;
}
If remember correct from your previous question Hashtagsforallvideos was a List<String>?
If this is correct then the values being added to the _allResults will not be QueryDocumentSnapshot as defined by the assignment but rather String, hence the error, _allResults expects a Firebase QueryDocumentSnapshot, but you are adding Strings to the List.
Do let me know if this is true.
I am attempting to combine two streams using RxDart and display the stream within a ListView.
I modeled this technique based on this post How do I join data from two Firestore collections in Flutter?, however, when I run this code, I am returning an empty dataset.
Any help would be greatly appreciated!
import 'package:rxdart/rxdart.dart';
class _HomeScreenState extends State<HomeScreen> {
Stream<List<CombineStream>> _combineStream;
String currentUserId = 'id12345'
#override
void initState() {
super.initState();
_combineStream =
usersChatsRef.child(currentUserId).onValue.map((userChatStream) {
return (userChatStream.snapshot.value).map((f) {
Stream<UsersChats> uDetail =
f.map<UsersChats>((node) => UsersChats.fromMap(node));
Stream<Chat> sDetail = chatsDetailsRef
.child(f.key)
.onValue
.map<Chat>((node2) => Chat.fromMap(node2.snapshot.value));
return Rx.combineLatest2(
uDetail, sDetail, (u, s) => CombineStream(u, s));
});
}).switchMap((streams) {
return (streams.length) > 0
? streams.combineLatestList(streams)
: streams.just([]);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: StreamBuilder(
stream: _combineStream,
builder:
(BuildContext context, AsyncSnapshot<List<CombineStream>> snap) {
if (snap.hasData && !snap.hasError) {
return ListView.builder(
itemCount: snap.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snap.data[index].combineUserChats.id),
subtitle: Text(myChats[index].combineUserChats.id),
);
},
);
} else {
return Center(child: Text("No data"));
}
},
),
);
}
}
class CombineStream {
final UsersChats combineUserChats;
final Chat combineChat;
CombineStream(this.combineUserChats, this.combineChat);
}
class Chat {
String id;
String name;
Chat({String id, String name}) {
this.id = id;
this.name = name;
}
factory Chat.fromMap(Map data) {
return Chat(
name: data['name'] ?? '');
}
}
class UsersChats {
String id;
String lastUpdate;
UsersChats(
{this.id,
this.lastUpdate});
factory UsersChats.fromMap(MapEntry<dynamic, dynamic> data) {
return UsersChats(
id: data.key ?? '',
lastUpdate: data.value['lastUpdate'] ?? '');
}
}
I am trying to paginate in Flutter with firebase realtime databse.
I have tried this in Firestore and it works fine there but I want this with realtime database.
I am fetching data for the first time like this.
Widget buildListMessage() {
return Flexible(
child: StreamBuilder(
stream: _firebase.firebaseDB
.reference()
.child("chats")
.child("nsbcalculator")
.orderByChild('timestamp')
.limitToFirst(15)
.onValue,
builder: (context, AsyncSnapshot<Event> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(themeColor)));
} else {
if (snapshot.data.snapshot.value != null) {
listMessage = Map.from(snapshot.data.snapshot.value)
.values
.toList()
..sort(
(a, b) => a['timestamp'].compareTo(b['timestamp']));
if (lastVisible == null) {
lastVisible = listMessage.last;
listMessage.removeLast();
}
}
return ListView.builder(
...
);
}
},
),
);
}
After that to paginate I am using a listener with ScrollController
void _scrollListener() async {
if (listScrollController.position.pixels ==
listScrollController.position.maxScrollExtent) {
_fetchMore();
}
}
and finally
_fetchMore() {
_firebase.firebaseDB
.reference()
.child("chats")
.child("nsbcalculator")
.orderByChild('timestamp')
.startAt(lastVisible['timestamp'])
.limitToFirst(5)
.once()
.then((snapshot) {
List snapList = Map.from(snapshot.value).values.toList()
..sort((a, b) => a['timestamp'].compareTo(b['timestamp']));
if (snapList.isNotEmpty) {
print(snapList.length.toString());
if (!noMore) {
listMessage.removeLast();
//Problem is here.....??
setState(() {
listMessage..addAll(snapList);
});
lastVisible = snapList.last;
print(lastVisible['content']);
}
if (snapList.length < 5) {
noMore = true;
}
}
});
}
Its working fine as realtime communication but when I try to paginate in _fetchMore() setState is called but it refreshes the state of whole widget and restarts the StreamBuilder again and all data is replaced by only new query. How can I prevent this??
Calling setState will redraw your whole widget and your list view. Now, since you supplying the steam that provides the first page, after redraw it just loads it. To avoid that you could use your own stream and supply new content to it. Then your StreamBuilder will handle the update automatically.
You need to store the full list of your items as a separate variable, update it and then sink to your stream.
final _list = List<Event>();
final _listController = StreamController<List<Event>>.broadcast();
Stream<List<Event>> get listStream => _listController.stream;
#override
void initState() {
super.initState();
// Here you need to load your first page and then add to your stream
...
_list.addAll(firstPageItems);
_listController.sink.add(_list);
}
#override
void dispose() {
super.dispose();
}
Widget buildListMessage() {
return Flexible(
child: StreamBuilder(
stream: listStream
...
}
_fetchMore() {
...
// Do your fetch and then just add items to the stream
_list.addAll(snapList);
_listController.sink.add(_list);
...
}
Try this one
pagination for RealTime list
class FireStoreRepository {
final CollectionReference _chatCollectionReference =
Firestore.instance.collection('Chat');
final StreamController<List<ChatModel>> _chatController =
StreamController<List<ChatModel>>.broadcast();
List<List<ChatModel>> _allPagedResults = List<List<ChatModel>>();
static const int chatLimit = 10;
DocumentSnapshot _lastDocument;
bool _hasMoreData = true;
Stream listenToChatsRealTime() {
_requestChats();
return _chatController.stream;
}
void _requestChats() {
var pagechatQuery = _chatCollectionReference
.orderBy('timestamp', descending: true)
.limit(chatLimit);
if (_lastDocument != null) {
pagechatQuery =
pagechatQuery.startAfterDocument(_lastDocument);
}
if (!_hasMoreData) return;
var currentRequestIndex = _allPagedResults.length;
pagechatQuery.snapshots().listen(
(snapshot) {
if (snapshot.documents.isNotEmpty) {
var generalChats = snapshot.documents
.map((snapshot) => ChatModel.fromMap(snapshot.data))
.toList();
var pageExists = currentRequestIndex < _allPagedResults.length;
if (pageExists) {
_allPagedResults[currentRequestIndex] = generalChats;
} else {
_allPagedResults.add(generalChats);
}
var allChats = _allPagedResults.fold<List<ChatModel>>(
List<ChatModel>(),
(initialValue, pageItems) => initialValue..addAll(pageItems));
_chatController.add(allChats);
if (currentRequestIndex == _allPagedResults.length - 1) {
_lastDocument = snapshot.documents.last;
}
_hasMoreData = generalChats.length == chatLimit;
}
},
);
}
void requestMoreData() => _requestChats();
}
ChatListView
class ChatView extends StatefulWidget {
ChatView({Key key}) : super(key: key);
#override
_ChatViewState createState() => _ChatViewState();
}
class _ChatViewState extends State<ChatView> {
FireStoreRepository _fireStoreRepository;
final ScrollController _listScrollController = new ScrollController();
#override
void initState() {
super.initState();
_fireStoreRepository = FireStoreRepository();
_listScrollController.addListener(_scrollListener);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Flexible(
child: StreamBuilder<List<ChatModel>>(
stream: _fireStoreRepository.listenToChatsRealTime(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
controller: _listScrollController,
shrinkWrap: true,
reverse: true,
itemBuilder: (context, index) {
...
}
);
}
)
),
);
}
void _scrollListener() {
if (_listScrollController.offset >=
_listScrollController.position.maxScrollExtent &&
!_listScrollController.position.outOfRange) {
_fireStoreRepository.requestMoreData();
}
}
}
ChatModel Class
class ChatModel {
final String userID;
final String message;
final DateTime timeStamp;
ChatModel({this.userID, this.message, this.timeStamp});
//send
Map<String, dynamic> toMap() {
return {
'userid': userID,
'message': message,
'timestamp': timeStamp,
};
}
//fetch
static ChatModel fromMap(Map<String, dynamic> map) {
if (map == null) return null;
return ChatModel(
userID: map['userid'],
message: map['message'],
timeStamp: DateTime.fromMicrosecondsSinceEpoch(map['timestamp'] * 1000),
);
}
}