Dynamically access properties of class in Dart / Flutter - flutter

I am trying to access a property in a class in Dart with a dynamic variable. In JavaScript I can use bracket notation and do something like:
var icons = {
mars: "male",
venus: "female"
};
getIcon = genderIcon => {
return icons[genderIcon];
};
console.log(getIcon("mars")); // Prints "male"
Can I do something similar in Dart?
I tried two approaches in Dart but got two different errors which I am not really understanding:
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class GenderSelection extends StatelessWidget {
final IconData genderIcon;
GenderSelection({#required this.genderIcon});
#override
Widget build(BuildContext context) {
return Container(
child: Icon(
// Using dot notation...
FontAwesomeIcons.genderIcon // error: The getter 'genderIcon' isn't defined for the class 'FontAwesomeIcons'. (undefined_getter at [bmi_calculator] lib/input_page.dart:71)
// ...or using bracket notation like JS
FontAwesomeIcons[genderIcon] // error: The operator '[]' isn't defined for the class 'Type'. (undefined_operator at [bmi_calculator] lib/input_page.dart:71)
),
);
}
}
Edit
I am adding the complete code to try to explain better what I am trying to achieve. Moreover this is about DYNAMICALLY accessing a property of a class in Dart. It is NOT about icons or FontAwesome.
This is the complete code:
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class InputPage extends StatefulWidget {
#override
_InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
BmiCard(
cardChild: GenderSelection(
genderIcon: 'mars', // Comment#1: This won't work.
genderLabel: 'male',
),
),
BmiCard(
cardChild: GenderSelection(
genderIcon: 'venus', // Comment#2: This won't work.
genderLabel: 'female',
),
),
],
),
),
],
),
),
),
);
}
}
class GenderSelection extends StatelessWidget {
final IconData genderIcon;
final String genderLabel;
GenderSelection({#required this.genderIcon, #required this.genderLabel});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Icon(
FontAwesomeIcons().genderIcon, // Comment#3: This doesn't work.
),
Text(
genderLabel.toUpperCase(),
),
],
),
);
}
}
class BmiCard extends StatelessWidget {
final int color;
final Widget cardChild;
BmiCard({this.color = 0xFF1d1e33, this.cardChild});
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: cardChild,
),
);
}
}
It will work If I change the lines in the comments to:
- Comment#1: genderIcon: FontAwesomeIcons.mars,
- Comment#2: genderIcon: FontAwesomeIcons.venus,
- Comment#3: genderIcon,

The problem here is that the FontAwesomeIcons library does not have a dynamic getter defined. There's nothing you can do to get dynamic object fetching to work on your side.
However, in your particular scenario, you can just initialize a few variables with FontAwesomeIcons that you can then use wherever you want, or use the FontAwesomeIcons' objects by directly referencing them (as you have noted at the end of your post).
There are other ways to achieve the same result, like hard-coding a map of icon names to the icon objects from the FontAwesomeIcons library. But there is no way to dynamically get a object from the class, because the getter has not been defined.

I had a similar challenge using font awesome icons that I was able to create a solution that could be helpful or at least thought provoking.
Short Answer
You can access the icons dynamically by passing the Unicode int value of the icon to the FontAwesome IconDataSolid widget
int unicodeIconString = 0xf1b9;
Icon(IconDataSolid(unicodeIconString))
Detailed Answer
My challenge was that I am using a CMS to manage content in a Flutter app. The icons are specified by a json file that loaded at run time. Initially I created a map of strings to FontAwesome icons, but I didn't want to maintain a map of icons in my own flutter code base.
My workable, but not so elegant solution was to place Unicode string in the fields in my database. Ex: '0xf1b9' for the car icon. The FontAwesomeIcons class with this mapping is generated and found here (your path will be slightly different): {your_flutter_sdk_path}/flutter/.pub-cache/hosted/pub.dartlang.org/font_awesome_flutter-8.8.1/lib/font_awesome_flutter.dart
When I pull the Unicode icon string (unicodeIconString) from the database I parse the string to an int and then pass it to IconDataSolid from the Flutter FontAwesome library:
Icon(IconDataSolid(int.parse(unicodeIconString)))
This allows me to dynamically access all the FontAwesome icons without manually creating a map. An obvious down-side is that my database has non-english unicode strings that need to be looked up when I modifying them.

Related

How to make a list of stateful widgets without passing inputs inside of the list?

