Flutter Search Bar with ListView with Checkboxes - flutter

I want to create a widget like this.
This contains the Textfield, Once type something in the field, It will be sorting the list and show the results below the field. The result list shown below can also have a checkbox field to select multiple items from the result.
Is there any built-in flutter widget that can be useful for this case?
Or any other package to achieve this.
Following is the screenshot for reference.
I tried with RawAutoComplete widget.
Here is the code.
class SearchBarDemo extends StatelessWidget {
const SearchBarDemo({super.key});
static List<String> suggestons = [
"USA",
"UK",
"Uganda",
"Uruguay",
"United Arab Emirates"
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: RawAutocomplete(
optionsBuilder: (textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
} else {
List<String> matches = <String>[];
matches.addAll(suggestons);
matches.retainWhere((s) {
return s
.toLowerCase()
.contains(textEditingValue.text.toLowerCase());
});
return matches;
}
},
fieldViewBuilder:
(context, textEditingController, focusNode, onFieldSubmitted) {
return TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(7),
),
hintText: 'Search',
contentPadding: EdgeInsets.symmetric(
vertical: 8,
horizontal: 4), // EdgeInsets.only(top: 8, left: 5),
prefixIcon: Container(
margin: EdgeInsets.symmetric(vertical: 8, horizontal: 4),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
border: Border(
right: BorderSide(
color: Colors.grey.shade400,
),
),
),
child: Icon(
Icons.search,
color: Colors.grey.shade400,
),
),
),
controller: textEditingController,
focusNode: focusNode,
onSubmitted: (String value) {},
);
},
onSelected: (selection) {},
optionsViewBuilder: (context, onSelected, options) {
return Material(
child: SingleChildScrollView(
child: Column(
children: options.map((opt) {
return InkWell(
onTap: () {
onSelected(opt);
},
child: Column(
children: [
Container(
height: 50,
width: 250,
alignment: Alignment.topLeft,
child: Card(
child: SizedBox(
child: ListTile(
title: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
opt,
style: TextStyle(fontSize: 12),
),
Transform.scale(
scale: 0.8,
child: Checkbox(
value: false,
onChanged: (val) {},
),
),
],
),
),
),
),
),
],
),
);
}).toList(),
),
),
);
},
),
);
}
}
Output of the above code is:
It covers the whole screen and show the result content in center.

You can customize the filter logic. Also you may like SearchDelegate-class
class SearchBarDemo extends StatefulWidget {
const SearchBarDemo({super.key});
#override
State<SearchBarDemo> createState() => _SearchBarDemoState();
}
class _SearchBarDemoState extends State<SearchBarDemo> {
static List<String> suggestons = [
"USA",
"UK",
"Uganda",
"Uruguay",
"United Arab Emirates"
];
List<String> filterItems = [];
List<String> checkedItems = [];
late final TextEditingController controller = TextEditingController()
..addListener(() {
/// filter logic will be here
final text = controller.text.trim();
filterItems = suggestons
.where(
(element) => element.toLowerCase().startsWith(text.toLowerCase()))
.toList();
setState(() {});
});
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
CupertinoTextField(
controller: controller,
),
Expanded(
child: ListView.builder(
itemCount: filterItems.length,
itemBuilder: (context, index) {
final bool isChecked =
checkedItems.contains(filterItems[index]);
return CheckboxListTile(
value: isChecked,
title: Text("${filterItems[index]}"),
onChanged: (value) {
if (isChecked) {
checkedItems.remove(filterItems[index]);
} else {
checkedItems.add(filterItems[index]);
}
setState(() {});
},
);
}),
),
],
));
}
}

Related

How to set a value in formTextField

