Flutter (dart) ListView Builder Widget giving Invalid Arguments Error - dart

So, I have created a basic map
var tracks = const [
{
'title':'Something',
'subtitle':'Something'
},
{
'title':'Something Else',
},
{
'title':'Admission',
},
{
'title':'University',
},
{
'title':'Exam',
'subtitle':'Something'
},
{
'title':'Job',
},
];
And this is being called by a ListView.builder in the following manner:
var trackTitles = tracks[index];
and then being used like this:
return PrimaryMail(
title: trackTitles['titles'],
)
But, it is throwing an "Invalid Argument(s): titles error in the final build. No dart-analytics issues seen. The only error I see (info, not serious) is This class (or a class which this class inherits from) is marked as '#immutable', but one or more of its instance fields are not final: Home.tracks (must_be_immutable] lib\main.dart:18). [edit: the immutable issue has been fixed now by replacing var with final, but the original problem remains]
So, any way to understand why it is throwing an invalid argument even though the title key does exist?
The full code of this page is here.

Add final before the field you declare in line lib\main.dart:18
If you have var there, replace var by final

Related

Dart: Maps nested in maps

I want to store various data for my app in a single place, in a map. In JS, I'd store in a JSON file, and I want to use the same sort of approach, but struggling with Dart. I can't seem to work with nested lists or maps.
Here's essentially what I want to do:
var items = {
"item1": {
"message" : "aa",
"nested1": {
"message": "bb",
"nested2" : {
"message" : "cc"
},
}
},
};
void main() {
var message1 = items["item1"]?["message"];
print(message1);
print(message1.runtimeType);
var message2 = items["item1"]?["nested1"]?["message"];
print(message2);
print(message2.runtimeType);
var message3 = items["item1"]?["nested1"]?["nested2"]?["message"];
print(message3);
print(message3.runtimeType);
}
I've been struggling to make this work in Dartpad.
message1 works as expected, but then I can't seem to work my way down the tree...
Is this a shortcoming with map literals? Do I need to use constructors? Or am I missing something bigger?
Your problem is that items is inferred to be of type Map<String, Map<String, Object>>, but Object does not have an operator []. Therefore when you eventually extract that Object, you will not be able to do anything with it until you cast it a more specific type.
What you probably want instead is to explicitly declare items as Map<String, dynamic> to disable static type-checking on the Map's values:
var items = <String, dynamic>{
"item1": ...
};
Of course, when you disable static type-checking, you are responsible for ensuring that the values you get from the Map are what you expect, or you will get NoSuchMethod or TypeError exceptions at runtime. If you do want static type-checking, you should use define custom classes instead of using a blob of key-value properties.

Mock data generation for storybook

I am trying to generate mock data using relay for storybook.
My query is
const QUERY_LIST = graphql`
query modelControllerAllUsersQuery #relay_test_operation {
allUsers {
pageInfo {
hasNextPage
}
edges {
node {
id
firstName
lastName
}
}
}
}
`
and provided RelayEnvironmentProvider as a decorator to the story. I'm trying to return some default values to my query using custom mock resolvers.
const customMockResolvers = {
...mockResolvers,
allUsers:() => ({
pageInfo:{
hasNextPage:false,
},
edges:[
{
node:{
id :'id',
firstName:'fname',
lastName :'lname',
},
},
],
}),
};
and calling it as
(operation) => MockPayloadGenerator.generate(operation, customMockResolvers)
I don't seem to be able to get the default values returned.
Currently, it is returning
{"allUsers":{"pageInfo":{"hasNextPage":false},"edges":[{"node":{"id":"<UserNode-mock-id-1>","firstName":"<mock-value-for-field-\"firstName\">","lastName":"<mock-value-for-field-\"lastName\">"}}]}}
What am I doing wrong?
When using the #relay-test-operation, the keys within your customMockResolvers object must match the type name of the fields, which can be different from the field names themselves.
For example, you could have the following in your schema:
type Foo {
id: ID!
name: String!
}
and the following query:
query FooQuery #relay_test_operation {
foo {
id
name
}
}
Then the customMockResolvers object would look like this:
const customMockResolvers = {
Foo: () => ({
id: "fooId",
name: "fooName"
})
}
Notice that I'm passing in Foo as the key instead of foo.
You can check your schema and see what the the type name of allUsers is. I suspect it would be something like AllUsers or allUsersConnection, or something similar.
Also, if you're interested in creating Storybook stories for Relay components, I created a NPM package just for that: https://www.npmjs.com/package/use-relay-mock-environment
It doesn't require adding the #relay-test-operation directive to your query, and instead relies only on resolving the String type (which is the default for all scalar properties). You can of course still add the #relay-test-operation directive and also extend the resolvers by providing customResolvers in the config.
You can also extend the the String resolver as well, by providing extendStringResolver in the config.
Feel free to review the source code here if you want to implement something similar: https://github.com/richardguerre/use-relay-mock-environment.
Note: it's still in its early days, so some things might change, but would love some feedback!

