I want to add textfield that has its own unique index. So I declared a list of TextFieldEditingController and call it in the Wrap(). controllers.add(TextEditingController()),. But I am getting this error while calling it. I am getting this error This expression has a type of 'void' so its value can't be used. Try checking to see if you're using the correct API; there might be a function or call that returns void you didn't expect. Also check type parameters and variables which might also be void..
List<TextEditingController> controllers = [];
Wrap(
children: retrieveData.map((order) {
return Row(
children: [
Expanded(
child: Card(
child:
SvgPicture.asset('assets/toiletpaper.svg')),
),
Expanded(
flex: 2,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
CustomText(
text: order.commodityName.toString()),
CustomText(text: '60 rolls per pack'),
SizedBox(
height: screenHeight(context) / 40,
),
controllers.add(TextEditingController()), // this is the error
Flexible(
child: TextFormValidator(
fillColor: Colors.white,
contentPadding:
EdgeInsets.symmetric(vertical: 8),
outlineInputBorder: OutlineInputBorder(),
controller: description,
textAlign: TextAlign.left),
)
],
),
),
)
],
);
}).toList()),
The line causing the error is just an expression: controllers.add(...) returns void. You placed that line inside of the children: [...] list, which has the type List<Widget>.
That's why this doesn't work:
children: [CustomText(...), CustomText(...), controllers.add(...), Flexible(...)]
^ Widget ^ Widget ^ void ^ Widget
I don't understand entirely what you are trying to achieve, that's why I can't give you a working solution, but maybe this will work:
Wrap(
children: retrieveData.map((order) {
controllers.add(TextEditingController()) // move the line here
return Row(
children: [
...
]
);
}).toList(),
)
the children property of the Column widget is just a List<Widget>, so it accepts only the actual flutter widget to be set inside of it, in your code, by doing the:
SizedBox(
height: screenHeight(context) / 40,
),
controllers.add(TextEditingController()),
//...
you're trying just to put another type than what is accepted from the children property, the add() method do just add elements to a List which is the controllers list and doesn't really return a Widget, it has a type of void, so the error is thrown.
you will need to take it and put it in a scope that accepts calling method (add()), like build() function scope, a lifecycle method such initState(), or even wrapping a widget with a Builder widget then call it inside the builder property.. :
Widget build() {
controllers.add(TextEditingController()),
// Your widget here
}
Related
I am trying to modify a TextField inside an ExpansionTile title and when I modify the text I want to update data in my database firestore, this part works fine. The problem is when I write something in my TextField I can only write one letter and then I have to select the TextField again.Initial text field value
Second text field value
In the second picture after writing the number 2 in test the 'blue mark' exits the TextField and if I want to write in the TextField again I have to select the text field again.
This is the TextField code inside the expansion tile, all this code is inside a stream where each list tile is an entry from the database:
return SizedBox(
width: double.infinity,
child: SingleChildScrollView(
child: Column(
children: [
Column(
children: [
ListView.builder(
physics: const ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: widget.shoppingListsCart.length,
itemBuilder: (BuildContext context, int index) {
String shoppingCartListName =
widget.shoppingListsCart.elementAt(index).name;
final shoppingListId =
widget.shoppingListsCart.elementAt(index).shoppingListId;
TextEditingController _textEditingControllerShoppingListName = TextEditingController();
_textEditingControllerShoppingListName.text = shoppingCartListName;
_textEditingControllerShoppingListName.addListener(() {
widget.firebaseCloudStorage.updateShoppingListName(
widget.shoppingCartListId, shoppingListId, _textEditingControllerShoppingListName.text);
});
return Column(
children: [
ExpansionTile(
backgroundColor: Colors.indigo,
title: SizedBox(
child: Row(
children: [
SizedBox(
width: 220,
child: TextField(
decoration: const InputDecoration(
border: InputBorder.none,
counterText: '',
),
controller: _textEditingControllerShoppingListName,
maxLength: 35,),
), ),
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(onPressed: (){
}, icon:Icon(Icons.edit,)),
IconButton(onPressed: () {}, icon: Icon(Icons.delete)),
],
),
]),
),
children: [
ProductsInShoppingListView(
shoppingListName: shoppingCartListName,
shoppingCartId: widget.shoppingCartListId,
shoppingListId: shoppingListId,
firebaseCloudStorage: widget.firebaseCloudStorage,
),
]),
],
);
},
),
Column(
children: [
ProductsInCartView(
shoppingCartId: state.shoppingCartId),
],
),
],
),],
),
),
);
I tried using a Future in the text field and changing from a stateless widget to a stateful widget, the error is something like this TextField accepts only one letter and loses focus after each letter but since I am using flutter that solution didn't work for me. I don't know what is the error because the console doesn't say anything. Please help, thank you.
did you try it? It's TextFormField. Here I fixed the value 1, we can make it a dynamic value. But I hope this will be helpful to think about next.
TextFormField(
onChanged: (value) {
if (value.length == 1) {
FocusScope.of(context).nextFocus(); //whatever you want to do
}
},
inputFormatters: [
LengthLimitingTextInputFormatter(1)
],
),
return Scaffold(
body: Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
color: Colors.red,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: SelectGroupCard(context, titles: titles, ids: ids,
onTap: (title, id) {
setState(() {
cardGroupResult = title + " " + id;
});
}),
),
),
],
),
There are several cards as seen in the picture. I want to get these right out. I even want to be able to show these cards in the middle of the page, in 3 columns, 4 rows.
Use widget GridView, it allows you to select the desired number of columns and rows. Also, it is possible to install paddings.
you can use GridView
GridView(
gridDelegate:const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // you can change this
),
children :[...] // put your cards here !
)
you can remove the width property of SelectGroupCard. for example, if SelectGroupCard is a row widget, the following code like this:
Row(
mainAxisSize: MainAxisSize.min,
children: [],
)
I'm already losing sleep over this.
I'm trying to display a chart inside a ListView (for scrolling). For some reason the contents of the Card flickers when scrolling and randomly completely disappears (the Card itself stays visible though).
Any idea why would that happen?
(...) ListView (...)
children: [Row ( children: [buildChartBox()] )] (...)
Expanded buildChartBox() {
return Expanded(
child: Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
chartTitles(
title: 'Items',
subtitle: 'by value'),
SizedBox(
height: 300,
child: ValuesChart(data: calculateValues(items)))
],
),
],
),
),
),
);
}
Row chartTitles({String title = '', String subtitle = ''}) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: text_charttitle),
Text(subtitle, style: text_chartsubtitle),
],
)
],
);
}
Things tried:
Both of these were originally Stateless Widgets; I changed to simple
methods to simplify but it didn't change the weird behaviour.
Replacing the chartTitles return with an empty Container (i.e. removing the titles) does mitigate the issue. The chart then stays displayed but also flickers slightly.
Replacing the ListView with a SingleChildScrollView doesn't change anything.
EDIT: Code for the ValuesChart:
import 'package:fl_chart/fl_chart.dart';
class ValuesChart extends StatelessWidget {
final Map<String, int> data;
const ValuesChart({required this.data});
#override
Widget build(BuildContext context) {
return Container(
child: PieChart(
_theData(data),
));
}
}
Note I'm using a package called 'fl_chart'. _theData just returns various parameters for the chart, I don't think it's relevant.
Try to replace ListView with SingleChildScrollView
ListViews in flutter by default using what it is called in Android RecyclerView to efficiently use render resources.
If you are interested here an article
https://medium.com/1mgofficial/how-recyclerview-works-internally-71290de5d2c4
I need to fix a minimum width to my Column Widgets. Inside each of them, I have Text Widgets which can be very short or very long. I need to fix a minimum width to them in order to have an acceptable size of Column even if the text is short. The other Column need obviously to adapt himself.
Row(children: [
Column(
children: [
Container(
constraints: BoxConstraints(minWidth: 80), // do not work
child: Text("short text"),
),
],
),
Column(
children: [
Container(
constraints: BoxConstraints(minWidth: 110), // do not work
child: RichText(
text: TextSpan(
text:"very very longggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg text")),
),
],
),
],
)
There's probably a dozen ways to do what you want. And likely none of them straightforward or easy to understand. (The subject of constraints & sizes is quite complicated. See this constraints page for more examples & explanations.)
Here's one potential solution.
This will set a minimum width for the blue column (based on stepWidth), but will expand/grow if the text (child) inside wants to.
The yellow column will resize to accommodate the blue column.
class ExpandedRowPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Expanded Row Page'),
),
body: SafeArea(
child: Center(
child: Row(
children: [
IntrinsicWidth(
stepWidth: 100,
// BLUE Column
child: Container(
color: Colors.lightBlueAccent,
child: Column(
children: [
//Text('Short'),
Text('shrt')
],
)
),
),
// YELLOW Column
Flexible(
child: Container(
alignment: Alignment.center,
color: Colors.yellow,
child: Column(
children: [
Text('Very lonnnnnnnnnnnnnnnnnnnnnnnnnnnng texttttttttttttt'),
],
)
),
)
],
)
),
),
);
}
}
You could do the above without a Flexible yellow column, but a very long text child would cause an Overflow warning without a Flexible or Expanded wrapping widget.
A Row widget by itself has an infinite width constraint. So if a child wants to be bigger than screen width, it can, and will cause an overflow. (Try removing Flexible above and rebuild to see.)
Flexible and Expanded, used only inside Row & Column (or Flex, their superclass), checks screen width and other widgets inside a Row, and provides its children with a defined constraint size instead of infinite. Children (inside Flexible/Expanded) can now look up to parent for a constraint and size themselves accordingly.
A Text widget for example, will wrap its text when it's too wide for constraints given by Flexible/Expanded.
use FittedBox();
suppose Example:
Row(
children: [
Column(
children: [
Container(
constraints: BoxConstraints(minWidth: 80), // do not work
child: Text("short text"),
),
],
),
Column(
children: [
Container(
constraints: BoxConstraints(minWidth: 110), // do not work
child:
FittedBox(
child: RichText(
text: TextSpan(
text:
"very very longggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg text")),
),
),
],
),
],
);
I have multiple forms inside a PageView, Forms are in different files like registration_form.dart contains the Sign-Up form and so on. In my App, each page contains a different Form. I want that when the user clicks on "Continue", the form will be validated and in an error situation, the user will be warned. I call all the pages in one class called Body as shown below. The "Continue" button is inside of it in the Opacity container. If there is a better approach to follow as a solution I am open to recommendations.
#override
Widget build(BuildContext context) {
return SafeArea(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: MediaQuery.of(context).size.height * 0.65,
child: Flex(
direction: Axis.horizontal,
children: [
Flexible(
child: PageView(
controller: _controller,
//physics: new NeverScrollableScrollPhysics(),
children: [
RegisterForm(),
WelcomeForm(),
//CompanyForm(),
//CompanyNextForm(),
//CompanyLogoForm(),
//FinancingDataForm(),
//UtilityForm(),
//MatrixInformationForm(),
//MatrixInformationNextForm(),
//MatrixInformationLastForm(),
//PriceBuildingForm(),
//InstallKitForm(),
//InstallKitDetailedForm(),
//CustomPricingForm(),
//CustomPricingNextForm(),
//FillRow1Form(),
//FillItem1Row1Form(),
//FillItem2Row1Form(),
//FillItem3Row1Form(),
//FillRow2Form(),
//FillItem1Row2Form(),
//FillItem2Row2Form(),
//FillItem3Row2Form(),
//FillRow3Form(),
//FillItem1Row3Form(),
//FillItem2Row3Form(),
//FillItem3Row3Form(),
//InvoicingForm(),
//FinancingForm(),
//FinancingNextForm(),
//FinancingLastForm(),
//FinalizeForm(),
//DoneForm(),
//BookingForm(),
],
),
),
],
),
),
SizedBox(
height: ResponsiveLayout.isSmallScreen(context)
? 10
: ResponsiveLayout.isMediumScreen(context)
? 10
: 10,
),
Opacity(
opacity: 1, //currentIndex == 20 ? 0 : 1,
child: Container(
height: 50,
decoration: BoxDecoration(
color: Color.fromRGBO(16, 88, 198, 1),
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child: GestureDetector(
onTap: () {
_controller.nextPage(
duration: Duration(milliseconds: 300),
curve: Curves.easeIn);
},
child: Container(
width: MediaQuery.of(context).size.width,
height: 100,
decoration: BoxDecoration(
color: Color.fromRGBO(16, 88, 198, 1),
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 100.0,
child: Center(
child: RichText(
text: TextSpan(children: [
WidgetSpan(
child: Text(
'Continue ',
style: TextStyle(
color: Colors.white,
fontSize: ResponsiveLayout
.isSmallScreen(context)
? 12
: ResponsiveLayout.isMediumScreen(
context)
? 12
: 15,
),
)),
WidgetSpan(
child: Icon(
Icons.arrow_forward,
size: ResponsiveLayout.isSmallScreen(
context)
? 12
: ResponsiveLayout.isMediumScreen(
context)
? 12
: 15,
color: Colors.white,
),
),
]),
),
)),
],
),
),
)),
),
],
),
),
);
}
Okay. I was struggling with the same question recently and was looking for a good approach. Maybe this answer will be helpful for any other developer looking for the answer.
Approach
Currently, in my case, I used form keys for validations and function callbacks. This solution did the job for me because I needed to take input as raw text. Others like multiple choice options similar where there were predefined outputs.
To describe my solution more explicitly. Consider this example, we want to get basic details of the user like name, age, city etc. For user input like the name, we can use TextFormField. This will give access to the onChanged callback for validation. To access the response in the PageView widget containing the class. You can use the TextEditingController.
Now, we can then simply add the Form widget at the parent of the basic form widget build method.
Finally for multiple choice questions. We can provide a callback function like onTap to the widget of PageView. This function will be called whenever the user interacts with the dropdown or similar widget.
Note: If we have multiple forms in the PageView widget. You will be needing separate form keys for individual forms.
Code Example
So, we have the main form_screen.dart containing the PageView widget and basic_profile.dart containing our form. Both the files should look something like this:
form_screen.dart
...
// define the variables and keys here
final _basicProfileKey = GlobalKey<FormState>();
final _userName = TextEditingController();
late String _userGender;
...
// callback function that we will be passing to the BasicProfile
// widget on the other page
void _userGender(String value) {
_userGender = value;
}
...
// the submission callback that will be called whenever the user
// clicks on the next or save button available in the class file
// (this file) containing the PageView widget
void _submissionCallback(){
if(_pageViewIndex == 0) {
final validationStatus = _basicProfileKey.currentState?.validate() ?? false;
if(validationStatus) {
// implement your logic here and then move to next page in the pageview
}
}
}
...
// Build method widget tree containing the PageView and BasicProfile
// widgets
child: PageView(
children: [
BasicProfile(
basicProfileKey: _basicProfileKey,
userName: _userName,
userGenderCallback: userGender
),
]
),
basic_profile.dart
...
// declare the variables for this widget which we will be initialised
// via constructor
final GlobalKey<FormState> basicProfileKey;
final TextEditingController userName;
final Function(String) userGenderCallback;
...
#override
void initState(){
// initialise the default values here if any and call the
// callback function received above
userGenderCallback(_defaultValue);
}
...
Widget build(BuildContext context){
...
child: Form(
key: basicProfileKey,
...
TextFieldForm(
onValidate: (){
// do the validation here
}
)
...
DropDown(
onChanged: (value) {
// logic for validation
userGenderCallback(value);
}
)
}
In my approach used setState as the state management solution but other state solutions can also be used for easier state sharing between the widgets.
Hope this helps!