I have a dropdown and a textfield, my goal is when i select a value from dropdown and it will called api, the api will return a string value, and the returned value will be displayed in the textfield. Any help and suggestions will be appreciated in advance.
for example:
when I select "CN" value from the dropdown, the IDDD field will be changed to 86
my code:
class VerifyPhone extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// implement createState
return VerifyPhoneState();
}
}
class VerifyPhoneState extends State<VerifyPhone> {
final _formKey = GlobalKey<FormState>();
String _phone = "";
String _code = "";
String? _iddd = '60';
TextEditingController _idddController = TextEditingController();
List<String> countryCode = ['MY', "CN"];
_showOtpDialog() {
var _form = _formKey.currentState as FormState;
if (_form.validate()) {
_form.save();
showOtpDialog(
context,
_phone,
success: (Map<String, String?>? result) {
if (result != null)
Provider.of<RegistLogic>(context, listen: false)
.doUpdateOtpResult(result);
RoutUtil.push(context, SetupPassword());
},
);
}
}
String? _phoneValidation(String? v) {
return [v.isMalaysiaMobileNo()].validate();
}
#override
void initState() {
super.initState();
_code = countryCode[0];
}
#override
void dispose() {
_idddController.dispose();
super.dispose();
}
_getIddd(String countryCode) async {
final response = await AuthService.countryInfo(
countryCode: countryCode,
);
if (response != null) {
_iddd = response['list01'][0]['int_phone_area_code'];
}
print('idd $_iddd');
}
#override
Widget build(BuildContext context) {
// implement build
return Scaffold(
backgroundColor: Colors.white,
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
children: <Widget>[
SingleChildScrollView(
child: Column(
children: <Widget>[
// _topTitleBar(context),
AuthHeader(options: HeaderOptions("Phone Number")),
_inputView(),
],
),
),
Positioned(
bottom: 18.0,
left: 20,
right: 20,
child: _postBotton(context),
)
],
),
));
}
Widget _inputView() {
return Container(
width: double.infinity,
margin: EdgeInsets.symmetric(horizontal: 10),
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text(
'Please enter your phone number to verify your information',
style: TextStyle(color: CustomThemeColor.coolGray)),
),
Column(
children: [
Row(
children: [
Container(
width: 50,
child: DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Country',
labelStyle: TextStyle(fontSize: 12)),
value: _code,
validator: (v) => [v.isRequired()].validate(),
isDense: true,
onChanged: (val) {
FocusScope.of(context).requestFocus(FocusNode());
_code = val!;
_getIddd(val);
},
items: countryCode.map((item) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
}).toList(),
),
),
SizedBox(
width: 20,
),
Container(
width: 50,
child: TextFormField(
initialValue: _iddd,
enabled: false,
decoration: InputDecoration(
labelText: "IDDD",
),
onChanged: (value) {
setState(() {
_iddd = value;
});
},
onSaved: (val) {
_iddd = val!;
},
autovalidateMode: AutovalidateMode.onUserInteraction,
),
),
SizedBox(
width: 20,
),
Container(
width: 200,
child: TextFormField(
decoration: InputDecoration(
labelText: "Enter your phone number",
),
onSaved: (val) {
_phone = val!;
},
keyboardType: TextInputType.phone,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(11),
],
validator: _phoneValidation,
autovalidateMode: AutovalidateMode.onUserInteraction,
),
)
],
)
],
)
],
),
));
}
Widget _postBotton(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 10),
padding: EdgeInsets.symmetric(vertical: 15),
child: RaisedButton(
padding: EdgeInsets.symmetric(vertical: 10),
onPressed: () {
_showOtpDialog();
},
color: CustomThemeColor.buttonBlue,
child:
Text('NEXT', style: TextStyle(fontSize: 20, color:
Colors.white)),
));
}
}
Thank you
_getIddd(String countryCode) async {
final response = await AuthService.countryInfo(
countryCode: countryCode,
);
if (response != null) {
_iddd = response['list01'][0]['int_phone_area_code'];
_idddController.text = _iddd;
}
print('idd $_iddd');
}

How to limit the dynamic textfield to 4 in flutter?

