问题
I have a page with 2 tabs in the TabBar, as it follows:
class SearchByCityOrPerson extends StatefulWidget {
SearchByCityOrPerson({Key key, this.title}) : super(key: key);
final String title;
@override
_SearchByCityOrPerson createState() => _SearchByCityOrPerson();
}
class _SearchByCityOrPerson extends State<SearchByCityOrPerson> {
List<String> _cities = ['Albania', 'Andorra', 'Armenia', 'Austria',
'Azerbaijan', 'Belarus', 'Belgium', 'Bosnia and Herzegovina', 'Bulgaria',
'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland',
'France', 'Georgia', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland',
'Italy', 'Kazakhstan', 'Kosovo', 'Latvia', 'Liechtenstein', 'Lithuania',
'Luxembourg', 'Macedonia', 'Malta', 'Moldova', 'Monaco', 'Montenegro',
'Netherlands', 'Norway', 'Poland', 'Portugal', 'Romania', 'Russia',
'San Marino', 'Serbia', 'Slovakia', 'Slovenia', 'Spain', 'Sweden',
'Switzerland', 'Turkey', 'Ukraine', 'United Kingdom', 'Vatican City'];
List<String> _persons = ["John Smith", "Alex Johnson", "Jane Doe", "Eric Johnson", "Michael Eastwood", "Benjamin Woods"];
List<String> _filteredList = [];
TextEditingController controller = new TextEditingController();
String filter = "";
Icon actionIcon = new Icon(Icons.search);
Widget appBarTitle = new Text("Search...");
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
void initState() {
setState(() {
_filteredList = _cities;
});
controller.addListener(() {
if (controller.text.isEmpty) {
setState(() {
filter = "";
_filteredList = _cities;
});
} else {
setState(() {
filter = controller.text;
});
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
ListTile personListTile(String bookOrPerson) =>
ListTile(
title: Text(
bookOrPerson,
style: TextStyle(
color: Colors.black45, fontWeight: FontWeight.bold),
),);
Card personCard(bookOrPeson) =>
Card(
child: Container(
decoration: BoxDecoration(color: Colors.grey[300]),
child: personListTile(bookOrPeson),
),
);
if ((filter.isNotEmpty)) {
List<String> tmpList = new List<String>();
for (int i = 0; i < _filteredList.length; i++) {
if (_filteredList[i].toLowerCase().contains(
filter.toLowerCase())) {
tmpList.add(_filteredList[i]);
}
}
_filteredList = tmpList;
}
final appBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _cities == null ? 0 : _filteredList.length,
itemBuilder: (BuildContext context, int index) {
return personCard(_filteredList[index]);
},
),
);
final appTopAppBar = AppBar(
elevation: 0.1,
bottom: TabBar(
tabs: [
GestureDetector(
child: Text("City"),
onTap: (){
_filteredList = _cities;
},
),
GestureDetector(
child: Text("Person"),
onTap: (){
_filteredList = _persons;
},
),
]
),
title: appBarTitle,
actions: <Widget>[
new IconButton(
icon: actionIcon,
onPressed: () {
setState(() {
if (this.actionIcon.icon == Icons.search) {
this.actionIcon = new Icon(Icons.close);
this.appBarTitle = new TextField(
controller: controller,
decoration: new InputDecoration(
/*prefixIcon: new Icon(Icons.search, color: Colors.white),*/
hintText: "Search...",
hintStyle: new TextStyle(color: Colors.white),
),
style: new TextStyle(
color: Colors.white,
),
autofocus: true,
cursorColor: Colors.white,
);
} else {
this.actionIcon = new Icon(Icons.search);
this.appBarTitle = new Text("Cities");
_filteredList = _cities;
controller.clear();
}
});
},
),
],
);
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: appTopAppBar,
body: appBody,
),
);
}
}
As you may have seen in the screen shoots the problem is whe the secon tab "Search by Person" option is tabed, it should show all the list of persons and allow the user to search by person name, I tried to achieve it in the on tab method of each tabs but it needs to be refreseh somehow and I suppose there is a better way to implement it, how should I implement it taking into account that the lists _cities and _persons will be loaded in a future doing an api call.
回答1:
Here the thing I did after going through an hour
- Added a TabBar View in Body to show person lists in second tabs.
- for search, honestly, I did nothing .. I just added the same code you did but with person lists.
- I removed DefaultTabController and added controller for an index, so that you can get a different title for different TabBar.
Here's the code
class SearchByCityOrPerson extends StatefulWidget {
SearchByCityOrPerson({Key key, this.title}) : super(key: key);
final String title;
@override
_SearchByCityOrPerson createState() => _SearchByCityOrPerson();
}
class _SearchByCityOrPerson extends State<SearchByCityOrPerson> with SingleTickerProviderStateMixin {
List<String> _cities = ['Albania', 'Andorra', 'Armenia', 'Austria',
'Azerbaijan', 'Belarus', 'Belgium', 'Bosnia and Herzegovina', 'Bulgaria',
'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland',
'France', 'Georgia', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland',
'Italy', 'Kazakhstan', 'Kosovo', 'Latvia', 'Liechtenstein', 'Lithuania',
'Luxembourg', 'Macedonia', 'Malta', 'Moldova', 'Monaco', 'Montenegro',
'Netherlands', 'Norway', 'Poland', 'Portugal', 'Romania', 'Russia',
'San Marino', 'Serbia', 'Slovakia', 'Slovenia', 'Spain', 'Sweden',
'Switzerland', 'Turkey', 'Ukraine', 'United Kingdom', 'Vatican City'];
List<String> _persons = ["John Smith", "Alex Johnson", "Jane Doe", "Eric Johnson", "Michael Eastwood", "Benjamin Woods"];
List<String> _filteredList = [];
List<String> _personsList = [];
TextEditingController controller = new TextEditingController();
TabController _tabController;
String filter = "";
String persons = "";
Icon actionIcon = new Icon(Icons.search);
Widget appBarTitle = new Text("Search...");
void _handleTabIndex() {
setState(() {});
}
@override
void dispose() {
controller.dispose();
_tabController.removeListener(_handleTabIndex);
_tabController.dispose();
super.dispose();
}
@override
void initState() {
_tabController = TabController(length: 2, vsync: this, initialIndex: 0);
_tabController.addListener(_handleTabIndex);
setState(() {
_filteredList = _cities;
_personsList = _persons;
});
controller.addListener(() {
if (controller.text.isEmpty) {
setState(() {
filter = "";
persons = "";
_filteredList = _cities;
_personsList = _persons;
});
} else {
setState(() {
filter = controller.text;
persons = controller.text;
});
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
ListTile personListTile(String bookOrPerson) =>
ListTile(
title: Text(
bookOrPerson,
style: TextStyle(
color: Colors.black45, fontWeight: FontWeight.bold),
),);
Card personCard(bookOrPerson) => Card(
child: Container(
decoration: BoxDecoration(color: Colors.grey[300]),
child: personListTile(bookOrPerson),
),
);
if ((filter.isNotEmpty)) {
List<String> tmpList = new List<String>();
for (int i = 0; i < _filteredList.length; i++) {
if (_filteredList[i].toLowerCase().contains(
filter.toLowerCase())) {
tmpList.add(_filteredList[i]);
}
}
_filteredList = tmpList;
}
if ((persons.isNotEmpty)) {
List<String> _tmpList2 = new List<String>();
for (int i = 0; i < _personsList.length; i++) {
if (_personsList[i].toLowerCase().contains(
persons.toLowerCase())) {
_tmpList2.add(_personsList[i]);
}
}
_personsList = _tmpList2;
}
final appBody = TabBarView(
controller: _tabController,
children: [
Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _cities == null ? 0 : _filteredList.length,
itemBuilder: (BuildContext context, int index) {
return personCard(_filteredList[index]);
},
),
),
Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _persons == null ? 0 : _personsList.length,
itemBuilder: (BuildContext context, int index) {
return personCard(_personsList[index]);
},
),
),
]);
final appTopAppBar = AppBar(
elevation: 0.1,
bottom: TabBar(
controller: _tabController,
tabs: [
Tab( text: "City"),
Tab(text: "person")
]
),
title: appBarTitle,
actions: <Widget>[
new IconButton(
icon: actionIcon,
onPressed: () {
setState(() {
if (this.actionIcon.icon == Icons.search) {
this.actionIcon = new Icon(Icons.close);
this.appBarTitle = new TextField(
controller: controller,
decoration: new InputDecoration(
/*prefixIcon: new Icon(Icons.search, color: Colors.white),*/
hintText: "Search...",
hintStyle: new TextStyle(color: Colors.white),
),
style: new TextStyle(
color: Colors.white,
),
autofocus: true,
cursorColor: Colors.white,
);
} else {
this.actionIcon = new Icon(Icons.search);
this.appBarTitle = new Text(_tabController.index == 0 ? "Cities" : "Persons");
_filteredList = _cities;
_personsList = _persons;
controller.clear();
}
});
},
),
],
);
return
Scaffold(
appBar: appTopAppBar,
body: appBody,
);
}
}
来源:https://stackoverflow.com/questions/62392702/how-to-implement-the-search-action-for-each-tab-in-tabbar