Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Search Bar Layout with DataTable Flutter

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!

like image 796
GrandMagus Avatar asked Apr 28 '26 10:04

GrandMagus


1 Answers

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.

like image 64
Arton Hoxha Avatar answered May 01 '26 00:05

Arton Hoxha