Bottom pixel overflow in stepper view widget inside scaffold - dart

I have flutter app that contain Stepper Widget. My stepper is wrapped with Scaffold. And in my stepper contains Column with two TextFormField.
Whenever i touch input Text field in my TextFormField, then Keyboard show up with warning/error bottom pixel overflowed by 11 pixels.
I already tried add resizeToAvoidBottomPadding: false on Scaffold in page root.dart. It does remove bottom pixel overflowed by 11 pixels warning/error. But my 'BottomAppNavigation' is dissapear, like this:
https://i.imgur.com/io5lHwJ.png
Also already add singleChildScrollViewon Scaffold in page root.dart and it gives error:
I/flutter (21466): The following RenderObject was being processed when the exception was fired:
I/flutter (21466): RenderStack#d0145 relayoutBoundary=up16 NEEDS-LAYOUT NEEDS-PAINT
Root.dart
return Scaffold(
body: PageView(
children: [
HomePage(
userId: _userId,
auth: widget.auth,
onSignedOut: _onSignedOut),
MyStepper(),
Account("Account screen"),
],
onPageChanged: onPageChanged,
controller: _pageController,
),
MyStepper() is calling different dart file. This is MyStepper() page code:
MyStepper.dart
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.lightGreen,
),
title: 'App',
home: Scaffold(
appBar: AppBar(title: Text('Add Device')),
body: Stack(
children: <Widget>[
_myStepper(),
Align(
alignment: Alignment.bottomCenter,
child: _buttonBottomBar(),
)
],
)),
);
}
Widget _myStepper() {
return Stepper(
type: StepperType.horizontal,
currentStep: _currentStep,
onStepTapped: (int step) => setState(() => _currentStep = step),
controlsBuilder: _createEventControlBuilder,
onStepContinue:
_currentStep < 2 ? () => setState(() => _currentStep += 1) : null,
onStepCancel:
_currentStep > 0 ? () => setState(() => _currentStep -= 1) : null,
steps: <Step>[
Step(
title: Text('Connect'),
content:
_stepOne(),
isActive: _currentStep >= 0,
state: _currentStep >= 0 ? StepState.complete : StepState.disabled,
),
],
);
}
Widget _stepOne() {
return Column(
children: <Widget>[
TextFormField(
onSaved: (String value) {
data.ssid = value;
},
maxLines: 1,
validator: (value) {
if (value.isEmpty || value.length < 1) {
return 'SSID!';
}
},
decoration: InputDecoration(
labelText: 'enter SSID',
hintText: 'enter SSID',
icon: const Icon(Icons.wifi),
labelStyle:
TextStyle(decorationStyle: TextDecorationStyle.solid)),
),
TextFormField(
onSaved: (String value) {
data.password = value;
},
maxLines: 1,
validator: (value) {
if (value.isEmpty || value.length < 1) {
return 'password SSID!';
}
},
decoration: InputDecoration(
labelText: 'pass SSID',
hintText: 'pass SSID',
icon: const Icon(Icons.signal_wifi_4_bar_lock),
labelStyle:
TextStyle(decorationStyle: TextDecorationStyle.solid)),
),
],
);
}

Related

Flutter - DropdownButtonFormField value not updating

