I'm having trouble figuring out the best way to set up some constants (mainly strings). Currently I'm using a constants.dart file that just has the const variables defined in there and import it whenever it's needed. No class or anything, just a blank dart file. This works, however, I recently implemented localization using the Flutter Intl plugin in Android Studio. I got everything to work and can do something like this S.of(context).settings and it gets the translation from the correct file. My problem comes with some constant list of strings I have in my constants.dart file. I use them in many places for option selects. They look like this:
const playType = [
'RP/Story Focused',
'Battle/Combat Focused',
'Puzzles and Challenges',
'Exploration/Travel',
];
const length = [
'One Shot',
'2-5 Sessions',
'5-10 Sessions',
'On-going Campaign',
];
I cant change the the strings to the Intl reference because there is not context to be passed. Not sure how to set up a class that is loaded but not sure how to set that up and use the Provider package to serve it up.
EDIT:
heres the Constants file. Calling this with the provider is fine. The issue comes when I need to use the localization on the strings in the lists
import 'package:scryer/generated/l10n.dart';
class Constants {
Constants._();
static final instance = Constants._();
static List<String> playType = [
S.of(context).rpstoryFocused,//need a reference to a context
'Battle/Combat Focused',
'Puzzles and Challenges',
'Exploration/Travel',
];
static const length = [
'One Shot',
'2-5 Sessions',
'5-10 Sessions',
'On-going Campaign',
];
}
Heres how I call the constants on the actual page. Its a button that on press goes to a new screen that is either a multiselect checkbox list or single select radiobutton list. I pass the constants as an argument for the list
MaterialPageRoute(builder: (context) {
return MultiSelectScreen(
args: MultiSelectArguments(
label: S.of(context).selectPreferredPlayStyle,
options: playType, //this is the constants list reference
selections:
profile.playType,
),
);
})
The easy solution is to not use a constant and just create the lists in these spots, only used like twice i think, but better practice is to pull it out since its being used multiple times
you can try.
class Constants {
final BuildContext context;
Constants(#required this.context);
// static final instance = Constants._(); // to make class a singleton
static const playType = [
'RP/Story Focused',
'Battle/Combat Focused',
'Puzzles and Challenges',
'Exploration/Travel',
];
static const length = [
'One Shot',
'2-5 Sessions',
'5-10 Sessions',
'On-going Campaign',
];
}
Then, at the root of your project you should provide this class like;
void main() {
/** WidgetsFlutterBinding.ensureInitialized() is required in Flutter v1.9.4+ before using any plugins if the code is executed before runApp. */
WidgetsFlutterBinding.ensureInitialized();
runApp( Provider(
create: (context) => Constants(context),
child: MyApp(),
),
);
}
You will be able to access these constants in any BuildContext anywhere down the widget tree as follows;
final Contants constants = Provider<Constants>.of(context);
you get the length constant like; constants.length
Related
still new to flutter and I was trying to take multiple values from TextFields in a form to display them in a new screen inside multiple Text elements.
Can someone explain how to do it ?
There are three ways to do it
First method: You can define a class and assign values to it like this:
class Global(){
String text;
}
and then you can import it and assign values or use it like this:
// assign data
Global().text = TextField_controller; // I assume you have already implemented a TextField
// use it
Text(Global().text)
This method is good for passing data between multiple pages but it's not recommended because you can't update the screen when the value changes, it's only good when you need to pass a static variable between multiple pages, for example a user name
Second method: passing data to next page directly
Make the SecondScreen constructor take a parameter for the type of data that you want to send to it. In this particular example, the data is defined to be a String value and is set here with this.text.
class SecondScreen extends StatelessWidget {
final String text;
SecondScreen({Key key, #required this.text}) : super(key: key);
...
Then use the Navigator in the FirstScreen widget to push a route to the SecondScreen widget. You put the data that you want to send as a parameter in its constructor.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(text: 'Hello',),
));
this method is great for passing data from a parent page to a child page however it can quickly become a nightmare if you want to pass the data to several children down the widget tree or move data back to the parent widget, in that case you can use method 1 or
Third method: using Provider, which is the recommended way, it is similar to the first method but with provider you can ``notify``` all of the listeners to the provider class, meaning you can update the widget whenever the the variable updates, I strongly recommend reading the documentation or watching some YouTube videos, but in short you can use it like this:
after installing the provider package you define your class:
Global extends ChangeNotifierProvider(){
String text;
}
and then add it to the root of your app in main.dart:
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Global()),
),
],
child: MyApp(),
);
}
and then you define your provider wherever you need to use it
Provider.of<Global>(context, listen: false); // Note that if you want to listen to changes you have to set listen to true
// then you can access your variable like in method 1
insatnce.text = TextField_controller;
// and then you can use it anywhere
Text(instance.text);
again if you find this confusing read the documentation or watch some videos
Can someone tell me what is happening in this program?
body: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new StuffInTiles(listOfTiles[index]);
},
itemCount: listOfTiles.length,
),
),
);
}
}
class StuffInTiles extends StatefulWidget{
final MyTile myTile;
const StuffInTiles(this.myTile);
#override
StuffInTilesState createState() => StuffInTilesState();
}
class StuffInTilesState extends State<StuffInTiles> {
#override
Widget build(BuildContext context) {
return Container(child:
//Text(widget.myTile.title),);
_buildTiles(widget.myTile));
}
Widget _buildTiles(MyTile t) {
I want to understand how passing parameters works,why i have
const StuffInTiles(this.myTile);
in this program, what this code is doing?
in my class StuffInTilesState extends State<StuffInTiles> i don't have any constructor, so how this code is working? why my parameters just happen to be there? before i was learning C++, so this is like a magic to me
If you learned C++ you are probably familiar with initializer list, which Dart has as well. In C++ you could do something:
MyClass {
MyClass(MyTitle title) : this.myTitle = title;
final MyTitle myTitle;
}
Which is also valid in Dart. However, Dart allows you to shorthand the call, by automatically assigning the reference to the new class property, without using any intermediate variable.
MyClass(this.myTitle);
Which is basically the same but ensures that any given property won't be null unless you explicitly pass null.
There are other type of constructors available on Dart, such as private constructors, factory constructors and named constructors. You may want to check the official documentation to learn more about it.
By default, when you create classes in Dart, there is just a default constructor implemented.
For example, if you have a class named AwesomeWidget. It will have a default constructor AwesomeWidget() that lets you create an instance of this widget.
So you could use a default constructor in code like so:
//Example 1
return AwesomeWidget();
//Example 2
AwesomeWidget myWidget = AwesomeWidget();
//Example 3
//...
Row(
children: [
Text("Example Code!"),
AwesomeWidget(),
Text("Example Footer Code!"),
],
),
//...
Now if you want to pass some values or some data to your Widget classes then you use the code you have posted above in your question.
The question is: Why would we want to send data to our widgets?
Answer: The biggest use case is when we make our list items as separate widgets. For example, in my food app, I have to show my user's order history in a ListView, so for the UI of each individual list item, I will just make a reusable Widget called OrderHistoryListItem.
In that OrderHistoryListItem, you want to show the date and time of the object. And the order id, and how much the user paid in that order or any other details, so to display this, we send this data to our Reusable Widget List Item, which displays it. Simple as that.
And that is one of the reasons why we pass values to Widgets. As a programmer, you can make use of this handy feature for more complex scenarios, be creative!
I have this dart file category_data.dart:
import 'package:flutter/material.dart';
final List categories = [
'Entertainment',
'Sports',
'Politics',
'Science',
'Technology',
'Travel'
];
/*
you can change your category here.
If you change, make sure you have changed in the admin panel.
Otherwise the app will show error.
*/
/*
if your change the defalut category, make sure your category item munber is equal to category colors item number.
Example: If you have 5 categories, then remove an color item in the category colors.
else if you have more than 6 categories, then you have to add color items in the category colors List down below.
*/
final List categoryColors = [
Colors.orange[200],
Colors.blue[200],
Colors.red[200],
Colors.pink[200],
Colors.purple[200],
Colors.blueGrey[400]
];
I want to add internationalization through intl plugin. For this I am using the follow code to get the locale translation of text:
AppLocalizations.of(context).categoryName
Then categories list would be the following:
import 'package:flutter/material.dart';
import 'package:news_app/generated/l10n.dart';
final List categories = [
AppLocalizations.of(context).entertainment,
AppLocalizations.of(context).sports,
AppLocalizations.of(context).politics,
AppLocalizations.of(context).science,
AppLocalizations.of(context).technology,
AppLocalizations.of(context).travel
];
But this code give me an error, because I don't have the context:
Undefined name 'context'.
Try correcting the name to one that is defined, or defining the name
How could you get the context to use internationalization? I have only seen the context in the build method of the widgets.
You can define a method and pass the context to it.
List _getCategoryList(BuildContext context) {
return [
AppLocalizations.of(context).entertainment,
AppLocalizations.of(context).sports,
AppLocalizations.of(context).politics,
AppLocalizations.of(context).science,
AppLocalizations.of(context).technology,
AppLocalizations.of(context).travel
];
}
You can use it like:
final List categories = _getCategoryList(context); -- Use this inside your widget.
I had the same issue before, the way i resolved it is to create a GlobalKey:
static GlobalKey rootWidgetKey = GlobalKey();
Then attach it to your root widget:
...
home: YourRootWidget(
key: rootWidgetKey,
...
Then you can access the root context anywhere by referring to rootWidgetKey.currentContext.
add your list after the build method like the example
#override
Widget build(BuildContext context) {
final List categories = [
AppLocalizations.of(context).entertainment,
AppLocalizations.of(context).sports,
AppLocalizations.of(context).politics,
AppLocalizations.of(context).science,
AppLocalizations.of(context).technology,
AppLocalizations.of(context).travel
];
return ...
I'm facing an issue where I'm trying to use multiple LocalizationsDelegates in a MaterialApp.
I'm using the Dart intl tools to provide translations to my labels. When I have multiple LocalizationsDelegates only the one that is specified the first gets the translated values. The labels of the next delegate, get the default value provided in the Intl.message() function.
Short, self contained, correct example
I've set up a minimal project as an example of this issue on GitHub.
Code snippets
In the MaterialApp, I define a bunch of localizationsDelegates, including two app specific ones: DogLocalizationsDelegate and CatLocalizationsDelegate.
MaterialApp(
// other properties
locale: Locale("en"),
localizationsDelegates: [
CatLocalizationsDelegate(),
DogLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('en'),
const Locale('nl'),
],
);
The delegates have the same boilerplate code, but provide different labels.
Here's how the DogLocalizations and its DogLocalizationsDelegate look like.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'messages_all.dart';
class DogLocalizations {
static Future<DogLocalizations> load(Locale locale) {
final String name = locale.languageCode;
final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName;
return DogLocalizations();
});
}
static DogLocalizations of(BuildContext context) {
return Localizations.of<DogLocalizations>(context, DogLocalizations);
}
String get bark {
return Intl.message(
'<insert dog sound>',
name: 'bark',
);
}
}
class DogLocalizationsDelegate extends LocalizationsDelegate<DogLocalizations> {
const DogLocalizationsDelegate();
#override
bool isSupported(Locale locale) => ['en', 'nl'].contains(locale.languageCode);
#override
Future<DogLocalizations> load(Locale locale) => DogLocalizations.load(locale);
#override
bool shouldReload(DogLocalizationsDelegate old) => false;
}
The CatLocalizations are the same, but with a meow String getter. Full example in the GitHub project.
Commands used to generate translations files
I'm using multiple extraction and generation commands instead of having multiple files in one command. This is because I'm actually having this problem with a library (with its own labels) and a consumer of that library (that also has its own labels).
Extract labels of both cats and dogs
flutter pub run intl_translation:extract_to_arb --output-dir=lib/cat_labels/gen lib/cat_labels/CatLabels.dart
flutter pub run intl_translation:extract_to_arb --output-dir=lib/dog_labels/gen lib/dog_labels/DogLabels.dart
Translate the generated intl_messages.arb to have two language files
intl_en.arb
intl_nl.arb
And then add the correct translated values to these files.
Generate the dart files from ARB
flutter pub run intl_translation:generate_from_arb --output-dir=lib/cat_labels lib/cat_labels/CatLabels.dart lib/cat_labels/gen/intl_*.arb
flutter pub run intl_translation:generate_from_arb --output-dir=lib/dog_labels lib/dog_labels/DogLabels.dart lib/dog_labels/gen/intl_*.arb
Issue
In this demo project, having the following order of delegates:
// main.dart (line 20)
DogLocalizationsDelegate(),
CatLocalizationsDelegate(),
will give the translation for the bark label, but not for the meow label.
When switching it:
// main.dart (line 20)
CatLocalizationsDelegate(),
DogLocalizationsDelegate(),
will give the translation for the meow label, but not for the bark label.
Why multiple localization delegates
In case you're wondering why: I'm using labels in a library and in the consumer apps of that library.
Important to know is that it's not (really) possible to specify both localization files in the same generator command because of this.
From what I learned, no, you can’t use multiple localizations delegates like this. That is because intl’s initializeMessages can only be called once per locale.
So in your example, once your CatLocalizationsDelegate runs initializeMessages, the one for DogLocalizationsDelegate has no effect. That’s why you are only seeing the translation Meow! but not Dog’s, or whichever one gets to run it first.
For additional reading, check out https://phrase.com/blog/posts/how-to-internationalize-a-flutter-app/ and please share feedback at https://github.com/flutter/flutter/issues/41437.
I find a solution with a dedicated flutter package available here: [https://pub.dev/packages/multiple_localization][1]
You can use the multiple_localizations package. it worked for me
I would like to start putting all my constant strings (like labels etc.) into a place that can be translated at a later stage.
How is this handled in Flutter?
Create a Localizations.dart file
Add the following code to that file:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show SynchronousFuture;
class DemoLocalizations {
DemoLocalizations(this.locale);
final Locale locale;
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
}
static Map<String, Map<String, String>> _localizedValues = {
'en': {
'title': 'App title',
'googleLogin': 'Login with Google'
},
'es': {
'title': 'Título de App',
'googleLogin': 'Conectar con Google'
},
};
String get title {
return _localizedValues[locale.languageCode]['title'];
}
String get googleLogin {
return _localizedValues[locale.languageCode]['googleLogin'];
}
}
class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
#override
bool isSupported(Locale locale) => ['en', 'es'].contains(locale.languageCode);
#override
Future<DemoLocalizations> load(Locale locale) {
// Returning a SynchronousFuture here because an async "load" operation
// isn't needed to produce an instance of DemoLocalizations.
return new SynchronousFuture<DemoLocalizations>(new DemoLocalizations(locale));
}
#override
bool shouldReload(DemoLocalizationsDelegate old) => false;
}
Import Localizations.dart into the file where you use the strings.
Add the delegate DemoLocalizationsDelegate in the MaterialApp
MaterialApp(
localizationsDelegates: [
MyLocalizationsDelegate(),
],
...
)
Substitute new Text("App Title"), with new Text(DemoLocalizations.of(context).title),
For each new string you want to localize, you need to add the translated text to each language's map and then add the String get... line.
It's a bit cumbersome but it does what you need it to.
This is a quick overview of one way of doing it.
You can read more about it in the Flutter docs: https://flutter.io/tutorials/internationalization/
I asked on gitter and I got the following:
Translation/Internationalization isn't a feature we consider "done"
yet. https://pub.dartlang.org/packages/intl works in Flutter. We have
a bug tracking this more generally: flutter/flutter#393
More complete internationalization (i18n) and accessibility support
are two of the big arcs of work ahead of Flutter in the coming months.
Another example of i18n work we have planned, is completing
Right-to-left (RTL) layouts for our provided Widgets (e.g. teaching
the Material library's Scaffold to place the Drawer on the left when
the locale is an RTL language). RTL Text support works today, but
there are no widgets which are out-of-the-box RTL-layout aware at this
moment.