Flutter ontap function runs when initiated - flutter

I have this code in my index file:
_ontapd(_navigationLink,BuildContext context){
print(_navigationLink);
}
ListView.builder(
padding: EdgeInsets.only(top: 20),
itemCount: pages.length,
itemBuilder: (context, index) {
return ProfileCard(pages[index],_ontapd(_navigations[index],context));
}),
And this code in "ProfileCard" widget:
String cardText;
Function _onTapFunc;
ProfileCard(#required this.cardText, this._onTapFunc);
GestureDetector(
onTap: _onTapFunc,
...
)
Now whenever I refresh the app, the _onTapFunc gets called for each item in the pages list. Why does this happen?

It's being called because you're calling it in your itemBuilder. It's odd that you're not getting a static analysis error here.
In the line return ProfileCard(pages[index],_ontapd(_navigations[index],context));, you're calling the _ontapd method. Note the parentheses, this means you're calling the method and passing the return value, not passing a reference to it. This value is then being passed to the ProfileCard.
To fix this you need to remove the parentheses. Just pass _ontapd
return ProfileCard(pages[index],_ontapd);
This does result in other issues however. onTap does not have the same parameters your _ontapd method requires, so it cannot be used without modification.
In your current implementation _ontapd just prints the first parameter passed to it, it doesn't even use the second parameter. So a better solution here would be to pass the value of _navigationLink to ProfileCard and define the onTap to print that value. _ontapd could then be completely removed
onTap: () {
print(parameterPassed);
}
The example you provide may be simplified, in which case you hopefully have enough understanding of the error to come to a solution yourself or you'll have to provide more details.

Related

Button built from ListView.builder seems to call the same function for all buttons built from the ListView

Basically, I have two nested ListView. Builders inside a third ListView builder. The two nested listview builders build two rows of custom buttons; the contents of the second row of buttons depend on the selected button in the two rows. The first row works entirely as intended, but the second row causes an issue.
Whenever a button from the second row is pressed, it affects the state of all the other buttons (and the data those lists are built on), as if calling the same function on all elements on the list. The strangest thing, though, is that it only seems to call PART of that function:
void toggleSubcategoryPanelButton(int panelNum, int subCategoryIndex) {
App()
.panelNames[panelNum]
.panelSettings
.categories[
App().panelNames[panelNum].panelSettings.activeCategoryIndex]
.activeSubcategory =
App()
.panelNames[panelNum]
.panelSettings
.categories[
App().panelNames[panelNum].panelSettings.activeCategoryIndex]
.subcategories[subCategoryIndex];
rerollName(panelNum);
saveSettingstoPrefs();
notifyListeners();
}
App() is a singleton that holds pretty much all the data for this lightweight app. The purpose of this function is to set a new index for activeSubcategory for only the 'panel' from which the function was called (given via panelNum). Instead, the function sets activeSubcategory of to the same index for all of the elements of panelNames in their categories at the current activeCategoryIndex.
Then, rerollName() seems to only be called for the panelNum provided.
The problem can be seen here. The top row's selection is set correctly.
[![Different function][1]][1]
You can (hopefully) see in this gif that the application correctly identifies the panelNum as it is displayed in the terminal (index of 1).
The ListView in question:
SizedBox(
height: 30.0,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
physics:
const NeverScrollableScrollPhysics(),
itemCount: widget
.panelSettings
.categories[widget.panelSettings
.activeCategoryIndex]
.subcategories
.length,
itemBuilder: ((context, index) {
return Consumer(
builder: (context, value, child) {
return PanelButtonToggleable(
icon: widget
.panelSettings
.categories[widget
.panelSettings
.activeCategoryIndex]
.subcategories[index]
.icon,
tooltip: widget
.panelSettings
.categories[widget
.panelSettings
.activeCategoryIndex]
.subcategories[index]
.getName(),
buttonBehavior: () =>
_onToggleSubcategoryClick(
index),
toggled: widget
.panelSettings
.categories[widget
.panelSettings
.activeCategoryIndex]
.subcategories[index]
.getName() ==
widget
.panelSettings
.categories[widget
.panelSettings
.activeCategoryIndex]
.activeSubcategory
.getName(),
);
},
);
}),
),
),
https://github.com/trevclaridge/Name-Generator-Extension
[1]: https://i.stack.imgur.com/Z2tg8.gif
I discovered that my issue actually had nothing to do with the ListViews at all. Rather the objects pointed to by widget.panelSettings.categories were actually the same instance of a List. Thus, assigning the entry for activeSubcategory was assigning it for all the objects that pointed to the categories list.
Confirm this in your own projects using Dart's identical method. Link here.
To solve this, I created a new class Categories with an empty constructor to hold the list, so every initialization of the list was from a new object.

Dynamic ListView of stateful widgets not working

