Flutter: How to select multiple options in checkboxlisttile - flutter

class PickLabelScreen extends StatefulWidget {
const PickLabelScreen({
Key? key,
required this.labelTitle,
}) : super(key: key);
final String labelTitle;
#override
State<PickLabelScreen> createState() => _PickLabelScreenState();
}
class _PickLabelScreenState extends State<PickLabelScreen> {
late String _labelChoosed;
#override
void initState() {
super.initState();
_labelChoosed = widget.labelTitle;
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
Navigator.of(context).pop(_labelChoosed);
return false;
},
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(
Icons.arrow_back,
),
onPressed: () {
Navigator.of(context).pop(_labelChoosed);
},
),
actions: [
IconButton(
onPressed: () async {
final String newLabel = await showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const DialogLabelWidget(),
);
setState(() {
if (newLabel.isNotEmpty) _labelChoosed = newLabel;
});
},
icon: const Icon(Icons.add),
),
],
),
body: Consumer<LabelProvider>(
builder: (context, labelProvider, child) =>
labelProvider.items.isEmpty
? child!
: ListView.builder(
itemBuilder: (context, index) {
final currentLabel = labelProvider.items[index];
return CheckboxListTile(
value: _labelChoosed == currentLabel.title,
title: Text(
currentLabel.title,
style: TextStyleConstants.titleStyle3,
),
secondary: const Icon(Icons.label_outline),
onChanged: (value) {
setState(() {
if (value == true) {
_labelChoosed = currentLabel.title;
} else {
_labelChoosed = '';
}
});
},
activeColor: ColorsConstant.blueColor,
);
},
itemCount: labelProvider.items.length,
),
child: const SizedBox.shrink(),
),
),
);
}
}
This is a flutter note app, and I am trying to label the notes.
Can anyone tell me how to select several options to label notes?
This code can create multiple options with dialoge, but cannot click them at once.
What I want is to create, select more than one checkbox, and save them to database.
Here is pick_label_screen.dart code.

Related

DropdowMenu does not show the selected Item

In the following code i can add and remove Tabs to the screen. For removing, i have defide a Button on the AppBar that after pressing it a DropdownMenu appears who let me select which Tab i want to remove and it removes the selected Item.
The problem that i have is that when i select a item DropdownMenu it does not show the selected item.
Thanks in advance for some help.
Follows the complete code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
String? selectedTab = tabs[0].text;
var tabName = "";
static List<Tab> tabs = [
const Tab(text: ""),
];
List<Widget> tabViewChildren = [
Container(
height: 400,
),
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 0,
length: tabs.length,
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Enter tab name"),
content: TextField(
onChanged: (String value) {
tabName = value;
},
),
actions: <Widget>[
ElevatedButton(
child: const Text("Add"),
onPressed: () {
setState(() {
tabs.add(Tab(text: tabName));
tabViewChildren.add(Container(height: 400));
});
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: const Icon(
Icons.add_box,
),
label: const Text('Add Tab'),
),
Opacity(
opacity: tabs.isNotEmpty ? 1 : 0.4,
child: ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Select tab to remove"),
content: tabs.isNotEmpty
? DropdownButton<String>(
items: tabs
.map((tab) => DropdownMenuItem<String>(
value: tab.text,
child: Text(tab.text ?? ""),
))
.toList(),
onChanged: (String? value) {
setState(() {
selectedTab = value;
});
},
value: selectedTab,
)
: Container(),
actions: <Widget>[
ElevatedButton(
child: const Text("Remove"),
onPressed: () {
setState(() {
int index = tabs.indexWhere((tab) => tab.text == selectedTab);
tabs.removeAt(index);
tabViewChildren.removeAt(index);
selectedTab = tabs.isNotEmpty ? tabs[0].text : null;
});
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: const Icon(Icons.remove),
label: const Text('Remove Tab'),
),
),
],
title: const Text("Tab in Flutter"),
bottom: TabBar(tabs: tabs),
),
body: TabBarView(children: tabViewChildren)));
}
}
The Problem:
Flutter works as a tree, each node has its own build context so showDialog is returning a build with a new build context, therefore in your code whenever you call setState in the dialog => you are calling the setState for the parent context (page), basically, you are updating the Screen widget not the dialog widget.
The Solution:
you have to use StatefulBuilder inside the Dialog widget so that it will have its own setState functionality. see the code below
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
String? selectedTab = tabs[0].text;
var tabName = "";
static List<Tab> tabs = [
const Tab(text: ""),
];
List<Widget> tabViewChildren = [
Container(
height: 400,
),
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 0,
length: tabs.length,
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Enter tab name"),
content: TextField(
onChanged: (String value) {
tabName = value;
},
),
actions: <Widget>[
ElevatedButton(
child: const Text("Add"),
onPressed: () {
setState(() {
tabs.add(Tab(text: tabName));
tabViewChildren.add(Container(height: 400));
});
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: const Icon(
Icons.add_box,
),
label: const Text('Add Tab'),
),
Opacity(
opacity: tabs.isNotEmpty ? 1 : 0.4,
child: ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: const Text("Select tab to remove"),
content: tabs.isNotEmpty
? DropdownButton<String>(
items: tabs
.map(
(tab) => DropdownMenuItem<String>(
value: tab.text,
child: Text(tab.text ?? ""),
))
.toList(),
onChanged: (String? value) {
selectedTab = value;
setState(() {});
},
value: selectedTab,
)
: Container(),
actions: <Widget>[
ElevatedButton(
child: const Text("Remove"),
onPressed: () {
setState(() {
int index = tabs.indexWhere(
(tab) => tab.text == selectedTab);
tabs.removeAt(index);
tabViewChildren.removeAt(index);
selectedTab =
tabs.isNotEmpty ? tabs[0].text : null;
});
Navigator.of(context).pop();
},
),
],
),
);
},
);
},
icon: const Icon(Icons.remove),
label: const Text('Remove Tab'),
),
),
],
title: const Text("Tab in Flutter"),
bottom: TabBar(tabs: tabs),
),
body: TabBarView(children: tabViewChildren)));
}
}