This is the code for the Createpoll module of my Polling app. I want to generate just 4 dynamic text fields, but the below code generates unlimited text fields. I'm not able to figure out which part to edit to fit my needs.
Create Poll Screenshot
I'm also unable to make changes to the hint text in the text field, It keeps repeating "Option 1", I want it to go like Option1, Option2.....so on.
import 'package:flutter/material.dart';
import 'package:justpoll/Constants.dart';
import 'package:justpoll/screens/create_poll/create_poll2.dart';
import 'package:justpoll/widgets/custom_input.dart';
class CreatePoll extends StatefulWidget {
#override
_CreatePollState createState() => _CreatePollState();
}
class _CreatePollState extends State<CreatePoll> {
final _formKey = GlobalKey<FormState>();
TextEditingController _nameController;
static List<String> friendsList = [null];
String emoji_id;
List<String> emoji = [
"❤️",
"🤩",
"✌️",
"😂",
"😡",
];
#override
void initState() {
super.initState();
_nameController = TextEditingController();
}
#override
void dispose() {
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: MyColors.white,
appBar: AppBar(
title: Padding(
padding: const EdgeInsets.all(75.0),
child: Text('New Poll'),
),
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(
Icons.close,
),
),
backgroundColor: Colors.black87,
),
body: ListView(
children: [
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// name textfield
Center(
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text("1/4"),
),
),
Padding(
padding: const EdgeInsets.only(right: 32.0),
child: CustomInput(
textEditingController: _nameController,
labletext: 'Question?*',
decoration: InputDecoration(hintText: 'Question*'),
validator: (v) {
if (v.trim().isEmpty) return 'Please enter something';
return null;
},
),
),
SizedBox(
height: 20,
),
// Text(
// 'Options',
// style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16),
// ),
// Padding(
// padding: const EdgeInsets.only(right: 2.0),
// child: Row(
// children: [
// Expanded(
// flex: 2,
// child: Padding(
// padding: const EdgeInsets.only(right: 20),
// child: CustomInput(
// textEditingController: _nameController,
// labletext: 'Option 1*',
// decoration: InputDecoration(hintText: 'Option'),
// validator: (v) {
// if (v.trim().isEmpty)
// return 'Please enter something';
// return null;
// },
// ),
// ),
// ),
// Expanded(
// flex: 1,
// child: Padding(
// padding: const EdgeInsets.only(right: 30),
// child: Container(
// padding: EdgeInsets.only(left: 10, right: 16),
// decoration: BoxDecoration(
// border: Border.all(
// color: MyColors.black, width: 1.5),
// borderRadius: BorderRadius.circular(10)),
// child: DropdownButton(
// hint: Text('❤️'),
// value: emoji_id,
// icon: Icon(Icons.arrow_drop_down),
// iconSize: 36,
// isExpanded: true,
// underline: SizedBox(),
// style: TextType.regularDarkText,
// onChanged: (newValue) {
// setState(() {
// emoji_id = newValue;
// });
// },
// items: emoji.map((emoji_id) {
// return DropdownMenuItem(
// value: emoji_id,
// child: Text(emoji_id),
// );
// }).toList(),
// ),
// ),
// ),
// ),
// ],
// ),
// ),
..._getOptions(),
SizedBox(
height: 30,
),
Align(
alignment: Alignment.bottomRight,
child: MaterialButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CreatePoll2(),
),
);
},
color: Colors.black,
textColor: Colors.white,
child: Icon(
Icons.arrow_forward,
size: 24,
),
padding: EdgeInsets.all(16),
shape: CircleBorder(),
),
),
],
),
),
),
],
),
),
);
}
/// get friends text-fields
List<Widget> _getOptions() {
List<Widget> friendsTextFields = [];
for (int i = 0; i < friendsList.length; i++) {
friendsTextFields.add(Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
children: [
Expanded(child: FriendTextFields(i)),
SizedBox(
width: 5,
),
// we need add button at last friends row
_addRemoveButton(i == friendsList.length - 1, i),
],
),
));
}
return friendsTextFields;
}
/// add / remove button
Widget _addRemoveButton(bool add, int index) {
return InkWell(
onTap: () {
if (add) {
// add new text-fields at the top of all friends textfields
friendsList.insert(0, null);
} else
friendsList.removeAt(index);
setState(() {});
},
child: Container(
width: 26,
height: 26,
decoration: BoxDecoration(
color: (add) ? Colors.black : Colors.red,
borderRadius: BorderRadius.circular(20),
),
child: Icon(
(add) ? Icons.add : Icons.remove,
color: Colors.white,
),
),
);
}
}
class FriendTextFields extends StatefulWidget {
final int index;
FriendTextFields(this.index);
#override
_FriendTextFieldsState createState() => _FriendTextFieldsState();
}
class _FriendTextFieldsState extends State<FriendTextFields> {
TextEditingController _nameController;
#override
void initState() {
super.initState();
_nameController = TextEditingController();
}
#override
void dispose() {
_nameController.dispose();
super.dispose();
}
String emoji_id;
List<String> emoji = [
"❤️",
"🤩",
"✌️",
"😂",
"😡",
];
#override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_nameController.text = _CreatePollState.friendsList[widget.index] ?? '';
});
return Row(
children: [
Expanded(
flex: 2,
child: CustomInput(
textEditingController: _nameController,
labletext: 'Option 1*',
decoration: InputDecoration(hintText: 'Option'),
validator: (v) {
if (v.trim().isEmpty) return 'Please enter something';
return null;
},
),
),
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
padding: EdgeInsets.only(left: 10, right: 16),
decoration: BoxDecoration(
border: Border.all(color: MyColors.black, width: 1.5),
borderRadius: BorderRadius.circular(10)),
child: DropdownButton(
hint: Text('❤️'),
value: emoji_id,
icon: Icon(Icons.arrow_drop_down),
iconSize: 36,
isExpanded: true,
underline: SizedBox(),
style: TextType.regularDarkText,
onChanged: (newValue) {
setState(() {
emoji_id = newValue;
});
},
items: emoji.map((emoji_id) {
return DropdownMenuItem(
value: emoji_id,
child: Text(emoji_id),
);
}).toList(),
),
),
),
),
],
);
}
}
Firstly, If you want to limit it to just 4 dynamic fields then please replace this line
_addRemoveButton(i == friendsList.length - 1, i)
with this
_addRemoveButton(i < 3 ? i == friendsList.length - 1 : false, i),
This will make sure that the add button is not shown on the 4th field. Instead it will show the remove button for the 4th field.
Secondly, to have it show each field as Option 1, Option 2, Option 3 etc, replace the line
labletext: 'Option 1*',
with
labletext: 'Option ${widget.index + 1}',

