Flutter Future returns null - dart

I've seen this already: https://stackoverflow.com/a/49146503/1757321
Followed the solution, but it is not working in my case.
Some enlightenment would do for me this afternoon
Future<String> loadInterest() async {
print('Going to load interests');
final whenDone = new Completer();
SharedPreferences prefs = await SharedPreferences.getInstance();
final token = await prefs.getString('token');
print('The token ${token}');
await this.api.interests(token).then((res) {
// print('The response: ${res['interests']}'); <-- this prints response alright. Data is coming.
whenDone.complete(res['interests']);
});
return whenDone.future;
}
Then I'm trying to use the above Future in a future builder like so:
new FutureBuilder(
future: loadInterest(),
builder: (BuildContext context, snapshot) {
return snapshot.connectionState == ConnectionState.done
? new Wrap(
children: InterestChips(snapshot.data),
)
: Center(child: CircularProgressIndicator());
},
),
Where InterestChips(...) is this:
InterestChips(items) {
print('Interest Chips ${items}');
List chipList;
for (Object item in items) {
chipList.add(Text('${item}'));
}
return chipList;
}
But, I always get null as the snapshot, which means the loadInterest() for the Future is not returning anything.
If I understand this answer correctly, then I'm doing mine along the lines of that: https://stackoverflow.com/a/49146503/1757321

