Pass a variable to another widget [flutter] - flutter

Summing up the situation, I'm making a simple App in Flutter, which displays a List of Items you've added (I won't detail the app, as it would be unnecessary).
The file I created (log.dart) has a Property Class
class LogItem { /* code and stuff inside */ }
And it has a List with items
List<LogItem> itemsList = [test01, test02];
I created a simple Widget to display data for each item in this List
class SpecificItem extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Text(
" R\$ ${itensList[i].price}",
),
Spacer(),
Text(
"${itensList[i].title}",
),
],
),
);
}
}
Just below in another widget, I created a for loop to make a variable "i" change, to display different items from this list.
class LogGraphical extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: [
for (int item = 0; item < itensList.length; item++) SpecificItem(item),
],
),
);
}
}
Can someone explain to me exactly how I do the Widget ACCEPT PARAMETERS and change it? In this way creating multiple items?
I tried in many ways, but I could never get it to work!
(In the code I inserted here, I didn't put the Widget accepting anything.)

Related

Flutter Wrap widget rendering glitches when height is updated

I am creating a form (not using the Form Widget) in Flutter where the user can add an arbitrary amount of items (treatments) which are rendered as InputChip widgets list in a Wrap widget.
The form uses a button (AddButton widget) which opens a form dialog which itself returns the newly created item (treatment) that is added to selectedItems:
class TreatmentsWidget extends StatefulWidget {
const TreatmentsWidget({super.key, required this.selectedItems});
final List<Treatment> selectedItems;
#override
State<TreatmentsWidget> createState() => _TreatmentsWidgetState();
}
class _TreatmentsWidgetState extends State<TreatmentsWidget> {
#override
Widget build(BuildContext context) {
var chips = widget.selectedItems.map(
(item) {
return InputChip(
label: Text('${item.name} - ${item.frequency}/${item.frequencyUnit.name})',
);
},
).toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
children: chips,
),
AddButton(onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return const TreatmentDialog();
}).then((value) {
if (value != null) {
Treatment item = value;
setState(() {
widget.selectedItems.add(item);
});
}
});
}),
],
);
}
}
For some reason, when a new item is added to selectedItem and that the item overflows the current line, the layout is not recomputed such that the Wrap widget overflows the button:
However, as soon as the user scroll (the whole screen content is inside a SingleChildScrollView), the layout is recomputed and the Wrap takes the right amount of space:
How can I force a redraw when a new item is added to prevent this glitch?
The issue seems to be that the Column does not recompute its size on the current frame when one of his child size changes.
I ended up forcing rebuilding the Column using a ValueKey whenever chips.length changes:
class _TreatmentsWidgetState extends State<TreatmentsWidget> {
#override
Widget build(BuildContext context) {
var chips = ...;
return Column(
key: ValueKey(chips.length),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
children: chips,
),
AddButton(...),
],
);
}
}
I am still interested in a cleaner solution if it exists. Using a provider seems overkill for this case.

Performance difference between widget function and class

