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;
}
}
}
Related
I created simple app to test bloc 7.2.0 and faced that BlocBuilder doesn't rebuild after first successful rebuild. On every other trigger bloc emits new state, but BlocBuilder ignores it.
Please note, if I remove extends Equatable and its override from both, state and event, then BlocBuilder rebuilds UI every time Button pressed. Flutter version 2.5.1
If Equatable is necessary, why it's not working with it? If Equatable isn't necessary, why it's been used in initial creation via VSCode extension.
My code:
bloc part
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
//bloc
class MainBloc extends Bloc<MainEvent, MainState> {
MainBloc() : super(MainInitial()) {
on<MainButtonPressedEvent>(_onMainButtonPressedEvent);
}
void _onMainButtonPressedEvent(
MainButtonPressedEvent event, Emitter<MainState> emit) {
emit(MainCalculatedState(event.inputText));
}
}
//states
abstract class MainState extends Equatable {
const MainState();
#override
List<Object> get props => [];
}
class MainInitial extends MainState {}
class MainCalculatedState extends MainState {
final String exportText;
const MainCalculatedState(this.exportText);
}
//events
abstract class MainEvent extends Equatable {
const MainEvent();
#override
List<Object> get props => [];
}
class MainButtonPressedEvent extends MainEvent {
final String inputText;
const MainButtonPressedEvent(this.inputText);
}
UI part
import 'package:bloc_test/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: BlocProvider(
create: (context) => MainBloc(),
child: SubWidget(),
),
),
);
}
}
class SubWidget extends StatelessWidget {
TextEditingController inputText = TextEditingController();
String? exportText;
#override
Widget build(BuildContext context) {
MainBloc mainBloc = BlocProvider.of<MainBloc>(context);
return BlocBuilder<MainBloc, MainState>(
builder: (context, state) {
if (state is MainCalculatedState) {
exportText = state.exportText;
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${exportText ?? ''} data'),
SizedBox(
width: 200,
child: TextField(
controller: inputText,
),
),
ElevatedButton(
onPressed: () =>
mainBloc.add(MainButtonPressedEvent(inputText.text)),
child: const Text('Button')),
],
),
);
},
);
}
}
Equatable is used to make it easy for you to program, how and when states are the same (no update) and when they are different (update).
Your updates do not work because you are sending the same state repeatedly, but you did not tell the Equatable extension how to find out if they are different. So they are all the same.
So to make sure your program understands that some states of the same kind are indeed different and should cause an update, you need to make sure you mention what makes them different:
class MainCalculatedState extends MainState {
final String exportText;
const MainCalculatedState(this.exportText);
// this tells the Equatable base class to consider your text property
// when trying to figure out if two states are different.
// If the text is the same, the states are the same, so no update
// If the text is different, the states are different, so it will update
#override
List<Object> get props => [this.exportText];
}
If you remove Equatable altogether, two newly instanciated states are never equal, so that would solve your problem as well... except that at some point you will want them to be, and then you need to add it back in.
Your MainCalculatedState needs to override the props getter from Equatable and return the list of all properties which should be used to assess equality. In your case it should return [exportText].
Example:
class MainCalculatedState extends MainState {
final String exportText;
const MainCalculatedState(this.exportText);
#override
List<Object> get props => [exportText];
}
I followed the BloC tutorial and did according to them, I have a Bloc which has this method
#override Stream<FridgeState> mapEventToState(FridgeEvent event) async* { .. }
where FridgeEvent is an abstract class
abstract class FridgeEvent {
const FridgeEvent();
}
class CreateFridgeEvent extends FridgeEvent {
final double lat, lng;
CreateFridgeEvent({required this.lat, required this.lng});
}
class DeleteFridgeEvent extends FridgeEvent {}
When instantiating the bloc within the widget and passing in a subclass of FridgeEvent, I get an error that the parameter type can't be assigned. What am I doing wrong?
Upon request, here's the code of the bloc
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:foodey_flutter/domain/fridge/entity/Fridge.dart';
import 'package:foodey_flutter/domain/fridge/usecase/CreateFridgeUseCase.dart';
import 'package:foodey_flutter/factory/FridgeFactory.dart';
import 'package:foodey_flutter/ui/fridge/create//event.dart';
import 'package:foodey_flutter/ui/fridge/create//state.dart';
class FridgeBloc extends Bloc<FridgeEvent, FridgeState> {
CreateFridgeUseCase? createFridgeUseCase;
FridgeBloc(FridgeState initialState) : super(initialState) {
FridgeFactory.inject(this);
}
#override
Stream<FridgeState> mapEventToState(FridgeEvent event) async* {
if (event is CreateFridgeEvent) {
Fridge? result = await createFridgeUseCase?.execute(event.lat, event.lng);
if (result != null)
yield FridgeSuccessfullyLoadedState(result);
else
yield FridgeErrorState(
exception: Exception("Failed to create a fridge"));
} else {
yield FridgeErrorState(exception: Exception("Operation not supported"));
}
}
}
Here's the code of the widget
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:foodey_flutter/ui/fridge/create/FridgeBloc.dart';
import 'package:foodey_flutter/ui/fridge/create/event.dart';
class CreateFridgeWidget extends StatefulWidget {
#override
_CreateFridgeState createState() => _CreateFridgeState();
}
class _CreateFridgeState extends State<CreateFridgeWidget> {
late FridgeBloc _bloc;
#override
void initState() {
super.initState();
this._bloc = BlocProvider.of<FridgeBloc>(context);
}
#override
Widget build(BuildContext context) {
return Container(
key: widget.key,
alignment: Alignment.center,
padding: EdgeInsets.all(8),
child: Column(
children: [
Text("Are you sure you want to create your fridge?"),
Row(
children: [
TextButton(
key: widget.key,
onPressed: () {
_bloc.add(CreateFridgeEvent(lat: 0, lng: 1));
},
child: Text("Yes"),
),
TextButton(
key: widget.key,
onPressed: () => {},
child: Text("No"),
),
],
)
],
),
);
}
}
And here are my defined events
abstract class FridgeEvent {
const FridgeEvent();
}
class CreateFridgeEvent extends FridgeEvent {
final double lat, lng;
CreateFridgeEvent({required this.lat, required this.lng});
}
class DeleteFridgeEvent extends FridgeEvent {}
Update: After copy pasting the code here and checking for completeness I found the issue. For anyone facing the same: the import of the event within the bloc code contains two /'s within the path. After removing one / the compiler didn't complain anymore that the wrong type is being passed. No idea how this second / was added and why the compiler didn't complain at all about that. Apparently, the objects defined in ..//event.dart are not the same when imported from ../event.dart
After copy pasting the code here and checking for completeness I found the issue. For anyone facing the same: the import of the event within the bloc code contains two /'s within the path. After removing one / the compiler didn't complain anymore that the wrong type is being passed. No idea how this second / was added and why the compiler didn't complain at all about that.
Apparently, the objects defined in ..//event.dart are not the same when imported from ../event.dart
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.
I am using shared_preferences in my Flutter application for iOS and Android. On the web I am using the http:dart dependency (window.localStorage) itself. Since Flutter for web was merged into the Flutter repo, I want to create a cross platform solution.
This means I need to import two seperate API's. This seems not to be very good supported in Dart yet, but this is what I did:
import 'package:some_project/stub/preference_utils_stub.dart'
if (dart.library.html) 'dart:html'
if (dart.library.io) 'package:shared_preferences/shared_preferences.dart';
In my preference_utils_stub.dart file, I implemented all classes/variables which need to be visible during compile time:
Window window;
class SharedPreferences {
static Future<SharedPreferences> get getInstance async {}
setString(String key, String value) {}
getString(String key) {}
}
class Window {
Map<String, String> localStorage;
}
This gets rid of all errors before compilation. Now I implemented some method which checks if the application is using the web or not:
static Future<String> getString(String key) async {
if (kIsWeb) {
return window.localStorage[key];
}
SharedPreferences preferences = await SharedPreferences.getInstance;
return preferences.getString(key);
}
However, this gives loads of errors:
lib/utils/preference_utils.dart:13:7: Error: Getter not found:
'window'.
window.localStorage[key] = value;
^^^^^^ lib/utils/preference_utils.dart:15:39: Error: A value of type 'Future<SharedPreferences> Function()' can't be assigned to a
variable of type 'SharedPreferences'.
- 'Future' is from 'dart:async'.
- 'SharedPreferences' is from 'package:shared_preferences/shared_preferences.dart'
('../../flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.5.4+3/lib/shared_preferences.dart').
SharedPreferences preferences = await SharedPreferences.getInstance;
^ lib/utils/preference_utils.dart:22:14: Error: Getter not found:
'window'.
return window.localStorage[key];
And so on. How can one use different methods/classes depending on the platform without these errors? Note that I am using more dependencies this way, not just preferences. Thanks!
Here is my approach to your issue. This is based on the implementations from http package as in here.
The core idea is as follows.
Create an abstract class to define the methods you will need to use.
Create implementations specific to web and android dependencies which extends this abstract class.
Create a stub which exposes a method to return the instance of this abstract implementation. This is only to keep the dart analysis tool happy.
In the abstract class import this stub file along with the conditional imports specific for mobile and web. Then in its factory constructor return the instance of the specific implementation. This will be handled automatically by conditional import if written correctly.
Step-1 and 4:
import 'key_finder_stub.dart'
// ignore: uri_does_not_exist
if (dart.library.io) 'package:flutter_conditional_dependencies_example/storage/shared_pref_key_finder.dart'
// ignore: uri_does_not_exist
if (dart.library.html) 'package:flutter_conditional_dependencies_example/storage/web_key_finder.dart';
abstract class KeyFinder {
// some generic methods to be exposed.
/// returns a value based on the key
String getKeyValue(String key) {
return "I am from the interface";
}
/// stores a key value pair in the respective storage.
void setKeyValue(String key, String value) {}
/// factory constructor to return the correct implementation.
factory KeyFinder() => getKeyFinder();
}
Step-2.1: Web Key finder
import 'dart:html';
import 'package:flutter_conditional_dependencies_example/storage/key_finder_interface.dart';
Window windowLoc;
class WebKeyFinder implements KeyFinder {
WebKeyFinder() {
windowLoc = window;
print("Widnow is initialized");
// storing something initially just to make sure it works. :)
windowLoc.localStorage["MyKey"] = "I am from web local storage";
}
String getKeyValue(String key) {
return windowLoc.localStorage[key];
}
void setKeyValue(String key, String value) {
windowLoc.localStorage[key] = value;
}
}
KeyFinder getKeyFinder() => WebKeyFinder();
Step-2.2: Mobile Key finder
import 'package:flutter_conditional_dependencies_example/storage/key_finder_interface.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SharedPrefKeyFinder implements KeyFinder {
SharedPreferences _instance;
SharedPrefKeyFinder() {
SharedPreferences.getInstance().then((SharedPreferences instance) {
_instance = instance;
// Just initializing something so that it can be fetched.
_instance.setString("MyKey", "I am from Shared Preference");
});
}
String getKeyValue(String key) {
return _instance?.getString(key) ??
'shared preference is not yet initialized';
}
void setKeyValue(String key, String value) {
_instance?.setString(key, value);
}
}
KeyFinder getKeyFinder() => SharedPrefKeyFinder();
Step-3:
import 'key_finder_interface.dart';
KeyFinder getKeyFinder() => throw UnsupportedError(
'Cannot create a keyfinder without the packages dart:html or package:shared_preferences');
Then in your main.dart use the KeyFinder abstract class as if its a generic implementation. This is somewhat like an adapter pattern.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_conditional_dependencies_example/storage/key_finder_interface.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
KeyFinder keyFinder = KeyFinder();
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SafeArea(
child: KeyValueWidget(
keyFinder: keyFinder,
),
),
);
}
}
class KeyValueWidget extends StatefulWidget {
final KeyFinder keyFinder;
KeyValueWidget({this.keyFinder});
#override
_KeyValueWidgetState createState() => _KeyValueWidgetState();
}
class _KeyValueWidgetState extends State<KeyValueWidget> {
String key = "MyKey";
TextEditingController _keyTextController = TextEditingController();
TextEditingController _valueTextController = TextEditingController();
#override
Widget build(BuildContext context) {
return Material(
child: Container(
width: 200.0,
child: Column(
children: <Widget>[
Expanded(
child: Text(
'$key / ${widget.keyFinder.getKeyValue(key)}',
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
),
Expanded(
child: TextFormField(
decoration: InputDecoration(
labelText: "Key",
border: OutlineInputBorder(),
),
controller: _keyTextController,
),
),
Expanded(
child: TextFormField(
decoration: InputDecoration(
labelText: "Value",
border: OutlineInputBorder(),
),
controller: _valueTextController,
),
),
RaisedButton(
child: Text('Save new Key/Value Pair'),
onPressed: () {
widget.keyFinder.setKeyValue(
_keyTextController.text,
_valueTextController.text,
);
setState(() {
key = _keyTextController.text;
});
},
)
],
),
),
);
}
}
some screen shots
Web
mobile
you can just use the package universal_html
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";
}