I want to be able to randomly select certain widgets and use them as cards and then input values into their parameters. For example, if Ralph wanted three different fish and knew that he wanted to name them Jack, Piggy, and Simon, but his parents were buying and choosing the types of fish for him, how could we make a list of different fish at the store without names?
class Fish extends StatefulWidget {
const Fish ({
super.key,
this.color,
this.child,
this.name,
});
final Color color;
final Widget? child;
final String name;
#override
State<Fish> createState() => _FishState();
}
class _FishState extends State<Fish> {
String name = widget.name;
double _size = 1.0;
void grow() {
setState(() { _size += 0.1; });
}
#override
Widget build(BuildContext context) {
return Container(
color: widget.color,
transform: Matrix4.diagonal3Values(_size, _size, 1.0),
child: widget.child,
);
}
}
If I try to make a list of fish without naming them, it won't work since it needs me to input a name parameter. How can I avoid this or change the names afterward?
I would love to do something like this:
List<Widget> fishAtTheStore = [
Fish(color: Colors.red, child: Text("This is a fish")),
Fish(color: Colors.blue, child: Text("This is a fish")),
Fish(color: Colors.yellow, child: Text("This is a fish")),
Fish(color: Colors.green, child: Text("This is a fish")),
Fish(color: Colors.orange, child: Text("This is a fish")),
]
class RalphsAquarium extends StatefulWidget {
const RalphsAquarium({super.key});
#override
State<RalphsAquarium> createState() => _RalphsAquariumState();
}
class _RalphsAquariumState extends State<RalphsAquarium> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
fishAtTheStore[0](name: "Jack"),
fishAtTheStore[3](name: "Piggy"),
fishAtTheStore[1](name: "Simon"),
],
);
}
}
The actual functionality outside of the aforementioned issues and the required parameters does not matter to me.
Looks like you need some State Management here.
You've got a lot of libraries available to achieve this : Provider (which I recommend), Riverpod, Bloc are the most common ones (please avoid GetX)
Once you pick your State Management library, the logic to implement is the following :
Create a class Fish (not a widget, a model) which will hold all the params of your Fish.
class Fish {
Fish(this.name);
final String name;
}
Use this class in your Widget allowing to pick fishes
Create a "controller" which job will be to keep in memory the fish which will be picked
In this controller, you can add all your logic (like creating methods allowing you to update the name of the fish)
I strongly advise you to read this article of the flutter documentation first, to fully understand how to implement what you need

how can i make communication between internationalization (Intl) with enums in flutter?

How can I make communication between internationalization (Intl) with enums in Flutter?
Just below I leave a snippet of code for example:
'import '../generated/l10n.dart';
enum AminoacidosEnum { TITULO(S.current.title), HELLOWORD(" ");
final String value;
const AminoacidosEnum(this.value); }'
'import 'package:flutter/material.dart'; import 'package:internacionalizacao/enums/aminoacidos_enum.dart';
import '../generated/l10n.dart';
class HomePage extends StatelessWidget { const HomePage({super.key});
#override Widget build(BuildContext context) { return Scaffold( body: Center( //child: Text (AppLocalizations.of(context)!.helloWorld), child: Column(children:[
Text(AminoacidosEnum.TITULO),
Text(S.of(context).helloWorld),
Text(S.of(context).concatenedText('Thiago C. Pedroso')),
Text(
S.of(context).textWithPlaceHolders("Pedroso", 'Thiago Cristian')),
Text(S.of(context).pageNotificationsCount(0)),
Text(S.of(context).pageNotificationsCount(1)),
Text(S.of(context).pageNotificationsCount(2)),
//Text(S.of(context).gender(1)),
Text(S.of(context).gender("male")),
Text(S.current.pageHomeBalance(1234567890, DateTime.now())),
]),
),
);
} }'
Arguments of a constant creation must be constant expressions. Try making the argument a valid constant, or use 'new' to call the constructor. Arguments of a constant creation must be constant expressions. Try making the argument a valid constant, or use 'new' to call the constructor. A value of type 'Null' can't be assigned to a parameter of type 'String' in a const constructor. Try using a subtype, or removing the keyword 'const'.
I got what I wanted, if it's useful to someone, I've added a few more ways to use it.
enum AminoacidosEnum {
TITULO("title"),
HELLOWORD("helloWorld");
final String value;
const AminoacidosEnum(this.value);
}
import 'package:flutter/material.dart';
import 'package:internacionalizacao/enums/aminoacidos_enum.dart';
// ignore: depend_on_referenced_packages
import 'package:intl/intl.dart';
import '../generated/l10n.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
//child: Text (AppLocalizations.of(context)!.helloWorld),
child: Column(children: [
//https://localizely.com/flutter-arb/
//https://api.flutter.dev/flutter/intl/DateFormat-class.html
Text(getEnumString(AminoacidosEnum.HELLOWORD)),
Text(getEnumString(AminoacidosEnum.TITULO)),
Text(Intl.message(AminoacidosEnum.HELLOWORD.value)),
Text(Intl.message(AminoacidosEnum.HELLOWORD.value,
name: AminoacidosEnum.HELLOWORD.value)),
Text(
S.of(context).textWithPlaceHolders("Pedroso", 'Thiago Cristian')),
Text(S.of(context).pageNotificationsCount(0)),
Text(S.of(context).pageNotificationsCount(1)),
Text(S.of(context).gender("male")),
Text(S.current.pageHomeBalance(1234567890, DateTime.now())),
]),
),
);
}
String getEnumString(AminoacidosEnum enumValue) {
switch (enumValue) {
case AminoacidosEnum.TITULO:
return Intl.message("AminoacidosEnum.TITULO", name: "title");
break;
case AminoacidosEnum.HELLOWORD:
return Intl.message("AminoacidosEnum.HELLOWORD", name: "helloWorld");
break;
}
}
}