How to refresh a listview from another widget in flutter?

I am new to flutter, and I am trying to refresh my list of files after adding a file in my PDF app. Listview and add button I am using are in different widgets. Refresh function I have used is working for the delete button which is in the same widget as listview and gives error for the add button which is in a different widget from the List view. In both instances ,I am trying to update the List view.
`
class ListFiles extends StatefulWidget {
const ListFiles({Key? key}) : super(key: key);
#override
State<ListFiles> createState() => _ListFilesState();
}
class _ListFilesState extends State<ListFiles> {
final storage = FirebaseStorage.instance;
late Future<ListResult> futureFiles;
String pathPDF = "";
final GlobalKey<SfPdfViewerState> _pdfViewerKey = GlobalKey();
#override
void initState() {
super.initState();
futureFiles = FirebaseStorage.instance.ref('files').listAll();
}
#override
Widget build(BuildContext context) => SizedBox(
height: 450,
child: RefreshIndicator(
onRefresh: onRefresh,
child: FutureBuilder<ListResult>(
future: futureFiles,
builder: (context, snapshot) {
if (snapshot.hasData) {
final files = snapshot.data!.items;
return ListView.builder(
itemCount: files.length,
itemBuilder: (context, index) {
final file = files[index];
return ListTile(
title: Text(file.name),
onTap: () async {
String url = await getFirebaseDownUrl(file);
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) =>
Scaffold(
appBar: AppBar(
title: Text(file.name),
backgroundColor: Colors.red,
),
body: SfPdfViewer.network(
url,
key: _pdfViewerKey,
)
),
),
);
},
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
onPressed: () {
_dialogBuilder(context, file);
},
icon: const Icon(
Icons.delete,
color: Colors.red,
),
),
],
),
);
}
);
} else if (snapshot.hasError) {
return Column(
children: [
ListTile(
leading: const Icon(
Icons.error,
color: Colors.redAccent,
),
title: const Text('Error occurred'),
trailing: IconButton(
onPressed: () {
setState(() {
onRefresh();
});
},
icon: const Icon(
Icons.refresh,
color: Colors.blue,
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
),
);
Future<String> getFirebaseDownUrl(Reference ref) async {
print(await ref.getDownloadURL());
return ref.getDownloadURL();
}
Future downloadFile(Reference ref) async {
List<int> textBytes = utf8.encode('{$ref}');
Uint8List data = Uint8List.fromList(textBytes);
String mimeType = "application/pdf";
DocumentFileSavePlus.saveFile(data, ref.name, mimeType);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Downloaded ${ref.name} successfully')),
);
}
Future deleteFile(Reference ref) async {
// Create a reference to the file to delete
final storageRef = FirebaseStorage.instance.ref();
final deleteRef = storageRef.child(ref.fullPath);
// Delete the file
await deleteRef.delete();
// setState(() {
// futureFiles = FirebaseStorage.instance.ref('files').listAll();
// });
// build(context);
onRefresh();
// WidgetsBinding.instance.addPostFrameCallback((_) => setState(() {}));
ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Deleted file ${ref.name}')),
SnackBar(
content: Row(
children: [
Text('${ref.name} deleted successfully'),
const Spacer(
flex: 2,
),
TextButton(
onPressed: () {
onRefresh();
},
child: const Text(
'Refresh',
style: TextStyle(color: Colors.blue),
),
),
],
),
),
);
}
Future<void> _dialogBuilder(BuildContext context, Reference file) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Warning'),
content: const Text('Are you sure you want to delete the file?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('NO', style: TextStyle(color: Colors.green)),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
deleteFile(file);
},
child:
const Text('YES', style: TextStyle(color: Colors.redAccent)),
),
],
);
},
);
}
Future onRefresh() async {
print('Page refreshing...');
setState(() {
futureFiles = FirebaseStorage.instance.ref('files').listAll();
print('Status updated...');
});
build(context);
// ListFiles();
print('Page refreshed...');
}
}
class AddButton extends StatefulWidget {
const AddButton({Key? key}) : super(key: key);
#override
State<AddButton> createState() => _AddButtonState();
}
class _AddButtonState extends State<AddButton> {
PlatformFile? pickedFile;
UploadTask? uploadTask;
late Future<ListResult> futureFiles;
#override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: FloatingActionButton(
backgroundColor: const Color(0xFFFF1D1D),
foregroundColor: Colors.white,
onPressed: () async {
addFile();
},
child: const Icon(Icons.add),
),
),
],
);
}
Future addFile() async {
final result = await FilePicker.platform.pickFiles();
if (result == null) return;
setState(() {
pickedFile = result.files.first;
});
uploadFile();
}
Future uploadFile() async {
final path = 'files/${pickedFile!.name}';
final file = File(pickedFile!.path!);
final ref = FirebaseStorage.instance.ref().child(path);
final _ListFilesState Listfile = new _ListFilesState();
setState(() {
uploadTask = ref.putFile(file);
});
setState(() {
uploadTask = null;
});
if (pickedFile != null) {
addtoFirestore();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const Text('File uploaded successfully!'),
const Spacer(
flex: 2,
),
TextButton(
onPressed: () => Listfile.onRefresh(),
child: const Text(
'Refresh',
style: TextStyle(color: Colors.blue),
),
),
],
),
),
);
}
}
`
This is the response msg I get when refresh button of the snackbar is clicked.
enter image description here
Is this implementation correct?
Thank you for your time.
add a callback function to your ListFiles class:
setStateAddedNewClient( item ) {
setState(() {
futureFiles.add(item);
});
}
then add function parameter to your AddButton class:
final Function(dynamic) callback;
const AddButton({Key? key ,required this.callback }) : super(key: key);
and send file to callback function in your addFile function.
Future addFile() async {
final result = await FilePicker.platform.pickFiles();
if (result == null) return;
setState(() {
pickedFile = result.files.first;
callback(result.files.first);
});
uploadFile();
}
you can use a HookWidget
useEffect((() {
//request that you fetch data to listview
context.read<Cubit>().getListRequest();
return null;
}), [dependenciesFromAnotherWidgetToRefreshList]);