You don't need to use a Completer for this. Since your method is already async, you should just do this for your first code block:
Future<String> loadInterest() async {
print('Going to load interests');
final whenDone = new Completer();
SharedPreferences prefs = await SharedPreferences.getInstance();
final token = await prefs.getString('token');
print('The token ${token}');
final res = await this.api.interests(token).then((res) {
// print('The response: ${res['interests']}'); <-- this prints response alright. Data is coming.
return res['interests']);
}
You might also want to check for snapshot.hasError to make sure you're not getting any exceptions in there.

Related

Hydrated Bloc not persisting

I'm trying to get my state to persist using hydrated bloc but it is not working. When i restart the app the state is not persisting
This is the code i have to start the app:
void bootstrap() async {
WidgetsFlutterBinding.ensureInitialized();
final storage = await HydratedStorage.build(
storageDirectory: await getApplicationDocumentsDirectory(),
);
HydratedBlocOverrides.runZoned(
() => runApp(
RepositoryProvider<void>(
create: (context) => DatabaseCubit(),
child: const RunApp(),
),
),
storage: storage,
);
}
this is the relevent code in the cubit:
class DatabaseCubit extends HydratedCubit<DatabaseState>{
DatabaseCubit() : super(databaseInitial);
#override
DatabaseState? fromJson(Map<String, dynamic> json) {
return DatabaseState.fromMap(json);
}
#override
Map<String, dynamic> toJson(DatabaseState state) {
return state.toMap();
}
I have set up unit tests that make sure my toMap and fromMap functions are working. The tests are passing, here is the code for them:
test('Database state should be converted to and from json', () {
final databaseStateAsJson = databaseState.toMap();
final databaseStateBackToNormal =
DatabaseState.fromMap(databaseStateAsJson);
expect(databaseStateBackToNormal, databaseState);
});
Please tell me what i am doing wrong
I've just solved a similar problem.
Try digging a little into hydrated_bloc.dart hydrate() method and you might find out why it refuses to load your state :).
Could be type conversion problem between collections - as it was in my case.

How to add two number fetched from SharedPreferences in Flutter

I was trying to add two numbers say point1 and point2. These points are stored in SharedPreferences .
I have fetched the points using a function Future<int> fetchPoints which is in below.
Then I called this from another function
fetchPoints:
Future<int> fetchFromSps(String field) async {
SharedPreferences sp = await SharedPreferences.getInstance();
return sp.getInt(field);
}
GetPoints:
Future<void> setPoints() async{
int _newPoints=await ((await fetchFromSps('point1'))+(await fetchFromSps('point2')));
setState(() {
_totalPoints=_newPoints.toString();
});
}
setInSharedPreference:
void setInSharedPreference() async{
SharedPreferences prefs=await SharedPreferences.getInstance();
prefs.setInt('point1', 0);
prefs.setInt('point2',0);
}
The function setInSharedPreference is in another dart file,which contains main function
I need to add two points which is named 'point1 and 'point2' from shared preference
just call them from any method like this sample:
fetchTwoPoints() async {
final point1 = await fetchFromSps("point1");
final point2 = await fetchFromSps("point2");
setState(() {
_totalPoints= (point1 + point2).toString();
});
}
Update your setInSharedPreference method because you are using async you need to wait to store the data.
Future<void> setInSharedPreference() async{
SharedPreferences prefs=await SharedPreferences.getInstance();
await prefs.setInt('point1', 0);
await prefs.setInt('point2',0);
}

Flutter Await Callback Not Give Any Response

I am still a beginner on dart flutter, now I am trying to retrieve data from the REST API and socket.IO. at this time I have a confusing problem, I have tried searching on the internet for 3 days, but there is no solution. I have async and await scripts, but the function I added await doesn't give any response and still pause.
it is assumed that I have two different files, the first is the main file and the second is the helper file.
main.dart
Future<List<ChatTile>> fetchChat(socketutil,id) async {
socketutil.join(id); //STACK IN HERE
SharedPreferences prefs = await SharedPreferences.getInstance();
String messagePrefs = prefs.getString('messagePrefs');
print("DUA");
return await compute(parseListChat, messagePrefs);
}
helper.dart
Future<void> join(String id_room) async {
String jsonData ='{"room_id" : "$id_room","user_id" : "5a91687811138e74009839c9","user_name" : "Denis Muhammad Ramdan","user_photo" : "photo.jpg","user_status" : "1"}';
socketIO.sendMessage("join", jsonData, null);
//subscribe event
return await socketIO.subscribe("updateMessageList", (result) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('messagePrefs', result);
print('SATU');
return await result;
});
}
my question is there something wrong with my code, and how is the best way?
many thanks,
I suggest you to add await_only_futures to your analyzer config
analysis_options.yaml
lint:
rules:
- await_only_futures
You also don't need to do return await something since your function already return a future, this is redondant.
And from what I see of the socketio subscribe method, it does not return the result like you expect but use a callback and does not return it (https://pub.dartlang.org/documentation/flutter_socket_io/latest/flutter_socket_io/SocketIO/subscribe.html)
to handle this you should use a Completer
final completer = Completer<String>()
socketIO.subscribe("updateMessageList", (result) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('messagePrefs', result);
socketIO.unSubscribe("updateMessageList");
completer.complete(result);
});
return completer.future;
you probably want to handle error when there is using completer.completeError(error)
Update
You can alos convert the subscription to a Dart Stream to handle more case.
StreamController<String> controller;
Stream<String> get onUpdateMessageList {
if (controller != null) return controller.stream;
constroller = StreamController<String>.broadcast(
onCancel: () => socketIO.unSubscribe("updateMessageList"),
);
socketIO.subscribe("updateMessageList", constroller.add);
return controller.stream;
}
Future<StreamSubscription> join(String id_room) async {
...
return onUpdateMessageList.listen((result) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('messagePrefs', result);
});
}

When should I use a FutureBuilder?