Flutter/Dart: reaction of ui depending on the inheritance class of an object

I have a class for menu-entries:
abstract class MenuSelection {
MenuSelection({required this.label});
final String label;
}
and two classes that inherit from this class:
///for menu entries with an image
abstract class ImageSelection extends MenuSelection {
ImageSelection({required this.image, required super.label});
final String image;
}
///for menu entries with an icon
abstract class IconSelection extends MenuSelection {
IconSelection({required this.iconData, required super.label});
final IconData iconData;
}
I want to have one DropDownMenu and react depending on the given class:
class _DropDownMenuItem extends StatelessWidget {
const _DropDownMenuItem({super.key, required this.selection});
final MenuSelection selection;
#override
Widget build(BuildContext context) {
return Row(
children: [
if (selection is ImageSelection)
Image.asset(
selection.image,
width: 30,
)
else if (selection is IconSelection)
Icon(
selection.iconData,
size: 30,
)
else
Container(),
const SizedBox(
width: 10,
),
Text(selection.label.tr()),
],
);
}
}
I thought it must work that way because it works for example when used with Bloc and getting the current type of state...
Actually AndroidStudio tells me that class MenuSelection has no image or icon property instead of recognizing that this is ImageSelection or IconSelection.
Can someone explain this behavior?
The implicit type promotion only works for local variables, not for class fields. You can read this answer if you need a detailed explanation and a possible solution.

Separate widgets in other files flutter

I want to make my code neater but I have a problem when I separate widgets that I use often in 1 file
here is it my main widget
import 'package:a_tiket/Helpers/widget_helper.dart';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
bool _isLoading = false;
var _message = '';
var _hasError = false;
#override
Widget build(BuildContext context) {
return
_isLoading ?
_loadingWidget(context)
:
Scaffold(
body: SingleChildScrollView(
child: Container(
),
],
),
)
)
)
;
}
}
this is my widget_helper.dart
Widget _loadingWidget (BuildContext context){
return Scaffold(
body: Center(
child: CircularProgressIndicator(
backgroundColor: ACCENT_COLOR,
valueColor: new AlwaysStoppedAnimation<Color>(PRIMARY_COLOR),
),
),
);
}
the problem is i got some error. i have add import for widget_helper but still get error
lib/Pages/loginPage.dart:193:7: Error: The method '_loadingWidget' isn't defined for the class '_LoginPageState'.
what should i do? i just want to make the code neater
please remove underline
change from
_loadingWidget(context)
to
loadingWidget(context)
There are a few issues with your code:
For such a small piece of code like showing a
CircularProgressIndicator you should not be putting a method in a separate
file. Instead of making your code "neater", you are making it harder
to read. If you really want to have it in a separate file, create a Stateless widget that shows the code you want. But then again you are just using a CircularProgressIndicator. You aren't saving any code, just creating more unnecessary code.
You already have a Scaffold where your are going to show the CircularProgressIndicator. You don't need to have another one. It's not doing anything.
While Dart uses camelCase for variable naming, file names use snake_case. Try to use it when naming files.

How to pass variables to a StatefulWidget: This class is marked as '#immutable'

I am calling a class (Titletext) that returns a row and while it works in my emulator the editor is giving me a warning so I am trying to figure out the proper way to handle this as the editor is displaying a warning.
I tried using a Stateless widget but it is supposed to accept values so that wouldn't work, I have tried google and here as well and while there are a good amount of posts on "This class (or a class which this class inherits from) is marked as '#immutable'" it doesn't really help me understand why what I'm doing is incorrect. When I add the final keyword, my constructor gets angry since my variables are then supposed to be final.
import 'package:flutter/material.dart';
import './header.dart';
import './title.dart';
class NotificationsScreen extends StatefulWidget {
createState() {
return NotificationsScreenState();
}
}
class NotificationsScreenState extends State<NotificationsScreen> {
String searchString = '';
Widget header = new Header();
Widget title = new TitleText(Icons.notifications, 'Notifications');
//team logo centered
//List of notifications
Widget build(context) {
return Container(
margin: EdgeInsets.all(20.0),
alignment: Alignment.center,
child: Column(
children: [
header,
Container(margin: EdgeInsets.only(top: 25.0)),
title,
],
),
);
}
}
import 'package:flutter/material.dart';
class TitleText extends StatefulWidget {
IconData icon = IconData(0);
String title = '';
TitleText(this.icon, this.title);
#override
_TitleState createState() => _TitleState();
}
class _TitleState extends State<TitleText> {
#override
Widget build(context) {
return Container(
width: double.infinity,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(widget.icon, size: 30),
Text(widget.title),
],
),
);
}
}
The output works as intended but with the warning I am clearly handling this wrong, I am looking for the way I should be passing values to a class like this that returns a widget.
Like the annotation says, all properties of a Widget subclass must be immutable/final.
As such, if you want to give your properties a default values, you have to do so in the constructor.
Instead of:
class Foo {
String bar = "default";
Foo({this.bar});
}
do:
class Foo {
final String bar;
Foo({this.bar = "default"});
}
or:
class Foo {
final String bar;
Foo({String bar}): bar = bar ?? "default";
}