Call Function From Another Flutter Class

I would like to call function between another clas. So when the menu tapped from grabDrawer it will change the currentIndex at Main() class. Do you know how to do that? Here is so far I have tried.
main.dart
class _MainState extends State<Main> {
int currentIndex = 0;
Map<String,dynamic> searchParameter = {};
List screens = [
Home(),
Search({}),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
actions: [
Builder(builder: (context){
return IconButton(
onPressed: (){
Scaffold.of(context).openEndDrawer();
},
icon: const Icon(Icons.menu),
);
}),
],
),
endDrawer: const Drawer(
child:DrawerObject(),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () async{
await Future.delayed(Duration(milliseconds: 100),(){
globals.scrollController.animateTo(0, duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
});
},
),
body: screens[currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
onTap: (index) => setState(() {
if (index == 1) {
getSearchForm(context);
} else {
currentIndex = index;
searchParameter = {};
}
}),
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey[100],
type: BottomNavigationBarType.shifting,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.blue[500],
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Pencarian',
backgroundColor: Colors.orange[500],
),
],
),
);
}
//main function ===> NEED TO CALL THIS FUNCTION INSIDE grabDrawer.dart
Future UpdateIndex({int Index = 0}) async{
setState(() {
currentIndex = Index;
});
}
Future getSearchForm(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SearchForm(parameter:searchParameter)),
);
setState(() {
if (result != null) {
currentIndex = 1;
if(result!=searchParameter){
searchParameter = result;
screens[1] = CallLoading(show: ''); //set default to load
//set to new parameter (rebuilding widget)
Future.delayed(Duration(milliseconds: 500),(){
setState(() {
screens[1] = Search(searchParameter);
});
});
}
}
else{
}
});
}
}
Under this file, I need to call function from Main.UpdateIndex.
grabDrawer.dart
class DrawerObject extends StatelessWidget {
const DrawerObject({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.home),
title: Text('Cari Properti?'),
onTap: (){
===> CALL IT HERE
}
),
],
),
);
}
}
I really appreciate any answers. Thank you.
Change your grabDrawer.dart like this
class DrawerObject extends StatelessWidget {
void Function()? UpdateIndex;
DrawerObject({
this.UpdateIndex,
});
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.home),
title: Text('Cari Properti?'),
onTap: (){
UpdateIndex!();
}
),
],
),
);
}
}
And in your main.dart, call Drawer class like this
endDrawer: const Drawer(
child:DrawerObject(
UpdateIndex: UpdateIndex,
);
),
Hope this works for you.
Here is the clear way to pass data between one class to another class
void main() {
runApp(MaterialApp(
home: Modalbtn(),
));
}
class Modalbtn extends StatefulWidget {
#override
_ModalbtnState createState() => _ModalbtnState();
}
class _ModalbtnState extends State<Modalbtn> {
String value = "0";
// Pass this method to the child page.
void _update(String newValue) {
setState(() => value = newValue);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
IconButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
child: Column(
children: [StatefulModalbtn(update: _update)],
),
);
});
},
icon: Icon(Icons.add),
iconSize: 20,
),
Text(
value,
style: TextStyle(fontSize: 40),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
class StatefulModalbtn extends StatelessWidget {
final ValueChanged<String> update;
StatefulModalbtn({required this.update});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => update("100"), // Passing value to the parent widget.
child: Text('Update (in child)'),
);
}
}