My Dropdown button value doesn't update until the AlertDialog box is closed and reopened.
I have the variable set at the top of my class
class _ItemListState extends State<ItemList> {
int _ratingController;
...
}
Within the class I have an AlertDialog that opens a form, within here I have the DropdownButtonFormField
AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
controller: _eateryController,
autofocus: true,
decoration:
InputDecoration(labelText: 'Eatery', hintText: 'eg Pizza Hut'),
),
TextField(
controller: _supplierController,
decoration: InputDecoration(
labelText: 'Supplier', hintText: 'eg Deliveroo'),
),
TextField(
controller: _descriptionController,
decoration: InputDecoration(
labelText: 'Description', hintText: 'eg cheese pizza'),
),
DropdownButtonFormField<int>(
value: _ratingController,
items: [1, 2, 3, 4, 5]
.map((label) => DropdownMenuItem(
child: Text(label.toString()),
value: label,
))
.toList(),
hint: Text('Rating'),
onChanged: (value) {
setState(() {
_ratingController = value;
});
},
),
],
),
actions: <Widget>[
FlatButton(
onPressed: () {
_handleSubmit(_eateryController.text, _supplierController.text,
_descriptionController.text, _ratingController);
Navigator.pop(context);
},
child: Text('Save'),
),
FlatButton(
onPressed: () => Navigator.pop(context),
child: Text('Cancel'),
)
],
);
the setState doesn't seem to be dynamically updating the fields value. The updated value will only show once I close and re open the AlertDialog.
How can I get this to update instantly?
Thanks
You need to create a new StatefulWidget class that should return your AlertDialog
class MyDialog extends StatefulWidget {
#override
_MyDialogState createState() => _MyDialogState();
}
class _MyDialogState extends State<MyDialog> {
int _ratingController;
#override
Widget build(BuildContext context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
controller: _eateryController,
autofocus: true,
decoration:
InputDecoration(labelText: 'Eatery', hintText: 'eg Pizza Hut'),
),
TextField(
controller: _supplierController,
decoration: InputDecoration(
labelText: 'Supplier', hintText: 'eg Deliveroo'),
),
TextField(
controller: _descriptionController,
decoration: InputDecoration(
labelText: 'Description', hintText: 'eg cheese pizza'),
),
DropdownButtonFormField<int>(
value: _ratingController,
items: [1, 2, 3, 4, 5]
.map((label) => DropdownMenuItem(
child: Text(label.toString()),
value: label,
))
.toList(),
hint: Text('Rating'),
onChanged: (value) {
setState(() {
_ratingController = value;
});
},
),
],
),
actions: <Widget>[
FlatButton(
onPressed: () {
_handleSubmit(_eateryController.text, _supplierController.text,
_descriptionController.text, _ratingController);
Navigator.pop(context);
},
child: Text('Save'),
),
FlatButton(
onPressed: () => Navigator.pop(context),
child: Text('Cancel'),
)
],
);
}
}
Use it like this
showDialog(
context: context,
builder: (context) {
return MyDialog();
},
);
You can use statefulBuilder inside alert dialog.
return AlertDialog(
title: new Text('Table'),
content: StatefulBuilder(builder:
(BuildContext context,
StateSetter setState) {
return Container(
child: new SingleChildScrollView(
scrollDirection: Axis.vertical,
child: new Column(
children: <Widget>[
DropdownButtonFormField<String>(
value: dropdownValue,
style: TextStyle(
color: Colors.black87),
items: <String>[
'Lot 1',
'Lot 2',
'Lot 3',
'Lot 4',
].map<
DropdownMenuItem<
String>>(
(String value) {
return DropdownMenuItem<
String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
),
```
It looks like you're calling setState in a wrong widget. The AlertDialog doesn't belong to ItemList's tree because it's located inside another Route. So calling setState inside _ItemListState won't rebuild the AlertDialog.
Consider pulling out content of AlertDialog into a separate StatefulWidget and putting int _ratingController into it's state.

Trouble with flutter radio button

I just want to have normal radio buttons where only one button in the group can be selected. According to tutorials online we need to set the groupvalue variable to the value in the setState method in onChanged. If i do this i can select all buttons and i dont want this to happen. I want only one button to be a selected at a time(from that group). if there is anything wrong with my code or any other way to do it let me know.
option is the string parameter for title optionvalue is the value for that option.
class Option extends StatefulWidget {
final String option;
final int optionvalue;
Option(this.option, this.optionvalue);
_OptionState createState() => _OptionState();
}
class _OptionState extends State<Option> {
int groupvalue;
#override
Widget build(BuildContext context) {
return Container(
child: Container(
child: RadioListTile(
title: Text(
widget.option,
style: TextStyle(fontSize: 20.0),
),
activeColor: Colors.black,
value: widget.optionvalue,
groupValue: groupvalue,
onChanged: (int a) {
print(a);
setState(() {
groupvalue = a;
});
},
),
),
);
}
}
Try this code
int _groupValue = -1;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
_myRadioButton(
title: "Checkbox 0",
value: 0,
onChanged: (newValue) => setState(() => _groupValue = newValue),
),
_myRadioButton(
title: "Checkbox 1",
value: 1,
onChanged: (newValue) => setState(() => _groupValue = newValue),
),
],
);
}
Widget _myRadioButton({String title, int value, Function onChanged}) {
return RadioListTile(
value: value,
groupValue: _groupValue,
onChanged: onChanged,
title: Text(title),
);
}
Output:
Try this for horizontal radio button list:
int _groupValue = -1;
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text('Gender', style: TextStyle(fontSize: 18)),
Container(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
flex: 1,
child: RadioListTile(
value: 0,
groupValue: _groupValue,
title: Text("Male"),
onChanged: (newValue) =>
setState(() => _groupValue = newValue),
activeColor: Colors.red,
selected: false,
),
),
Expanded(
flex: 1,
child: RadioListTile(
value: 1,
groupValue: _groupValue,
title: Text("Female"),
onChanged: (newValue) =>
setState(() => _groupValue = newValue),
activeColor: Colors.red,
selected: false,
),
),
],
),
],
),
),
],
),
),

Another exception was thrown: No Material widget found

So basically there is this product edit page which behaves differently uppon called.
If the product is being created for first time, then it is shown in a tab view controller.
if the product is being updated, its body is returned in scaffold.
here are some screenshots
when I submit through create product, i encounter no error.
But when I submit through update product, though the logic works, i get a short red screen with
like this
error Another exception was thrown: No Material widget found.
Here is the code for the screen
`
import 'package:flutter/material.dart';
import 'package:academy_app/models/products.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:academy_app/scoped-model/Products.dart';
class ProductEdit extends StatefulWidget {
ProductEdit();
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return ProductEditState();
}
}
class ProductEditState extends State<ProductEdit> {
String title;
String description;
double price;
Product product;
final focusnode2 = FocusNode();
final focusnode3 = FocusNode();
Map<String, dynamic> formData = {
"name": null,
"desc": null,
"price": null,
"image": "asset/foood.jpg"
};
GlobalKey<FormState> formkey = GlobalKey<FormState>();
Widget buildTitle(productItem) {
return TextFormField(
initialValue: productItem != null ? productItem.title : "",
validator: (String value) {
if (value.isEmpty || value.length < 3) {
return 'title cannot be empty';
}
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(labelText: "Title"),
onFieldSubmitted: (String value) {
FocusScope.of(context).requestFocus(focusnode2);
},
onSaved: (String valuee) {
setState(() {
formData["name"] = valuee;
});
},
);
}
Widget buildDesc(productItem) {
return TextFormField(
initialValue: productItem != null ? productItem.description : "",
validator: (String value) {
if (value.isEmpty || value.length < 3) {
return 'Cant have that short description';
}
},
textInputAction: TextInputAction.next,
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(focusnode3);
},
focusNode: focusnode2,
maxLines: 3,
decoration: InputDecoration(labelText: "Description"),
onSaved: (String valuee) {
setState(() {
formData["desc"] = valuee;
});
},
);
}
Widget buildPrice(productItem) {
return TextFormField(
initialValue: productItem != null ? productItem.price.toString() : "",
textInputAction: TextInputAction.done,
focusNode: focusnode3,
decoration: InputDecoration(labelText: " How much"),
keyboardType: TextInputType.number,
onFieldSubmitted: (value) {
focusnode3.unfocus();
},
validator: (value) {
if (!RegExp(r'^(?:[1-9]\d*|0)?(?:\.\d+)?$').hasMatch(value)) {
return ' Enter numbers only';
}
},
onSaved: (String valuee) {
setState(() {
formData["price"] = double.parse(valuee);
});
},
);
}
void submitForm(Function addProduct, Function updateProduct, int index) {
if (!formkey.currentState.validate()) {
return;
}
formkey.currentState.save();
setState(() {
if (index == null) {
addProduct(Product(
price: formData["price"],
title: formData["name"],
description: formData["desc"],
image: "asset/foood.jpg"));
} else {
updateProduct(
Product(
price: formData["price"],
title: formData["name"],
description: formData["desc"],
image: "asset/foood.jpg"),
);
}
Navigator.pushReplacementNamed(context, '/');
});
}
Widget buildSubmitButton() {
return ScopedModelDescendant<ProductsModel>(
builder: (BuildContext context, Widget, ProductsModel) {
return RaisedButton(
child: Text("Save"),
onPressed: () => submitForm(ProductsModel.addProduct,
ProductsModel.updateProduct, ProductsModel.selected_index));
},
);
}
Widget _buildPageContent(BuildContext context, Product product) {
final double deviceWidth = MediaQuery.of(context).size.width;
final double targetWidth = deviceWidth > 550.0 ? 500.0 : deviceWidth * 0.95;
final double targetPadding = deviceWidth - targetWidth;
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
margin: EdgeInsets.all(10.0),
child: Form(
key: formkey,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: targetPadding / 2),
children: <Widget>[
buildTitle(product),
buildDesc(product),
buildPrice(product),
SizedBox(
height: 10.0,
),
buildSubmitButton()
],
),
),
),
);
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<ProductsModel>(
builder: (context, Widget child, ProductsModel) {
product = ProductsModel.getproduct();
return ProductsModel.selected_index == null
? _buildPageContent(context, product)
: Scaffold(
appBar: AppBar(
title: Text("Update Item"),
),
body:_buildPageContent(context, product) ,
);
},
);
}
}
`
why am i getting that red screen error? i confused about passing the contexts. why arent the textfiled accessing the material parent through in scaffold?
As the error suggests that no material widget found, you need to wrap the Container of the _buildPateContent function into Material Widget. Here is the change that you can do:
Widget _buildPageContent(BuildContext context, Product product) {
final double deviceWidth = MediaQuery.of(context).size.width;
final double targetWidth = deviceWidth > 550.0 ? 500.0 : deviceWidth * 0.95;
final double targetPadding = deviceWidth - targetWidth;
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Material(
child: Container(
margin: EdgeInsets.all(10.0),
child: Form(
key: formkey,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: targetPadding / 2),
children: <Widget>[
buildTitle(product),
buildDesc(product),
buildPrice(product),
SizedBox(
height: 10.0,
),
buildSubmitButton()
],
),
),
),
));
}
try wrapping you app in materialApp or wrap TextField in material Widget.
In my case I forgot to wrap my widget with Scaffold widget. A lot of widgets need to be wrapped with it to work properly. So change this
Widget build(BuildContext context) {
return YourScreenContent();
}
to this
Widget build(BuildContext context) {
return Scaffold(
body: YourScreenContent(),
);
}
I had same issue.
I got the error when I had code like this.
Widget _getLCSBar(int index) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 0.1),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_getLikeButton(index),
_getCommentButton(index),
_getShareButton(index),
],
));
}
What I have done is wrapped it with Material
Widget _getLCSBar(int index) {
return Material(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 0.1),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_getLikeButton(index),
_getCommentButton(index),
_getShareButton(index),
],
)));
}
Problem Solved!
Just wrap the Gesture Detector inside Material:
return Material(
child: GestureDetector(
...
),
);

Textfield validation in Flutter

I am working on Flutter TextField widget. I want to show an error message below the TextField widget if the user does not fill that TextField. I only have to use TextField Widget not TextFormField in this case.
A Minimal Example of what you Want:
class MyHomePage extends StatefulWidget {
#override
MyHomePageState createState() {
return new MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
final _text = TextEditingController();
bool _validate = false;
#override
void dispose() {
_text.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TextField Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Error Showed if Field is Empty on Submit button Pressed'),
TextField(
controller: _text,
decoration: InputDecoration(
labelText: 'Enter the Value',
errorText: _validate ? 'Value Can\'t Be Empty' : null,
),
),
RaisedButton(
onPressed: () {
setState(() {
_text.text.isEmpty ? _validate = true : _validate = false;
});
},
child: Text('Submit'),
textColor: Colors.white,
color: Colors.blueAccent,
)
],
),
),
);
}
}
Flutter handles error text itself, so we don't require to use variable _validate. It will check at runtime whether you satisfied the condition or not.
final confirmPassword = TextFormField(
controller: widget.confirmPasswordController,
obscureText: true,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_open, color: Colors.grey),
hintText: 'Confirm Password',
errorText: validatePassword(widget.confirmPasswordController.text),
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
);
String validatePassword(String value) {
if (!(value.length > 5) && value.isNotEmpty) {
return "Password should contain more than 5 characters";
}
return null;
}
Note: User must add at least one character to get this error message.
I would consider using a TextFormField with a validator.
Example:
class MyHomePageState extends State<MyHomePage> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TextFormField validator'),
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
hintText: 'Enter text',
),
textAlign: TextAlign.center,
validator: (text) {
if (text == null || text.isEmpty) {
return 'Text is empty';
}
return null;
},
),
RaisedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// TODO submit
}
},
child: Text('Submit'),
)
],
),
),
);
}
}
If you use TextFormField then you could easily implement 'Error
below your text fields'.
You can do this without using _validate or any other flags.
In this example, I have used validator method of TextFormField
widget. This makes the work a lot more easier and readable at the
same time.
I also used FormState to make the work more easier
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final _form = GlobalKey<FormState>(); //for storing form state.
//saving form after validation
void _saveForm() {
final isValid = _form.currentState.validate();
if (!isValid) {
return;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Form(
key: _form, //assigning key to form
child: ListView(
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Full Name'),
validator: (text) {
if (!(text.length > 5) && text.isNotEmpty) {
return "Enter valid name of more then 5 characters!";
}
return null;
},
),
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
validator: (text) {
if (!(text.contains('#')) && text.isNotEmpty) {
return "Enter a valid email address!";
}
return null;
},
),
RaisedButton(
child: Text('Submit'),
onPressed: () => _saveForm(),
)
],
),
),
),
);
}
}
I hope this helps!
For TextFiled and TextFormFiled Validation you can use this Example I hope this will helpful for you people.
TextField(
enableInteractiveSelection: true,
autocorrect: false,
enableSuggestions: false,
toolbarOptions: ToolbarOptions(
copy: false,
paste: false,
cut: false,
selectAll: false,
),
controller: _currentPasswordController,
obscureText: passwordVisible,
decoration: InputDecoration(
errorText: Validators.password(
_currentPasswordController.text),
filled: true,
fillColor: Colors.white,
contentPadding:
const EdgeInsets.fromLTRB(20, 24, 12, 16),
border: const OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(8.0))),
// filled: true,
labelText: 'Password',
hintText: 'Enter your password',
suffixIcon: GestureDetector(
onTap: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
child: Container(
margin: const EdgeInsets.all(13),
child: Icon(
passwordVisible
? FontAwesomeIcons.eyeSlash
: Icons.remove_red_eye_sharp,
color: ColorUtils.primaryGrey,
size: 25)),
),
),
),
Validation Message Example Code
static password(String? txt) {
if (txt == null || txt.isEmpty) {
return "Invalid password!";
}
if (txt.length < 8) {
return "Password must has 8 characters";
}
if (!txt.contains(RegExp(r'[A-Z]'))) {
return "Password must has uppercase";
}
if (!txt.contains(RegExp(r'[0-9]'))) {
return "Password must has digits";
}
if (!txt.contains(RegExp(r'[a-z]'))) {
return "Password must has lowercase";
}
if (!txt.contains(RegExp(r'[#?!#$%^&*-]'))) {
return "Password must has special characters";
} else
return;
}

How can I remove internal padding on a RadioListTile so I can use 3 RadioListTiles in a row?

I am pretty new to Flutter and Dart and I can't seem to find any hints for this particular topic. I am trying to put 3 RadioListTiles in a Row like so:
Row(
children: [
Expanded(
child:RadioListTile<GoalSelection>(
title: Text(
'Net',
style: Theme.of(context).textTheme.body1,
),
value: GoalSelection.net,
groupValue: _goalSelection,
onChanged: (GoalSelection value) {
setState(() {
_goalSelection = value;
});
},
),
),
Expanded(
child: RadioListTile<GoalSelection>(
title: Text(
'Gross',
style: Theme.of(context).textTheme.body1,
),
value: GoalSelection.gross,
groupValue: _goalSelection,
onChanged: (GoalSelection value) {
setState(() {
_goalSelection = value;
});
},
),
),
Expanded(
child: RadioListTile<GoalSelection>(
title: Text(
'Salary',
style: Theme.of(context).textTheme.body1,
),
value: GoalSelection.salary,
groupValue: _goalSelection,
onChanged: (GoalSelection value) {
setState(() {
_goalSelection = value;
});
},
),
),
],
),
The buttons layout fine, but there seems to be a lot of wasted space for the label. I put a screenshot of what it currently looks like below. I have tried wrapping the Expanded, the RadioListTile, and the Text in Padding widgets (all one at a time) to manually set the padding to 0, but it didn't do anything. I have also tried to change Expanded to Flexible even though I didn't think that would change anything. I am at a loss now. Is there any way to get this layout to work? I am kind of assuming it is something really dumb that I am doing.
You can use Radio + text widget instead of RadioListTile. For removing internal padding in Radio widget set:
Radio(
visualDensity: const VisualDensity(
horizontal: VisualDensity.minimumDensity,
vertical: VisualDensity.minimumDensity),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
.....
),
You can use a Radio and Text widget in a row. But the Radio also has the same padding problem. To remove the padding you can put the Radio as a child of a SizedBox.
eg:- SizedBox(height: 20, width: 20, child: Radio(.......))
RadioListTile is used with the purpose of taking the full width in a vertical scroll list.
If you don't want this behavior, don't use it. Use Radio instead.
just set contentPadding: EdgeInsets.zero
RadioListTile(contentPadding: EdgeInsets.zero)
We can control the padding of the RadioListTile using Flexible widget. As you want to arrange 3 RadioListTiles inside a Row Widget. Please try with the below code, it will work.
Row(
children: <Widget>[
Flexible(
fit: FlexFit.loose,
child:
RadioListTile(
title: const Text('hello'),
onChanged: (value) {
setState(() {});
},
),
),
Flexible(
fit: FlexFit.loose,
child:
RadioListTile(
title: const Text('Lafayette'),
onChanged: (value) {
setState(() {});
},
),
)
],
),
Do, let me know. Once you tried with the above code. If it resolved you problem, please accept my answer as useful and provide your valuable comments.
I got the same problem. You could try to customize with Radio, Text, InkWell, Padding.
class LabeledRadio extends StatelessWidget {
const LabeledRadio({
this.label,
this.padding,
this.groupValue,
this.value,
this.onChanged,
});
final String label;
final EdgeInsets padding;
final bool groupValue;
final bool value;
final Function onChanged;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
if (value != groupValue)
onChanged(value);
},
child: Padding(
padding: padding,
child: Row(
children: <Widget>[
Radio<bool>(
groupValue: groupValue,
value: value,
onChanged: (bool newValue) {
onChanged(newValue);
},
),
Text(label),
],
),
),
);
}
}
// ...
bool _isRadioSelected = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <LabeledRadio>[
LabeledRadio(
label: 'This is the first label text',
padding: const EdgeInsets.symmetric(horizontal: 5.0),
value: true,
groupValue: _isRadioSelected,
onChanged: (bool newValue) {
setState(() {
_isRadioSelected = newValue;
});
},
),
LabeledRadio(
label: 'This is the second label text',
padding: const EdgeInsets.symmetric(horizontal: 5.0),
value: false,
groupValue: _isRadioSelected,
onChanged: (bool newValue) {
setState(() {
_isRadioSelected = newValue;
});
},
),
],
),
);
}
The documentation: https://api.flutter.dev/flutter/material/RadioListTile-class.html#material.RadioListTile.3
This is how I fix the padding:
enum ContactSex { nam, nu, khac }
class CreateContactScreen extends StatefulWidget {
static const routeName = './create_contact';
#override
_CreateContactScreenState createState() => _CreateContactScreenState();
}
class _CreateContactScreenState extends State<CreateContactScreen> {
ContactSex _contaxtSex = ContactSex.nu;
final _form = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'TẠO LIÊN HỆ',
style: kHeaderTextStyle,
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('XONG', style: TextStyle(color: Colors.white),),
)
],
),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Form(
key: _form,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
labelText: 'Tên*',
),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
// TODO: when submit this text field
},
validator: (value) {
if (value.isEmpty) {
return 'Hãy nhập tên cho liên hệ.';
}
return null;
},
onSaved: (value) {
// TODO : when save the whole form
},
),
TextFormField(
decoration: InputDecoration(
labelText: 'Họ',
),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
// TODO: when submit this text field
},
// validator: (value) {
// if (value.isEmpty) {
// return null;
// }
// return null;
// },
onSaved: (value) {
// TODO : when save the whole form
},
),
TextFormField(
decoration: InputDecoration(
labelText: 'Số điện thoại*',
),
keyboardType: TextInputType.phone,
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
// TODO: when submit this text field
},
validator: (value) {
if (value.isEmpty) {
return 'Hãy nhập số điện thoại cho liên hệ.';
}
return null;
},
onSaved: (value) {
// TODO : when save the whole form
},
),
TextFormField(
decoration: InputDecoration(
labelText: 'Email',
),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
// TODO: when submit this text field
},
// validator: (value) {
// if (value.isEmpty) {
// return null;
// }
// return null;
// },
onSaved: (value) {
// TODO : when save the whole form
},
),
Container(
padding: EdgeInsets.only(top: 15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Giới tính',
style: TextStyle(fontSize: 14.0),
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
LabeledRadio(
label: 'Nữ',
padding: EdgeInsets.all(0),
groupValue: _contaxtSex,
value: ContactSex.nu,
onChanged: (ContactSex newValue) {
setState(() {
_contaxtSex = newValue;
});
},
),LabeledRadio(
label: 'Nam',
padding: EdgeInsets.all(0),
groupValue: _contaxtSex,
value: ContactSex.nam,
onChanged: (ContactSex newValue) {
setState(() {
_contaxtSex = newValue;
});
},
),LabeledRadio(
label: 'Khác',
padding: EdgeInsets.all(0),
groupValue: _contaxtSex,
value: ContactSex.khac,
onChanged: (ContactSex newValue) {
setState(() {
_contaxtSex = newValue;
});
},
),
],
),
],
),
)
],
)),
),
),
);
}
}
class LabeledRadio extends StatelessWidget {
final String label;
final EdgeInsets padding;
final ContactSex groupValue;
final ContactSex value;
final Function onChanged;
const LabeledRadio(
{this.label, this.padding, this.groupValue, this.value, this.onChanged});
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
if (value != groupValue) {
onChanged(value);
}
},
child: Padding(
padding: padding,
child: Row(
children: <Widget>[
Radio<ContactSex>(
groupValue: groupValue,
value: value,
onChanged: (ContactSex newValue) {
onChanged(newValue);
},
),
Text(label),
],
),
),
);
}
}
You just need to set the "dense" property to true, example:
RadioListTile<String>(
title: "My radio",
dense: true, // <= here it is !
value: '1',
);
you should achieve this manually like
make a group of Radio() and Text() and wrap with InkWell() for state handling. now remove extra space of radio by materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, That's it. Get idea by sample code.
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
setState(() {
_radioVenue = 0;
});
},
child: Row(
children: [
Radio(
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: _radioVenue,
onChanged: (value) {},
value: 0,
),
Text('From our list')
],
),
),
InkWell(
onTap: () {
setState(() {
_radioVenue = 1;
});
},
child: Row(
children: [
Radio(
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: _radioVenue,
onChanged: (value) {},
value: 1,
),
Text('From our list')
],
),
),
],
),
We covered both both the issues in this sample.
Removed extra spaces.
whole group is selectable radio + text, Now it behaves like RadioListTile().
Simply use RadioListTile and remove extra padding, by default it's 18
RadioListTile(contentPadding: EdgeInsets.symmetric(horizontal: 0.0)),
OR
RadioListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
value: null,
groupValue: null,
onChanged: null,
),
glad to answer
I was looking for same question and ended up on Flutter Documentation
I was working on Column and RadioListTile and I faced same issue, there's a horizontal padding between content inside RadioListTile
So, here it's the answer
Looking for this documentation ! RadioListTile content padding
Just add contentPadding: EdgeInsets.symmetric(horizontal: 0) and here you go, there's no horizontal padding anymore
Just copy paste this code and enjoy
Container(
height:35,
child: Row(
children: [
Radio(
groupValue: data.selected,
value: e,
onChanged: (DataBindModel? value) {
listener.value = MultiChoiceData(selected: value, items: listener.value.items);
onChanged(value);
onSelected(value);
},
),
Text(
e.value,
style: body14,
)
],
),
)
Copy the RadioListTile code and create your on new new file and paste it in there.
Remove the imports causing errors:
import 'package:flutter/widgets.dart'; // leave it
import 'package:flutter/material.dart'; //add
import 'list_tile.dart'; //remove
import 'radio.dart'; //remove
import 'theme.dart'; //remove
import 'theme_data.dart'; //remove
Then add the following padding to it, like this:
//Inside the file locate this widget and Add the padding or remove it. I needed to remove it and add 5.
return MergeSemantics(
child: ListTileTheme.merge(
contentPadding: EdgeInsets.only( // Add this
left: 5,
right: 0,
bottom: 0,
top: 0
),
selectedColor: activeColor ?? Theme.of(context).accentColor,
child: ListTile(
leading: leading,
title: title,
subtitle: subtitle,
trailing: trailing,
isThreeLine: isThreeLine,
dense: dense,
enabled: onChanged != null,
onTap: onChanged != null && !checked ? () { onChanged(value); } : null,
selected: selected,
),
),
);
then Import the file into your project like this:
import 'package:Project_Name/common/customComponets/custom_radio_list_tile.dart' as CustomRadioListTile;
Then use it like this:
CustomRadioListTile.RadioListTile(); // and that's how I managed to do it. Thought I should share.
This is my way of reducing the space. I have three Radio in one row.
Row(
children: [
Expanded(
child: RadioListTile(
contentPadding: EdgeInsets.all(0.0),
value: DayoffType.Range,
groupValue: _dayoffType,
title: Transform.translate(offset: const Offset(-18, 0), child: Text('Range')),
onChanged: (DayoffType? val) {
setState(() {
_dayoffType = val!;
});
},
),
),
Expanded(...Radio2...),
Expanded(...Radio3...)
)

Resources