Search Bar Layout with DataTable Flutter
Okay after your comment i finally made it work like i think you want. The idea is to uses two lists instead of one and not using the List.generate
method because of that empty row. When you change the _searchResult
value you filter the userFiltered
list with the original values coming from the users
lists.
I used the flutter sample for DataTable with those edits and it works:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: MyStatelessWidget(),
),
);
}
}
class User{
String name;
int age;
String role;
User({this.name, this.age, this.role});
}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatefulWidget {
MyStatelessWidget({Key key}) : super(key: key);
@override
_MyStatelessWidgetState createState() => _MyStatelessWidgetState();
}
class _MyStatelessWidgetState extends State<MyStatelessWidget> {
List<User> users = [User(name: "Sarah", age: 19, role: "Student"), User(name: "Janine", age: 43, role: "Professor")];
List<User> usersFiltered = [];
TextEditingController controller = TextEditingController();
String _searchResult = '';
@override
void initState() {
super.initState();
usersFiltered = users;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Card(
child: new ListTile(
leading: new Icon(Icons.search),
title: new TextField(
controller: controller,
decoration: new InputDecoration(
hintText: 'Search', border: InputBorder.none),
onChanged: (value) {
setState(() {
_searchResult = value;
usersFiltered = users.where((user) => user.name.contains(_searchResult) || user.role.contains(_searchResult)).toList();
});
}),
trailing: new IconButton(
icon: new Icon(Icons.cancel),
onPressed: () {
setState(() {
controller.clear();
_searchResult = '';
usersFiltered = users;
});
},
),
),
),
DataTable(
columns: const <DataColumn>[
DataColumn(
label: Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Age',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Role',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
rows: List.generate(usersFiltered.length, (index) =>
DataRow(
cells: <DataCell>[
DataCell(Text(usersFiltered[index].name)),
DataCell(Text(usersFiltered[index].age.toString())),
DataCell(Text(usersFiltered[index].role)),
],
),
),
),
],
);
}
}
OLD POST:
I was looking for a way to filter a datatable and your problem fixed mine thanks ( i will try to help you now!). By using a PaginatedDataTable widget instead of DataTable i can achieve the result you want to. The idea is to filter the list before you pass it to the source property. This is a part of the code i used to filter my list. Inside the switch block i filter it to remove the other elements:
switch(filter){
case "Id d'expédition":
expeditionsList = expeditionsList.where((e) => e.expeditionId.toLowerCase() == stringToSearch.toLowerCase()).toList();
break;
}
return PaginatedDataTable(
showCheckboxColumn: false,
rowsPerPage: 5,
source: DataTableSourceExpedition(
expeditions: expeditionsList,
onRowClicked: (index) async {
await ExpeditionRowDialog.buildExpeditionRowDialog(
context, expeditionsList[index].expeditionsDetails)
.show();
},
header: Container(
width: 100,
child: Text("Expéditions"),
),
columns: [
DataColumn(
label: Text("Id d'expédition"), numeric: false, tooltip: "id"),
],
);
Then i need to pass the data to the table by using the source property which expects a DataTableSource object. I created a separate class which extends DataTableSource. I pass the filtered list as a parameter of this class and override the methods of the DataTableSource class:
class DataTableSourceExpedition extends DataTableSource {
List<Expedition> expeditions = List();
Function onRowClicked;
Function onDeleteIconClick;
final df = DateFormat('dd.MM.yyyy');
DataTableSourceExpedition({this.expeditions, this.onRowClicked,
this.onDeleteIconClick});
DataRow getRow(int index) {
final _expedition = expeditions[index];
return DataRow.byIndex(
index: index,
cells: <DataCell>[
DataCell(Text("${_expedition.expeditionId}")),
DataCell(IconButton(
icon: Icon(Icons.delete_forever, color: kReturnColor,),
onPressed: (){onDeleteIconClick(index);},
))
],
onSelectChanged: (b) => onRowClicked(index));
}
bool get isRowCountApproximate => false;
int get rowCount => expeditions.length;
int get selectedRowCount => 0;
}
Like this, i can get the only item filtered without the need of adding an empty row as you can see on the image below:
It works also if the list is empty.
GrandMagus
Updated on December 28, 2022Comments
-
GrandMagus over 1 year
I've made a simple search bar for my
DataTable
list, but the problem is I can't return just the item I search for but instead I get empty fields and the item I search for. I've tried various things, but I get the error that I need rows as much as I have columns, so this is the only way for now that I've made it to work.But I wanted it to make it like this:
Here is the code:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'models/vehicle.dart'; import 'services/vehicle_api.dart'; import 'models/vehicle_data_provider.dart'; class VehicleList extends StatefulWidget { @override _VehicleList createState() => _VehicleList(); } class _VehicleList extends State<VehicleList> { TextEditingController controller = TextEditingController(); String _searchResult = ''; _getPosts() async { HomePageProvider provider = Provider.of<HomePageProvider>(context, listen: false); var postsResponse = await fetchVehicles(); if (postsResponse.isSuccessful) { provider.setPostsList(postsResponse.data, notify: false); } else { provider.mergePostsList( postsResponse.data, ); } provider.setIsHomePageProcessing(false); } @override void initState() { _getPosts(); super.initState(); } @override Widget build(BuildContext context) { return Column( children: [ Card( child: new ListTile( leading: new Icon(Icons.search), title: new TextField( controller: controller, decoration: new InputDecoration( hintText: 'Search', border: InputBorder.none), onChanged: (value) { setState(() { _searchResult = value; }); }), trailing: new IconButton( icon: new Icon(Icons.cancel), onPressed: () { setState(() { controller.clear(); _searchResult = ''; }); }, ), ), ), Consumer<HomePageProvider>( builder: (context, vehicleData, child) { return Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Container( decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.all( Radius.circular(12.0), ), ), child: SingleChildScrollView( child: DataTable( columnSpacing: 30, columns: <DataColumn>[ DataColumn( numeric: false, label: Text( 'Friendly Name', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Licence Plate', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Delete', style: TextStyle(fontStyle: FontStyle.italic), ), ), ], rows: List.generate( vehicleData.postsList.length, (index) { VehicleData post = vehicleData.getPostByIndex(index); return post.licencePlate .toLowerCase() .contains(_searchResult) || '${post.model}' .toLowerCase() .contains(_searchResult) || '${post.make}' .toLowerCase() .contains(_searchResult) || post.type .toLowerCase() .contains(_searchResult) ? DataRow( cells: <DataCell>[ DataCell( Text('${post.friendlyName}'), ), DataCell( Text('${post.licencePlate}'), ), DataCell( IconButton( icon: Icon(Icons.delete), onPressed: () { vehicleData.deletePost(post); }, ), ), ], ) : DataRow( /// This is the part where I return empty rows with one row with the search bar results, so I assume this must me changed cells: <DataCell>[ DataCell(Text('')), DataCell(Text('')), DataCell(Text('')), ], ); }, ), ), ), ), ], ); }, ), ], ); } }
Can't seem to figure this one out. Thanks in advance for the help!
-
GrandMagus about 3 yearsI need to use
DataTable
in this case and I'm not sure how to pass the source. And not sure how to pass the search query back to the table to so it only displays the wanted item.. -
Soorya almost 3 yearsSaved my day! Perfect & simple solution for DataTable search.