I was wondering when I should use the future builder. For example, if I want to make an http request and show the results in a list view, as soon as you open the view, should I have to use the future builder or just build a ListViewBuilder like:
new ListView.builder(
itemCount: _features.length,
itemBuilder: (BuildContext context, int position) {
...stuff here...
}
Moreover, if I don't want to build a list view but some more complex stuff like circular charts, should I have to use the future builder?
Hope it's clear enough!
FutureBuilder removes boilerplate code.
Let's say you want to fetch some data from the backend on page launch and show a loader until data comes.
Tasks for ListBuilder:
Have two state variables, dataFromBackend and isLoadingFlag
On launch, set isLoadingFlag = true, and based on this, show loader.
Once data arrives, set data with what you get from backend and set isLoadingFlag = false (inside setState obviously)
We need to have a if-else in widget creation. If isLoadingFlag is true, show the loader else show the data. On failure, show error message.
Tasks for FutureBuilder:
Give the async task in future of Future Builder
Based on connectionState, show message (loading, active(streams), done)
Based on data(snapshot.hasError), show view
Pros of FutureBuilder
Does not use the two state variables and setState
Reactive programming (FutureBuilder will take care of updating the view on data arrival)
Example:
FutureBuilder<String>(
future: _fetchNetworkCall, // async work
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return Text('Loading....');
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return Text('Result: ${snapshot.data}');
}
},
)
Performance impact:
I just looked into the FutureBuilder code to understand the performance impact of using this.
FutureBuilder is just a StatefulWidget whose state variable is _snapshot
Initial state is _snapshot = AsyncSnapshot<T>.withData(ConnectionState.none, widget.initialData);
It is subscribing to future which we send via the constructor and update the state based on that.
Example:
widget.future.then<void>((T data) {
if (_activeCallbackIdentity == callbackIdentity) {
setState(() {
_snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
});
}
}, onError: (Object error) {
if (_activeCallbackIdentity == callbackIdentity) {
setState(() {
_snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error);
});
}
});
So the FutureBuilder is a wrapper/boilerplate of what we do typically, hence there should not be any performance impact.
FutureBuilder Example
When you want to rander widget after async call then use FutureBuilder()
class _DemoState extends State<Demo> {
#override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: downloadData(), // function where you call your api
builder: (BuildContext context, AsyncSnapshot<String> snapshot) { // AsyncSnapshot<Your object type>
if( snapshot.connectionState == ConnectionState.waiting){
return Center(child: Text('Please wait its loading...'));
}else{
if (snapshot.hasError)
return Center(child: Text('Error: ${snapshot.error}'));
else
return Center(child: new Text('${snapshot.data}')); // snapshot.data :- get your object which is pass from your downloadData() function
}
},
);
}
Future<String> downloadData()async{
// var response = await http.get('https://getProjectList');
return Future.value("Data download successfully"); // return your response
}
}
In future builder, it calls the future function to wait for the result, and as soon as it produces the result it calls the builder function where we build the widget.
AsyncSnapshot has 3 state:
connectionState.none = In this state future is null
connectionState.waiting = [future] is not null, but has not yet completed
connectionState.done = [future] is not null, and has completed. If the future completed successfully, the [AsyncSnapshot.data] will be set to the value to which the future completed. If it completed with an error, [AsyncSnapshot.hasError] will be true
FutureBuilder is a Widget that will help you to execute some asynchronous function and based on that function’s result your UI will update.
I listed some use cases, why you will use FutureBuilder?
If you want to render widget after async task then use it.
We can handle loading process by simply using ConnectionState.waiting
Don't need any custom error controller. Can handle error simply dataSnapshot.error != null
As we can handle async task within the builder we do not need any setState(() { _isLoading = false; });
When we use the FutureBuilder widget we need to check for future state i.e future is resolved or not and so on. There are various State as follows:
ConnectionState.none: It means that the future is null and initialData is used as defaultValue.
ConnectionState.active: It means the future is not null but it is not resolved yet.
ConnectionState.waiting: It means the future is being resolved, and we will get the result soon enough.
ConnectionState.done: It means that the future has been resolved.
A simple implementation
Here OrdersProvider is a provider class and fetchAndSetOrders() is the method of that provider class.
body: FutureBuilder(
future: Provider.of<OrdersProvider>(context, listen: false)
.fetchAndSetOrders(),
builder: (context, dataSnapshot) {
if (dataSnapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
if (dataSnapshot.error != null) {
return Center(
child: Text('An error occured'),
);
} else {
return Consumer<OrdersProvider>(
builder: (context, orderData, child) => ListView.builder(
itemCount: orderData.orders.length,
itemBuilder: (context, i) => OrderItem(orderData.orders[i]),
),
);
}
}
},
),

Flutter Load Image from Firebase Storage

