I trying to implement rows Search on Data Table on my flutter web app using TextEditingController. The data comes from The API as Json formte using Employee model. I used FutureBuilder to get the data from the API. And i inserted Snapshot data into 'empList' as List. Also Created empsFiltered List to show search filtered data.
The issue is: Unable to show actual data in the datatable on startup. But the data is shown while searching and after clearing the searchtextfield.
I want to show the actual data on startup. And also the data should be shown as searched.
How to do this.
class EditorHome extends StatefulWidget {
const EditorHome({Key? key}) : super(key: key);
@override
_EditorHomeState createState() => _EditorHomeState();
}
class _EditorHomeState extends State<EditorHome> {
TextEditingController searchController = TextEditingController();
String _searchResult = '';
List empList = [];
List empsFiltered = [];
@override
void initState() {
super.initState();
empsFiltered = empList;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Table Example with search'),
),
body: SingleChildScrollView(
child: Column(
children: [
Card(
child: ListTile(
leading: const Icon(Icons.search),
title: TextField(
controller: searchController,
decoration: const InputDecoration(
hintText: 'Search', border: InputBorder.none),
onChanged: (value) {
setState(() {
_searchResult = value;
empsFiltered = empList
.where((e) =>
e.name.contains(_searchResult.toLowerCase()) ||
e.email.contains(_searchResult.toLowerCase()))
.toList();
});
}),
trailing: IconButton(
icon: const Icon(Icons.cancel),
onPressed: () {
setState(() {
searchController.clear();
_searchResult = '';
empsFiltered = empList;
});
},
),
),
),
FutureBuilder<List<Employees>>(
//initialData: const <Employees>[],
future: fetchResults(),
builder: (context, snapshot) {
if (snapshot.hasError ||
snapshot.data == null ||
snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
empList = snapshot.data!;
return DataTable(
headingTextStyle: const TextStyle(
fontWeight: FontWeight.bold, color: Colors.black),
headingRowHeight: 50,
showBottomBorder: true,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1)),
columns: const [
DataColumn(label: SizedBox(width: 30, child: Text('ID'))),
DataColumn(
label: SizedBox(width: 100, child: Text('Name'))),
DataColumn(
label: SizedBox(width: 100, child: Text('Email'))),
],
rows: List.generate(
empsFiltered.length,
(index) {
var emp = empsFiltered[index];
return DataRow(cells: [
DataCell(
Text(emp.id.toString()),
),
DataCell(
Text(emp.name),
),
DataCell(
Text(emp.email),
),
]);
},
).toList(),
);
},
),
],
),
),
);
}
}
Below is my API:
Future<List<Employees>> fetchResults() async {
//List<Employees> _results = [];
Uri url = Uri.parse(" http:link ");
var response = await http.get(url);
var resultsJson = json.decode(response.body).cast<Map<String, dynamic>>();
List<Employees> emplist = await resultsJson
.map<Employees>((json) => Employees.fromJson(json))
.toList();
return emplist;
}
I got a solution. In this Case I used Two StatefulWidgets. One is for calling Future with fetchResults() and converted into a List, And second is for Table with Search filter, And called that first stateFulWidget List into a variable on second StatefulWidgets and set that List variable in initState as into empFiltered List. Working Fine.
Example Code:
class EditorHome extends StatefulWidget {
const EditorHome({Key? key}) : super(key: key);
@override
_EditorHomeState createState() => _EditorHomeState();
}
class _EditorHomeState extends State<EditorHome> {
List empList = [];
@override
Widget build(BuildContext context) {
return FutureBuilder<List<Employees>>(
future: fetchResults(),
builder: (context, snapshot) {
if (snapshot.hasError ||
snapshot.data == null ||
snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
empList = snapshot.data!;
return TableSec(fempList: empList);
});
}
}
class TableSec extends StatefulWidget {
final List fempList;
const TableSec({Key? key, required this.fempList}) : super(key: key);
@override
_TableSecState createState() => _TableSecState();
}
class _TableSecState extends State<TableSec> {
late List empList = widget.fempList;
List empsFiltered = [];
TextEditingController searchController = TextEditingController();
String _searchResult = '';
@override
void initState() {
super.initState();
empsFiltered = empList;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Table"),
),
body: SingleChildScrollView(
child: Column(
children: [
Card(
child: ListTile(
leading: const Icon(Icons.search),
title: TextField(
controller: searchController,
decoration: const InputDecoration(
hintText: 'Search', border: InputBorder.none),
onChanged: (value) {
setState(() {
_searchResult = value;
empsFiltered = empList
.where((e) =>
e.name.contains(_searchResult.toLowerCase()) ||
e.email.contains(_searchResult.toLowerCase()))
.toList();
//print(_searchResult);
});
}),
trailing: IconButton(
icon: const Icon(Icons.cancel),
onPressed: () {
setState(() {
searchController.clear();
_searchResult = '';
empsFiltered = empList;
});
},
),
),
),
DataTable(
headingTextStyle: const TextStyle(
fontWeight: FontWeight.bold, color: Colors.black),
headingRowHeight: 50,
showBottomBorder: true,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1)),
columns: const [
DataColumn(label: SizedBox(width: 30, child: Text('ID'))),
DataColumn(label: SizedBox(width: 100, child: Text('Name'))),
DataColumn(label: SizedBox(width: 100, child: Text('Email'))),
],
rows: List.generate(
empsFiltered.length,
(index) {
var emp = empsFiltered[index];
return DataRow(cells: [
DataCell(
Text(emp.id.toString()),
),
DataCell(
Text(emp.name),
),
DataCell(
Text(emp.email),
),
]);
},
).toList(),
)
],
),
),
);
}
}