The argument type 'Null' can't be assigned to the parameter type 'int'. (Flutter)

I am stuck on this one null error, I cannot fix this error that reads "error: The argument type 'Null' can't be assigned to the parameter type 'int'." The error shows up at the selected: null, line. I have added my code below hopefully someone can help. Let me know if any other code is needed.
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StudyPage(
title: 'Add a study',
selected: null,
)));
addition
import 'package:flutter/material.dart';
import 'package:timestudy_test/models/study.dart';
import 'package:timestudy_test/models/task.dart';
import 'package:timestudy_test/viewmodels/study_viewmodel.dart';
class StudyPage extends StatefulWidget {
final String title;
final int selected;
StudyPage({required this.title, required this.selected});
#override
State createState() => StudyPageState();
}
class StudyPageState extends State<StudyPage> {
late Study study;
late TextField nameField;
TextEditingController nameController = new TextEditingController();
late TextField taskNameField;
TextEditingController taskNameController = new TextEditingController();
#override
void initState() {
nameField = new TextField(
controller: nameController,
decoration: InputDecoration(
labelText: 'Study name'),
);
taskNameField = new TextField(
controller: taskNameController,
decoration:
InputDecoration(labelText: 'Task name'),
);
if(widget.selected != null) {
study = StudyViewModel.studies[widget.selected];
nameController.text = study.name;
} else {
study = new Study(
name: "",
tasks: <Task>[]
);
}
super.initState();
}
#override
void dispose() {
nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(widget.title),
),
body: Material(
child: Padding(padding: EdgeInsets.all(16.0), child: Column(
children: <Widget>[
Padding(padding: EdgeInsets.only(bottom: 8.0), child: nameField),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Tasks:', style: TextStyle(fontSize: 18.0),),
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Add a task'),
content: taskNameField,
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text('Accept'),
onPressed: () {
if(taskNameController.text == ""){
errorDialog(context, 'Please enter a task name!');
} else {
setState(() {
study.tasks.add(new Task(
name: taskNameController.text,
elapsedTime:
StudyViewModel.milliToElapsedString(
0)));
taskNameController.clear();
});
Navigator.of(context).pop();
}
},
),
],
);
});
},
)
],
),
Expanded(
child: ListView.builder(
itemCount: study.tasks.length,
itemBuilder: (context, int index) {
return ListTile(
title: Text(study.tasks[index].name),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
study.tasks.removeAt(index);
});
},
),
);
},
),
), Spacer(),
Center(
child: RaisedButton(
color: Theme.of(context).accentColor,
child: Text('Save'),
onPressed: () async {
if (nameController.text == "") {
errorDialog(context, 'Please enter a study name!');
} else {
if (study.tasks.length < 1) {
errorDialog(context, 'Please add at least one task!');
} else {
study.name = nameController.text;
if (widget.selected != null) {
StudyViewModel.studies[widget.selected] = study;
await StudyViewModel.saveFile();
Navigator.of(context).pop();
} else {
if (StudyViewModel.checkName(nameController.text)) {
errorDialog(context, 'Study name already taken!');
} else {
StudyViewModel.studies.add(study);
await StudyViewModel.saveFile();
Navigator.of(context).pop();
}
}
}
}
},
))
],
),
)));
}
void errorDialog(BuildContext context, String message) async {
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
}
);
}
}
you cant assign a value null to not Null Variable
int selected; <------- it only allow the int value such as 0,1,2,3, and goes on
if you try to pass value to this page you can check the value either it is an integer or a null value
int? nullableInterger;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StudyPage(
title: 'Add a study',
selected: nullableInterger ?? 0,// here 0 is const value you can chose whatever you want
)));
You have to specify the selected argument of StudyPage as nullable. To do so
edit this code:
class StudyPage extends StatefulWidget {
final String title;
final int selected;
StudyPage({required this.title, required this.selected});
#override
State createState() => StudyPageState();
}
And change it to
class StudyPage extends StatefulWidget {
final String title;
final int? selected;
StudyPage({required this.title, required this.selected});
#override
State createState() => StudyPageState();
}
You can't pass null to a nonnull property
class StudyPage extends StatefulWidget {
final String title;
final int selected;
.....
change to
class StudyPage extends StatefulWidget {
final String title;
final int? selected; <<<-----

showSearch with API

I am trying to implement the search feature and want to get the results from the API.
Under the method buildResults() you will find my comment // data is null but the problem is that I am getting data from the API call. Am I missing something here?
Under buildsResults() I am calling the Future _getResults and returning the received data. I logged the data which you can see.
class SearchBar extends StatefulWidget {
#override
_SearchBarState createState() => new _SearchBarState();
}
class _SearchBarState extends State<SearchBar> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: new IconThemeData(color: Theme.of(context).hintColor),
elevation: 1,
backgroundColor: Theme.of(context).primaryColor,
actions: <Widget>[
IconButton(
autofocus: true,
icon: Icon(Icons.search),
onPressed: () async {
final results = await showSearch<SearchModel>(context: context, delegate: DataSearch(context));
})
],
centerTitle: true,
title: Text('Search content'),
),
);
}
}
class DataSearch extends SearchDelegate<SearchModel> {
final BuildContext parentContext;
final Logger logger = new Logger();
DataSearch(this.parentContext);
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = "";
},
)
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
onPressed: () {
Navigator.pop(context);
Navigator.pop(parentContext);
},
);
}
#override
Widget buildResults(BuildContext context) {
return FutureBuilder<List<SearchModel>>(
future: _getResults(),
builder: (context, AsyncSnapshot<List<SearchModel>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
logger.d(snapshot.hasData);
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index].title),
onTap: () {
close(context, snapshot.data[index]);
},
);
},
itemCount: snapshot.data.length, // data is null
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
#override
Widget buildSuggestions(BuildContext context) {
return Container();
}
Future<List<SearchModel>> _getResults() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String language = prefs.getString('language');
var data;
await http.get(Constants.BASE_URL + "/search/" + language + "/" + query,).then((response) {
data = convert.jsonDecode(response.body) as List;
});
logger.d(data);
return data.map((model) => SearchModel.fromJson(model)).toList();
}
}
I think that's how it works:
onTap: () async {
final results = await showSearch(context: context, delegate: SearchBar(),query:query);
}
Result gets the return value
Query is the argument passed