I see there are a lot of examples on how to upload an image using flutter to firebase storage but nothing on actually downloading/reading/displaying one that's already been uploaded.
In Android, I simply used Glide to display the images, how do I do so in Flutter? Do I use the NetworkImage class and if so, how do I first get the url of the image stored in Storage?
To view the images inside your storage, what you need is the name of the file in the storage. Once you've the file name for the specific image you need.
In my case if i want the testimage to be loaded,
final ref = FirebaseStorage.instance.ref().child('testimage');
// no need of the file extension, the name will do fine.
var url = await ref.getDownloadURL();
print(url);
Once you've the url,
Image.network(url);
That's all :)
New alternative answer based on one of the comments.
I don't see anywhere google is charging extra money for downloadURL.
So if you're posting some comments please attach a link to it.
Once you upload a file to storage, make that filename unique and save that name somewhere in firestore, or realtime database.
getAvatarUrlForProfile(String imageFileName) async {
final FirebaseStorage firebaseStorage = FirebaseStorage(
app: Firestore.instance.app,
storageBucket: 'gs://your-firebase-app-url.com');
Uint8List imageBytes;
await firebaseStorage
.ref()
.child(imageFileName)
.getData(100000000)
.then((value) => {imageBytes = value})
.catchError((error) => {});
return imageBytes;
}
Uint8List avatarBytes =
await FirebaseServices().getAvatarUrlForProfile(userId);
and use it like,
MemoryImage(avatarBytes)
update
In newer versions use
await ref.getDownloadURL();
See How to get full downloadUrl from UploadTaskSnapshot in Flutter?
original
someMethod() async {
var data = await FirebaseStorage.instance.ref().child("foo$rand.txt").getData();
var text = new String.fromCharCodes(data);
print(data);
}
see Download an image from Firebase to Flutter
or
final uploadTask = imageStore.putFile(imageFile);
final url = (await uploadTask.future).downloadUrl;
In the later case you'd need to store the downloadUrl somewhere and then use NetworkImage or similar to get it rendered.
Here's an example of a stateful widget that loads an image from Firebase Storage object and builds an Image object:
class _MyHomePageState extends State<MyHomePage> {
final FirebaseStorage storage = FirebaseStorage(
app: Firestore.instance.app,
storageBucket: 'gs://my-project.appspot.com');
Uint8List imageBytes;
String errorMsg;
_MyHomePageState() {
storage.ref().child('selfies/me2.jpg').getData(10000000).then((data) =>
setState(() {
imageBytes = data;
})
).catchError((e) =>
setState(() {
errorMsg = e.error;
})
);
}
#override
Widget build(BuildContext context) {
var img = imageBytes != null ? Image.memory(
imageBytes,
fit: BoxFit.cover,
) : Text(errorMsg != null ? errorMsg : "Loading...");
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new ListView(
children: <Widget>[
img,
],
));
}
}
Note that FirebaseApp initialization is handled by the Firestore class, so no further initialization code is necessary.
The way I did it to respect the Storage rules and keep the image in cache is downloading the image as a File and store in the device. Next time I want to display the image I just check if the file already exists or not.
This is my widget:
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
class FirebaseImage extends StatefulWidget {
final String storagePath;
FirebaseImage({
required this.storagePath,
}) : super(key: Key(storagePath));
#override
State<FirebaseImage> createState() => _FirebaseImageState();
}
class _FirebaseImageState extends State<FirebaseImage> {
File? _file;
#override
void initState() {
init();
super.initState();
}
Future<void> init() async {
final imageFile = await getImageFile();
if (mounted) {
setState(() {
_file = imageFile;
});
}
}
Future<File?> getImageFile() async {
final storagePath = widget.storagePath;
final tempDir = await getTemporaryDirectory();
final fileName = widget.storagePath.split('/').last;
final file = File('${tempDir.path}/$fileName');
// If the file do not exists try to download
if (!file.existsSync()) {
try {
file.create(recursive: true);
await FirebaseStorage.instance.ref(storagePath).writeToFile(file);
} catch (e) {
// If there is an error delete the created file
await file.delete(recursive: true);
return null;
}
}
return file;
}
#override
Widget build(BuildContext context) {
if (_file == null) {
return const Icon(Icons.error);
}
return Image.file(
_file!,
width: 100,
height: 100,
);
}
}
Note: The code can be improved to show a loading widget, error widget, etc.

Resources