In a scenario like the following, how to pass a variable List<Document> itemList by reference ?
Future.wait([futDocs]).then((dataRet){
dataRet.forEach((doco) {
var docList = doco.documents;
docList.forEach((doc){
var docTitle = doc['title'];
print("data is $docTitle");
itemList.add(docTitle); <--- change not reflected outside this function
The itemList does not change when itemList.add(docTitle) is performed in the above Future.wait(). I believe the reason is because itemList is not passed by reference. If I cannot pass itemList by reference, how can I make this work? Can I return dataRet as a list and use it outside Future.wait()?
Adding more info...
Above call is performed within the constructor of class ListBuilder as shown in the following code:
class ListBuilder {
List<Document> itemList = new List<Document>();
ListBuilder(){
var futDocs = Firestore.instance.collection('Data').orderBy('time').limit(10).getDocuments();
Future.wait([futDocs]).then((dataRet) {
dataRet.forEach((doco) {
var docList = doco.documents;
docList.forEach((doc){
var docTitle = doc['title'];
print("data is $docTitle");
itemList.add(docTitle); <--- change not reflected outside this function
[...]
While waiting for an answer, i have tried custom setters and getters as well as the use of this.itemList.add(...) but without success.
Any help, it is much appreciated.
Thanks in advance!
All objects in Dart are passed by reference.
The problem is likely [itemList] isn't updated immediately because Future.wait is an async function. Any code dealing with itemList has to run inside the callback function ( .then((dataret) { ... }); ).
class ListBuilder {
List<Document> itemList = new List<Document>();
ListBuilder() {
var Docs =
Firestore.instance.collection('Data').orderBy('time').limit(10).getDocuments();
Future.wait([futDocs]).then((dataRet) {
dataRet.forEach((doco) {
var docList = doco.documents;
docList.forEach((doc) {
var docTitle = doc['title'];
itemList.add(docTitle);
});
});
/*
itemList has been updated here because we are inside a callback function that
runs _after_ Future.wait has completed.
*/
});
/*
itemList hasn't been updated here _yet_ because this code runs immediately,
regardless how long the Future.wait call takes to complete.
*/
}
}
If you want you can use async/await logic instead of a callback function, which is often easier to read and maintain. More info here: https://www.dartlang.org/tutorials/language/futures
More code would be helpful, but the first things that come to mind are these:
It will probably work to call await before your Future.wait so that the asynchronous code is for sure run
This seems less likely, but you may have to call setState when adding your to your list
Edit:
I would suggest code like this:
class ListBuilder {
List<Document> itemList = new List<Document>();
Future<void> myFunc(var futDocs) async {
await Future.wait([futDocs]).then((dataRet){
[...] *Rest of your Future code here*
}
ListBuilder() {
var futDocs = Firestore.instance.collection('Data').orderBy('time').limit(10).getDocuments();
myFunc(futDocs)
[...]
I have solved the problem however it was not related to pass value by reference. Thank you for your answers, they actually got me thinking and pointed me in the right direction.
The following was the solution to a problem which is not the one raised above :D LOL
Tl;dr; answer:
Change the widget to use streamBuilder
Long answer
The problem was due to the fact that changes to a list were not reflected outside the .then() function. Well, the issue itself is not related to how the list was passed to the function but to the use of async/await logic. When the app is executed, it will populate the itemList in Widget with the current items in the list - this is empty when it starts. In the meanwhile, with an async call, firebase fetch the data and list is updated. However, the UI is not changed. Now, setState gave me an idea but was not the solution. In my case, I don't need a stateful Widget but rather the use of StreamBuilder within the creation of the widget. This bind the list to the async call with the use of stream
new StreamBuilder(
stream: Firestore.instance.collection('data').orderBy('time').limit(10).snapshots(),
builder: (context, snapshot) {
return itemList...
Thanks again!
Related
So i need to run some code that needs to get some data later.
I imagine it like:
voin runWithContext(void Function () fn, dynamic context) {
// ... magic
fn();
}
And later in the call stack of fn()
void otherFn() {
dynamic context = getContext();
}
If functions are not async we could just store the context as global variable, but the main requirement to support dart:async
I was looking into dart:mirrors and stacktrace but i can't find no way to bind some data.
You can do this with Zones.
Zones, and their "zone variables" are the canonical way to create a context that is preserved throughout an asynchronous computation).
Since you want single global function to access the context, it will have to be untyped (or typed at the one type that you need, but not generally reusable). That would me me want to reconsider the design, but if it works for you, I'd do it as:
import "dart:async";
/// Container for a mutable value.
class _Context {
dynamic value;
}
final _Context _rootContext = _Context();
R runWithNewContext<R>(R Function() body) =>
runZoned(body, zoneValues: {
#_context: _Context()..value = context,
});
dynamic get context => (Zone.current[#_context] ?? _rootContext).value;
set context(dynamic value) {
(Zone.current[#_context] ?? _rootContext).value = value;
}
If you don't need the mutability, you can simplify things a little, but not much.
The typed and unmodifable alternative is something like
class _Box { // Distinguishable from `null`, even if value is `null`
final Object? value;
_Box(this.value);
}
class ZoneStorage<T> {
final _Box _initialValue;
ZoneStorage(T initialValue) : _initialValue = _Box(initialValue);
R update<R>(T newValue, R Function() body) =>
runZoned(body, zoneValues: {this: _Box(newValue)});
T get value =>
(Zone.current[this] as _Box? ?? _initialValue).value as T;
}
That allows you to create multiple independent zone stores, like:
var zs1 = ZoneStorage<int>(1);
var zs2 = ZoneStorage<String>("yup");
zs1.update(42, () {
print(zs1.value);
print(zs2.value);
});
zs2.update("yup yup yup", () {
print(zs1.value);
print(zs2.value);
});
So the thing that is need is https://api.dart.dev/stable/2.17.3/dart-async/Zone-class.html
Hope that answer will help someone else
I'm trying to convert one of my functions that takes something from Firebase and converts it into a list of models. I noticed I keep using this logic so maybe the best way to refactor it was to use generics.
My original code:
Future<List<Guideline>> getList ({DatabaseReference query}) async {
DataSnapshot snap = await query.once();
Map<dynamic, dynamic> map = snap.value;
_myList = new List<MyModel>();
map.forEach((key, value) {
_myList.add(MyModel.fromJson(key: key, snapshot: value));
});
return _myList;
}
I tried to use this similar logic to create a generic version of it but I got an error while trying to make it work and I couldn't find a similar problem while searching here so I decided to ask out.
Future<List<T>> getList<T>({DatabaseReference query}) async {
DataSnapshot snap = await query.once();
//dynamic generates an error: The name 'dynamic' isn't a type so it can't be used as a type argument.
Map<dynamic, dynamic> json = snap.value;
return list;
}
If I hadn't gotten the error above I would continue my code as how I did with my first function except that fromJson would be a callback function so I could pass its implementation.
I want to get a current user location to update nearby stores based on latitude/longitude inside the url.
but I can't figure out how to interact data between two different class.
I want to make it work something like 'AppConfig.latitude = _position.latitude;'. I tried with several methods including inherited widget that I found on stackoverflow and youtube, but still don't work. It's definitely that I'm missing something.
when I use a bloc, I have no clue how to update data inside 'class AppConfig' with bloc. Can it be done simply using SetState? I spent the whole day yesterday Googling for this problem. please guide me to right approach
class _CurrentLocationState extends State<CurrentLocation> {
Position _position;
Future<void> _initPlatformState() async {
Position position;
try {
final Geolocator geolocator = Geolocator()
...
setState(() {
_position = position;
// print(${_position.latitude})
// 35.9341...
// print(${_position.longitude})
// -117.0912...
<*I want to make it work something like this*>
AppConfig.latitude = _position.latitude;
AppConfig.longitude = _position.longitude;
<*this is how I tried with bloc*>
latLongBloc.getUserLocation(LatLng(position.latitude, position.longitude));
});
<* latitude/longitude need to be updated with a current user location *>
abstract class AppConfig {
static const double latitude = 0;
static const double longitude = 0;
static const List<String> storeName = ['starbucks'];
}
<* I need to use AppConfig.latitude for url in Repository class*>
class Repository {
...
Future<List<Business>> getBusinesses() async {
String webAddress =
"https://api.yelp.com/v3/businesses/search?latitude=${AppConfig.latitude}&longitude=${AppConfig.longitude}&term=${AppConfig.storeName}";
...
}
this is my bloc.dart file
class LatLongBloc {
StreamController _getUserLocationStreamController = StreamController<LatLng>();
Stream get getUserLocationStream => _getUserLocationStreamController.stream;
dispose(){
_getUserLocationStreamController.close();
}
getUserLocation(LatLng userLatLong) {
_getUserLocationStreamController.sink.add(userLatLong);
}
}
final latLongBloc = LatLongBloc();
You want to share state between classes/widgets, right? There are also other state management patterns like ScopedModel or Redux. Each pattern has its pros and cons, but you don't have to use BLoC if you don't understand it.
I would recommend to use ScopedModel because it's quite easy to understand in my opinion. Your data/state is in a central place and can be accessed by using ScopedModel. If you don't like to use this approach then try Redux or other patterns :)
Hope it helped you :D
Yours Glup3
I like the await for construct in Dart.
How can I implement something similar with a regular for loop?
Something like
// beware! fictional code.
var element = stream.next();
for(; stream.isEndReached(); element = stream.next()) {
// use element here
}
// or probably it will be like this, right?
var element = await stream.next();
for(; await stream.isEndReached(); element = await stream.next()) {
// use element here
}
But I can't figure out what functions to use instead of next() and isEndReached() here. If you could give me a full example that acts exactly like async for, that would be great.
Edit: Here is the actual reason that I asked for this: I want to do something like this:
if (!stream.isEndReached()) {
var a = await stream.next();
// use a
}
if (!stream.isEndReached()) {
var b = await stream.next();
// use b
}
// have an arbitrary number of these
I need to consume items one by one like this. This is why I'm asking what my made up .next() and .isEndReached() methods map to which actual methods in the stream class.
The async package contains a StreamQueue class that might do what you want.
See also this great article http://news.dartlang.org/2016/04/unboxing-packages-async-part-3.html
StreamQueue provides a pull-based API for streams.
A code snipped from the article mentioned above
void main() async {
var queue = new StreamQueue(new Stream.fromIterable([1, 2, 3]));
var first = queue.next;
var second = queue.next;
var third = queue.next;
print(await Future.wait([first, second, third])); // => [1, 2, 3]
}
update
WebStorm (uses a feature of the dartanalyzer) doesn't provide quick fixes for imports when nothing was yet imported from that package. It doesn't read packages if they are not refered to in your source code. As mentioned in my answer StreamQueue is from the async package. import 'package:async/async.dart'; is usually enough (it's a convention to name the main entrypoint file (async.dart) of a package the same as the package) and all exported identifiers become available in your library. Otherwise you can search the source of your project and WebStorm will also search dependencies and show what library contains the StreamQueue class. Then you can import this file.
Have been seeing the term "Expando" used recently with Dart. Sounds interesting. The API did not provide much of a clue to me.
An example or two could be most helpful!
(Not sure if this is related, but I am most anxious for a way to add methods (getters) and/or variables to a class. Hoping this might be a key to solving this problem. (hint: I am using the Nosuchmethod method now and want to be able to return the value of the unfound method.))
Thanks in advance,
_swarmii
Just to clarify the difference between expando and maps: as reported in the groups, expando has weak references.
This means that a key can be garbage collected even if it's still present in the expando (as long as there are no other references to it).
For all other intents and purposes it's a map.
Expandos allow you to associate objects to other objects. One very useful example of this is an HTML DOM element, which cannot itself be sub-classed. Let's make a top-level expando to add some functionality to an element - in this case a Function signature given in the typedef statement:
typedef CustomFunction(int foo, String bar);
Expando<CustomFunction> domFunctionExpando = new Expando<CustomFunction>();
Now to use it:
main(){
// Assumes dart:html is imported
final myElement = new DivElement();
// Use the expando on our DOM element.
domFunctionExpando[myElement] = someFunc;
// Now that we've "attached" the function to our object,
// we can call it like so:
domFunctionExpando[myElement](42, 'expandos are cool');
}
void someFunc(int foo, String bar){
print('Hello. $foo $bar');
}
I played with it a little bit. Here's what I've got.
import 'dart:html';
const String cHidden = 'hidden';
class ExpandoElement {
static final Expando<ExpandoElement> expando =
new Expando<ExpandoElement>("ExpandoElement.expando");
final Element element;
const ExpandoElement._expand(this.element);
static Element expand(Element element) {
if (expando[element] == null)
expando[element] = new ExpandoElement._expand(element);
return element;
}
// bool get hidden => element.hidden; // commented out to test noSuchMethod()
void set hidden(bool hidden) {
if (element.hidden = hidden)
element.classes.add(cHidden);
else
element.classes.remove(cHidden);
}
noSuchMethod(InvocationMirror invocation) => invocation.invokeOn(element);
}
final Expando<ExpandoElement> x = ExpandoElement.expando;
Element xquery(String selector) => ExpandoElement.expand(query(selector));
final Element input = xquery('#input');
void main() {
input.classes.remove(cHidden);
assert(!input.classes.contains(cHidden));
input.hidden = true;
assert(x[input].hidden); // Dart Editor warning here, but it's still true
assert(!input.classes.contains(cHidden)); // no effect
input.hidden = false;
assert(!x[input].hidden); // same warning, but we'll get input.hidden via noSuchMethod()
assert(!input.classes.contains(cHidden));
x[input].hidden = true;
assert(input.hidden); // set by the setter of ExpandoElement.hidden
assert(input.classes.contains(cHidden)); // added by the setter
assert(x[input].hidden);
assert(x[input].classes.contains(cHidden)); // this is input.classes
x[input].hidden = false;
assert(!input.hidden); // set by the setter
assert(!input.classes.contains(cHidden)); // removed by the setter
assert(!x[input].hidden);
assert(!x[input].classes.contains(cHidden));
// confused?
assert(input is Element);
assert(x[input] is! Element); // is not
assert(x[input] is ExpandoElement);
assert(x is Expando<ExpandoElement>);
}