Search Bar Layout with DataTable Flutter

5,530

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:

PaginatedDataTable with one filtered row only

It works also if the list is empty.

Share:
5,530
GrandMagus
Author by

GrandMagus

Updated on December 28, 2022

Comments

  • GrandMagus
    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.

    enter image description here

    But I wanted it to make it like this:

    enter image description here

    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
    GrandMagus about 3 years
    I 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
    Soorya almost 3 years
    Saved my day! Perfect & simple solution for DataTable search.