I need a little help,
I create TextFields dynamically and I want to add the TextField values to a list,
Here is how I create TextFields dynamically,
List<TextEditingController> descriptionControllers = List.generate(11,(_) => TextEditingController(),);
Here is how I display all these TextFields in a DataTable
DataCell(Container(
width: 150,
child: TextField(
minLines: 1,
maxLines: null,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.only(left: 10, right: 10),
),
controller: descriptionControllers[index],
))),
Here is how I am collecting the values of all these TextFields,
void collectData() {
descriptions.clear();
for (int index = 0; index < descriptionControllers.length; index++) {
descriptions.add(descriptionControllers[index].text);
print(descriptions);
}
}
but the problem is, when i print the data, it creates 11 Lists because I have 11 TextFields but I dont want that, I want a single list with all these values in it.
here is the printed lists,
I/flutter (29182): [Just]
I/flutter (29182): [Just, To]
I/flutter (29182): [Just, To, Check]
I/flutter (29182): [Just, To, Check, The ]
I/flutter (29182): [Just, To, Check, The , Whole ]
I/flutter (29182): [Just, To, Check, The , Whole , List]
I/flutter (29182): [Just, To, Check, The , Whole , List, ]
I/flutter (29182): [Just, To, Check, The , Whole , List, , ]
how can i add all these values to a single list, instead of all these many lists.
Related
I ran into a weird behavior related to the TextFormField, so I maintained a list of Objects in the parent widget, I'm using the following code to render a list of child widgets
children: <Widget>[
...childProfiles
.map((e) => ChildProfileCard(
childProfile: e,
removeChildProfile: removeChildProfile))
.toList(),
And the ChildProfileCard includes a TextFormField, the code is like the following
TextFormField(
decoration: const InputDecoration(
contentPadding: EdgeInsets.only(top: 0),
hintText: "Enter child's name",
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Name is required';
}
return null;
},
),
There is a "remove" function that simply removes one of the items from the list like the following
setState(() {
childProfiles = childProfiles
.where((childProfile) => childProfile.id != childProfileToRemove.id)
.toList();
});
When there are more than two items (two child widgets), I input some texts in the TextFormField in the first child widgets, then I remove the first item, the text will always automatically apply to the second child widget, what did I do wrong? I can confirm the list is correctly updated, but the text behaves strangely.
Before deleting, you can see we have different texts for different widgets
After deleting, the first widget's text is wrongfully copied over to the next widget, you can see the uuid is the second widget's.
You should Use any unique key while building ChildProfileCard
ChildProfileCard(key: Key(<ANY UNIQUE VALUE>),)
Example
ChildProfileCard(key: Key(e.id),)
I am trying to create a list of reorderable items with with Reorderables package; I have a reorderable wrap, inside it I have reorderable columns. I'm using two diffirent list for this.
List<Widget> items = [];
List childrenItems = [];
The lists are created from data looking like this
List taskList = [
// First child Item
{
"name":"NOrbert kRoSs",
"sub-children":[
"child1","child2"
],
},
// Second child Item
{
"name":"Genius",
"sub-children":[
"Relerx","Yiuja","No filter"
],
},
];
The Method used to populate the lists
generateList(List incoming){
List<Widget> children = [];
children.clear();
if(childrenItems.length > 0) {childrenItems.clear();}
if(items != null) items.clear();
if( incoming != null )
for(int n=0; n<=incoming.length-1;n++){
children.clear();
for(int sub = 0; sub<=incoming[n]["sub-children"].length-1;sub++){
children.add(
cardWidget(
header: incoming[n]["sub-children"][sub]["name"].toString(),
key: sub.toString(),
parentKey: incoming != null?n.toString():"0",
),
);
}
childrenItems.insert(n, children);
items.add(
mainListItem(
nPos: n,
header:incoming !=null?incoming[n]["name"].toString():"Aberor",
mainKeys: n.toString(),
),
);
}
}
The reorderables
They
ReorderableWrap(
children: items != null?
items:
(<Widget>[
Container(
key: ValueKey("newVal"),),
]),
onReorder: setRedorder),
ReorderableColumn(
children: childrenItems[nPos] != null?
childrenItems[nPos]:
(<Widget>[
Container(
height: 30,
color: Colors.redAccent,
width: 60.0,
key: ValueKey("value"),
),
]),
onReorder: setNewOrder,
),
The Problem
When I run the app I get the error below
Duplicate GlobalKeys detected in widget tree.
The following GlobalKeys were specified multiple times in the widget tree. This will lead to parts of the widget tree being truncated unexpectedly, because the second time a key is seen, the previous instance is moved to the new location. The keys were:
[GlobalObjectKey ValueKey<String>#c9f0c]
[GlobalObjectKey ValueKey<String>#7990e]
[GlobalObjectKey ValueKey<String>#89542]
This was determined by noticing that after widgets with the above global keys were moved out of their respective previous parents, those previous parents never updated during this frame, meaning that they either did not update at all or updated before the widgets were moved, in either case implying that they still think that they should have a child with those global keys.
The specific parents that did not update after having one or more children forcibly removed due to GlobalKey reparenting are:
Column(direction: vertical, mainAxisAlignment: start, mainAxisSize: min, crossAxisAlignment: center, renderObject: RenderFlex#1c68d relayoutBoundary=up36)
I am a 100% sure there are no duplicate keys and that from the error message its the parents that are not updating. How can I fix this
For some reason I do not understand, doing
children = [];
Instead of
children.clear();
Solves my issue.
Try to use the different ValueKey.
The description in the key.dart said that LocalKey(ValueKey extended LocalKey) must be unique amongst the Element with the same parent.
I'm trying to arrange two texts in a row. The second text is only a few words, but the first can be very long. My goal is to show the second text fully, and fill the remaining space with the first. If the first widget is too wide, I want to truncate it with an ellipsis.
Expected layout:
My code is
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
GlobalKey key = GlobalKey();
return Row(
children: [
Container(
child: Text("Really really really long long long teeeeext",
maxLines: 1,
overflow: TextOverflow.ellipsis),
width: MediaQuery.of(context).size.width - MediaQuery.of(key.currentContext).size.width),
Text("short text", key: key)
],
);
}
}
It fails with The following assertion was thrown building MyHomePage(dirty, dependencies: [MediaQuery]): I/flutter (28083): 'package:flutter/src/widgets/media_query.dart': Failed assertion: line 810 pos 12: 'context != I/flutter (28083): null': is not true.
I guess because the Flutter engine hasn't started rendering the "short text" widget yet, so it's size is unknown at this time.
Any help will be appreciated.
What if you try to init your second Text Widget like Text _secondText=Text("short text")? So Flutter should already know its content when you use your first widget. You can give it a try.
MediaQuery.of(context).size isn't available during build phase. While building the widget, sizes aren't calculated yet.
However you don't need MediaQuery.of(context).size to implement the layout. Instead, you can simply wrap left text with Expanded. Then it will take all remaining space (total width minus right text width).
Row(
children: [
Expanded(
child: Text(
"Really really really long long long teeeeext 123 123 123 123 123 123 123 123",
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Text(
"short text",
),
],
)
Result:
I want to display a list of widgets with variable sizes.
Flutter's Wrap is a great fit for my layout needs.
final List<dynamic> someItems;
return SingleChildScrollView(
child: Wrap(
children: someItems.map((item) => _createTile(context, item)).toList(),
),
scrollDirection: Axis.vertical,
);
But a fatal problem, Wrap can't create widgets lazily. For example, if there are less than 100 Tile widgets displayed in the image above, but the number of data list is more, Wrap creates even invisible widgets.
// The result of taking a log once for each item when configuring Wrap with a list of 1000 data.
I/flutter ( 4437): create Widget 0
I/flutter ( 4437): create Widget 1
I/flutter ( 4437): create Widget 2
I/flutter ( 4437): create Widget 3
I/flutter ( 4437): create Widget 4
I/flutter ( 4437): create Widget 5
I/flutter ( 4437): create Widget 6
I/flutter ( 4437): create Widget 7
I/flutter ( 4437): create Widget 8
...
I/flutter ( 4437): create Widget 999
ListView and GridView create widgets lazily, but (at least as far as I currently know) they are not free to lay out widgets like Wrap.
Is there a way to implement the layout I want?
Try this:
Wrap(
children: someItems.map((item) => _createTile(context, item)).toList().cast<Widget>(),
)
I am trying to add dynamic translation to text in a stateful widget. (Maybe there is a better approach to this)
I have a function that retrieves a translation from the google api for each Text in my Flutter app. The problem is that neither "drawer" nor "expansionTile" have an ontap(extended does have an 'on expansion changed') that I can use so I have tried to create a "oneshot" approach by only enabling the function and setState if the language is changed but it seems like a bit of a kludge. There has to be a better way. And as it has to look up each one sequentially it becomes quite slow. Is there any way to do a complete conversion of all Text widgets in the background and store it? Do I need to build a map? Might get real messy real fast though. Gotta be something I am missing.
The whole widget is pretty big so I have just included the relevant code here.
class _HomePageState extends State<HomePage> {
//call check wifi connection
bool _wifi = false;
bool _isButtonDisabled = false;
String _textTranslated = "no translation";
String _languageSelected = "en";
String _oldLanguageSelected;
Map<String,String> _getLanguageList; // roughed in for getting google languages
Future<bool> _translate(text) async {
// capture translation so that it will not keep looping when no new language is selected
if(_languageSelected == _oldLanguageSelected){
print('languages match');
return true;
}else{
_oldLanguageSelected = _languageSelected;
print("translate input: $text");
// TODO add wifi check
final translator = new GoogleTranslator();
final textTranslated = await translator.translate(text, to: '$_languageSelected');
setState(() {
print('Translation is $textTranslated');
_textTranslated = textTranslated;
return true;
});
//_textTranslated = text;
print('translated output: $_textTranslated');
return true;
}
}
And here is my drawer:
drawer: Drawer(
child: ListView(
children: <Widget>[
ExpansionTile(
title: Text('Select a New Language\n(RB Experimental)',textAlign: TextAlign.center, textScaleFactor: 1.2, style: TextStyle(color: Colors.blue[900])),
children: <Widget>[
ListTile(
leading: Container(height: 30, width: 60, child: Image.asset('images/flags/nl.png')),
title: Text('Dutch'),
trailing: Text("code: nl"),
onTap:() async { print("Selecting nl language");
_languageSelected = "nl";
//_translate('test nl string');
Navigator.pop(context,true); // close drawer
},
),
ListTile(
leading: Container(height: 30, width: 60, child: Image.asset('images/flags/fr.png')), //Icon(FontAwesomeIcons.signLanguage),
title: Text('French'),
trailing: Text("code: fr"),
onTap:() { print("Selecting fr language");
_languageSelected = "fr";
_translate('test fr string');
Navigator.pop(context,true); // close drawer
},
),
And here is where I call the _translate function in the body
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 150.0),//(25.0),
child: _translate('Configuration Tool') != null? Text('$_textTranslated ',
textScaleFactor: 1.5,):Text('no translation'),
),
And finally my Future function in my NetworkUtilities class:
class NetworkUtilities {
static Future<Map<String,String>> getGoogleLanguageList() async{
// https://translation.googleapis.com/language/translate/v2/languages
print('Ping google translate api for its language list');
//TODO get this list from Google
final translations = {'Dutch':'nl', 'French': 'fr', 'German':'de', 'Italian':'it', 'Spanish':'es', 'Default to English':'en'};
return(translations);
}
And this is the output from terminal for my prints:
I/flutter ( 9874): Selecting fr language
I/flutter ( 9874): languages match
I/flutter ( 9874): Selecting de language
I/flutter ( 9874): translate input: test de string
I/flutter ( 9874): Translation is Test de Zeichenfolge
I/flutter ( 9874): translated output: Test de Zeichenfolge
I/flutter ( 9874): languages match
I/flutter ( 9874): Selecting de language
I/flutter ( 9874): languages match
I/flutter ( 9874): Selecting it language
I/flutter ( 9874): translate input: test it string
I/flutter ( 9874): Translation is Test si stringa
I/flutter ( 9874): translated output: Test si stringa
I/flutter ( 9874): languages match
After asking the question and putting words to screen it became pretty obvious that this is not the way to go for translations, as it is just too slow. Instead I am going to go to a static model and map out all of my Text widgets into an indexed list.