Dears,
it could be just a mistake of mine but ...it's driving me crazy ;)
I have a ListView inside my statefulwidget:
Expanded(
child: ListView.builder(
padding: EdgeInsets.all(10.0),
itemCount: searchableFoodList.length,
itemBuilder: (BuildContext context, int index) {
print('4 - SFL - ${searchableFoodList[index].id} - ${searchableFoodList[index].name}');
return
FoodItem(
id: searchableFoodList[index].id,
baseUnit: searchableFoodList[index].baseUnit,
baseUnitS: searchableFoodList[index].baseUnitS,
baseVal: searchableFoodList[index].baseVal,
baseValCal: searchableFoodList[index].baseValCal,
mediumVal: searchableFoodList[index].mediumVal,
mediumValCal: searchableFoodList[index].mediumValCal,
name: searchableFoodList[index].name,
note: searchableFoodList[index].note
);
},
),
searchableFoodList is modified (according user selection) inside a setState().
Let say that we have a 3 FoodItem(s), so my searchableFoodList is [FoodItem-1, FoodItem-2, FoodItem-3] and everything is working fine.
If I select a single item in my list, let say "FoodItem-2", the searchableFoodList becomes [FoodItem-2] and the list displayed contains (correctly) just one item but it is FoodItem-1.
Note that I inserted a print ...and it prints "FoodItem-2"
I guess that the problem is that it is considered the "original list" and it is changed only the length of the list but the list itself is not re-generated.
I have no more ideas ...any suggestions to solve this problem?
P.S.
FoodItem is stateful widget as well (an animated one). I did something similar with using stateless widget and I didn't have any problem

Flutter: Notification listener, how to make onNotification dynamic

I am new to dart and hence flutter. I am using NotificationListener like this
String x = a; // or could be x=b, x=c, etc, comes as function parameter
return NotificationListener(
onNotification: onNotificationHandler,
child: new ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
// other codes comes here
Problem,
I want value of onNotification to be dynamic on the basis of the value of variable x
Can anyone help me on this?
In Dart, Function are first class citizen, it means that a Function can be returned from a method.
You can then create a method which takes x as parameter and which returns a function which is a NotificationListenerCallback. The returned function is what is called a Closure which means that is has access to the variables which are in it's lexical scope (x is in this case) even if it is executed outside.
In your example, it can be:
String x = a; // or could be x=b, x=c, etc, comes as function parameter
return NotificationListener(
onNotification: _notificationHandler(x), // The return the appropriate function with x scoped to the function to be used later
child: new ListView.builder(
// other codes comes here
def _notificationHandler(String value) => (T notification) {
// Note that the returned function has access to `value`
// even if it will be executed elsewhere
return (value == 'Hey);
}

Difference between flutter onTap() and javascript/jquery $(this) in click listener

I'm still in the process of learning dart/flutter. I'm experimenting with ListView/ListTile and specifically onTap() from ListTile.
I'm somewhat familiar with JS/Jquery and am having trouble understanding the differences in approach between JS and flutter.
Please try to ignore any semantic or technical errors in the code below. I'm more concerned with theory/approach than if this is a perfectly formatted and syntactically correct example.
In JS, it's common to do something like make an AJAX call to an API to get a list of items for sale from a database. Assume the API returned 4 fields (primary key ID, isAvailable, item name, and item price). You then process the list and create/insert DOM elements for display.
<div id="234" data-isavailable="y" class=".itemsForSale">TV: $800</div>
<div id="345" data-isavailable="y" class=".itemsForSale">Microwave: $200</div>
<div id="456" data-isavailable="n" class=".itemsForSale">Book: $30</div>
<div id="567" data-isavailable="y" class=".itemsForSale">Sofa: $450</div>
You can then set listeners arbitrarily. For instance, you could do...
$( ".itemsForSale" ).click(function() {
// do something
});
The click handler gets applied in "real-time". The code executed is aware of what was clicked and can analyze/interact with that item in the list. So, you can do something like:
$( ".itemsForSale" ).click(function() {
var isAvailable = $(this).attr('data-isavailable');
if( isAvailable == 'n' )
alert('sorry but that item is out of stock!');
else
addItemToCart( $(this).attr('id') );
});
The point being, that the click handler doesn't necessarily know or care about the underlying data of each item. It becomes "context" aware during runtime and then pulls the relevant values out of the UI/etc.
With flutter, I'm looking to recreate similar functionality using a ListView and ListTile. Specifically, ListTile's onTap(). I'm getting confused because it seems like everything is coded "ahead of time".
Here is a basic example (ignore for now I'm not displaying price/etc)...
import 'package:flutter/material.dart';
class SecondScreen extends StatelessWidget {
var mapper = {
'234': 'TV',
'345': 'Microwave',
'456': 'Book',
'567': 'Sofa'
};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body:
ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: mapper.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
key: new Key(mapper.keys.elementAt(index).toString()),
title: Text('${mapper.values.elementAt(index)}'),
onTap: () {
print('you clicked: ' + mapper.keys.elementAt(index).toString() );
addToShoppingCart( mapper.keys.elementAt(index).toString() ); // this doesnt exist yet and is just an example
}
);
}
)
);
}
}
First of all, I'm not even sure I need to assign a custom key to each ListView item (based on the database's primary key), but that's irrelevant for now.
What I'm getting stuck on is that the onTap handler doesn't seem to have an equivalent of "$(this)". Does flutter give each ViewTile it's "own" copy of onTap() and that each relevant key info is "hardcoded" into each one (for each itemBuilder loop, the current value of mapper.keys.elementAt(index).toString() is substituted into onTap() )?
I'm probably not describing my confusion properly and I'm not even sure all of the issues I'm going to run into. I'm sure my way of doing this is very "old" and not aligned with react and other newer approaches/frameworks (combining data within the UI elements).
I'm just having trouble seeing how I'm going to be able to add more complex logic (like checking availability of what was clicked).
Does anyone have any references or explanations that help me bridge this gap? I don't even know how to describe the concept of $(this) and how I would search for it in comparison to flutter/etc. I just know that something feels very different.
Thanks!
I understand your confusion probably because I'd a similar question when I started with Flutter just a few months back. And here is what I think -
It doesn't really matter whether the ListTile item has it's own copy of onTap() or not. A. It does not have it's own copy. (Following code snippet will explain) and B. Every programming language / SDK / whatever has its own way of working. What you are trying to say, probably, is that you've a BMW. It has got certain type of breaks. And you are trying to make exact type of break in Audi. It may not be right to do it as the other systems related to the breaks may not work in the optimised way.
Now, look at the following code snippet :
#override
Widget build(BuildContext context) {
return ListView.separated(
separatorBuilder: (context, index) => ListViewDivider(),
itemCount: faceRecTransactions.length,
itemBuilder: (BuildContext context, int index) {
return FacerecTransactionListItem(facerecTransaction: faceRecTransactions[index], onTap:()=> _onTap(faceRecTransactions[index],context),);
},
);
}
void _onTap(FacerecTransaction facerecTransaction, BuildContext context) {
print('Status : ${facerecTransaction.userId}');
Navigator.push(context, MaterialPageRoute(builder: (context) => FacerecDetailPage(
facerecTransaction: facerecTransaction,
criminalList: this.criminalList,)));
}
There's no copy of onTap for every list item. It just 'feels' as it has because we write onTap inside ListView. If you look at the example above, when the user taps on certain item, then the onTap receives the information. We don't create array of onTap as such.
Since, there's no copy of onTaps, it's not really ahead of time code. It's pretty much works like in Ajax where the onTap doesn't really care about the payload. It just does the action(s) specified.
Now, in the above example, the Detailed Page can check the availability of the particular item. And if the item is not available then either appropriate message can be shown. Depending on the requirement, we can either write this code within onTap or on the next page. In short, the onTap can handle the complex logic as you need.