FLUTTER How to get variable based on passed string name?

I have stored variables in a class with their code names.
Suppose I want to get XVG from that class, I want to do
String getIconsURL(String symbol) {
var list = new URLsList();
//symbol = 'XVG'
return list.(symbol);
}
class URLsList{
var XVG = 'some url';
var BTC = 'some url';
}
Can someone help me achieve this or provide me with a better solution?
Dart when used in flutter doesn't support reflection.
If it's text that you want to have directly in your code for some reason, I'd advise using a text replace (using your favourite tool or using intellij's find + replace with regex) to change it into a map, i.e.
final Map<String, String> whee = {
'XVG': 'url 1',
'BTC': 'url 2',
};
Another alternative is saving it as a JSON file in your assets, and then loading it and reading it when the app opens, or even downloading it from a server on first run / when needed (in case the URLs need updating more often than you plan on updating the app). Hardcoding a bunch of data like that isn't necessarily always a good idea.
EDIT: how to use.
final Map<String, String> whee = .....
String getIconsURL(String symbol) {
//symbol = 'XVG'
return whee[symbol];
}
If you define it in a class make sure you set it to static as well so it doesn't make another each time the class is instantiated.
Also, if you want to iterate through them you have the option of using entries, keys, or values - see the Map Class documentation
I'd just implement a getProperty(String name) method or the [] operator like:
class URLsList{
var XVG = 'some url';
var BTC = 'some url';
String get operator [](String key) {
switch(key) {
case 'XVG': return XVG;
case 'BTC': return BTC;
}
}
}
String getIconsURL(String symbol) {
var list = new URLsList();
return list[symbol];
}
You can also use reflectable package that enables you to use reflection-like code by code generation.
Assuming that the class is being created from a JSON Object, you can always use objectName.toJSON() and then use the variable names are array indices to do your computations.

EXT JS 5: viewModel links variable id

I am using Ext JS 5.0.1 and I am trying to use links in the viewModel defined inside a view.
The example below works.
Ext.define("MyViewPackage.MyView", {
extend: "Ext.form.Panel",
alias: "widget.myview",
theIdToUse: 47,
viewModel: {
links: {
theProject: {
type 'mypackage.MyModelClassName'
id: 17 //This works. But not theIdToUse or this.theIdToUse.
//I would like to use a value provided from my view
}
}
}
});
I would like to use the value of 'theIdToUse' for the id property of 'theProject' defined in 'links'.
I have tried to simply put theIdToUse or this.theIdToUse but I always got the following error:
Cannot use bind config without a viewModel
Do you know how could I managed to use links with a variable id?
Thanks in advance!
Use linkTo, like:
Ext.define("MyViewPackage.MyView", {
extend: "Ext.form.Panel",
alias: "widget.myview",
theIdToUse: 47,
constructor: function(){
this.callParent(arguments);
this.linkTo('theProject',{
type 'mypackage.MyModelClassName',
id: this.theIdToUse
});
}
});
this.theIdToUse does not work because within the scope of the viewModel, this no longer refers to MyViewPackage.MyView, but to the viewModel itself.
Even if you could get a reference back to MyViewPackage.MyView, say using ComponentQuery, the component does not yet exist at the point that the viewModel is being initialized, so you will get an error Cannot read property 'theIdToUse' of undefined.
You would probably be better off using some sort of two way binding between the view and viewModel, but I would need to know more about what you're trying to achieve to say exactly how.
Previous answer were pretty correct, except that in your case this would refer to window, not viewModel. This is due to in Ext.define you are passing anonymous object without some scope, so window scope would be used by default.
Suppose you should use something like this:
Ext.define("MyViewPackage.MyView", {
extend: "Ext.form.Panel",
alias: "widget.myview",
bind: {theIdToUse: "{id}"}
viewModel: {
links: {
theProject: {
type 'mypackage.MyModelClassName'
id: 17 //This works. But not theIdToUse or this.theIdToUse.
//I would like to use a value provided from my view
}
}
}
});
Although the question is from long time ago, I leave a possible solution for further viewers.
Try:
Ext.define("MyViewPackage.MyView", {
extend: "Ext.form.Panel",
alias: "widget.myview",
config: {
theIdToUse: 47,
},
viewModel: {
links: {
theProject: {
type 'mypackage.MyModelClassName'
id: null // Will be copied from config in initConfig
}
}
},
initConfig: function (config) {
this.config.viewModel.links.theProject.id = config.theIdToUse ;
this.callParent([config]) ;
}
});
You will be able to instanciate your panel with different ids:
items: [
{
xtype: 'myview',
theIdToUse: 47
},{
xtype: 'myview',
theIdToUse: 32
}
],
Even with:
Ext.create('MyViewPackage.MyView', {theIdToUse: 12}) ;

SAPUI5: Example for a composite control?

I am searching for a good example of a composite control.
My current problem is, that I plan to bind a simple value (for example a string) that will be reused in some other control inside a composite control.
Following code seems not correct:
metadata : {
properties : {
"head" : {type : "string", defaultValue : ""},
...
},
},
init : function() {
... some control with content ...
content : [
new sap.m.Label({text : this.getHead()})
]
...
My plan to call the composite control would look like this:
var oTemplate = new MyControl({ head : "{Name}" });
Using the template for example in a list.
Binding may work, but because of the fact that I build the control in the "init" part it looks like the property is not initialised and will not be updated automatically.
A further experiment (that will not work):
jQuery.sap.declare("StrangeControl");
sap.m.HBox.extend("StrangeControl", {
metadata : {
properties : {
},
aggregations : {
input : {type : "sap.m.Input", multiple : false},
}
},
// will be called during creation time
init : function() {
sap.m.HBox.prototype.init.call(this);
this.addItem(this.getAggregation("input"));
},
renderer : {},
onAfterRendering : function() {
if (sap.m.HBox.prototype.onAfterRendering!==undefined) {
sap.m.HBox.prototype.onAfterRendering.call(this);
}
}
});
I assume to use the control that way:
new StrangeControl({
input : new sap.m.Input({value : "test"})
})
But during init input is not defined (==null). The mentioned example https://github.com/SAP/openui5/blob/master/src/sap.m/src/sap/m/SelectDialog.js seems to handle the "items" in a different way but for me it is not clear how.
Meanwhile there a documentation exists (at least in OpenUI5beta SDK).
https://openui5.hana.ondemand.com/#/topic/c1512f6ce1454ff1913e3857bad56392
If the link does not work search for "Composite Controls" inside the "DEVELOPER GUIDE" section.
A: Just add your internal control to a hidden aggregation - it will automatically get all the data binding for free, you just have to bind the properties/aggregations of that control accordingly.
B: You could also overwrite the setters of your outer control which then call the setters of the inner control in order to propagate the values.
setHead : function(oValue) {
return this.getAggregation("_myHiddenInnerControl").setValue(oValue);
}
It is still necessary to add the inner control to an aggregation, to avoid memory leaks (else you have to make sure everything is cleaned up in the exit method.

Resources