Flutter textfield onChanged don't work when textfield bring value from controller

I'm working on flutter project and came across a problem. the textfield supposed to take a value from controller which work perfectly but onChanged function wont work. However it is not working.i must retype value to show results .
how can i get result from onchanged when my textfield brings the value ?
code:
// search function that call api
class _SearchPageTState extends State<SearchPageT> {
GlobalState _store = GlobalState.instance;
List<dynamic> searchResults = [];
searchT(value) async {
SearchServicet.searchApiT(value).then((responseBody) {
List<dynamic> data = jsonDecode(responseBody);
setState(() {
data.forEach((value) {
searchResults.add(value);
});
});
});
}
#override
///////
//code textfield search onchanged
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
centerTitle: true,
),
body: ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
autofocus: true,
controller: TextEditingController()
..text = '${_store.get('num')}',
onChanged: (value) {
searchResults.clear();
searchCodeighniterT(value);
},
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 25.0),
labelText: 'number',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0),
),
suffixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: null,
),
),
),
),
SizedBox(
height: 10.0,
),
ListView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
itemCount: searchResults.length,
itemBuilder: (BuildContext context, int index) {
return buildResultCard(context, searchResults[index]);
},
),
],
),
),
);
}
//here where shows the result
Widget buildResultCard(BuildContext context, data) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
new Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/bgwlgo.png"), fit: BoxFit.cover),
),
margin: EdgeInsets.all(5.0),
child: Column(
children: <Widget>[
new Card(
child: ListTile(
title: Text(
"DATE:",
),
subtitle: Text(
data['DATETIME'].toString(),
),
),
),
new Card(
child: ListTile(
title: Text(
"TRAITEMENT:",
),
subtitle: Text(
data['TRAITEMENT_DETAIL'].toString()
),
),
),
new Padding(
padding: EdgeInsets.all(9.0),
),
],
),
),
Divider(
color: Colors.black,
)
],
),
);
}
As discussed and understood in the comments, here are a couple of possible solutions for the issue.
If you are using a stateful Widget and need a controller for your TextFormField, you can use it to set the text on the TextFormField. Otherwise, you can just use the initialValue property:
class TextFieldIssue64061076 extends StatelessWidget {
final TextEditingController _textEditingController = TextEditingController();
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children:[
Text('Page 1'),
TextFormField(
controller: _textEditingController,
),
FlatButton(
child: Text('Submit'),
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return SecondPage64061076(text: _textEditingController.text);
}
)),
),
],
);
}
}
class SecondPage64061076 extends StatefulWidget {
final String text;
SecondPage64061076({
this.text
});
#override
_SecondPage64061076State createState() => _SecondPage64061076State();
}
class _SecondPage64061076State extends State<SecondPage64061076> {
final TextEditingController _textEditingController = TextEditingController();
#override
void initState() {
// You can either use a text controller to set the text on the text field
// or the initialValue below
if(widget.text != null && widget.text != ''){
_textEditingController.text = widget.text;
methodToCallOnChanged(widget.text);
}else{
_textEditingController.text = '';
}
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Page 2'),
TextFormField(
controller: _textEditingController,
onChanged: (value) => methodToCallOnChanged(value),
// As mentioned you could just use the initial value, it you don't need
// a Stateful Widget
// initialValue: widget.text ?? '',
),
]
),
);
}
void methodToCallOnChanged(data){
print('I was called onChange or when the view opened and there was data');
}
}
Widget build(BuildContext context) {
bool isRecording = false;
bool isShowSendButton = false;
/// make sure textcontroller and any variabloes or booleans are not defined
///in the build method
}
You can't be using the constructor of the TextEditingController and assign it to the controller.
The controller of the TextField only takes the variable assigned to the TextEditingController. Check the following code.
controller:theNameOfYourController
Expanded(
flex: 8,
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 10),
child: TextFormField(
onChanged: (value){
messageFieldController.text=value;
},
//controller: messageFieldController,
minLines: 1,
maxLines: 2,
textCapitalization: TextCapitalization.sentences,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
prefixIcon: IconButton(
onPressed: () {},
icon: const Icon(Icons.add, color: Colors.blue),
),
suffixIcon: processing == true
? const CircularProgressIndicator()
: IconButton(
onPressed: () {
try {
/*for (int i = 0;
i < widget.selectedMembers.length;
i++) {
sendMessage(
widget.selectedMembers[i].number);
}*/
for (int i = 0;
i < widget.selectedGroups.length;
i++) {
if (widget.selectedGroups.isNotEmpty) {
sendMessageToGroup(
widget.selectedGroups[i].id,
widget.selectedGroups[i].groupName);
}
}
} catch (err) {
Fluttertoast.showToast(
msg: 'Unable to send Message');
setState(() {
processing = false;
});
}
},
icon:
const Icon(Icons.send, color: Colors.blue),
),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(50),
),
),
),
),
),
)