How to dynamically add Children to Scaffold Widget

Let's say, I have a chat screen that looks like this.
Now, when the user clicks the "Press when ready" button, the method fetchNewQuestion() is called.
My intention is that this will make a HTTP request, and display the result using
_buildUsersReply(httpResponse);
But, the problem is that this return must be made inside the current scaffold's widget as a child under the existing children, so that it is built at the bottom with the previous ones still there. The result would be like this:
You can find my complete code here.
Is this possible to be done pro-grammatically? Or do I have to change the concept of how I do this?
[Update, I now understand that my approach above is wrong and I have to use a listview builder. CurrentStatus below shows my progress towards achieving that goal.]
Current status:
I have built a list of Widgets:
List<Widget> chatScreenWidgets = [];
And on setState, I am updating that with a new Widget using this:
setState(() { chatScreenWidgets.add(_buildUsersReply("I think there were 35 humans and one horse.")); });
Now at this point, I am not sure how to pass the widget inside the scaffold. I have written some code that does not work. For instance, I tried this:
Code in the image below and in the gist here:
Just for future reference, here is what I really needed to do:
1. Create a list of widgets
List<Widget> chatScreenWidgets = [];
2. Inside my method, I needed to use a setState in order to add elements to that list. Every widget I add to this will be displayed on ths Scaffold.
`setState(() {
chatScreenWidgets.add(_buildUsersReply("Some Text"));
});`
3. And then, load that inside my Scaffold, I used an itemBuilder in order to return a list of widgets to my ListView. I already had that ListView (where I was manually adding children). Now this just returns them through the setState method inside my business logic method (in this case, fetchNewQuestion()).
body: Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 0),
child: new ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 25),
itemCount: chatScreenWidgets.length,
itemBuilder: (BuildContext context, int itemCount) {
return chatScreenWidgets[itemCount];
}
),
),
],
),
);`
I hope this helps future flutter engineers!
forget about the scaffold the idea is about what you really want to change, lets say it is
a list and your getting the data from an array if you update the array, then the list will update,if it is another type widgets then you can handle it in a different way i will edit this answer if you clarify what each part does in your widget as i cant read your full code.
first you have to create an object with two attributes one is the type of the row(if it is a user replay or other messages) and the second attribute is the string or the text.
now create a global list in the listview class from the above object, so you get the data from the user or even as a news and you create a new object from the above class and add your data to it and add it to the list.
item builder returns a widget so according to the the widget that you return the row will be set , so according to the data in the object call one of your functions that return the views like _buildUsersReply(string text).
if you have any other question you can ask :) if this what you need please mark it as the answer.