What is the difference between both options in this case regarding rebuilding widgets and performance aspects?
Widget class:
class Dummy() extends StatelessWidget {
const Dummy();
#override
Widget build(BuildContext context) {
return const Text(„text“);
}
}
Option 1:
class Option1 extends StatelessWidget {
const Option1();
#override
Widget build(BuildContext context) {
return SizedBox(
child: const Dummy(),
);
}
}
Option 2:
class Option2 extends StatelessWidget {
const Option2();
Widget createDummyWidget() {
return const Dummy();
}
#override
Widget build(BuildContext context) {
return SizedBox(
child: createDummyWidget(),
);
}
}
Splitting widgets to methods is an antipattern
So, for example, if we have a widget that looks something like this:
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Row(
children: [
Text('Counter: $_counter'),
Container(
child: Column(
children: [
Text('Hello'),
Row(
children: [
Text('there'),
Text('world!'),
],
),
],
),
),
],
);
}
}
if use function widget
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _counter = 0;
Widget _buildNonsenseWidget() {
return Container(
child: Column(
children: [
Text('Hello'),
Row(
children: [
Text('there'),
Text('world!'),
],
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Row(
children: [
Text('Counter: $_counter'),
// The deeply nesting widget is now refactored into a
// separate method and we have a cleaner build method. Yay!
_buildNonsenseWidget(),
],
);
}
}
So what’s the problem, really?
Whenever the value of _counter changes, the framework calls the build method. This triggers our widget to rebuild itself. The problem is that _buildNonsenseWidget() gets called every time the value of _counter changes - which ends up rebuilding the widget tree over and over again.
Rebuilding for nothing
In this case, there’s no reason to rebuild that particular widget tree.
The widget tree returned by _buildNonsenseWidget() is stateless by nature - we only need to build it once. Sadly, because the widget tree is built by the _buildNonsenseWidget() method, the Flutter framework rebuilds it every time when the parent widget rebuilds.
Essentially, we’re wasting precious CPU cycles in rebuilding something that doesn’t need to be rebuilt. This happens because from the framework’s perspective, there’s no difference between a long-ass build method and a build method split into multiple smaller methods. Mind you, this is only a simple example - this has a more significant impact on more complex apps.
Splitting long build methods - revisited
The solution for this one is relatively simple, although it results in a couple of extra lines of code. Instead of splitting build methods into smaller methods, we split them into widgets - StatelessWidgets, that is.
When we refactor the previous example, we’ll end up with this:
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Row(
children: [
Text('Counter: $_counter'),
// The deeply nesting widget is now refactored into a
// stateless const widget. No more needless rebuilding!
const _NonsenseWidget(),
],
);
}
}
class _NonsenseWidget extends StatelessWidget {
const _NonsenseWidget();
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Text('Hello'),
Row(
children: [
Text('there'),
Text('world!'),
],
),
],
),
);
}
}
Conclusion
Instead of splitting you build methods into multiple smaller methods, split them into StatelessWidgets. This way, you won’t be rebuilding your static widget trees multiple times for nothing but wasted CPU cycles. When it comes to optimizing performance of Flutter apps, this is probably one of the lowest hanging fruits.
I used this article : https://iiro.dev/splitting-widgets-to-methods-performance-antipattern/

Flutter SetState from custom widget (or more down the line)

I have a simple flutter app. It has a stateful widget in the Home Page with a simple Scaffold which only has a Column. One of the children in the column is a custom widget called buildBodyLayout(). This widget has its own column with some Text widgets, and another custom widget called buildButton(). This new widget has a button which needs to setState of a variable in the Home view. I pass the value of the variable when calling the widget. But each widget is in its own dart file since I am re-using the same widget in other pages.
How do I setState the main stateful widget from inside custom widgets?
If I write everything inside the same page, it all works fine. How do I use a widget in a different dart file to set the sate of a parent widget?
Sample Code
Home Stateful Widget
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int changeValue;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("Welcome to my App"),
Text("The Change Value is: $changeValue"),
buildBodyLayout(changeValue),
],
),
);
}
}
buildBodyLayouot Widget
Widget buildBodyLayout(int value){
return Column(
children: [
Text("Press the + and - Buttons to change Value"),
buildButtons(value),
],
);
buildButtons Widget
Widget buildButtons(int value){
return Column(
children: [
RaisedButton(
child: Text("Increase Value"),
onPressed: (){
value = value + 1; //THIS SHOULD SET STATE
}) ,
RaisedButton(
child: Text("Decrease Value"),
onPressed: (){
value = value - 1; //THIS SHOULD SET STATE
})
],
);
}
}
Thank you for any help!
Widgets in flutter are classes that extend the widget(StatefulWidget, StatelessWidget) class.
In your code your using functions to build your widgets, which is not recommended.
You can see the difference between class widgets and function widgets here:
What is the difference between functions and classes to create reusable widgets?
Aside from that, using function or classes, to solve your problem you need to use a callback.
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int changeValue = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("Welcome to my App"),
Text("The Change Value is: $changeValue"),
buildBodyLayout(changeValue,
addToValue: (int increment){
setState((){
changeValue += increment;
});
}
),
],
),
);
}
}
Widget buildBodyLayout(int value, Function(int newValue) addToValue){
return Column(
children: [
Text("Press the + and - Buttons to change Value"),
buildButtons(value, addToValue),
],
);
}
Widget buildButtons(int value, Function(int newValue) addToValue){
return Column(
children: [
RaisedButton(
child: Text("Increase Value"),
onPressed: (){
addToValue(1);
}),
RaisedButton(
child: Text("Decrease Value"),
onPressed: (){
addToValue(-1);
})
],
);
}
You also don't need to put your widgets in different files to reuse them, but it's recommended that you do that.