Best way to create a scrollable list that dynamically grows

I have a flutter web app (which may become a desktop app in the future) that contains a dialog box. The dialog allows the user to edit the properties of a model object that contains a list of elements. The user should be able to edit the list by adding or deleting elements to the list. So the dialog contents are of variable height -- it depends on the number of elements in the list.
I am having trouble creating a layout that dynamically resizes appropriately. What I want is for the dialog to grow as items are added to the list, up to the maximum size that would fit on the device's screen. If the contents grow larger than this, the elements in the list should be scrollable.
I've attached two screenshots of what I have working at the moment; the first has a list with only two items, which is easily displayed. The second is the same dialog with many items showing the overflow.
Here is the code for the Dialog:
Widget build(BuildContext context) {
return AlertDialog(
actions: [
FlatButton(
child: const Text('CANCEL'),
onPressed: () => Navigator.of(context).pop(),
),
FlatButton(
child: const Text('OK'),
onPressed: () =>
Navigator.of(context).pop<Modifier>(_createModifier()),
),
],
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Damage Editor'),
Divider(),
columnSpacer,
DiceSpinner(
onChanged: (value) => setState(() => _dice = value),
initialValue: _dice,
textFieldWidth: 90.0,
),
columnSpacer,
Container(
padding: const EdgeInsets.only(left: 12.0, right: 12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.0),
border: Border.all(color: Colors.grey),
),
child: DropdownButton<DamageType>(
underline: Container(),
value: _type,
items: _damageTypeItems(),
onChanged: (value) => setState(() => _type = value),
),
),
columnSpacer,
SwitchListTile(
value: _direct,
onChanged: (state) => setState(() => _direct = state),
title: Text(_direct ? 'Internal (Direct)' : 'External (Indirect)'),
),
if (!_direct) ...<Widget>[
columnSpacer,
CheckboxListTile(
value: _explosive,
onChanged: (state) => setState(() => _explosive = state),
title: Text('Explosive'),
),
],
columnSpacer,
DynamicListHeader(
title: 'Enhancements/Limitations',
onPressed: () => setState(() =>
_modifiers.add(TraitModifier(name: 'Undefined', percent: 0))),
),
SingleChildScrollView(
child: ListBody(
children: _enhancementList(),
),
),
],
),
);
}
List<Widget> _enhancementList() {
var list = <Widget>[];
_modifiers.forEach(
(element) {
if (_modifiers.length > 0) list.add(columnSpacer);
list.add(_EnhancerEditor(element, index: _modifiers.indexOf(element),
onChanged: (index, enhancer) {
_modifiers[index] = enhancer;
}));
},
);
return list;
}
typedef TraitModifierCallback = void Function(int, TraitModifier);
class _EnhancerEditor extends StatefulWidget {
_EnhancerEditor(this.enhancer, {this.onChanged, this.index});
final TraitModifier enhancer;
final TraitModifierCallback onChanged;
final int index;
#override
__EnhancerEditorState createState() => __EnhancerEditorState();
}
class __EnhancerEditorState extends State<_EnhancerEditor> {
TextEditingController _nameController;
TextEditingController _percentController;
bool _validInput = true;
#override
void initState() {
super.initState();
_nameController = TextEditingController(text: widget.enhancer.name);
_nameController.addListener(_onChanged);
_percentController =
TextEditingController(text: widget.enhancer.percent.toString());
_percentController.addListener(_onChanged);
}
#override
void dispose() {
_nameController.removeListener(_onChanged);
_nameController.dispose();
_percentController.removeListener(_onChanged);
_percentController.dispose();
super.dispose();
}
void _onChanged() {
String text = _percentController.text.trim();
setState(() {
int value = int.tryParse(text);
_validInput = (value != null);
if (_validInput) {
widget.onChanged(widget.index,
TraitModifier(name: _nameController.text, percent: value));
}
});
}
#override
Widget build(BuildContext context) {
return IntrinsicHeight(
child: Row(
children: [
Expanded(
child: TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Enhancer/Limitation',
border: const OutlineInputBorder(),
),
),
),
rowSmallSpacer,
SizedBox(
width: 80.0,
child: TextField(
controller: _percentController,
textAlign: TextAlign.end,
keyboardType: TextInputType.numberWithOptions(signed: true),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9\-]'))
],
decoration: const InputDecoration(
suffixText: '%',
labelText: 'Pct',
border: const OutlineInputBorder(),
),
),
),
],
),
);
}
}
It may sound counterintuitive but you should have ListView within Column. You need both.
In dialog Column will deal with dialog size, and ListView will deal with scrolling within.
Column(
mainAxisSize: MainAxisSize.min,
children:[
ListView(padding: EdgeInsets.all(0),
shrinkWrap: true,
physics: ClampingScrollPhysics(),
Ok, here you go ... Simple call of dialog ...
FlatButton(
onPressed: () {
showDialog(
useRootNavigator: false,
context: context,
builder: (BuildContext context) => Test(),
);
},
child: Text('Suppper'),
)
then code of dialog ... it is pretty straight forward ...
all controllers you can place in a map that you will deal with when you add new entries... both adding and removing is super easy ... let me know if further assitance is needed.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
#override
void initState() {
super.initState();
}
Widget rowEntry(_nameController, _percentController) {
return IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Enhancer/Limitation',
border: const OutlineInputBorder(),
),
),
),
SizedBox(
width: 80.0,
child: TextField(
controller: _percentController,
textAlign: TextAlign.end,
keyboardType: TextInputType.numberWithOptions(signed: true),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[0-9\-]'))],
decoration: const InputDecoration(
suffixText: '%',
labelText: 'Pct',
border: const OutlineInputBorder(),
),
),
),
],
),
);
}
addEntry() async {
entries.add('new value');
setState(() {});
}
List entries = [];
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Container(
padding: EdgeInsets.all(16),
width: 300,
child: ListView(shrinkWrap: true, children: [
Text('here you will put all that is above (+)'),
Container(
margin: EdgeInsets.only(top: 16, bottom: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Exhancements/Limitations'),
InkWell(
onTap: () {
addEntry();
},
child: Icon(Icons.add),
),
],
),
),
ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.all(0),
scrollDirection: Axis.vertical,
itemCount: entries == null ? 0 : (entries.length),
itemBuilder: (BuildContext context, int index) {
return rowEntry(null, null);
}),
]),
),
);
}
}

