I am using Flutter DataTables to display list of items in cart. Now I want to edit the quantity of any selected row. Is there a way to get the information of the row user has tapped?
Following is complete code of my DataTable:
class _DataTableSampleState extends State<DataTableSample> {
void _getSelectedRowInfo() {
print('Selected Item Row Name Here...')
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DataTable Sample'),
),
body: Container(
child: DataTable(
onSelectAll: (b) {},
sortAscending: true,
columns: <DataColumn>[
DataColumn(
label: Text('Item'),
),
DataColumn(
label: Text('Price'),
),
],
rows: items
.map(
(itemRow) => DataRow(
cells: [
DataCell(
Text(itemRow.itemName),
showEditIcon: false,
placeholder: false,
),
DataCell(
Text(itemRow.itemPrice),
showEditIcon: true,
placeholder: false,
onTap: _getSelectedRowInfo,
),
],
),
)
.toList(),
),
),
);
}
}
class ItemInfo {
String itemName;
String itemPrice;
ItemInfo({
this.itemName,
this.itemPrice,
});
}
var items = <ItemInfo>[
ItemInfo(
itemName: 'Item A',
itemPrice: '250',
),
ItemInfo(
itemName: 'Item B',
itemPrice: '100',
),
ItemInfo(
itemName: 'Item C',
itemPrice: '150',
),
];
When edit icon is clicked "_getSelectedRowInfo" method is called. I want to get complete detail of selected/tapped row in this function.
you can use onSelectChanged property from DataRow.
rows: items
.map(
(itemRow) => DataRow(
onSelectChanged: (bool selected) {
if (selected) {
log.add('row-selected: ${itemRow.index}');
}
},
cells: [
// ..
],
),
Try this :
DataTable(
showCheckboxColumn: false, // <-- this is important
columns: [
DataColumn(label: Text('FirstName')),
DataColumn(label: Text('LastName')),
],
rows:[
DataRow(
cells: [
DataCell(Text(obj['user1'])),
DataCell(Text(obj['name-a'])),
],
onSelectChanged: (newValue) {
print('row 1 pressed');
},
),
DataRow(
cells: [
DataCell(Text(obj['user2'])),
DataCell(Text(obj['name-b'])),
],
onSelectChanged: (newValue) {
print('row 2 pressed');
},
),
]
),
Hope this helps. Thanks
Each DataCell has an onTap callback. You could use this without the unhideable checkbox appearing on your table rows.
For example
DataCell(Text(itemrow.itemname),
onTap: () {
// Your code here
})
This works for me. If you want the onTap to work for the entire DataRow instead of only a DataCell, you could just add the logic to the onTap of each DataCell and get the desired result.
You can get it done by using a closure , a function object that has access to variables in its lexical scope and basically 'remembers' them.
Change the 'onTap' property of your DataCell to :
onTap: (){_getSelectedRowInfo(itemRow.itemName,itemRow.itemPrice);},
and modify the _getSelectedRowInfo function to accommodate the following changes:
void _getSelectedRowInfo(dynamic name,dynamic price) {
print('Name:$name price: $price');
}
Here's how the entire thing should look like:
class _DataTableSampleState extends State<DataTableSample> {
void _getSelectedRowInfo(dynamic name,dynamic price) {
print('Name:$name price: $price');
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DataTable Sample'),
),
body: Container(
child: DataTable(
onSelectAll: (b) {},
sortAscending: true,
columns: <DataColumn>[
DataColumn(
label: Text('Item'),
),
DataColumn(
label: Text('Price'),
),
],
rows: items
.map(
(itemRow) => DataRow(
cells: [
DataCell(
Text(itemRow.itemName),
showEditIcon: false,
placeholder: false,
),
DataCell(
Text(itemRow.itemPrice),
showEditIcon: true,
placeholder: false,
onTap: (){_getSelectedRowInfo(itemRow.itemName,itemRow.itemPrice);},
),
],
),
)
.toList(),
),
),
);
}
}
class ItemInfo {
String itemName;
String itemPrice;
ItemInfo({
this.itemName,
this.itemPrice,
});
}
var items = <ItemInfo>[
ItemInfo(
itemName: 'Item A',
itemPrice: '250',
),
ItemInfo(
itemName: 'Item B',
itemPrice: '100',
),
ItemInfo(
itemName: 'Item C',
itemPrice: '150',
),
];
Small correction to anyone wondering how to do it on the latest versions of flutter. It takes in a bool? selected rather than a bool selected. This is due to the new null safety values syntax. Ensuring that even if the row is not rendered for whatever reason the data table can still function.
set showCheckboxColumn: false to hide checkbox
Related
I am trying to allow a user to click on a picture and allow them to get information about that image, how would I go about that? I was thinking about wrapping the subtitle(with the image url) in the gesture detector and I was getting some errors so I would like to know how I could fix this error. Thank you
import 'package:flutter/material.dart';
class ProfilePage extends StatefulWidget {
const ProfilePage({Key? key}) : super(key: key);
#override
_ProfilePageState createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> {
// This holds a list of fiction users
// You can use data fetched from a database or a server as well
final List<Map<String, dynamic>> _allHerbs = [
{
"id": 1,
"name": "plant1",
"urlImage":
'https://www.southernexposure.com/media/products/originals/sweet-genovese-basil-809aaf7e3d9a3f3fa7ce2f0eb4480e95.jpg'
},
{"id": 2, "name": "plant2", "urlImage": ''},
{"id": 3, "name": "plant3", "urlImage": ''},
{"id": 4, "name": "plant4", "urlImage": ''},
{"id": 5, "name": "plant5", "urlImage": ''},
{"id": 6, "name": "plant6", "urlImage": ''},
{"id": 7, "name": "plant7", "urlImage": ''},
{"id": 8, "name": "plant8", "urlImage": ''},
{"id": 9, "name": "plant9", "urlImage": ''},
{"id": 10, "name": "plant10", "urlImage": ''},
];
// This list holds the data for the list view
List<Map<String, dynamic>> _foundHerbs = [];
#override
initState() {
// at the beginning, all users are shown
_foundHerbs = _allHerbs;
super.initState();
}
// This function is called whenever the text field changes
void _runFilter(String enteredKeyword) {
List<Map<String, dynamic>> results = [];
if (enteredKeyword.isEmpty) {
// if the search field is empty or only contains white-space, we'll display all users
results = _allHerbs;
} else {
results = _allHerbs
.where((user) =>
user["name"].toLowerCase().contains(enteredKeyword.toLowerCase()))
.toList();
// we use the toLowerCase() method to make it case-insensitive
}
// Refresh the UI
setState(() {
_foundHerbs = results;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Herb Search'),
),
body: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
const SizedBox(
height: 20,
),
TextField(
onChanged: (value) => _runFilter(value),
decoration: const InputDecoration(
labelText: 'Search', suffixIcon: Icon(Icons.search)),
),
const SizedBox(
height: 20,
),
Expanded(
child: _foundHerbs.isNotEmpty
? ListView.builder(
itemCount: _foundHerbs.length,
itemBuilder: (context, index) => Card(
key: ValueKey(_foundHerbs[index]["id"]),
color: Colors.blueAccent,
elevation: 4,
margin: const EdgeInsets.symmetric(vertical: 10),
child: ListTile(
leading: Text(
_foundHerbs[index]["id"].toString(),
style: const TextStyle(fontSize: 24),
),
title: Text(_foundHerbs[index]['name']),
subtitle: Image.Network('${_foundHerbs[index]["urlImage"]} '),
),
),
)
: const Text(
'No results found',
style: TextStyle(fontSize: 24),
),
),
],
),
),
);
}
}
Firstly you don't need to use another widget to handle tap-event. ListTile already have onTap: callback method.
Secondly, There are two issue on yoursubtitle, Image.Network will be Image.network and providing path having extra space on url-part, or you can just use likeImage.network( _foundHerbs[index]["urlImage"],),.
ListTile will be like
child: ListTile(
onTap: () {
print(_foundHerbs[index].toString());
},
leading: Text(
_foundHerbs[index]["id"].toString(),
style: const TextStyle(fontSize: 24),
),
title: Text(_foundHerbs[index]['name']),
subtitle: Container(
color: Colors.amber,
child: Image.network(
'${_foundHerbs[index]["urlImage"]}',
),
)),
Validation is working well but incase of validation error, option to select on option is disabled.
DropdownButtonFormField(
isExpanded: true,
hint: Text('Gender'),
value: _selectedGender,
onChanged: (newValue) {
setState(() {
_selectedGender = newValue;
});
},
items: _gender.map((gender) {
return DropdownMenuItem(
child: new Text(gender),
value: gender,
);
}).toList(),
validator: (value) {
if (value == null)
return "Please select your gender";
return null;
},
),
Above code is in a page view,
my variables
List<String> _gender = ['Male', 'Female'];
String _selectedGender;
My entire Form code : just reduced to one field its very long
class SignUp extends StatefulWidget {
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
final List<GlobalKey<FormState>> _page = [
GlobalKey<FormState>(),
GlobalKey<FormState>(),
GlobalKey<FormState>(),
];
final _key = GlobalKey<ScaffoldState>();
List<String> _gender = ['Male', 'Female'];
String _selectedGender;
void changePage() {
if (currentPageValue < 3) {
if (_page[currentPageValue].currentState.validate()) {
setState(() {
currentPageValue += 1;
});
}
}
}
#override
Widget build(BuildContext context) {
var deviceSize = MediaQuery.of(context).size;
var deviceWidth = deviceSize.width;
return Scaffold(
key: _key,
backgroundColor: _backgroundColor,
appBar: AppBar(
backgroundColor: _backgroundColor,
leading: IconButton(
icon: Icon(
currentPageValue == 0 ? Icons.close : Icons.keyboard_backspace,
size: 20.0,
color: _headerColor,
),
onPressed: currentPageValue == 0
? () => Navigator.pop(context)
: () => back()),
centerTitle: true,
elevation: 0.0,
),
body: Form(
key: _page[0],
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: deviceWidth * 0.9,
height: dropDownHeight,
child: ButtonTheme(
child: DropdownButtonFormField(
isExpanded: true,
hint: Text('Gender'),
value: _selectedGender,
onChanged: (newValue) {
setState(() {
_selectedGender = newValue;
});
},
items: _gender.map((gender) {
return DropdownMenuItem(
child: new Text(gender),
value: gender,
);
}).toList(),
validator: (value) {
if (value == null) return "Please select your gender";
return null;
},
),
),
),
RaisedButton(
color: buttonColor,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10.0),
),
child: Text(
'Next',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
),
),
onPressed: () => changePage(),
),
],
),
),
),
);
Looking at your code, I strongly recommend you create 3 different Forms and validate them separately, so you don't have any problem regarding form state. To achieve this, you can just wrap the fields into their respective Form and be sure not to mix the forms nor have them one inside the other.
Below, an example based on your code and on what I have said:
class SignUp extends StatefulWidget {
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
final _page1 = GlobalKey<FormState>();
final _page2 = GlobalKey<FormState>();
final _page3 = GlobalKey<FormState>();
final _key = GlobalKey<ScaffoldState>();
int currentPageValue;
List<String> _gender = ['Male', 'Female'];
String _selectedGender;
void changePage(GlobalKey<FormState> page) {
if (currentPageValue < 3) {
if (page.currentState.validate()) {
setState(() {
currentPageValue += 1;
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
body: ListView(
children: <Widget>[
Form(
key: _page1,
child: Column(
children: [
DropdownButtonFormField(
isExpanded: true,
hint: Text('Gender'),
value: _selectedGender,
onChanged: (newValue) {
setState(() {
_selectedGender = newValue;
});
},
items: _gender.map((gender) {
return DropdownMenuItem(
child: new Text(gender),
value: gender,
);
}).toList(),
validator: (value) {
if (value == null) return "Please select your gender";
return null;
},
),
RaisedButton(
child: Text('Next'),
onPressed: () => changePage(_page1),
),
],
),
),
Form(
key: _page2,
child: Column(
children: [
// SomeField(
//
// ),
// SomeOtherField(
//
// ),
RaisedButton(
child: Text('Next'),
onPressed: () => changePage(_page2),
),
],
),
),
],
),
);
}
}
I am having serious difficulty in sorting columns in my DataTable.
Here is my code
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
sortColumnIndex: 0,
sortAscending: true,
columnSpacing: 5.0,
horizontalMargin: 10,
columns: [
DataColumn(label: Text('Col1'),
DataColumn(label: Text('Col2'),numeric: false ),
DataColumn(label: Text('Col3'),numeric: true ),
DataColumn(label: Text('Col4'), numeric: true),
DataColumn(label: Text('Col5'), numeric: true),
],
rows: myList
.map(
((element) => DataRow(
cells: <DataCell>[
DataCell(Text(element["Col1"]), placeholder: true,showEditIcon: false),
DataCell(Text(element["Col2"].toString())),
DataCell(Text(element["Col3"].toString())),
DataCell(Text(element["Col4"].toString())),
DataCell(Text(element["Col5"].toString())),
],
)),
).toList(),
),
));
What more am I meant to do? Ideally, I'd like to have the sort function on whichever column the user presses on, but for now, I'd be delighted with just 1 column working being sortable.
As you can see, I have added the constraints:
sortColumnIndex: 0,
sortAscending: true,
But all I get is an arrow near 1st column (ColumnIndex = 0) - which really does nothing when you press it.
What am I doing wrong?
You have to add an onSort property to the column that will be sorted, and call a sorting function.
bool sort = true; // or `false`...
DataTable(
sortAscending: sort,
sortColumnIndex: 0,
columns: [
DataColumn(
label: Text("Col1"),
onSort: (columnIndex, ascending) {
setState(() {
sort = !sort;
});
onSortColum(columnIndex, ascending);
}),
...
And the onSortColum function:
onSortColum(int columnIndex, bool ascending) {
if (columnIndex == 0) {
if (ascending) {
yourDataList.sort((a, b) => a['name'].compareTo(b['name']));
} else {
yourDataList.sort((a, b) => b['name'].compareTo(a['name']));
}
}
}
The DataTable should be part of StatefulWidget
The StatefulWidget is needed re-render the widget whenever the sort is required on different column
To trigger re-render build(BuildContext context) method, we should add setState(() {} method inside the onSort: (columnIndex, ascending) {} like below
DataColumn(
label: const Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
onSort: (columnIndex, ascending) {
print("columnIndex:$columnIndex");
print("ascending:$ascending");
setState(() {
sortColumnIndex = columnIndex;
isAscending = ascending;
});
onSort: (columnIndex, ascending) {} will be called every time the column header is clicked.
DataTable should have state variable e.g. sortColumnIndex, so that every sorting click on different column will reset this index, and corresponding index column will get arrow Icon.
child: DataTable(
border: TableBorder.symmetric(),
sortAscending: isAscending,
sortColumnIndex: sortColumnIndex,
isAscending will be used to revert the arrow, if the same column pressed again.
Similar to sortColumnIndex state variable; we have to sort the table data manually so that new sorted values will be rendered after the click.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
var isAscending = true;
var sortColumnIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(
border: TableBorder.symmetric(),
sortAscending: isAscending,
sortColumnIndex: sortColumnIndex,
columns: <DataColumn>[
DataColumn(
label: const Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
onSort: (columnIndex, ascending) {
print("columnIndex:$columnIndex");
print("ascending:$ascending");
setState(() {
sortColumnIndex = columnIndex;
isAscending = ascending;
});
},
),
DataColumn(
label: const Text(
'Age',
style: TextStyle(fontStyle: FontStyle.italic),
),
onSort: (columnIndex, ascending) {
print("columnIndex:$columnIndex");
print("ascending:$ascending");
setState(() {
sortColumnIndex = columnIndex;
isAscending = ascending;
});
},
),
DataColumn(
label: const Text(
'Role',
style: TextStyle(fontStyle: FontStyle.italic),
),
onSort: (columnIndex, ascending) {
print("columnIndex:$columnIndex");
print("ascending:$ascending");
setState(() {
sortColumnIndex = columnIndex;
isAscending = ascending;
});
},
),
],
rows: const <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('Janine')),
DataCell(Text('43')),
DataCell(Text('Professor')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('William')),
DataCell(Text('27')),
DataCell(Text('Associate Professor')),
],
),
],
),
),
);
}
}
I've created on the dropdown and i want to validate that dropdown. when a user doesn't select an item from drop-down then it will show a validation.
here is some code I've tried so far.
in this code I've created one drop down when I am click submit then it will show a validation when the user doesn't select an item from the dropdown.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Dropdown Validation',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> _type = <String>[
'Passport',
'Driving License',
'Voter ID card',
'Ration Card',
'Aadhar',
'Other Id'
];
String type = 'Local Goverment';
Widget _buildlicenseissuingauthority() {
return FormField(
builder: (FormFieldState state) {
return InputDecorator(
decoration: InputDecoration(
prefixIcon: Icon(Icons.location_on),
labelText: 'Select Personal Identification type',
),
isEmpty: type == '',
child: new DropdownButtonHideUnderline(
child: new DropdownButton(
value: type,
isDense: true,
onChanged: (String newValue) {
setState(() {
type = newValue;
});
},
items: _type.map(
(String value) {
return new DropdownMenuItem(
value: value,
child: new Text(value),
);
},
).toList(),
),
),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[_buildlicenseissuingauthority()],
),
),
);
}
}
please use pacakge https://pub.dev/packages/flutter_form_builder
github https://github.com/danvick/flutter_form_builder
It has more than 10 buildin validators, in your case FormBuilderValidators.required()
code snippet
FormBuilderDropdown(
attribute: "gender",
decoration: InputDecoration(
labelText: "Gender",
icon: const Icon(Icons.category),
hintText: "Select hint",
),
// initialValue: 'Male',
onChanged: _onChanged,
hint: Text('Select Gender'),
validators: [FormBuilderValidators.required()],
items: ['Male', 'Female', 'Other']
.map((gender) => DropdownMenuItem(
value: gender,
child: Text('$gender'),
))
.toList(),
)
full example code
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:intl/intl.dart';
import './data.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter FormBuilder Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
MyHomePageState createState() {
return MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
var data;
bool autoValidate = true;
bool readOnly = false;
bool showSegmentedControl = true;
final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>();
final GlobalKey<FormFieldState> _specifyTextFieldKey =
GlobalKey<FormFieldState>();
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
ValueChanged _onChanged = (val) => print(val);
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("FormBuilder Example"),
),
body: Padding(
padding: EdgeInsets.all(10),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
FormBuilder(
// context,
key: _fbKey,
autovalidate: true,
// readonly: true,
child: Column(
children: <Widget>[
FormBuilderCustomField(
attribute: "name",
validators: [
FormBuilderValidators.required(),
],
formField: FormField(
// key: _fieldKey,
enabled: true,
builder: (FormFieldState<dynamic> field) {
return InputDecorator(
decoration: InputDecoration(
icon: const Icon(Icons.laptop),
hintText: "Select hint",
labelText: "Select option",
contentPadding:
EdgeInsets.only(top: 10.0, bottom: 0.0),
border: InputBorder.none,
errorText: field.errorText,
),
child: DropdownButton(
isExpanded: true,
items: ["One", "Two"].map((option) {
return DropdownMenuItem(
child: Text("$option"),
value: option,
);
}).toList(),
value: field.value,
onChanged: (value) {
field.didChange(value);
},
),
);
},
),
),
FormBuilderChipsInput(
decoration: InputDecoration(
labelText: "Chips",
icon: const Icon(Icons.person),
hintText: "Select hint",
),
attribute: 'chips_test',
// readonly: true,
onChanged: _onChanged,
// valueTransformer: (val) => val.length > 0 ? val[0] : null,
initialValue: [
Contact('Andrew', 'stock#man.com',
'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),
],
maxChips: 5,
findSuggestions: (String query) {
if (query.length != 0) {
var lowercaseQuery = query.toLowerCase();
return mockResults.where((profile) {
return profile.name
.toLowerCase()
.contains(query.toLowerCase()) ||
profile.email
.toLowerCase()
.contains(query.toLowerCase());
}).toList(growable: false)
..sort((a, b) => a.name
.toLowerCase()
.indexOf(lowercaseQuery)
.compareTo(b.name
.toLowerCase()
.indexOf(lowercaseQuery)));
} else {
return const <Contact>[];
}
},
chipBuilder: (context, state, profile) {
return InputChip(
key: ObjectKey(profile),
label: Text(profile.name),
avatar: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
onDeleted: () => state.deleteChip(profile),
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
);
},
suggestionBuilder: (context, state, profile) {
return ListTile(
key: ObjectKey(profile),
leading: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
title: Text(profile.name),
subtitle: Text(profile.email),
onTap: () => state.selectSuggestion(profile),
);
},
),
FormBuilderDateTimePicker(
attribute: "date",
onChanged: _onChanged,
inputType: InputType.date,
format: DateFormat("yyyy-MM-dd"),
decoration: InputDecoration(
labelText: "Appointment Time",
icon: const Icon(Icons.calendar_today),
hintText: "Select hint",
),
// readonly: true,
),
FormBuilderSlider(
attribute: "slider",
validators: [FormBuilderValidators.min(6)],
onChanged: _onChanged,
min: 0.0,
max: 10.0,
initialValue: 1.0,
divisions: 20,
decoration: InputDecoration(
labelText: "Number of somethings",
icon: const Icon(Icons.snooze),
hintText: "Select hint",
),
),
FormBuilderCheckbox(
decoration: InputDecoration(
labelText: "terms",
icon: const Icon(Icons.subject),
hintText: "Select hint",
),
attribute: 'accept_terms',
initialValue: false,
onChanged: _onChanged,
leadingInput: true,
label: Text(
"I have read and agree to the terms and conditions"),
validators: [
FormBuilderValidators.requiredTrue(
errorText:
"You must accept terms and conditions to continue",
),
],
),
FormBuilderDropdown(
attribute: "gender",
decoration: InputDecoration(
labelText: "Gender",
icon: const Icon(Icons.category),
hintText: "Select hint",
),
// initialValue: 'Male',
onChanged: _onChanged,
hint: Text('Select Gender'),
validators: [FormBuilderValidators.required()],
items: ['Male', 'Female', 'Other']
.map((gender) => DropdownMenuItem(
value: gender,
child: Text('$gender'),
))
.toList(),
),
FormBuilderTextField(
attribute: "age",
decoration: InputDecoration(
labelText: "Age",
icon: const Icon(Icons.accessibility),
hintText: "Select hint",
),
onChanged: _onChanged,
valueTransformer: (text) => num.tryParse(text),
validators: [
FormBuilderValidators.numeric(),
FormBuilderValidators.max(70),
],
),
FormBuilderTypeAhead(
// initialValue: "Canada",
decoration: InputDecoration(
labelText: "Country",
icon: const Icon(Icons.golf_course),
hintText: "Select hint",
),
attribute: 'country',
onChanged: _onChanged,
itemBuilder: (context, country) {
return ListTile(
title: Text(country),
);
},
suggestionsCallback: (query) {
if (query.length != 0) {
var lowercaseQuery = query.toLowerCase();
return allCountries.where((country) {
return country
.toLowerCase()
.contains(lowercaseQuery);
}).toList(growable: false)
..sort((a, b) => a
.toLowerCase()
.indexOf(lowercaseQuery)
.compareTo(
b.toLowerCase().indexOf(lowercaseQuery)));
} else {
return allCountries;
}
},
),
FormBuilderRadio(
decoration: InputDecoration(
labelText: 'My chosen language',
icon: const Icon(Icons.language),
hintText: "Select hint",
),
attribute: "best_language",
leadingInput: true,
onChanged: _onChanged,
validators: [FormBuilderValidators.required()],
options: [
"Dart",
"Kotlin",
"Java",
"Swift",
"Objective-C"
]
.map((lang) => FormBuilderFieldOption(value: lang))
.toList(growable: false),
),
FormBuilderSegmentedControl(
decoration: InputDecoration(
labelText: "Movie Rating (Archer)",
icon: const Icon(Icons.crop_rotate),
hintText: "Select hint",
),
attribute: "movie_rating",
options: List.generate(5, (i) => i + 1)
.map(
(number) => FormBuilderFieldOption(value: number))
.toList(),
onChanged: _onChanged,
),
FormBuilderSwitch(
decoration: InputDecoration(
labelText: "conditions",
icon: const Icon(Icons.dashboard),
hintText: "Select hint",
),
label: Text('I Accept the tems and conditions'),
attribute: "accept_terms_switch",
initialValue: true,
onChanged: _onChanged,
),
FormBuilderStepper(
decoration: InputDecoration(
labelText: "Stepper",
icon: const Icon(Icons.account_box),
hintText: "Select hint",
),
attribute: "stepper",
initialValue: 10,
step: 1,
validators: [
(val) {
if (!_fbKey.currentState.fields["accept_terms_switch"]
.currentState.value &&
val >= 10) {
return "You can only put more than 10 if you've accepted terms";
}
}
],
),
FormBuilderRate(
decoration: InputDecoration(
labelText: "Rate this form",
icon: const Icon(Icons.branding_watermark),
hintText: "Select hint",
),
attribute: "rate",
iconSize: 32.0,
initialValue: 1,
max: 5,
onChanged: _onChanged,
),
FormBuilderCheckboxList(
decoration: InputDecoration(
labelText: "The language of my people",
icon: const Icon(Icons.collections),
hintText: "Select hint",
),
attribute: "languages",
initialValue: ["Dart"],
leadingInput: true,
options: [
FormBuilderFieldOption(value: "Dart"),
FormBuilderFieldOption(value: "Kotlin"),
FormBuilderFieldOption(value: "Java"),
FormBuilderFieldOption(value: "Swift"),
FormBuilderFieldOption(value: "Objective-C"),
],
onChanged: _onChanged,
),
FormBuilderCustomField(
attribute: 'custom',
valueTransformer: (val) {
if (val == "Other")
return _specifyTextFieldKey.currentState.value;
return val;
},
formField: FormField(
builder: (FormFieldState<String> field) {
var languages = [
"English",
"Spanish",
"Somali",
"Other"
];
return InputDecorator(
decoration: InputDecoration(
labelText: "What's your preferred language?",
icon: const Icon(Icons.bug_report),
hintText: "Select hint",
),
child: Column(
children: languages
.map(
(lang) => Row(
children: <Widget>[
Radio<dynamic>(
value: lang,
groupValue: field.value,
onChanged: (dynamic value) {
field.didChange(lang);
},
),
lang != "Other"
? Text(lang)
: Expanded(
child: Row(
children: <Widget>[
Text(
lang,
),
SizedBox(width: 20),
Expanded(
child: TextFormField(
key:
_specifyTextFieldKey,
),
),
],
),
),
],
),
)
.toList(growable: false),
),
);
},
),
),
FormBuilderSignaturePad(
decoration: InputDecoration(
labelText: "Signature",
icon: const Icon(Icons.card_travel),
hintText: "Select hint",
),
attribute: "signature",
// height: 250,
clearButtonText: "Start Over",
onChanged: _onChanged,
),
],
),
),
Row(
children: <Widget>[
Expanded(
child: MaterialButton(
color: Theme.of(context).accentColor,
child: Text(
"Submit",
style: TextStyle(color: Colors.white),
),
onPressed: () {
_fbKey.currentState.save();
if (_fbKey.currentState.validate()) {
print(_fbKey.currentState.value);
} else {
print(_fbKey.currentState.value);
print("validation failed");
_scaffoldKey.currentState.showSnackBar(
SnackBar(
content: Text('Validation failed'),
duration: Duration(seconds: 3),
));
}
},
),
),
SizedBox(
width: 20,
),
Expanded(
child: MaterialButton(
color: Theme.of(context).accentColor,
child: Text(
"Reset",
style: TextStyle(color: Colors.white),
),
onPressed: () {
_fbKey.currentState.reset();
},
),
),
],
),
],
),
),
),
);
}
}
Data.dart
class Contact {
final String name;
final String email;
final String imageUrl;
const Contact(this.name, this.email, this.imageUrl);
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is Contact &&
runtimeType == other.runtimeType &&
name == other.name;
#override
int get hashCode => name.hashCode;
#override
String toString() {
return name;
}
}
const allCountries = [
"Afghanistan",
"Albania",
"Algeria",
"American Samoa",
"Andorra",
"Angola",
"Anguilla",
];
const contacts = <Contact>[
Contact('Andrew', 'stock#man.com',
'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),
Contact('Paul', 'paul#google.com',
'https://mbtskoudsalg.com/images/person-stock-image-png.png'),
Contact('Fred', 'fred#google.com',
'https://media.istockphoto.com/photos/feeling-great-about-my-corporate-choices-picture-id507296326'),
Contact('Brian', 'brian#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
];
demo, after click submit button, you can see gender has red text "This field can not be empty"
Flutter already has its own Form DropDownButtom for this
List<String> typeNeg = [
"One",
"Two",
"Three",];
String dropdownValue = "One";
DropdownButtonFormField<String>(
value: dropdownValue,
hint: Text("Type of business"),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
validator: (String value) {
if (value?.isEmpty ?? true) {
return 'Please enter a valid type of business';
}
},
items: typeNeg
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onSaved: (val) => setState(() => _user.typeNeg = val),
),
I'm implementing a DataTable to show some data, to do this I'm using a PaginatedDataTable, so I can load and show my data, the problem is that my DataTable shows a CheckBox per row and I don't want it.
This is my current result:
I want remove these CheckBox but I not have idea how to do it.
Code:
ExpensesDataSource _expensesDataSource = ExpensesDataSource([expense]);
Widget getDataTable() {
return PaginatedDataTable(
header: Text('Despesas', style: TextStyle(color: Color(0xFF4C4C4C), fontWeight: FontWeight.bold, fontSize: 15),),
columns: <DataColumn>[
DataColumn(
label: Text("Data"),
numeric: false,
),
DataColumn(
label: Text("Descrição"),
numeric: false,
),
DataColumn(
label: Text("Total"),
numeric: false,
),
],
source: _expensesDataSource,
);
}
class ExpensesDataSource extends DataTableSource {
List<Expense> _expenses = <Expense>[];
int _selectedCount = 0;
ExpensesDataSource(List<Expense> listExpenses) {
this._expenses = listExpenses;
}
#override
DataRow getRow(int index) {
final Expense expense = _expenses[index];
return DataRow.byIndex(
index: index,
onSelectChanged: (bool value) {
print('Row selected: $value ${expense.name}');
},
cells: [
DataCell(Text(expense.date)),
DataCell(Text(expense.name)),
DataCell(Text(Utils.convert2currency(expense.total_amount)))
]
);
}
#override
// TODO: implement rowCount
int get rowCount => _expenses.length;
#override
bool get isRowCountApproximate => false;
#override
int get selectedRowCount => _selectedCount;
}
UPDATE
Available now on Stable channel.
It's possible if you are on master channel not on stable channel.
You have to add only one property to DataTable which is showCheckboxColumn to be false.
Your full code after edit will be
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Sample",
style: TextStyle(color: Colors.white),
),
body: Column(children: <Widget>[
DataTable(
showCheckboxColumn: false,
sortAscending: true,
columns: <DataColumn>[
DataColumn(
label: Text('Product name'),
),
DataColumn(
label: Text('Product Quantity'),
),
],
rows: items
.map(
(itemRow) => DataRow(
onSelectChanged: (bool selected) {
if (selected) {
//'row-selected: ${itemRow.index}'
}
},
cells: [
DataCell(
Text(itemRow.itemName),
showEditIcon: false,
placeholder: false,
),
DataCell(
Text(itemRow.itemQuantity),
showEditIcon: true,
placeholder: false,
//onTap: _getSelectedRowInfo,
),
],
),
)
.toList(),
)
]),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
));
}
}
Some flutter developer doesn't recommend change to master, but if no problem with you, you can change it using these commands:
flutter channel master
flutter upgrade
Currently only by omitting onSelectionChange: ... or passing null
instead of
onSelectChanged: (bool value) {
print('Row selected: $value ${expense.name}');
},
https://docs.flutter.io/flutter/material/DataRow/onSelectChanged.html
I delete these two of selected: and onSelectChanged: in DataRow.byIndex() and checkbox gone.
Remove the onSelectChanged and selected property from DataRow and add this on each data cell..
DataCell(
Text(
employee.lastName.toUpperCase(),
),
onTap: () {
print("Tapped " + employee.firstName);
// do whatever you want
},
),
You have to add only one property to DataTable which is showCheckboxColumn to be false.
Your full code after the edit will be:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Sample",
style: TextStyle(color: Colors.white),
),
body: Column(children: <Widget>[
DataTable(
showCheckboxColumn: false,
sortAscending: true,
columns: <DataColumn>[
DataColumn(
label: Text('Product name'),
),
DataColumn(
label: Text('Product Quantity'),
),
],
rows: items
.map(
(itemRow) => DataRow(
onSelectChanged: (bool selected) {
if (selected) {
//'row-selected: ${itemRow.index}'
}
},
cells: [
DataCell(
Text(itemRow.itemName),
showEditIcon: false,
placeholder: false,
),
DataCell(
Text(itemRow.itemQuantity),
showEditIcon: true,
placeholder: false,
//onTap: _getSelectedRowInfo,
),
],
),
)
.toList(),
)
]),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
));
}
}