How to add multiple rows or columns after the body

I'm still trying to understand how to structure widgets. I have placed a container in the body already so how can I now add another row. I've removed some code to simplify my situation but hopefully this gives an idea of how my project is structured at the moment.
class AddButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.bottomCenter,
//Stats Button
child: Row(
), //container
//How can I enter a new row here <------- WHERE I WANT TO ENTER A ROW
);
}
}
The short answer is, you cannot. You can take advantage of the children property of a Column, the most common layout widget in all of Flutter. Flutter works on a system of nested widgets, you cannot have many parents as it all starts with one widget.
class AddButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.bottomCenter,
//Stats Button
child: Column(
children: <Widget>[
Row(
children: <Widget>[
// nested widgets
],
),
],
),
),
);
}
}
Judging by your class name, you just want a button. Not every widget starts with a Scaffold, that's only if you want an entire layout with an app bar or a bottom navigation bar. For simple widgets like a button, you can get rid of Scaffold entirely and just use MaterialButton like this.
class AddButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialButton(
onPressed: () {}, //empty function
);
}
}
More reading:
https://flutter.dev/docs/development/ui/layout
https://pusher.com/tutorials/flutter-building-layouts
https://medium.com/flutter-community/flutter-layout-cheat-sheet-5363348d037e

Flutter iterate through list of buttons with map

I want to iterate through a list of very similar buttons but I'm not sure what the best way to do it is since each button will need an onPressed function that passes a single argument.
Column(
children: buttons
.map(
(item) => Row(
children: <Widget>[
myButton(item[0], item[2]),
myButton(item[1], item[2]),
],
),
)
.toList(),
),
class MyButton extends StatelessWidget {
MyButton(this.abool, this.onPressed);
final bool abool;
final Function onPressed;
#override
Widget build(BuildContext context) {
return RawMaterialButton(
child:
Image.asset('images/button.png'),
onPressed: onPressed,
);
So of course I have a problem with storing the different onPressed functions in a List array. What's the best way to iterate through a list of similar buttons like this in a column?
If the Buttons don't share similar behavior , you should not have to make the Mybutton class.
you can't make one class for Sign up button and Login button ,each one has different responsibility and behavior .
This class should be used when you have data model (shopItem) for example (and from your code i think this is the case) , and you need (add to cart) functionality for example , this is a case where you have to make addToCartButton and be something like this :
class AddToCartButton extends StatefulWidget {
final ShoptItem item;
AddToCartButton({Key key, this.item}) : super(key: key);
#override
_AddToCartButtonState createState() => _AddToCartButtonState();
}
class _AddToCartButtonState extends State<AddToCartButton> {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text('Add to Cart'),
onPressed: () {
setState(() {
cartList.add(item);
});
},
);
}
}
notice that i've used stateful widget (not necessary ) if there is any kind of state in the button itself like
Text(inCart? 'already in cart':'add to cart'),'
unless you are using state management like provider ,bloc, etc.
the second notice here (most Important) that i've used cartList which basically a list of ShopItem .
Now all the buttons have similar functionality (adding item to the cart) ,but every button should add different item , that can be done in column you're using or the ListView builder which i prefer
the Column approach:
Column(
children: <Widget>[
for( item in itemsList)
AddToCartButton(item : item);
],
ListViewBuilder approach :
ListView.builder(
itemCount: itemsList.length,
itemBuilder: (context,index){
return AddToCartButton(item : itemsList[index]);
},
)
I hope that can help
You can use list comprehension (also called UI-as-code) since Dart 2.3 to generate buttons on the fly right inside the list.
List<String> labels = ['apple', 'banana', 'pineapple', 'kiwi'];
List<VoidCallback> actions = [_buyApple, _doSomething, _downloadData, () => print('Hi')];
...
Column(
children: [
for (int i = 0; i < labels.length; i++)
RaisedButton(
label: Text(labels[i]),
onPressed: actions[i],
),
],
)