FLUTTER : How to make container size of children

By definition, a container with children grows enough to show them. In this example that I am developing, I am not able to make the container fit the size of the children, I have to hardcode the Weight and Height, both, otherwise the container disappear (is the one with a red background, I put the whole code so you can copy-paste but it is only that one that I can not control the behaviour).
import 'package:flutter/material.dart';
import 'package:littlebusiness/logic/Category.dart';
import 'package:hive/hive.dart';
class FormCategoryPage extends StatefulWidget {
#override
_FormCategoryPageState createState() => _FormCategoryPageState();
}
class _FormCategoryPageState extends State<FormCategoryPage> {
final _formKey = GlobalKey<FormState>();
List<RadioModel> sampleData = new List<RadioModel>();
#override
void initState() {
// TODO: implement initState
super.initState();
sampleData.add(new RadioModel(true, 'A', 0xffe6194B));
sampleData.add(new RadioModel(false, 'B', 0xfff58231));
sampleData.add(new RadioModel(false, 'C', 0xffffe119));
sampleData.add(new RadioModel(false, 'D', 0xffbfef45));
sampleData.add(new RadioModel(true, 'A', 0xffe6194B));
sampleData.add(new RadioModel(false, 'B', 0xfff58231));
}
String _name;
Color _color;
String _selectedValue;
void addCategory(Category cat) {
Hive.box('categories').add(cat);
}
void getColor(String value) {
_selectedValue = value;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('PERFORMANCE'),
),
body: Center(
child: Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
// hintText: 'Enter your email',
labelText: 'Name',
),
onSaved: (value) => _name = value,
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 100,
width: 320,
color: Colors.red,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: sampleData.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
setState(() {
sampleData.forEach(
(element) => element.isSelected = false);
sampleData[index].isSelected = true;
});
},
child: RadioItem(sampleData[index]),
);
},
),
),
],
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Colors.red)),
child: Text('Add New Contact'),
color: Colors.teal,
textColor: Colors.white,
onPressed: () {
_formKey.currentState.save();
// final newContact = Contact(_name, int.parse(_age));
// addContact(newContact);
},
),
],
),
),
),
),
);
}
}
class RadioItem extends StatelessWidget {
final RadioModel _item;
RadioItem(this._item);
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(15.0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
height: 35.0,
width: 35.0,
alignment: Alignment.center,
child: Container(
height: 25.0,
width: 25.0,
decoration: BoxDecoration(
color: Color(_item.colorCode),
borderRadius:
const BorderRadius.all(const Radius.circular(15)),
)),
decoration: BoxDecoration(
color: Colors.transparent,
border: Border.all(
width: 3.0,
color: _item.isSelected
? Color(_item.colorCode)
: Colors.transparent),
borderRadius: const BorderRadius.all(const Radius.circular(25)),
),
),
Container(margin: EdgeInsets.only(left: 20.0))
],
),
);
}
}
class RadioModel {
bool isSelected;
final String buttonText;
final int colorCode;
RadioModel(this.isSelected, this.buttonText, this.colorCode);
}
This is the actuar result:
Anyone knows why it is happening that? I am lost, and giving a with of double.infinity does not work...
Thanks!
instead of using fixed values on wisth and height , you can use relative values to device by using
MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height
you can also use them like
MediaQuery.of(context).size.width * 0.5
which means 50% of the device screen
hope it will help
You have to specified both width and height because your Listview is a child of a Column and a Row
You can replace your Listview by a Row of RadioItem in a SingleChildScrollView
Container(
color: Colors.red,
child: Builder(
builder: (context) {
var childs = <Widget>[];
for (var item in sampleData) {
childs.add(RadioItem(item));
}
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: childs,
),
);
},
),
),