In my flutter project, I want to make the screen which is user enter quantity for multiple product. I want to manage text controllers for multiple data rows in the data table.
My data table code is:
TextEditingController textController = TextEditingController();
AlertDialog(
content: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.height,
margin: EdgeInsets.all(0),
child: ListView(
padding: EdgeInsets.all(0),
primary: true,
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: [
DataTable(
columns: [
DataColumn(
label: Text(
"Product",
softWrap: true,
),
),
DataColumn(
label: Text(
"Qty",
),
),
DataColumn(
label: Text(
"Rs",
)),
DataColumn(
label: SizedBox(
width: 40,
child: Text(
'BalQty',
),
),
),
DataColumn(
label: SizedBox(
width: 40,
child: Text(
'Stock',
),
),
),
],
rows: listOfProduct!
.map(
((element) => DataRow(
cells: <DataCell>[
DataCell(Text(
element.name,
)),
DataCell(
TextField(
controller: textEditingController,
maxLength: 4,
decoration: InputDecoration(
labelText: 'Qty',
labelStyle: TextStyle(fontSize: 9.sp),
counterText: "",
),
),
),
DataCell(
Text(
(element.pTR ?? 0).toStringAsFixed(2),
),
),
DataCell(Text(
element.text!.printDashIfEmpty(),
)),
DataCell(Text(
(element.balQty ?? 0).toStringAsFixed(2),
)),
],
)),
)
.toList(),
),
],
),
),
actions: <Widget>[
OutlinedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(
"Cancel",
)),
SizedBox(
width: 10,
),
OutlinedButton(
onPressed: () async {
print("QUANTITY : $textEditingController");
listSearchProductMaster!.forEach((e) {
return print("TEXT CONTROLLER: ${e.textEditingController}");
});
},
child: Text(
"Add",
))
],
)
The output I see is that the quantity that the user inserts should be added to the particular product.
Currently, my application output is
Does anyone help?
Well, in my case, my approach was:
When user tap item to edit, show modal with initial value
User make input and tab Confirm button
after valid input, apply
It wrap focus node and controller, so easy to use.
But it force user to dig in 1 more UI level, so maybe there are better ways, but it works anyway.
I made TextEditModal widget and function to show that.
Future<String> showSingleTextEditDialog({
#required BuildContext context,
String initText = '',
}) async {
String text = initText;
bool submit = false;
await showDialog(
context: context,
builder: (_) {
return _SingleTextEditDialog(
text: text,
textChanged: (value) => text = value,
onSubmit: () => submit = true,
);
},
);
return submit ? text : null;
}
class _SingleTextEditDialog extends StatefulWidget {
_SingleTextEditDialog({
Key key,
#required this.text,
#required this.textChanged,
this.onSubmit,
}) : super(key: key);
final String text;
final ValueChanged<String> textChanged;
final VoidCallback onSubmit;
#override
_SingleTextEditDialogState createState() => _SingleTextEditDialogState();
}
class _SingleTextEditDialogState extends State<_SingleTextEditDialog> {
TextEditingController textController;
#override
void initState() {
super.initState();
textController = TextEditingController(text: widget.text);
}
#override
void dispose() {
textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SimpleDialog(
children: [
DecoInput(
controller: textController,
onChanged: widget.textChanged,
),
VSpace.md,
StyledInterfaceButton(
onTap: () {
setState(() => widget.onSubmit());
Navigator.of(context).pop();
},
text: 'Confirm',
),
// VSpace.md,
],
);
}
}
and use like
onTap: () async {
HapticFeedback.mediumImpact();
final input = await showSingleTextEditDialog(
context: context,
initText: text,
);
if (input == null) return;
// valid user input here
valueChanged(input); // apply user input
},
This is not complete code. I minimized code for readability. So ignore if there are any error.
Add your table code inside SingleChildScrollView() with scrollDirection: Axis.horizontal refer below code hope it help to you
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.all(20),
child: Table(
defaultColumnWidth: FixedColumnWidth(80.0),
border: TableBorder.all(
color: Colors.black,
style: BorderStyle.solid,
width: 2),
children: [
TableRow(
children: [
Column(
children: [
Text(
'Website',
style: TextStyle(fontSize: 20.0),
),
],
),
Column(
children: [
Text(
'Tutorial',
style: TextStyle(fontSize: 20.0),
),
],
),
Column(
children: [
Text(
'Review',
style: TextStyle(fontSize: 20.0),
),
],
),
Column(children: [
Text(
'Review',
style: TextStyle(fontSize: 20.0),
),
]),
Column(
children: [
Text(
'Review',
style: TextStyle(fontSize: 20.0),
),
],
),
],
),
TableRow(children: [
Column(
children: [
Text('Javatpoint'),
],
),
Column(
children: [
Text('Flutter'),
],
),
Column(
children: [
Text('5*'),
],
),
Column(
children: [
Text('5*'),
],
),
Column(
children: [
Text('5*'),
],
),
]),
TableRow(
children: [
Column(children: [
Text('Javatpoint'),
]),
Column(
children: [
Text('MySQL'),
],
),
Column(
children: [
Text('5*'),
],
),
Column(children: [
Text('5*'),
]),
Column(children: [
Text('5*'),
]),
],
),
],
),
),
],
),
),
Scroll your table horizontaly
Your screen like - >
Related
how to insert value textfield controller in looping condition, I want to create new textfield when the button 'Tambah' have presse, but there is problem when I check criemHistories value, this is my code
class _TestingState extends State<Testing> {
int textFieldCount = 1;
List<Map<String, String>> crimeHistories = [];
List<Widget> buildTextFields() {
List<Widget> textFields = [];
for (int i = 0; i < textFieldCount; i++) {
TextEditingController pengadilanC = TextEditingController();
TextEditingController polresC = TextEditingController();
Map<String, String> crimeHistory = {
"court": pengadilanC.text,
"police_department": polresC.text,
};
crimeHistories.add(crimeHistory);
textFields.add(Column(
children: [
Container(
margin: EdgeInsets.only(top: 20),
child: Column(
children: [
Row(
children: [
Expanded(
flex: 3,
child: RichText(
text: TextSpan(
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
text: 'Pengadilan Negeri',
),
),
),
Expanded(
flex: 7,
child: Align(
alignment: Alignment.center,
child: TextField(
controller: pengadilanC,
onChanged: (value) {
crimeHistory["court"] = value;
},
),
),
),
],
),
],
),
),
Container(
margin: EdgeInsets.only(top: 20),
child: Column(
children: [
Row(
children: [
Expanded(
flex: 3,
child: RichText(
text: TextSpan(
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
text: 'Polres/Polsek',
),
),
),
Expanded(
flex: 7,
child: Align(
alignment: Alignment.center,
child: TextField(
controller: polresC,
onChanged: (value) {
crimeHistory["police_department"] = value;
},
),
),
),
],
),
],
),
),
],
));
// Masukkan Map ke dalam list crimeHistories
}
return textFields;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Column(children: buildTextFields()),
ElevatedButton(
child: Text('Tambah'),
onPressed: () {
textFieldCount++;
},
),
ElevatedButton(
child: Text('Remove'),
onPressed: () {
setState(() {
textFieldCount--;
});
},
),
ElevatedButton(
child: Text("Simpan"),
onPressed: () {
print(crimeHistories);
},
),
],
),
);
}
}
this code can run and add form textfield properly, but when I check crimeHistories value, there is 3 data list, how come it's can be happend? and how to solve it?
I want to use a homemade widget with two text fields inside a row and a column. One of the textfield should be able to resize the widget, but when I use the widget inside a ListView.Builder, nothing appears until I assign a size to the main container.
This is my widget :
import 'package:flutter/material.dart';
import 'functions/cardsFunctions.dart';
Widget concept(String type, String title, String description, int index, setState, String id) {
String titre = '';
return Container(
child: Column(
/// the complete block
children: [
Expanded(
child: Row(
/// title and three dots
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: TextFormField(
textAlign: TextAlign.justify,
enableSuggestions: true,
maxLength: 75,
onChanged: (String text) {
titre = text;
},
),
),
SizedBox(height: 50,
child: PopupMenuButton(
offset: const Offset(-50, 0),
itemBuilder: (context) => [
PopupMenuItem(
onTap: () {
updateCard( 'hello', titre, id);
model();
},
child: const ListTile(
leading: Icon(Icons.update_rounded),
title: Text('Update')),
),
PopupMenuItem(
onTap: () {deleteCard(id, index);},
child: const ListTile(
leading: Icon(Icons.delete_forever_rounded),
title: Text('Delete')),
),
]),
),
],
),
),
/// description
const SizedBox(height: 10
),
const Expanded(
child: TextField(
maxLines: null,
textAlign: TextAlign.justify,
enableSuggestions: true,
maxLength: 2000,
decoration: InputDecoration(
hintText: 'What is it ?',
border: InputBorder.none,
)),
)
],
),
);
}
You need to remove Expanded widget. Here is the widget structure,
Widget concept(String type, String title, String description, int index,
setState, String id) {
String titre = '';
return Container(
child: Column(
/// the complete block
children: [
Row(
/// title and three dots
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: TextFormField(
textAlign: TextAlign.justify,
enableSuggestions: true,
maxLength: 75,
onChanged: (String text) {
titre = text;
},
),
),
SizedBox(
height: 50,
child: PopupMenuButton(
offset: const Offset(-50, 0),
itemBuilder: (context) => [
PopupMenuItem(
onTap: () {},
child: const ListTile(
leading: Icon(Icons.update_rounded),
title: Text('Update')),
),
PopupMenuItem(
onTap: () {},
child: const ListTile(
leading: Icon(Icons.delete_forever_rounded),
title: Text('Delete')),
),
]),
),
],
),
/// description
const SizedBox(height: 10),
TextField(
maxLines: null,
textAlign: TextAlign.justify,
enableSuggestions: true,
maxLength: 2000,
decoration: InputDecoration(
hintText: 'What is it ?',
border: InputBorder.none,
)),
],
),
);
}
I am working on an onboarding screen where I want to have the onboarding in 3 steps, so using the Stepper widget. The Stepper widget is inside a Column as I want to have some text displayed over the Stepper first. But now when I am trying to use a Row inside the Step widget to display some data horizontally, it does not show up. But it works if I make it a Column.
What could be causing this and any possible fix?
Flutter version: 3.3.8
What I am trying:
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 40),
const Text('Hi there!', style: AppStyles.heading),
const Text(
'Let\'s get you started',
style: AppStyles.subheading,
),
const SizedBox(
height: 50,
),
Stepper(
type: StepperType.vertical,
currentStep: _currentStep,
physics: const ScrollPhysics(),
onStepTapped: (step) => onTapped(step),
onStepContinue: onContinued,
onStepCancel: onCancel,
steps: [
Step(
title: const Text('Select a book'),
content: CustomButton(onPressed: () {}, text: 'Find Book'),
),
Step(
title: const Text('Set your goal'),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const TextField(
decoration: InputDecoration(
hintText: 'Pages',
),
keyboardType: TextInputType.number,
),
const SizedBox(width: 10),
CustomButton(onPressed: () {}, text: 'Set Goal'),
],
)),
const Step(
title: Text('When you want to be reminded'),
content: TimePickerDialog(
initialTime: TimeOfDay(hour: 8, minute: 0),
))
],
controlsBuilder: (context, _) {
return Row(
children: <Widget>[
TextButton(
onPressed: () => onContinued(),
child: const Text('Next'),
),
TextButton(
onPressed: () => onCancel(),
child: const Text('Back'),
),
],
);
},
)
],
),
),
),
);
}
CustomButton widget:
class CustomButton extends StatelessWidget {
final String text;
final bool isOutlined;
final bool isLoading;
final bool isDisabled;
final VoidCallback onPressed;
const CustomButton(
{Key? key,
required this.text,
this.isOutlined = false,
this.isLoading = false,
this.isDisabled = false,
required this.onPressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
backgroundColor: isOutlined ? Colors.white : Pallete.primaryBlue,
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 18),
foregroundColor: isOutlined ? Pallete.primaryBlue : null,
elevation: 4,
side: isOutlined
? const BorderSide(color: Pallete.primaryBlue, width: 1.0)
: null,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
child: Stack(
children: [
Visibility(
visible: isLoading ? false : true,
child: Text(text,
style: const TextStyle(
fontSize: 18.0, fontWeight: FontWeight.w600)),
),
Visibility(
visible: isLoading,
child: Loader(
color: isOutlined ? Pallete.primaryBlue : Pallete.white,
))
],
),
);
}
}
Output
Wrap your TextField and CustomButton (while it has ElevatedButton) with Expanded widget.
Step(
title: const Text('Set your goal'),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: const TextField(
decoration: InputDecoration(
hintText: 'Pages',
),
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 10),
Expanded(
child: CustomButton(
onPressed: () {}, text: 'Set Goal')),
],
)),
Find more about constraints
I have the next widget, which is rendered with overflow. I have tried to solve, but i don't know. Can anyone help me? The aim is to do a custom card inside listview.
I have tried to wrap with expanded buth then, the error is referenced with constraints.
import 'package:flutter/material.dart';
import '../../shared/AppTheme.dart';
class ComandaScreen extends StatefulWidget {
const ComandaScreen({Key? key}) : super(key: key);
#override
State<ComandaScreen> createState() => _ComandaScreenState();
}
class _ComandaScreenState extends State<ComandaScreen> {
bool expanded = false;
int unidades = 0;
final List<Map<String, dynamic>> _items = List.generate(
10, (index) => {'id': index, 'Nombre': 'Nuggets $index',
'isExpanded': false, "unidades": 8});
#override
Widget build(BuildContext context) {
final ButtonStyle flatButtonStyle = TextButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
);
return Scaffold(
appBar: AppBar(
title: const Text('Comanda'),
backgroundColor: AppTheme.backgroundColor,
foregroundColor: AppTheme.primaryTextColor,
elevation: 0,
),
body: SingleChildScrollView(
child: ExpansionPanelList(
elevation: 3,
// expandedHeaderPadding: const EdgeInsets.all(10),
expansionCallback: (index, isExpanded) {
setState(() {
_items[index]['isExpanded'] = !isExpanded;
});
},
animationDuration: const Duration(milliseconds: 200),
children: _items
.map(
(item) => ExpansionPanel(
canTapOnHeader: true,
// backgroundColor: item['isExpanded'] == true ? Colors.cyan[100] : Colors.white,
headerBuilder: (context, isExpanded) {
return Container(
margin: const EdgeInsets.all(10),
child: Row(children: [
const CircleAvatar(
child: Text(
'1',
textAlign: TextAlign.center,
)),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Nuggets',
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w600),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Text(
'Unidades: ${7}',
style: TextStyle(color: Colors.black),
),
Text(
'Pendientes: 400',
style: TextStyle(color: Colors.black),
),
],
),
const SizedBox(
width: 20,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'Precio: 10 €',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.black),
),
Text(
'Total: 70 €',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.black),
),
],
),
],
),
],
),
),
]),
);
},
body: ButtonBar(
alignment: MainAxisAlignment.spaceAround,
buttonHeight: 52.0,
buttonMinWidth: 90.0,
children: <Widget>[
TextButton(
style: flatButtonStyle,
onPressed: () {
setState(() {
item['unidades'] += 1;
});
},
child: Column(
children: const <Widget>[
Icon(
Icons.add,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Más'),
],
),
),
TextButton(
style: flatButtonStyle,
onPressed: () {
setState(() {
item['unidades'] -= 1;
});
},
child: Column(
children: const <Widget>[
Icon(
Icons.remove,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Menos'),
],
),
),
TextButton(
style: flatButtonStyle,
onPressed: () {},
child: Column(
children: const <Widget>[
Icon(
Icons.edit_outlined,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Editar'),
],
),
),
TextButton(
style: flatButtonStyle,
onPressed: () {},
child: Column(
children: const <Widget>[
Icon(
Icons.delete_outline_outlined,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Eliminar'),
],
),
),
TextButton(
style: flatButtonStyle,
onPressed: () {},
child: Column(
children: const <Widget>[
Icon(
Icons.card_giftcard_outlined,
color: AppTheme.grismedio,
),
// Padding(
// padding: EdgeInsets.symmetric(vertical: 2.0),
// ),
// Text('Invitar'),
],
),
)
],
),
isExpanded: item['isExpanded'],
),
)
.toList(),
// Card_lineaComanda(flatButtonStyle),
),
),
);
}
}
I 've edited the code to show all screen widget.
Image of result of code before:
For desktop applications, you can prevent the resize with breakpoint, so the error won't happen. In the pubsec.yaml file, add the following dependency.
window_size:
git:
url: https://github.com/google/flutter-desktop-embedding.git
path: plugins/window_size
And in your main method before runapp add this code with min-width and min-height below which the app won't resize.
const double desktopMinWidth = 800.0;
const double desktopMinHeight = 600.0;
if (Platform.isMacOS || Platform.isWindows) {
setWindowMinSize(const Size(desktopMinWidth, desktopMinHeight));
setWindowMaxSize(Size.infinite);
}
Note: Once done restart your app.
For mobile, it is entirely a different case. You might need to restructure the design
this is two search and dropdown sections I have implemented using animated_custom_dropdown.
I want that "Get Quote Filter " button to place next to the(right side) set location drop down..................................................................................................................................
........................................................................................................................................
import 'package:animated_custom_dropdown/custom_dropdown.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../constants/colors.dart';
const _labelStyle = TextStyle(fontWeight: FontWeight.w600);
class FantomSearch extends StatefulWidget {
const FantomSearch({Key? key}) : super(key: key);
#override
State<FantomSearch> createState() => _FantomSearchState();
}
class _FantomSearchState extends State<FantomSearch> {
final formKey = GlobalKey<FormState>();
final List<String> list = ['Heating', 'Electricians', 'Repair or Service', 'Accessibility Planner'];
final jobRoleFormDropdownCtrl = TextEditingController(),
jobRoleSearchDropdownCtrl = TextEditingController();
#override
void dispose() {
jobRoleFormDropdownCtrl.dispose();
jobRoleSearchDropdownCtrl.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Scaffold(
//backgroundColor:AppGreen,
appBar: AppBar(
systemOverlayStyle: SystemUiOverlayStyle.dark.copyWith(
statusBarColor: AppGreen,
),
backgroundColor: AppGreen,
elevation: .10,
),
body: Container(
height: 200,
color: AppGreen,
child: ListView(
padding: const EdgeInsets.all(16.0),
children: [
CustomDropdown.search(
hintText: 'Search Services',
items: list,
controller: jobRoleSearchDropdownCtrl,
fillColor: DarkGreen,
),
const SizedBox(height: 24),
// using form for validation
Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.only(right: 150),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomDropdown(
hintText: 'Set Location',
items: list,
controller: jobRoleFormDropdownCtrl,
excludeSelected: false,
fillColor: DarkGreen,
),
const SizedBox(height: 16),
SizedBox(
child: ElevatedButton(
onPressed: () {
if (!formKey.currentState!.validate()) {
return;
}
},
child: const Text(
'Get Quotes filter',
style: TextStyle(fontWeight: FontWeight.w600),
),
style: ElevatedButton.styleFrom(primary: ContainerGreen),
),
),
],
),
),
),
],
),
),
);
}
}
From your code, I believe that currently "Get quotes filter" showing below to the "Set Location" correct?
If this is the issue, you need to update Column widget to Row which is inside Padding.
Like,
Container(
height: 200,
child: SingleChildScrollView(
child: Column(
children: [
/**/
Row(
children: [
Expanded(
child: CustomDropdown.search(
hintText: 'Search Services',
items: list,
controller: jobRoleSearchDropdownCtrl,
fillColor: DarkGreen,
),
),
Padding(
padding: EdgeInsets.only(left: 15, top: 20, right: 15, bottom: 20),
child: Text(
"cancel"
),
)
],
),
const SizedBox(height: 24),
// using form for validation
Padding(
padding: const EdgeInsets.only(right: 70),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/**/
Expanded(
child: CustomDropdown(
hintText: 'Set Location',
items: list,
controller: jobRoleFormDropdownCtrl,
excludeSelected: false,
fillColor: DarkGreen,
),
),
const SizedBox(width: 16),
SizedBox(
child: ElevatedButton(
onPressed: () {
if (!formKey.currentState!.validate()) {
return;
}
},
child: const Text(
'Get Quotes filter',
style: TextStyle(fontWeight: FontWeight.w600),
),
style: ElevatedButton.styleFrom(primary: Colors.green),
),
),
],
),
),
],
),
),
)
If this still not worked, please share the expected output and what you are getting now. Because I am not able to compile your code due to custom widgets.
I have updated the color so please update it as per your need. The output is something like,