Flutter The return type 'Map<int, int>' isn't a 'void', as required by the closure's context - flutter

Hi i want to return a map from a FirebaseDatabase. But i get the Error Code:
The return type 'Map<int, int>' isn't a 'void', as required by the closure's context.
if i print the map i get the right result. Im new in Flutter and i dont get it why its doesnt work. I guess i need to change the method type, but how?
String u1= 'Backsquat';
Dataread(String u1);
DatabaseReference data = FirebaseDatabase.instance.reference();
FirebaseAuth auth = FirebaseAuth.instance;
Map? read() {
Map <int,int> werte;
data.child("Kraftwerte").child(auth.currentUser.uid).child(u1).onValue.listen((event) {
werte = event.snapshot.value;
print(werte);
return werte;
}); ```

The error comes from the fact that your stream is asynchronous, while your actual function is synchronous.
If all you want is to return event.snapshot.value for every item on your stream, you can do this:
Stream<Map?> read() {
return data.child("Kraftwerte").child(auth.currentUser.uid).child(u1).onValue.map<Map>((event) => event.snapshot.value);
}
If what you want is to get the first value of the stream:
Future<Map?> read() async {
final event = await data.child("krafwerte").child(auth.currentUser.uid).child(u1).onValue.first;
return event.snapshot.value as Map?;
}
Either way, your code must be async
example of using the code:
class MyWidget extends StatelessWidget {
#override
Widget buuld(BuildContext context) {
return FutureBuilder(
future: _read(),
builder: (context, snapshot) {
if (snapshot.hasError) return Text(snapshot.error!);
if (snapshot.hasData) return Text(snapshot.data!['key']); // snapshot.data is the map, it's null if the future is not done.
return CircularProgressIndicator();
}
);
}
Future<Map> _read() {
final event = await data.child("krafwerte").child(auth.currentUser.uid).child(u1).onValue.first;
return event.snapshot.value as Map?;
}
}
To better understand how the future builder widget works, please read the future builder docs

Related

Conver a stateLessWidget to function

I'm a new flutter developer.
I have a code to read data from firebase for one time
this code:
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>;
return Text("Full Name: ${data['full_name']} ${data['last_name']}");
}
return Text("loading");
},
);
}
}
it's work fine but I want to put these method into my Provider as function like this
Future<DocumentSnapshot> getUserName(String uid) => _database.doc(uid).snapshots();
so I want to put a function into provider class when I call this function it return a field data of this documents... (Replace GetUserName class as shown app, to be a function method only)
so how to write this function and how to call it as a map of data?
Edit:
as shown in this image:
here I got data as StreamBuilder and its work fine
here the explained method for stream in my provider class
as shown in the Following Image
Map<String, dynamic> data
I use data like
data['username']
it works fine so I want to put in My Provider class a function and returns a String, has two parameters for Example:
Text(myfunction(uid, value));
and it returns a string from (uid),
value = data[value]
add it before return HomePage();
auth.userData = data;
return HomePage();
add it in Auth provider
class Auth implements AuthBase {
Map<String,dynamic>? userData;
String getUserName(String parameter) {
return userData![parameter].toString();
}
}

Flutter how I will get Future<List<Placemark>> to <List<Placemark>?

I have a provider where a method , by this method if I send lat and long it will give me place name.
Future<List<Placemark>> getAndSetAddressFromLatLong(double startLat)async {
List<Placemark> placemarksStart = await placemarkFromCoordinates(startLat,startLong);
return placemarksStart;
}
So, When I'm trying to call and fetch the data in view file like below
#override
Widget build(BuildContext context) {
var data = Provider.of<MapProvider>(context).getAndSetAddressFromLatLong(
widget.history.startLat!.toDouble(),
widget.history.startLong!.toDouble(),
).then((value) => value);
print(data);
I'm getting the output I/flutter (25255): Instance of 'Future<List<Placemark>>' , But In then() if I print value without return I'm getting my desire list.
How I will get List<Placemark> here from Instance of 'Future<List>' ?
Since you're using provider call notifyListeners() after awaiting the results. In the widget use consumer to show the results
List<Placemark> _placemarksStart = [];
List<Placemark> get placemarksStart => [..._placemarksStart];
Future<void> getAndSetAddressFromLatLong(double startLat, double startLong) async {
_placemarksStart = await placemarkFromCoordinates(startLat,startLong);
notifyListeners();
}
Widget, similarly you can achieve loading with a boolean
Consumer<MyType>(
builder: (context, provider, child) {
if (provider.placemarksStart.isEmpty) {
return Center(child: Text('Loading...'),);
}
return ListView.builder(itemBuilder: (context, index) {
final item = provider.placemarksStart[index];
return Text("TODO");
}, itemCount: provider.placemarksStart.length,);
},
),
And call the method getAndSetAddressFromLatLong in the initState
late List<placemark> data;
#override
Widget build(BuildContext context) {
Provider.of<MapProvider>(context).getAndSetAddressFromLatLong(
widget.history.startLat!.toDouble(),
widget.history.startLong!.toDouble(),
).then((value){
data = value;
print(data);
}
);

How to set multiple StateNotifierProvider (s) with dynamicaly loaded async data?

I'm completely stuck with the task below.
So, the idea is to solve these steps using Riverpod
Fetch data from db with some kind of Future async while pausing the app (display SomeLoadingPage() etc.)
Once the data has loaded:
2.1 initialize multiple global StateNotifierProviders which utilize the data in their constructors and can further be used throughout the app with methods to update their states.
2.2 then show MainScreen() and the rest of UI
So far I've tried something like this:
class UserData extends StateNotifier<AsyncValue<Map>> { // just <Map> for now, for simplicity
UserData() : super(const AsyncValue.loading()) {
init();
}
Future<void> init() async {
state = const AsyncValue.loading();
try {
final HttpsCallableResult response =
await FirebaseFunctions.instance.httpsCallable('getUserData').call();
state = AsyncValue.data(response.data as Map<String, dynamic>);
} catch (e) {
state = AsyncValue.error(e);
}}}
final userDataProvider = StateNotifierProvider<UserData, AsyncValue<Map>>((ref) => UserData());
final loadingAppDataProvider = FutureProvider<bool>((ref) async {
final userData = await ref.watch(userDataProvider.future);
return userData.isNotEmpty;
});
class LoadingPage extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return FutureBuilder(
future: ref.watch(loadingAppDataProvider.future),
builder: (ctx, AsyncSnapshot snap) {
// everything here is simplified for the sake of a question
final Widget toReturn;
if (snap.connectionState == ConnectionState.waiting) {
toReturn = const SomeLoadingPage();
} else {
snap.error != null
? toReturn = Text(snap.error.toString())
: toReturn = const SafeArea(child: MainPage());
}
return toReturn;},);}}
I intentionally use FutureBuilder and not .when() because in future i may intend to use Future.wait([]) with multiple futures
This works so far, but the troubles come when I want to implement some kind of update() methods inside UserData and listen to its variables through the entire app. Something like
late Map userData = state.value ?? {};
late Map<String, dynamic> settings = userData['settings'] as Map<String, dynamic>;
void changeLang(String lang) {
print('change');
for (final key in settings.keys) {
if (key == 'lang') settings[key] = lang;
state = state.whenData((data) => {...data});
}
}
SomeLoadingPage() appears on each changeLang() method call.
In short:
I really want to have several StateNotifierProviders with the ability to modify their state from the inside and listen to it from outside. But fetch the initial state from database and make the intire app wait for this data to be fetched and these providers to be initilized.
So, I guess I figured how to solve this:
final futureExampleProvider = FutureProvider<Map>((ref) async {
final HttpsCallableResult response =
await FirebaseFunctions.instance.httpsCallable('getUserData').call();
return response.data as Map;
});
final exampleProvider = StateNotifierProvider<Example, Map>((ref) {
// we get AsyncValue from FutureNotifier
final data = ref.read(futureExampleProvider);
// and wait for it to load
return data.when(
// in fact we never get loading state because of FutureBuilder in UI
loading: () => Example({'loading': 'yes'}),
error: (e, st) => Example({'error': 'yes'}),
data: (data) => Example(data),
);
});
class LoadingPage extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return FutureBuilder(
// future: ref.watch(userDataProvider.future),
future: ref.watch(futureExampleProvider.future),
builder: (ctx, AsyncSnapshot snap) {
final Widget toReturn;
if (snap.data != null) {
snap.error != null
? toReturn = Text(snap.error.toString())
: toReturn = const SafeArea(child: MainPage());
} else {
// this is the only 'Loading' UI the user see before everything get loaded
toReturn = const Text('loading');
}
return toReturn;
},
);
}
}
class Example extends StateNotifier<Map> {
Example(this.initData) : super({}) {
// here comes initial data loaded from FutureProvider
state = initData;
}
// it can be used further to refer to the initial data, kinda like cache
Map initData;
// this way we can extract any parts of initData
late Map aaa = state['bbb'] as Map
// this method can be called from UI
void ccc() {
// modify and update data
aaa = {'someKey':'someValue'};
// trigger update
state = {...state};
}
}
This works for me, at least on this level of complexity.
I'll leave question unsolved in case there are some better suggestions.

My future method is not working fine, while using flutter builder widget, Where did I go wrong?

Here is my stateful widget and url is a property pass it to the widget from parent widget. I don't know where did I go wrong?? I created a future builder widget that has getData() as a future. But the print statement inside was not executed ever. Why is that and it returns me always null value, and this results me a red container appearing on screen and not the table widget.
class TimeTable extends StatefulWidget {
final url;
const TimeTable({Key? key,required this.url}) : super(key: key);
#override
_TimeTableState createState() => _TimeTableState();
}
class _TimeTableState extends State<TimeTable> {
Future<List<Train>> getData() async{
final list = await TrainClient(url: widget.url).getName();
print("this line not executed");
return list;
}
#override
Widget build(BuildContext context) {
return Scaffold(body: FutureBuilder(
future: getData(),
builder: (context,projectSnap){
if(projectSnap.connectionState == ConnectionState.none ||
projectSnap.data == null) {
return Container(color: Colors.red,);
}
return buildDataTable(trains: projectSnap.data);
}));
}
}
getData is a future method and it returns a list, The list gets printed when I call that object Train Client. I had my print statement inside TrainClient class to check whether the list is created successfully.
Here is the code of TrainClient
class TrainClient {
final String url;
TrainClient({required this.url});
Future<List<Train>> getName() async {
final uri = Uri.parse(url);
final response = await get(uri);
if (response.statusCode == 200) {
print("ulla");
final data = json.decode(response.body);
final result = data["RESULTS"]["directTrains"]["trainsList"];
final list = result.map((json) => Train.fromJson(json));
print(list);
return list;
}else{
throw Exception();
}
}
}
The TrainClient class has no error since it printed the list successfully as shown below
(Instance of 'Train', Instance of 'Train', Instance of 'Train', ..., Instance of 'Train', Instance of 'Train')
You should always obtain future earlier (in initState/didChangeDependencies).
Each time your build is executed, new future is created. So it never finishes, if your widget rebuilds often.
late final _dataFuture = getData();
...
FutureBuilder(
future: _dataFuture,
builder: (context,projectSnap){
...
}
);

Flutter error : The argument type 'List<Future<Widget>>' can't be assigned to the parameter type 'List<Widget>'

I'm trying to do a list of item from Firebase Firestore (this is done) and to get for each item a different image URL from Firebase Cloud Storage.
I use a function called getPhotoUrl to change the value of the variable photoUrl. The problem is that the return is executed before getPhotoUrl. If I add await in front of the function getPhotoUrl and async after _docs.map((document), I got an error saying that The argument type 'List<Future>' can't be assigned to the parameter type 'List'.
My code:
class PhotosList extends StatefulWidget {
#override
_PhotosListState createState() => _PhotosListState();
}
class _PhotosListState extends State<PhotosList> {
String photoUrl = 'lib/assets/default-image.png';
List<DocumentSnapshot> _docs;
getPhotoUrl(documentID) {
Reference ref = storage
.ref('Users')
.child(currentUser.uid)
.child('Photos')
.child(documentID)
.child('image_1.jpg');
ref.getDownloadURL().then((value) {
setState(() {
photoUrl = value.toString();
});
}).catchError((e) {
setState(() {
print(e.error);
});
});
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: firestore
.collection('Users')
.doc(currentUser.uid)
.collection('Photos')
.orderBy('date')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return CircularProgressIndicator();
_docs = snapshot.data.docs;
if (_docs.isEmpty)
return Center(
child: Text("The list is empty."));
return Container(
child: ResponsiveGridList(
desiredItemWidth: 100,
squareCells: true,
minSpacing: 5,
children: _docs.map((document) {
getPhotoUrl(document.id);
return PhotosListItem(photoUrl: photoUrl);
}).toList(),
),
);
},
);
}
}
I think you mix 2 different ways. In every build cicle you map your docs and request that photoUrl, but inside that method you call setState, which re-triggers your build method. That way you should end in infinite loop of getting photo url and building your widget.
You have three options:
Load your photoUrls and store them inside your widget -> call set state -> check inside your mapping function if your photo is loaded, if yes, take it, if no, call your getPhotoUrl function
Load your photoUrls synchronously and return url from your function and set it to your PhotosListItem
(I would prefer this) Add your documentId to your photosListItem in your mapping function and inside your item you load this photo url. In this PhotoListItem you have a variable with your imageUrl and in initState you call your getPhotoUrl function
Inside your PhotoItem:
String imageUrl;
#override
void initState() {
Future.delayed(Duration.zero, () {
setState(() {
// load your data and set it to your variable
imageUrl = ..
});
});
super.initState();
}
You might use a FutureBuilder because StreamBuilder seems to be synchronous :
How to convert Future<List> to List in flutter?
Thanks for your answers guys, actually I found an other solution which is to get and write the URL in Firestore directly after uploading the image on the Storage.
This is the article which helped me a lot : https://medium.com/swlh/uploading-images-to-cloud-storage-using-flutter-130ac41741b2
(PS: some of the Firebase names changed since this article but it's still helpful.)
Regards.